mirror of
https://github.com/fastapi/fastapi.git
synced 2025-12-25 07:08:11 -05:00
Compare commits
134 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
40e33e492d | ||
|
|
b36047b54a | ||
|
|
7eadeb69bd | ||
|
|
55035f440b | ||
|
|
0903da78c9 | ||
|
|
4b2b14a8e8 | ||
|
|
35df20c79c | ||
|
|
8eb3c5621f | ||
|
|
2ada1615a3 | ||
|
|
3a5fd71f55 | ||
|
|
88d4f2cb18 | ||
|
|
0fc6e34135 | ||
|
|
2a4351105e | ||
|
|
ed66d70513 | ||
|
|
e50facaf22 | ||
|
|
93e50e373b | ||
|
|
4a94fe3c82 | ||
|
|
492943fdb1 | ||
|
|
c8e644d19e | ||
|
|
ba0bb6212e | ||
|
|
24b8f2668b | ||
|
|
212fd5e247 | ||
|
|
8dc882f751 | ||
|
|
b0eedbb580 | ||
|
|
74451189f6 | ||
|
|
a4c5f7f62f | ||
|
|
eb45bade63 | ||
|
|
944b6e507e | ||
|
|
e69ba26386 | ||
|
|
a4a7925045 | ||
|
|
73d4f347df | ||
|
|
80e2cd1274 | ||
|
|
bc715d55bc | ||
|
|
fc601bcb4b | ||
|
|
da4670cf77 | ||
|
|
a67167dce3 | ||
|
|
c49c4e7df8 | ||
|
|
270aef71c4 | ||
|
|
3a4431b6fe | ||
|
|
ec2a508292 | ||
|
|
b501fc6daf | ||
|
|
edb584199f | ||
|
|
4b9e5b3a74 | ||
|
|
b60d36e753 | ||
|
|
bde12faea2 | ||
|
|
74842f0a60 | ||
|
|
e68d8c60fb | ||
|
|
4ff22a0c41 | ||
|
|
a11e392f5f | ||
|
|
4633b1bca9 | ||
|
|
1b06b53267 | ||
|
|
c411b81c29 | ||
|
|
d86f660302 | ||
|
|
179f838c36 | ||
|
|
afdda4e50b | ||
|
|
e787f854dd | ||
|
|
7bad7c0975 | ||
|
|
965fc8301e | ||
|
|
999eeb6c76 | ||
|
|
96c7e7e0f3 | ||
|
|
8224addd8f | ||
|
|
b69e8b24af | ||
|
|
8e6cf9ee9c | ||
|
|
ccb19c4c35 | ||
|
|
0f3e65b007 | ||
|
|
832e634fd4 | ||
|
|
aa21814a89 | ||
|
|
7213d421f5 | ||
|
|
68e5ef6968 | ||
|
|
1f64a1bb55 | ||
|
|
9b2a9333b3 | ||
|
|
f42fd9aac2 | ||
|
|
cbdc58b1b7 | ||
|
|
3feed9dd8c | ||
|
|
560c43269d | ||
|
|
7eae925443 | ||
|
|
7d69943a22 | ||
|
|
56cfecc1bf | ||
|
|
e26229ed98 | ||
|
|
c1c57336b0 | ||
|
|
7537bac43f | ||
|
|
6b3d1c6d4e | ||
|
|
a6ad088183 | ||
|
|
b63b4189ee | ||
|
|
17f1f7b5bd | ||
|
|
92bdfbc7ba | ||
|
|
b203d7a15f | ||
|
|
23bda0ffeb | ||
|
|
d5c6cf8122 | ||
|
|
d08b95ea57 | ||
|
|
3660c7a063 | ||
|
|
5b7fa3900e | ||
|
|
8d7d89e8c6 | ||
|
|
08547e1d57 | ||
|
|
75c4e7fc44 | ||
|
|
581aacc4a9 | ||
|
|
47b3351be9 | ||
|
|
eebc6c3d54 | ||
|
|
83422b1923 | ||
|
|
0077af9719 | ||
|
|
6c8a205db1 | ||
|
|
c37f2c976d | ||
|
|
5827b922c3 | ||
|
|
1b5984b23b | ||
|
|
6ca7b8c608 | ||
|
|
a2458d594f | ||
|
|
9519380764 | ||
|
|
4eec14fa8c | ||
|
|
6e98249c21 | ||
|
|
17a29149e4 | ||
|
|
4cf3421178 | ||
|
|
ffa6d2eafd | ||
|
|
ae27540348 | ||
|
|
3332895251 | ||
|
|
9b35d355bf | ||
|
|
a930128910 | ||
|
|
cabed9efb6 | ||
|
|
48bf0db58f | ||
|
|
4909e44a7f | ||
|
|
be9abcf353 | ||
|
|
f41f6234af | ||
|
|
9416e89bd7 | ||
|
|
5fdbeed792 | ||
|
|
3a96938771 | ||
|
|
bd1b77548f | ||
|
|
b5cbff9521 | ||
|
|
c692176d42 | ||
|
|
d8e526c1db | ||
|
|
6aa44a85a2 | ||
|
|
e4727ed20a | ||
|
|
9656895b60 | ||
|
|
866c6987fc | ||
|
|
ead4f8c6a4 | ||
|
|
7ff5da8bf2 |
4
.github/labeler.yml
vendored
4
.github/labeler.yml
vendored
@@ -7,6 +7,8 @@ docs:
|
||||
- all-globs-to-all-files:
|
||||
- '!fastapi/**'
|
||||
- '!pyproject.toml'
|
||||
- '!docs/en/data/sponsors.yml'
|
||||
- '!docs/en/overrides/main.html'
|
||||
|
||||
lang-all:
|
||||
- all:
|
||||
@@ -28,6 +30,8 @@ internal:
|
||||
- .pre-commit-config.yaml
|
||||
- pdm_build.py
|
||||
- requirements*.txt
|
||||
- docs/en/data/sponsors.yml
|
||||
- docs/en/overrides/main.html
|
||||
- all-globs-to-all-files:
|
||||
- '!docs/*/docs/**'
|
||||
- '!fastapi/**'
|
||||
|
||||
1
.github/workflows/build-docs.yml
vendored
1
.github/workflows/build-docs.yml
vendored
@@ -113,6 +113,7 @@ jobs:
|
||||
with:
|
||||
name: docs-site-${{ matrix.lang }}
|
||||
path: ./site/**
|
||||
include-hidden-files: true
|
||||
|
||||
# https://github.com/marketplace/actions/alls-green#why
|
||||
docs-all-green: # This job does nothing and is only used for the branch protection
|
||||
|
||||
9
.github/workflows/issue-manager.yml
vendored
9
.github/workflows/issue-manager.yml
vendored
@@ -2,7 +2,7 @@ name: Issue Manager
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "10 3 * * *"
|
||||
- cron: "13 22 * * *"
|
||||
issue_comment:
|
||||
types:
|
||||
- created
|
||||
@@ -16,6 +16,7 @@ on:
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
issue-manager:
|
||||
@@ -26,7 +27,7 @@ jobs:
|
||||
env:
|
||||
GITHUB_CONTEXT: ${{ toJson(github) }}
|
||||
run: echo "$GITHUB_CONTEXT"
|
||||
- uses: tiangolo/issue-manager@0.5.0
|
||||
- uses: tiangolo/issue-manager@0.5.1
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
config: >
|
||||
@@ -35,8 +36,8 @@ jobs:
|
||||
"delay": 864000,
|
||||
"message": "Assuming the original need was handled, this will be automatically closed now. But feel free to add more comments or create new issues or PRs."
|
||||
},
|
||||
"changes-requested": {
|
||||
"waiting": {
|
||||
"delay": 2628000,
|
||||
"message": "As this PR had requested changes to be applied but has been inactive for a while, it's now going to be closed. But if there's anyone interested, feel free to create a new PR."
|
||||
"message": "As this PR has been waiting for the original user for a while but seems to be inactive, it's now going to be closed. But if there's anyone interested, feel free to create a new PR."
|
||||
}
|
||||
}
|
||||
|
||||
3
.github/workflows/latest-changes.yml
vendored
3
.github/workflows/latest-changes.yml
vendored
@@ -34,8 +34,7 @@ jobs:
|
||||
if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.debug_enabled == 'true' }}
|
||||
with:
|
||||
limit-access-to-actor: true
|
||||
- uses: docker://tiangolo/latest-changes:0.3.0
|
||||
# - uses: tiangolo/latest-changes@main
|
||||
- uses: tiangolo/latest-changes@0.3.1
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
latest_changes_file: docs/en/docs/release-notes.md
|
||||
|
||||
2
.github/workflows/publish.yml
vendored
2
.github/workflows/publish.yml
vendored
@@ -35,7 +35,7 @@ jobs:
|
||||
TIANGOLO_BUILD_PACKAGE: ${{ matrix.package }}
|
||||
run: python -m build
|
||||
- name: Publish
|
||||
uses: pypa/gh-action-pypi-publish@v1.9.0
|
||||
uses: pypa/gh-action-pypi-publish@v1.10.1
|
||||
- name: Dump GitHub context
|
||||
env:
|
||||
GITHUB_CONTEXT: ${{ toJson(github) }}
|
||||
|
||||
6
.github/workflows/test.yml
vendored
6
.github/workflows/test.yml
vendored
@@ -37,7 +37,7 @@ jobs:
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
run: pip install -r requirements-tests.txt
|
||||
- name: Install Pydantic v2
|
||||
run: pip install "pydantic>=2.0.2,<3.0.0"
|
||||
run: pip install --upgrade "pydantic>=2.0.2,<3.0.0"
|
||||
- name: Lint
|
||||
run: bash scripts/lint.sh
|
||||
|
||||
@@ -79,7 +79,7 @@ jobs:
|
||||
run: pip install "pydantic>=1.10.0,<2.0.0"
|
||||
- name: Install Pydantic v2
|
||||
if: matrix.pydantic-version == 'pydantic-v2'
|
||||
run: pip install "pydantic>=2.0.2,<3.0.0"
|
||||
run: pip install --upgrade "pydantic>=2.0.2,<3.0.0"
|
||||
- run: mkdir coverage
|
||||
- name: Test
|
||||
run: bash scripts/test.sh
|
||||
@@ -91,6 +91,7 @@ jobs:
|
||||
with:
|
||||
name: coverage-${{ matrix.python-version }}-${{ matrix.pydantic-version }}
|
||||
path: coverage
|
||||
include-hidden-files: true
|
||||
|
||||
coverage-combine:
|
||||
needs: [test]
|
||||
@@ -123,6 +124,7 @@ jobs:
|
||||
with:
|
||||
name: coverage-html
|
||||
path: htmlcov
|
||||
include-hidden-files: true
|
||||
|
||||
# https://github.com/marketplace/actions/alls-green#why
|
||||
check: # This job does nothing and is only used for the branch protection
|
||||
|
||||
@@ -14,7 +14,7 @@ repos:
|
||||
- id: end-of-file-fixer
|
||||
- id: trailing-whitespace
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: v0.6.1
|
||||
rev: v0.6.5
|
||||
hooks:
|
||||
- id: ruff
|
||||
args:
|
||||
|
||||
@@ -52,9 +52,8 @@ The key features are:
|
||||
<a href="https://bump.sh/fastapi?utm_source=fastapi&utm_medium=referral&utm_campaign=sponsor" target="_blank" title="Automate FastAPI documentation generation with Bump.sh"><img src="https://fastapi.tiangolo.com/img/sponsors/bump-sh.svg"></a>
|
||||
<a href="https://github.com/scalar/scalar/?utm_source=fastapi&utm_medium=website&utm_campaign=main-badge" target="_blank" title="Scalar: Beautiful Open-Source API References from Swagger/OpenAPI files"><img src="https://fastapi.tiangolo.com/img/sponsors/scalar.svg"></a>
|
||||
<a href="https://www.propelauth.com/?utm_source=fastapi&utm_campaign=1223&utm_medium=mainbadge" target="_blank" title="Auth, user management and more for your B2B product"><img src="https://fastapi.tiangolo.com/img/sponsors/propelauth.png"></a>
|
||||
<a href="https://docs.withcoherence.com/configuration/frameworks/?utm_medium=advertising&utm_source=fastapi&utm_campaign=docs#fastapi-example" target="_blank" title="Coherence"><img src="https://fastapi.tiangolo.com/img/sponsors/coherence.png"></a>
|
||||
<a href="https://www.withcoherence.com/?utm_medium=advertising&utm_source=fastapi&utm_campaign=website" target="_blank" title="Coherence"><img src="https://fastapi.tiangolo.com/img/sponsors/coherence.png"></a>
|
||||
<a href="https://www.mongodb.com/developer/languages/python/python-quickstart-fastapi/?utm_campaign=fastapi_framework&utm_source=fastapi_sponsorship&utm_medium=web_referral" target="_blank" title="Simplify Full Stack Development with FastAPI & MongoDB"><img src="https://fastapi.tiangolo.com/img/sponsors/mongodb.png"></a>
|
||||
<a href="https://konghq.com/products/kong-konnect?utm_medium=referral&utm_source=github&utm_campaign=platform&utm_content=fast-api" target="_blank" title="Kong Konnect - API management platform"><img src="https://fastapi.tiangolo.com/img/sponsors/kong.png"></a>
|
||||
<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://fine.dev?ref=fastapibadge" target="_blank" title="Fine's AI FastAPI Workflow: Effortlessly Deploy and Integrate FastAPI into Your Project"><img src="https://fastapi.tiangolo.com/img/sponsors/fine.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>
|
||||
@@ -394,7 +393,7 @@ Coming back to the previous code example, **FastAPI** will:
|
||||
* Check if there is an optional query parameter named `q` (as in `http://127.0.0.1:8000/items/foo?q=somequery`) for `GET` requests.
|
||||
* As the `q` parameter is declared with `= None`, it is optional.
|
||||
* Without the `None` it would be required (as is the body in the case with `PUT`).
|
||||
* For `PUT` requests to `/items/{item_id}`, Read the body as JSON:
|
||||
* For `PUT` requests to `/items/{item_id}`, read the body as JSON:
|
||||
* Check that it has a required attribute `name` that should be a `str`.
|
||||
* Check that it has a required attribute `price` that has to be a `float`.
|
||||
* Check that it has an optional attribute `is_offer`, that should be a `bool`, if present.
|
||||
|
||||
@@ -72,7 +72,7 @@ Beachten Sie, dass die Testfunktion jetzt `async def` ist und nicht nur `def` wi
|
||||
|
||||
Dann können wir einen `AsyncClient` mit der App erstellen und mit `await` asynchrone Requests an ihn senden.
|
||||
|
||||
```Python hl_lines="9-10"
|
||||
```Python hl_lines="9-12"
|
||||
{!../../../docs_src/async_tests/test_main.py!}
|
||||
```
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ $ pytest
|
||||
|
||||
⤴️ 👥 💪 ✍ `AsyncClient` ⏮️ 📱, & 📨 🔁 📨 ⚫️, ⚙️ `await`.
|
||||
|
||||
```Python hl_lines="9-10"
|
||||
```Python hl_lines="9-12"
|
||||
{!../../../docs_src/async_tests/test_main.py!}
|
||||
```
|
||||
|
||||
|
||||
@@ -264,6 +264,14 @@ Articles:
|
||||
author_link: https://devonray.com
|
||||
link: https://devonray.com/blog/deploying-a-fastapi-project-using-aws-lambda-aurora-cdk
|
||||
title: Deployment using Docker, Lambda, Aurora, CDK & GH Actions
|
||||
- author: Shubhendra Kushwaha
|
||||
author_link: https://www.linkedin.com/in/theshubhendra/
|
||||
link: https://theshubhendra.medium.com/mastering-soft-delete-advanced-sqlalchemy-techniques-4678f4738947
|
||||
title: 'Mastering Soft Delete: Advanced SQLAlchemy Techniques'
|
||||
- author: Shubhendra Kushwaha
|
||||
author_link: https://www.linkedin.com/in/theshubhendra/
|
||||
link: https://theshubhendra.medium.com/role-based-row-filtering-advanced-sqlalchemy-techniques-733e6b1328f6
|
||||
title: 'Role based row filtering: Advanced SQLAlchemy Techniques'
|
||||
German:
|
||||
- author: Marcel Sander (actidoo)
|
||||
author_link: https://www.actidoo.com
|
||||
|
||||
@@ -17,15 +17,12 @@ gold:
|
||||
- url: https://www.propelauth.com/?utm_source=fastapi&utm_campaign=1223&utm_medium=mainbadge
|
||||
title: Auth, user management and more for your B2B product
|
||||
img: https://fastapi.tiangolo.com/img/sponsors/propelauth.png
|
||||
- url: https://docs.withcoherence.com/configuration/frameworks/?utm_medium=advertising&utm_source=fastapi&utm_campaign=docs#fastapi-example
|
||||
- url: https://www.withcoherence.com/?utm_medium=advertising&utm_source=fastapi&utm_campaign=website
|
||||
title: Coherence
|
||||
img: https://fastapi.tiangolo.com/img/sponsors/coherence.png
|
||||
- url: https://www.mongodb.com/developer/languages/python/python-quickstart-fastapi/?utm_campaign=fastapi_framework&utm_source=fastapi_sponsorship&utm_medium=web_referral
|
||||
title: Simplify Full Stack Development with FastAPI & MongoDB
|
||||
img: https://fastapi.tiangolo.com/img/sponsors/mongodb.png
|
||||
- url: https://konghq.com/products/kong-konnect?utm_medium=referral&utm_source=github&utm_campaign=platform&utm_content=fast-api
|
||||
title: Kong Konnect - API management platform
|
||||
img: https://fastapi.tiangolo.com/img/sponsors/kong.png
|
||||
- url: https://zuplo.link/fastapi-gh
|
||||
title: 'Zuplo: Scale, Protect, Document, and Monetize your FastAPI'
|
||||
img: https://fastapi.tiangolo.com/img/sponsors/zuplo.png
|
||||
|
||||
@@ -72,7 +72,7 @@ Note that the test function is now `async def` instead of just `def` as before w
|
||||
|
||||
Then we can create an `AsyncClient` with the app, and send async requests to it, using `await`.
|
||||
|
||||
```Python hl_lines="9-10"
|
||||
```Python hl_lines="9-12"
|
||||
{!../../../docs_src/async_tests/test_main.py!}
|
||||
```
|
||||
|
||||
|
||||
@@ -4,9 +4,9 @@ By default, **FastAPI** will return the responses using `JSONResponse`.
|
||||
|
||||
You can override it by returning a `Response` directly as seen in [Return a Response directly](response-directly.md){.internal-link target=_blank}.
|
||||
|
||||
But if you return a `Response` directly, the data won't be automatically converted, and the documentation won't be automatically generated (for example, including the specific "media type", in the HTTP header `Content-Type` as part of the generated OpenAPI).
|
||||
But if you return a `Response` directly (or any subclass, like `JSONResponse`), the data won't be automatically converted (even if you declare a `response_model`), and the documentation won't be automatically generated (for example, including the specific "media type", in the HTTP header `Content-Type` as part of the generated OpenAPI).
|
||||
|
||||
But you can also declare the `Response` that you want to be used, in the *path operation decorator*.
|
||||
But you can also declare the `Response` that you want to be used (e.g. any `Response` subclass), in the *path operation decorator* using the `response_class` parameter.
|
||||
|
||||
The contents that you return from your *path operation function* will be put inside of that `Response`.
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ This gives you a lot of flexibility. You can return any data type, override any
|
||||
|
||||
## Using the `jsonable_encoder` in a `Response`
|
||||
|
||||
Because **FastAPI** doesn't do any change to a `Response` you return, you have to make sure it's contents are ready for it.
|
||||
Because **FastAPI** doesn't do any change to a `Response` you return, you have to make sure its contents are ready for it.
|
||||
|
||||
For example, you cannot put a Pydantic model in a `JSONResponse` without first converting it to a `dict` with all the data types (like `datetime`, `UUID`, etc) converted to JSON-compatible types.
|
||||
|
||||
|
||||
@@ -36,12 +36,12 @@ In this case, it will be mounted at the path `/subapi`:
|
||||
|
||||
### Check the automatic API docs
|
||||
|
||||
Now, run `uvicorn` with the main app, if your file is `main.py`, it would be:
|
||||
Now, run the `fastapi` command with your file:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ uvicorn main:app --reload
|
||||
$ fastapi dev main.py
|
||||
|
||||
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
|
||||
```
|
||||
|
||||
@@ -474,7 +474,7 @@ It is the recommended server for Starlette and **FastAPI**.
|
||||
|
||||
The main web server to run **FastAPI** applications.
|
||||
|
||||
You can combine it with Gunicorn, to have an asynchronous multi-process server.
|
||||
You can also use the `--workers` command line option to have an asynchronous multi-process server.
|
||||
|
||||
Check more details in the [Deployment](deployment/index.md){.internal-link target=_blank} section.
|
||||
|
||||
|
||||
@@ -292,7 +292,7 @@ For example:
|
||||
|
||||
### Concurrency + Parallelism: Web + Machine Learning
|
||||
|
||||
With **FastAPI** you can take the advantage of concurrency that is very common for web development (the same main attraction of NodeJS).
|
||||
With **FastAPI** you can take advantage of concurrency that is very common for web development (the same main attraction of NodeJS).
|
||||
|
||||
But you can also exploit the benefits of parallelism and multiprocessing (having multiple processes running in parallel) for **CPU bound** workloads like those in Machine Learning systems.
|
||||
|
||||
@@ -387,7 +387,7 @@ In previous versions of NodeJS / Browser JavaScript, you would have used "callba
|
||||
|
||||
## Coroutines
|
||||
|
||||
**Coroutine** is just the very fancy term for the thing returned by an `async def` function. Python knows that it is something like a function that it can start and that it will end at some point, but that it might be paused ⏸ internally too, whenever there is an `await` inside of it.
|
||||
**Coroutine** is just the very fancy term for the thing returned by an `async def` function. Python knows that it is something like a function, that it can start and that it will end at some point, but that it might be paused ⏸ internally too, whenever there is an `await` inside of it.
|
||||
|
||||
But all this functionality of using asynchronous code with `async` and `await` is many times summarized as using "coroutines". It is comparable to the main key feature of Go, the "Goroutines".
|
||||
|
||||
|
||||
@@ -170,7 +170,7 @@ If you run the examples with, e.g.:
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ uvicorn tutorial001:app --reload
|
||||
$ fastapi dev tutorial001.py
|
||||
|
||||
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
|
||||
```
|
||||
|
||||
@@ -14,4 +14,4 @@ You might want to try their services and follow their guides:
|
||||
|
||||
* <a href="https://docs.platform.sh/languages/python.html?utm_source=fastapi-signup&utm_medium=banner&utm_campaign=FastAPI-signup-June-2023" class="external-link" target="_blank">Platform.sh</a>
|
||||
* <a href="https://docs.porter.run/language-specific-guides/fastapi" class="external-link" target="_blank">Porter</a>
|
||||
* <a href="https://docs.withcoherence.com/docs/configuration/frameworks?utm_medium=advertising&utm_source=fastapi&utm_campaign=banner%20january%2024#fast-api-example" class="external-link" target="_blank">Coherence</a>
|
||||
* <a href="https://www.withcoherence.com/?utm_medium=advertising&utm_source=fastapi&utm_campaign=website" class="external-link" target="_blank">Coherence</a>
|
||||
|
||||
@@ -94,7 +94,7 @@ In most cases, when you create a web API, you want it to be **always running**,
|
||||
|
||||
### In a Remote Server
|
||||
|
||||
When you set up a remote server (a cloud server, a virtual machine, etc.) the simplest thing you can do is to use `fastapi run`, Uvicorn (or similar) manually, the same way you do when developing locally.
|
||||
When you set up a remote server (a cloud server, a virtual machine, etc.) the simplest thing you can do is use `fastapi run` (which uses Uvicorn) or something similar, manually, the same way you do when developing locally.
|
||||
|
||||
And it will work and will be useful **during development**.
|
||||
|
||||
@@ -178,7 +178,7 @@ For example, this could be handled by:
|
||||
|
||||
## Replication - Processes and Memory
|
||||
|
||||
With a FastAPI application, using a server program like Uvicorn, running it once in **one process** can serve multiple clients concurrently.
|
||||
With a FastAPI application, using a server program like the `fastapi` command that runs Uvicorn, running it once in **one process** can serve multiple clients concurrently.
|
||||
|
||||
But in many cases, you will want to run several worker processes at the same time.
|
||||
|
||||
@@ -232,9 +232,7 @@ The main constraint to consider is that there has to be a **single** component h
|
||||
|
||||
Here are some possible combinations and strategies:
|
||||
|
||||
* **Gunicorn** managing **Uvicorn workers**
|
||||
* Gunicorn would be the **process manager** listening on the **IP** and **port**, the replication would be by having **multiple Uvicorn worker processes**.
|
||||
* **Uvicorn** managing **Uvicorn workers**
|
||||
* **Uvicorn** with `--workers`
|
||||
* One Uvicorn **process manager** would listen on the **IP** and **port**, and it would start **multiple Uvicorn worker processes**.
|
||||
* **Kubernetes** and other distributed **container systems**
|
||||
* Something in the **Kubernetes** layer would listen on the **IP** and **port**. The replication would be by having **multiple containers**, each with **one Uvicorn process** running.
|
||||
|
||||
@@ -167,22 +167,22 @@ def read_item(item_id: int, q: Union[str, None] = None):
|
||||
Now in the same project directory create a file `Dockerfile` with:
|
||||
|
||||
```{ .dockerfile .annotate }
|
||||
# (1)
|
||||
# (1)!
|
||||
FROM python:3.9
|
||||
|
||||
# (2)
|
||||
# (2)!
|
||||
WORKDIR /code
|
||||
|
||||
# (3)
|
||||
# (3)!
|
||||
COPY ./requirements.txt /code/requirements.txt
|
||||
|
||||
# (4)
|
||||
# (4)!
|
||||
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
|
||||
|
||||
# (5)
|
||||
# (5)!
|
||||
COPY ./app /code/app
|
||||
|
||||
# (6)
|
||||
# (6)!
|
||||
CMD ["fastapi", "run", "app/main.py", "--port", "80"]
|
||||
```
|
||||
|
||||
@@ -232,6 +232,38 @@ Review what each line does by clicking each number bubble in the code. 👆
|
||||
|
||||
///
|
||||
|
||||
/// warning
|
||||
|
||||
Make sure to **always** use the **exec form** of the `CMD` instruction, as explained below.
|
||||
|
||||
///
|
||||
|
||||
#### Use `CMD` - Exec Form
|
||||
|
||||
The <a href="https://docs.docker.com/reference/dockerfile/#cmd" class="external-link" target="_blank">`CMD`</a> Docker instruction can be written using two forms:
|
||||
|
||||
✅ **Exec** form:
|
||||
|
||||
```Dockerfile
|
||||
# ✅ Do this
|
||||
CMD ["fastapi", "run", "app/main.py", "--port", "80"]
|
||||
```
|
||||
|
||||
⛔️ **Shell** form:
|
||||
|
||||
```Dockerfile
|
||||
# ⛔️ Don't do this
|
||||
CMD fastapi run app/main.py --port 80
|
||||
```
|
||||
|
||||
Make sure to always use the **exec** form to ensure that FastAPI can shutdown gracefully and [lifespan events](../advanced/events.md){.internal-link target=_blank} are triggered.
|
||||
|
||||
You can read more about it in the <a href="https://docs.docker.com/reference/dockerfile/#shell-and-exec-form" class="external-link" target="_blank">Docker docs for shell and exec form</a>.
|
||||
|
||||
This can be quite noticeable when using `docker compose`. See this Docker Compose FAQ section for more technical details: <a href="https://docs.docker.com/compose/faq/#why-do-my-services-take-10-seconds-to-recreate-or-stop" class="external-link" target="_blank">Why do my services take 10 seconds to recreate or stop?</a>.
|
||||
|
||||
#### Directory Structure
|
||||
|
||||
You should now have a directory structure like:
|
||||
|
||||
```
|
||||
@@ -368,10 +400,10 @@ COPY ./requirements.txt /code/requirements.txt
|
||||
|
||||
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
|
||||
|
||||
# (1)
|
||||
# (1)!
|
||||
COPY ./main.py /code/
|
||||
|
||||
# (2)
|
||||
# (2)!
|
||||
CMD ["fastapi", "run", "main.py", "--port", "80"]
|
||||
```
|
||||
|
||||
@@ -424,11 +456,11 @@ Without using containers, making applications run on startup and with restarts c
|
||||
|
||||
## Replication - Number of Processes
|
||||
|
||||
If you have a <abbr title="A group of machines that are configured to be connected and work together in some way.">cluster</abbr> of machines with **Kubernetes**, Docker Swarm Mode, Nomad, or another similar complex system to manage distributed containers on multiple machines, then you will probably want to **handle replication** at the **cluster level** instead of using a **process manager** (like Gunicorn with workers) in each container.
|
||||
If you have a <abbr title="A group of machines that are configured to be connected and work together in some way.">cluster</abbr> of machines with **Kubernetes**, Docker Swarm Mode, Nomad, or another similar complex system to manage distributed containers on multiple machines, then you will probably want to **handle replication** at the **cluster level** instead of using a **process manager** (like Uvicorn with workers) in each container.
|
||||
|
||||
One of those distributed container management systems like Kubernetes normally has some integrated way of handling **replication of containers** while still supporting **load balancing** for the incoming requests. All at the **cluster level**.
|
||||
|
||||
In those cases, you would probably want to build a **Docker image from scratch** as [explained above](#dockerfile), installing your dependencies, and running **a single Uvicorn process** instead of running something like Gunicorn with Uvicorn workers.
|
||||
In those cases, you would probably want to build a **Docker image from scratch** as [explained above](#dockerfile), installing your dependencies, and running **a single Uvicorn process** instead of using multiple Uvicorn workers.
|
||||
|
||||
### Load Balancer
|
||||
|
||||
@@ -458,21 +490,38 @@ And normally this **load balancer** would be able to handle requests that go to
|
||||
|
||||
In this type of scenario, you probably would want to have **a single (Uvicorn) process per container**, as you would already be handling replication at the cluster level.
|
||||
|
||||
So, in this case, you **would not** want to have a process manager like Gunicorn with Uvicorn workers, or Uvicorn using its own Uvicorn workers. You would want to have just a **single Uvicorn process** per container (but probably multiple containers).
|
||||
So, in this case, you **would not** want to have a multiple workers in the container, for example with the `--workers` command line option.You would want to have just a **single Uvicorn process** per container (but probably multiple containers).
|
||||
|
||||
Having another process manager inside the container (as would be with Gunicorn or Uvicorn managing Uvicorn workers) would only add **unnecessary complexity** that you are most probably already taking care of with your cluster system.
|
||||
Having another process manager inside the container (as would be with multiple workers) would only add **unnecessary complexity** that you are most probably already taking care of with your cluster system.
|
||||
|
||||
### Containers with Multiple Processes and Special Cases
|
||||
|
||||
Of course, there are **special cases** where you could want to have **a container** with a **Gunicorn process manager** starting several **Uvicorn worker processes** inside.
|
||||
Of course, there are **special cases** where you could want to have **a container** with several **Uvicorn worker processes** inside.
|
||||
|
||||
In those cases, you can use the **official Docker image** that includes **Gunicorn** as a process manager running multiple **Uvicorn worker processes**, and some default settings to adjust the number of workers based on the current CPU cores automatically. I'll tell you more about it below in [Official Docker Image with Gunicorn - Uvicorn](#official-docker-image-with-gunicorn-uvicorn).
|
||||
In those cases, you can use the `--workers` command line option to set the number of workers that you want to run:
|
||||
|
||||
```{ .dockerfile .annotate }
|
||||
FROM python:3.9
|
||||
|
||||
WORKDIR /code
|
||||
|
||||
COPY ./requirements.txt /code/requirements.txt
|
||||
|
||||
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
|
||||
|
||||
COPY ./app /code/app
|
||||
|
||||
# (1)!
|
||||
CMD ["fastapi", "run", "app/main.py", "--port", "80", "--workers", "4"]
|
||||
```
|
||||
|
||||
1. Here we use the `--workers` command line option to set the number of workers to 4.
|
||||
|
||||
Here are some examples of when that could make sense:
|
||||
|
||||
#### A Simple App
|
||||
|
||||
You could want a process manager in the container if your application is **simple enough** that you don't need (at least not yet) to fine-tune the number of processes too much, and you can just use an automated default (with the official Docker image), and you are running it on a **single server**, not a cluster.
|
||||
You could want a process manager in the container if your application is **simple enough** that can run it on a **single server**, not a cluster.
|
||||
|
||||
#### Docker Compose
|
||||
|
||||
@@ -480,16 +529,6 @@ You could be deploying to a **single server** (not a cluster) with **Docker Comp
|
||||
|
||||
Then you could want to have **a single container** with a **process manager** starting **several worker processes** inside.
|
||||
|
||||
#### Prometheus and Other Reasons
|
||||
|
||||
You could also have **other reasons** that would make it easier to have a **single container** with **multiple processes** instead of having **multiple containers** with **a single process** in each of them.
|
||||
|
||||
For example (depending on your setup) you could have some tool like a Prometheus exporter in the same container that should have access to **each of the requests** that come.
|
||||
|
||||
In this case, if you had **multiple containers**, by default, when Prometheus came to **read the metrics**, it would get the ones for **a single container each time** (for the container that handled that particular request), instead of getting the **accumulated metrics** for all the replicated containers.
|
||||
|
||||
Then, in that case, it could be simpler to have **one container** with **multiple processes**, and a local tool (e.g. a Prometheus exporter) on the same container collecting Prometheus metrics for all the internal processes and exposing those metrics on that single container.
|
||||
|
||||
---
|
||||
|
||||
The main point is, **none** of these are **rules written in stone** that you have to blindly follow. You can use these ideas to **evaluate your own use case** and decide what is the best approach for your system, checking out how to manage the concepts of:
|
||||
@@ -509,7 +548,7 @@ And then you can set those same memory limits and requirements in your configura
|
||||
|
||||
If your application is **simple**, this will probably **not be a problem**, and you might not need to specify hard memory limits. But if you are **using a lot of memory** (for example with **machine learning** models), you should check how much memory you are consuming and adjust the **number of containers** that runs in **each machine** (and maybe add more machines to your cluster).
|
||||
|
||||
If you run **multiple processes per container** (for example with the official Docker image) you will have to make sure that the number of processes started doesn't **consume more memory** than what is available.
|
||||
If you run **multiple processes per container** you will have to make sure that the number of processes started doesn't **consume more memory** than what is available.
|
||||
|
||||
## Previous Steps Before Starting and Containers
|
||||
|
||||
@@ -529,80 +568,26 @@ If in your use case there's no problem in running those previous steps **multipl
|
||||
|
||||
### Single Container
|
||||
|
||||
If you have a simple setup, with a **single container** that then starts multiple **worker processes** (or also just one process), then you could run those previous steps in the same container, right before starting the process with the app. The official Docker image supports this internally.
|
||||
If you have a simple setup, with a **single container** that then starts multiple **worker processes** (or also just one process), then you could run those previous steps in the same container, right before starting the process with the app.
|
||||
|
||||
## Official Docker Image with Gunicorn - Uvicorn
|
||||
### Base Docker Image
|
||||
|
||||
There is an official Docker image that includes Gunicorn running with Uvicorn workers, as detailed in a previous chapter: [Server Workers - Gunicorn with Uvicorn](server-workers.md){.internal-link target=_blank}.
|
||||
There used to be an official FastAPI Docker image: <a href="https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker" class="external-link" target="_blank">tiangolo/uvicorn-gunicorn-fastapi</a>. But it is now deprecated. ⛔️
|
||||
|
||||
This image would be useful mainly in the situations described above in: [Containers with Multiple Processes and Special Cases](#containers-with-multiple-processes-and-special-cases).
|
||||
You should probably **not** use this base Docker image (or any other similar one).
|
||||
|
||||
* <a href="https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker" class="external-link" target="_blank">tiangolo/uvicorn-gunicorn-fastapi</a>.
|
||||
If you are using **Kubernetes** (or others) and you are already setting **replication** at the cluster level, with multiple **containers**. In those cases, you are better off **building an image from scratch** as described above: [Build a Docker Image for FastAPI](#build-a-docker-image-for-fastapi).
|
||||
|
||||
/// warning
|
||||
And if you need to have multiple workers, you can simply use the `--workers` command line option.
|
||||
|
||||
There's a high chance that you **don't** need this base image or any other similar one, and would be better off by building the image from scratch as [described above in: Build a Docker Image for FastAPI](#build-a-docker-image-for-fastapi).
|
||||
/// note | Technical Details
|
||||
|
||||
The Docker image was created when Uvicorn didn't support managing and restarting dead workers, so it was needed to use Gunicorn with Uvicorn, which added quite some complexity, just to have Gunicorn manage and restart the Uvicorn worker processes.
|
||||
|
||||
But now that Uvicorn (and the `fastapi` command) support using `--workers`, there's no reason to use a base Docker image instead of building your own (it's pretty much the same amount of code 😅).
|
||||
|
||||
///
|
||||
|
||||
This image has an **auto-tuning** mechanism included to set the **number of worker processes** based on the CPU cores available.
|
||||
|
||||
It has **sensible defaults**, but you can still change and update all the configurations with **environment variables** or configuration files.
|
||||
|
||||
It also supports running <a href="https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker#pre_start_path" class="external-link" target="_blank">**previous steps before starting**</a> with a script.
|
||||
|
||||
/// tip
|
||||
|
||||
To see all the configurations and options, go to the Docker image page: <a href="https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker" class="external-link" target="_blank">tiangolo/uvicorn-gunicorn-fastapi</a>.
|
||||
|
||||
///
|
||||
|
||||
### Number of Processes on the Official Docker Image
|
||||
|
||||
The **number of processes** on this image is **computed automatically** from the CPU **cores** available.
|
||||
|
||||
This means that it will try to **squeeze** as much **performance** from the CPU as possible.
|
||||
|
||||
You can also adjust it with the configurations using **environment variables**, etc.
|
||||
|
||||
But it also means that as the number of processes depends on the CPU the container is running, the **amount of memory consumed** will also depend on that.
|
||||
|
||||
So, if your application consumes a lot of memory (for example with machine learning models), and your server has a lot of CPU cores **but little memory**, then your container could end up trying to use more memory than what is available, and degrading performance a lot (or even crashing). 🚨
|
||||
|
||||
### Create a `Dockerfile`
|
||||
|
||||
Here's how you would create a `Dockerfile` based on this image:
|
||||
|
||||
```Dockerfile
|
||||
FROM tiangolo/uvicorn-gunicorn-fastapi:python3.9
|
||||
|
||||
COPY ./requirements.txt /app/requirements.txt
|
||||
|
||||
RUN pip install --no-cache-dir --upgrade -r /app/requirements.txt
|
||||
|
||||
COPY ./app /app
|
||||
```
|
||||
|
||||
### Bigger Applications
|
||||
|
||||
If you followed the section about creating [Bigger Applications with Multiple Files](../tutorial/bigger-applications.md){.internal-link target=_blank}, your `Dockerfile` might instead look like:
|
||||
|
||||
```Dockerfile hl_lines="7"
|
||||
FROM tiangolo/uvicorn-gunicorn-fastapi:python3.9
|
||||
|
||||
COPY ./requirements.txt /app/requirements.txt
|
||||
|
||||
RUN pip install --no-cache-dir --upgrade -r /app/requirements.txt
|
||||
|
||||
COPY ./app /app/app
|
||||
```
|
||||
|
||||
### When to Use
|
||||
|
||||
You should probably **not** use this official base image (or any other similar one) if you are using **Kubernetes** (or others) and you are already setting **replication** at the cluster level, with multiple **containers**. In those cases, you are better off **building an image from scratch** as described above: [Build a Docker Image for FastAPI](#build-a-docker-image-for-fastapi).
|
||||
|
||||
This image would be useful mainly in the special cases described above in [Containers with Multiple Processes and Special Cases](#containers-with-multiple-processes-and-special-cases). For example, if your application is **simple enough** that setting a default number of processes based on the CPU works well, you don't want to bother with manually configuring the replication at the cluster level, and you are not running more than one container with your app. Or if you are deploying with **Docker Compose**, running on a single server, etc.
|
||||
|
||||
## Deploy the Container Image
|
||||
|
||||
After having a Container (Docker) Image there are several ways to deploy it.
|
||||
@@ -615,98 +600,9 @@ For example:
|
||||
* With another tool like Nomad
|
||||
* With a cloud service that takes your container image and deploys it
|
||||
|
||||
## Docker Image with Poetry
|
||||
## Docker Image with `uv`
|
||||
|
||||
If you use <a href="https://python-poetry.org/" class="external-link" target="_blank">Poetry</a> to manage your project's dependencies, you could use Docker multi-stage building:
|
||||
|
||||
```{ .dockerfile .annotate }
|
||||
# (1)
|
||||
FROM python:3.9 as requirements-stage
|
||||
|
||||
# (2)
|
||||
WORKDIR /tmp
|
||||
|
||||
# (3)
|
||||
RUN pip install poetry
|
||||
|
||||
# (4)
|
||||
COPY ./pyproject.toml ./poetry.lock* /tmp/
|
||||
|
||||
# (5)
|
||||
RUN poetry export -f requirements.txt --output requirements.txt --without-hashes
|
||||
|
||||
# (6)
|
||||
FROM python:3.9
|
||||
|
||||
# (7)
|
||||
WORKDIR /code
|
||||
|
||||
# (8)
|
||||
COPY --from=requirements-stage /tmp/requirements.txt /code/requirements.txt
|
||||
|
||||
# (9)
|
||||
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
|
||||
|
||||
# (10)
|
||||
COPY ./app /code/app
|
||||
|
||||
# (11)
|
||||
CMD ["fastapi", "run", "app/main.py", "--port", "80"]
|
||||
```
|
||||
|
||||
1. This is the first stage, it is named `requirements-stage`.
|
||||
|
||||
2. Set `/tmp` as the current working directory.
|
||||
|
||||
Here's where we will generate the file `requirements.txt`
|
||||
|
||||
3. Install Poetry in this Docker stage.
|
||||
|
||||
4. Copy the `pyproject.toml` and `poetry.lock` files to the `/tmp` directory.
|
||||
|
||||
Because it uses `./poetry.lock*` (ending with a `*`), it won't crash if that file is not available yet.
|
||||
|
||||
5. Generate the `requirements.txt` file.
|
||||
|
||||
6. This is the final stage, anything here will be preserved in the final container image.
|
||||
|
||||
7. Set the current working directory to `/code`.
|
||||
|
||||
8. Copy the `requirements.txt` file to the `/code` directory.
|
||||
|
||||
This file only lives in the previous Docker stage, that's why we use `--from-requirements-stage` to copy it.
|
||||
|
||||
9. Install the package dependencies in the generated `requirements.txt` file.
|
||||
|
||||
10. Copy the `app` directory to the `/code` directory.
|
||||
|
||||
11. Use the `fastapi run` command to run your app.
|
||||
|
||||
/// tip
|
||||
|
||||
Click the bubble numbers to see what each line does.
|
||||
|
||||
///
|
||||
|
||||
A **Docker stage** is a part of a `Dockerfile` that works as a **temporary container image** that is only used to generate some files to be used later.
|
||||
|
||||
The first stage will only be used to **install Poetry** and to **generate the `requirements.txt`** with your project dependencies from Poetry's `pyproject.toml` file.
|
||||
|
||||
This `requirements.txt` file will be used with `pip` later in the **next stage**.
|
||||
|
||||
In the final container image **only the final stage** is preserved. The previous stage(s) will be discarded.
|
||||
|
||||
When using Poetry, it would make sense to use **Docker multi-stage builds** because you don't really need to have Poetry and its dependencies installed in the final container image, you **only need** to have the generated `requirements.txt` file to install your project dependencies.
|
||||
|
||||
Then in the next (and final) stage you would build the image more or less in the same way as described before.
|
||||
|
||||
### Behind a TLS Termination Proxy - Poetry
|
||||
|
||||
Again, if you are running your container behind a TLS Termination Proxy (load balancer) like Nginx or Traefik, add the option `--proxy-headers` to the command:
|
||||
|
||||
```Dockerfile
|
||||
CMD ["fastapi", "run", "app/main.py", "--proxy-headers", "--port", "80"]
|
||||
```
|
||||
If you are using <a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">uv</a> to install and manage your project, you can follow their <a href="https://docs.astral.sh/uv/guides/integration/docker/" class="external-link" target="_blank">uv Docker guide</a>.
|
||||
|
||||
## Recap
|
||||
|
||||
@@ -722,5 +618,3 @@ Using container systems (e.g. with **Docker** and **Kubernetes**) it becomes fai
|
||||
In most cases, you probably won't want to use any base image, and instead **build a container image from scratch** one based on the official Python Docker image.
|
||||
|
||||
Taking care of the **order** of instructions in the `Dockerfile` and the **Docker cache** you can **minimize build times**, to maximize your productivity (and avoid boredom). 😎
|
||||
|
||||
In certain special cases, you might want to use the official Docker image for FastAPI. 🤓
|
||||
|
||||
@@ -67,6 +67,8 @@ There are several alternatives, including:
|
||||
* <a href="https://www.uvicorn.org/" class="external-link" target="_blank">Uvicorn</a>: a high performance ASGI server.
|
||||
* <a href="https://hypercorn.readthedocs.io/" class="external-link" target="_blank">Hypercorn</a>: an ASGI server compatible with HTTP/2 and Trio among other features.
|
||||
* <a href="https://github.com/django/daphne" class="external-link" target="_blank">Daphne</a>: the ASGI server built for Django Channels.
|
||||
* <a href="https://github.com/emmett-framework/granian" class="external-link" target="_blank">Granian</a>: A Rust HTTP server for Python applications.
|
||||
* <a href="https://unit.nginx.org/howto/fastapi/" class="external-link" target="_blank">NGINX Unit</a>: NGINX Unit is a lightweight and versatile web application runtime.
|
||||
|
||||
## Server Machine and Server Program
|
||||
|
||||
@@ -84,11 +86,9 @@ When you install FastAPI, it comes with a production server, Uvicorn, and you ca
|
||||
|
||||
But you can also install an ASGI server manually.
|
||||
|
||||
Make sure you create a [virtual environment](../virtual-environments.md){.internal-link target=_blank}, activate it, and then you can install the server:
|
||||
Make sure you create a [virtual environment](../virtual-environments.md){.internal-link target=_blank}, activate it, and then you can install the server application.
|
||||
|
||||
//// tab | Uvicorn
|
||||
|
||||
* <a href="https://www.uvicorn.org/" class="external-link" target="_blank">Uvicorn</a>, a lightning-fast ASGI server, built on uvloop and httptools.
|
||||
For example, to install Uvicorn:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
@@ -100,6 +100,8 @@ $ pip install "uvicorn[standard]"
|
||||
|
||||
</div>
|
||||
|
||||
A similar process would apply to any other ASGI server program.
|
||||
|
||||
/// tip
|
||||
|
||||
By adding the `standard`, Uvicorn will install and use some recommended extra dependencies.
|
||||
@@ -110,32 +112,10 @@ When you install FastAPI with something like `pip install "fastapi[standard]"` y
|
||||
|
||||
///
|
||||
|
||||
////
|
||||
|
||||
//// tab | Hypercorn
|
||||
|
||||
* <a href="https://github.com/pgjones/hypercorn" class="external-link" target="_blank">Hypercorn</a>, an ASGI server also compatible with HTTP/2.
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ pip install hypercorn
|
||||
|
||||
---> 100%
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
...or any other ASGI server.
|
||||
|
||||
////
|
||||
|
||||
## Run the Server Program
|
||||
|
||||
If you installed an ASGI server manually, you would normally need to pass an import string in a special format for it to import your FastAPI application:
|
||||
|
||||
//// tab | Uvicorn
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
@@ -146,22 +126,6 @@ $ uvicorn main:app --host 0.0.0.0 --port 80
|
||||
|
||||
</div>
|
||||
|
||||
////
|
||||
|
||||
//// tab | Hypercorn
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ hypercorn main:app --bind 0.0.0.0:80
|
||||
|
||||
Running on 0.0.0.0:8080 over http (CTRL + C to quit)
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
////
|
||||
|
||||
/// note
|
||||
|
||||
The command `uvicorn main:app` refers to:
|
||||
@@ -177,9 +141,11 @@ from main import app
|
||||
|
||||
///
|
||||
|
||||
Each alternative ASGI server program would have a similar command, you can read more in their respective documentation.
|
||||
|
||||
/// warning
|
||||
|
||||
Uvicorn and others support a `--reload` option that is useful during development.
|
||||
Uvicorn and other servers support a `--reload` option that is useful during development.
|
||||
|
||||
The `--reload` option consumes much more resources, is more unstable, etc.
|
||||
|
||||
@@ -187,43 +153,6 @@ It helps a lot during **development**, but you **shouldn't** use it in **product
|
||||
|
||||
///
|
||||
|
||||
## Hypercorn with Trio
|
||||
|
||||
Starlette and **FastAPI** are based on <a href="https://anyio.readthedocs.io/en/stable/" class="external-link" target="_blank">AnyIO</a>, which makes them compatible with both Python's standard library <a href="https://docs.python.org/3/library/asyncio-task.html" class="external-link" target="_blank">asyncio</a> and <a href="https://trio.readthedocs.io/en/stable/" class="external-link" target="_blank">Trio</a>.
|
||||
|
||||
Nevertheless, Uvicorn is currently only compatible with asyncio, and it normally uses <a href="https://github.com/MagicStack/uvloop" class="external-link" target="_blank">`uvloop`</a>, the high-performance drop-in replacement for `asyncio`.
|
||||
|
||||
But if you want to directly use **Trio**, then you can use **Hypercorn** as it supports it. ✨
|
||||
|
||||
### Install Hypercorn with Trio
|
||||
|
||||
First you need to install Hypercorn with Trio support:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ pip install "hypercorn[trio]"
|
||||
---> 100%
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
### Run with Trio
|
||||
|
||||
Then you can pass the command line option `--worker-class` with the value `trio`:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ hypercorn main:app --worker-class trio
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
And that will start Hypercorn with your app using Trio as the backend.
|
||||
|
||||
Now you can use Trio internally in your app. Or even better, you can use AnyIO, to keep your code compatible with both Trio and asyncio. 🎉
|
||||
|
||||
## Deployment Concepts
|
||||
|
||||
These examples run the server program (e.g Uvicorn), starting **a single process**, listening on all the IPs (`0.0.0.0`) on a predefined port (e.g. `80`).
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Server Workers - Gunicorn with Uvicorn
|
||||
# Server Workers - Uvicorn with Workers
|
||||
|
||||
Let's check back those deployment concepts from before:
|
||||
|
||||
@@ -9,125 +9,92 @@ Let's check back those deployment concepts from before:
|
||||
* Memory
|
||||
* Previous steps before starting
|
||||
|
||||
Up to this point, with all the tutorials in the docs, you have probably been running a **server program** like Uvicorn, running a **single process**.
|
||||
Up to this point, with all the tutorials in the docs, you have probably been running a **server program**, for example, using the `fastapi` command, that runs Uvicorn, running a **single process**.
|
||||
|
||||
When deploying applications you will probably want to have some **replication of processes** to take advantage of **multiple cores** and to be able to handle more requests.
|
||||
|
||||
As you saw in the previous chapter about [Deployment Concepts](concepts.md){.internal-link target=_blank}, there are multiple strategies you can use.
|
||||
|
||||
Here I'll show you how to use <a href="https://gunicorn.org/" class="external-link" target="_blank">**Gunicorn**</a> with **Uvicorn worker processes**.
|
||||
Here I'll show you how to use **Uvicorn** with **worker processes** using the `fastapi` command or the `uvicorn` command directly.
|
||||
|
||||
/// info
|
||||
|
||||
If you are using containers, for example with Docker or Kubernetes, I'll tell you more about that in the next chapter: [FastAPI in Containers - Docker](docker.md){.internal-link target=_blank}.
|
||||
|
||||
In particular, when running on **Kubernetes** you will probably **not** want to use Gunicorn and instead run **a single Uvicorn process per container**, but I'll tell you about it later in that chapter.
|
||||
In particular, when running on **Kubernetes** you will probably **not** want to use workers and instead run **a single Uvicorn process per container**, but I'll tell you about it later in that chapter.
|
||||
|
||||
///
|
||||
|
||||
## Gunicorn with Uvicorn Workers
|
||||
## Multiple Workers
|
||||
|
||||
**Gunicorn** is mainly an application server using the **WSGI standard**. That means that Gunicorn can serve applications like Flask and Django. Gunicorn by itself is not compatible with **FastAPI**, as FastAPI uses the newest **<a href="https://asgi.readthedocs.io/en/latest/" class="external-link" target="_blank">ASGI standard</a>**.
|
||||
You can start multiple workers with the `--workers` command line option:
|
||||
|
||||
But Gunicorn supports working as a **process manager** and allowing users to tell it which specific **worker process class** to use. Then Gunicorn would start one or more **worker processes** using that class.
|
||||
//// tab | `fastapi`
|
||||
|
||||
And **Uvicorn** has a **Gunicorn-compatible worker class**.
|
||||
|
||||
Using that combination, Gunicorn would act as a **process manager**, listening on the **port** and the **IP**. And it would **transmit** the communication to the worker processes running the **Uvicorn class**.
|
||||
|
||||
And then the Gunicorn-compatible **Uvicorn worker** class would be in charge of converting the data sent by Gunicorn to the ASGI standard for FastAPI to use it.
|
||||
|
||||
## Install Gunicorn and Uvicorn
|
||||
|
||||
Make sure you create a [virtual environment](../virtual-environments.md){.internal-link target=_blank}, activate it, and then install `gunicorn`:
|
||||
If you use the `fastapi` command:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ pip install "uvicorn[standard]" gunicorn
|
||||
$ <pre> <font color="#4E9A06">fastapi</font> run --workers 4 <u style="text-decoration-style:single">main.py</u>
|
||||
<font color="#3465A4">INFO </font> Using path <font color="#3465A4">main.py</font>
|
||||
<font color="#3465A4">INFO </font> Resolved absolute path <font color="#75507B">/home/user/code/awesomeapp/</font><font color="#AD7FA8">main.py</font>
|
||||
<font color="#3465A4">INFO </font> Searching for package file structure from directories with <font color="#3465A4">__init__.py</font> files
|
||||
<font color="#3465A4">INFO </font> Importing from <font color="#75507B">/home/user/code/</font><font color="#AD7FA8">awesomeapp</font>
|
||||
|
||||
---> 100%
|
||||
╭─ <font color="#8AE234"><b>Python module file</b></font> ─╮
|
||||
│ │
|
||||
│ 🐍 main.py │
|
||||
│ │
|
||||
╰──────────────────────╯
|
||||
|
||||
<font color="#3465A4">INFO </font> Importing module <font color="#4E9A06">main</font>
|
||||
<font color="#3465A4">INFO </font> Found importable FastAPI app
|
||||
|
||||
╭─ <font color="#8AE234"><b>Importable FastAPI app</b></font> ─╮
|
||||
│ │
|
||||
│ <span style="background-color:#272822"><font color="#FF4689">from</font></span><span style="background-color:#272822"><font color="#F8F8F2"> main </font></span><span style="background-color:#272822"><font color="#FF4689">import</font></span><span style="background-color:#272822"><font color="#F8F8F2"> app</font></span><span style="background-color:#272822"> </span> │
|
||||
│ │
|
||||
╰──────────────────────────╯
|
||||
|
||||
<font color="#3465A4">INFO </font> Using import string <font color="#8AE234"><b>main:app</b></font>
|
||||
|
||||
<font color="#4E9A06">╭─────────── FastAPI CLI - Production mode ───────────╮</font>
|
||||
<font color="#4E9A06">│ │</font>
|
||||
<font color="#4E9A06">│ Serving at: http://0.0.0.0:8000 │</font>
|
||||
<font color="#4E9A06">│ │</font>
|
||||
<font color="#4E9A06">│ API docs: http://0.0.0.0:8000/docs │</font>
|
||||
<font color="#4E9A06">│ │</font>
|
||||
<font color="#4E9A06">│ Running in production mode, for development use: │</font>
|
||||
<font color="#4E9A06">│ │</font>
|
||||
<font color="#4E9A06">│ </font><font color="#8AE234"><b>fastapi dev</b></font><font color="#4E9A06"> │</font>
|
||||
<font color="#4E9A06">│ │</font>
|
||||
<font color="#4E9A06">╰─────────────────────────────────────────────────────╯</font>
|
||||
|
||||
<font color="#4E9A06">INFO</font>: Uvicorn running on <b>http://0.0.0.0:8000</b> (Press CTRL+C to quit)
|
||||
<font color="#4E9A06">INFO</font>: Started parent process [<font color="#34E2E2"><b>27365</b></font>]
|
||||
<font color="#4E9A06">INFO</font>: Started server process [<font color="#06989A">27368</font>]
|
||||
<font color="#4E9A06">INFO</font>: Waiting for application startup.
|
||||
<font color="#4E9A06">INFO</font>: Application startup complete.
|
||||
<font color="#4E9A06">INFO</font>: Started server process [<font color="#06989A">27369</font>]
|
||||
<font color="#4E9A06">INFO</font>: Waiting for application startup.
|
||||
<font color="#4E9A06">INFO</font>: Application startup complete.
|
||||
<font color="#4E9A06">INFO</font>: Started server process [<font color="#06989A">27370</font>]
|
||||
<font color="#4E9A06">INFO</font>: Waiting for application startup.
|
||||
<font color="#4E9A06">INFO</font>: Application startup complete.
|
||||
<font color="#4E9A06">INFO</font>: Started server process [<font color="#06989A">27367</font>]
|
||||
<font color="#4E9A06">INFO</font>: Waiting for application startup.
|
||||
<font color="#4E9A06">INFO</font>: Application startup complete.
|
||||
</pre>
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
That will install both Uvicorn with the `standard` extra packages (to get high performance) and Gunicorn.
|
||||
////
|
||||
|
||||
## Run Gunicorn with Uvicorn Workers
|
||||
//// tab | `uvicorn`
|
||||
|
||||
Then you can run Gunicorn with:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ gunicorn main:app --workers 4 --worker-class uvicorn.workers.UvicornWorker --bind 0.0.0.0:80
|
||||
|
||||
[19499] [INFO] Starting gunicorn 20.1.0
|
||||
[19499] [INFO] Listening at: http://0.0.0.0:80 (19499)
|
||||
[19499] [INFO] Using worker: uvicorn.workers.UvicornWorker
|
||||
[19511] [INFO] Booting worker with pid: 19511
|
||||
[19513] [INFO] Booting worker with pid: 19513
|
||||
[19514] [INFO] Booting worker with pid: 19514
|
||||
[19515] [INFO] Booting worker with pid: 19515
|
||||
[19511] [INFO] Started server process [19511]
|
||||
[19511] [INFO] Waiting for application startup.
|
||||
[19511] [INFO] Application startup complete.
|
||||
[19513] [INFO] Started server process [19513]
|
||||
[19513] [INFO] Waiting for application startup.
|
||||
[19513] [INFO] Application startup complete.
|
||||
[19514] [INFO] Started server process [19514]
|
||||
[19514] [INFO] Waiting for application startup.
|
||||
[19514] [INFO] Application startup complete.
|
||||
[19515] [INFO] Started server process [19515]
|
||||
[19515] [INFO] Waiting for application startup.
|
||||
[19515] [INFO] Application startup complete.
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
Let's see what each of those options mean:
|
||||
|
||||
* `main:app`: This is the same syntax used by Uvicorn, `main` means the Python module named "`main`", so, a file `main.py`. And `app` is the name of the variable that is the **FastAPI** application.
|
||||
* You can imagine that `main:app` is equivalent to a Python `import` statement like:
|
||||
|
||||
```Python
|
||||
from main import app
|
||||
```
|
||||
|
||||
* So, the colon in `main:app` would be equivalent to the Python `import` part in `from main import app`.
|
||||
|
||||
* `--workers`: The number of worker processes to use, each will run a Uvicorn worker, in this case, 4 workers.
|
||||
|
||||
* `--worker-class`: The Gunicorn-compatible worker class to use in the worker processes.
|
||||
* Here we pass the class that Gunicorn can import and use with:
|
||||
|
||||
```Python
|
||||
import uvicorn.workers.UvicornWorker
|
||||
```
|
||||
|
||||
* `--bind`: This tells Gunicorn the IP and the port to listen to, using a colon (`:`) to separate the IP and the port.
|
||||
* If you were running Uvicorn directly, instead of `--bind 0.0.0.0:80` (the Gunicorn option) you would use `--host 0.0.0.0` and `--port 80`.
|
||||
|
||||
In the output, you can see that it shows the **PID** (process ID) of each process (it's just a number).
|
||||
|
||||
You can see that:
|
||||
|
||||
* The Gunicorn **process manager** starts with PID `19499` (in your case it will be a different number).
|
||||
* Then it starts `Listening at: http://0.0.0.0:80`.
|
||||
* Then it detects that it has to use the worker class at `uvicorn.workers.UvicornWorker`.
|
||||
* And then it starts **4 workers**, each with its own PID: `19511`, `19513`, `19514`, and `19515`.
|
||||
|
||||
Gunicorn would also take care of managing **dead processes** and **restarting** new ones if needed to keep the number of workers. So that helps in part with the **restart** concept from the list above.
|
||||
|
||||
Nevertheless, you would probably also want to have something outside making sure to **restart Gunicorn** if necessary, and also to **run it on startup**, etc.
|
||||
|
||||
## Uvicorn with Workers
|
||||
|
||||
Uvicorn also has an option to start and run several **worker processes**.
|
||||
|
||||
Nevertheless, as of now, Uvicorn's capabilities for handling worker processes are more limited than Gunicorn's. So, if you want to have a process manager at this level (at the Python level), then it might be better to try with Gunicorn as the process manager.
|
||||
|
||||
In any case, you would run it like this:
|
||||
If you prefer to use the `uvicorn` command directly:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
@@ -151,13 +118,15 @@ $ uvicorn main:app --host 0.0.0.0 --port 8080 --workers 4
|
||||
|
||||
</div>
|
||||
|
||||
////
|
||||
|
||||
The only new option here is `--workers` telling Uvicorn to start 4 worker processes.
|
||||
|
||||
You can also see that it shows the **PID** of each process, `27365` for the parent process (this is the **process manager**) and one for each worker process: `27368`, `27369`, `27370`, and `27367`.
|
||||
|
||||
## Deployment Concepts
|
||||
|
||||
Here you saw how to use **Gunicorn** (or Uvicorn) managing **Uvicorn worker processes** to **parallelize** the execution of the application, take advantage of **multiple cores** in the CPU, and be able to serve **more requests**.
|
||||
Here you saw how to use multiple **workers** to **parallelize** the execution of the application, take advantage of **multiple cores** in the CPU, and be able to serve **more requests**.
|
||||
|
||||
From the list of deployment concepts from above, using workers would mainly help with the **replication** part, and a little bit with the **restarts**, but you still need to take care of the others:
|
||||
|
||||
@@ -172,13 +141,11 @@ From the list of deployment concepts from above, using workers would mainly help
|
||||
|
||||
In the next chapter about [FastAPI in Containers - Docker](docker.md){.internal-link target=_blank} I'll tell some strategies you could use to handle the other **deployment concepts**.
|
||||
|
||||
I'll also show you the **official Docker image** that includes **Gunicorn with Uvicorn workers** and some default configurations that can be useful for simple cases.
|
||||
|
||||
There I'll also show you how to **build your own image from scratch** to run a single Uvicorn process (without Gunicorn). It is a simple process and is probably what you would want to do when using a distributed container management system like **Kubernetes**.
|
||||
I'll show you how to **build your own image from scratch** to run a single Uvicorn process. It is a simple process and is probably what you would want to do when using a distributed container management system like **Kubernetes**.
|
||||
|
||||
## Recap
|
||||
|
||||
You can use **Gunicorn** (or also Uvicorn) as a process manager with Uvicorn workers to take advantage of **multi-core CPUs**, to run **multiple processes in parallel**.
|
||||
You can use multiple worker processes with the `--workers` CLI option with the `fastapi` or `uvicorn` commands to take advantage of **multi-core CPUs**, to run **multiple processes in parallel**.
|
||||
|
||||
You could use these tools and ideas if you are setting up **your own deployment system** while taking care of the other deployment concepts yourself.
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ fastapi[standard]>=0.112.0,<0.113.0
|
||||
|
||||
that would mean that you would use the versions `0.112.0` or above, but less than `0.113.0`, for example, a version `0.112.2` would still be accepted.
|
||||
|
||||
If you use any other tool to manage your installations, like Poetry, Pipenv, or others, they all have a way that you can use to define specific versions for your packages.
|
||||
If you use any other tool to manage your installations, like `uv`, Poetry, Pipenv, or others, they all have a way that you can use to define specific versions for your packages.
|
||||
|
||||
## Available versions
|
||||
|
||||
|
||||
@@ -243,8 +243,6 @@ This way, when you type `python` in the terminal, the system will find the Pytho
|
||||
|
||||
////
|
||||
|
||||
This way, when you type `python` in the terminal, the system will find the Python program in `/opt/custompython/bin` (the last directory) and use that one.
|
||||
|
||||
So, if you type:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
BIN
docs/en/docs/img/tutorial/cookie-param-models/image01.png
Normal file
BIN
docs/en/docs/img/tutorial/cookie-param-models/image01.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 44 KiB |
BIN
docs/en/docs/img/tutorial/header-param-models/image01.png
Normal file
BIN
docs/en/docs/img/tutorial/header-param-models/image01.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 61 KiB |
BIN
docs/en/docs/img/tutorial/query-param-models/image01.png
Normal file
BIN
docs/en/docs/img/tutorial/query-param-models/image01.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 44 KiB |
BIN
docs/en/docs/img/tutorial/request-form-models/image01.png
Normal file
BIN
docs/en/docs/img/tutorial/request-form-models/image01.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 43 KiB |
@@ -390,7 +390,7 @@ Coming back to the previous code example, **FastAPI** will:
|
||||
* Check if there is an optional query parameter named `q` (as in `http://127.0.0.1:8000/items/foo?q=somequery`) for `GET` requests.
|
||||
* As the `q` parameter is declared with `= None`, it is optional.
|
||||
* Without the `None` it would be required (as is the body in the case with `PUT`).
|
||||
* For `PUT` requests to `/items/{item_id}`, Read the body as JSON:
|
||||
* For `PUT` requests to `/items/{item_id}`, read the body as JSON:
|
||||
* Check that it has a required attribute `name` that should be a `str`.
|
||||
* Check that it has a required attribute `price` that has to be a `float`.
|
||||
* Check that it has an optional attribute `is_offer`, that should be a `bool`, if present.
|
||||
|
||||
@@ -324,7 +324,7 @@ In Python 3.6 and above (including Python 3.10) you can declare it by importing
|
||||
{!../../../docs_src/python_types/tutorial009.py!}
|
||||
```
|
||||
|
||||
Using `Optional[str]` instead of just `str` will let the editor help you detecting errors where you could be assuming that a value is always a `str`, when it could actually be `None` too.
|
||||
Using `Optional[str]` instead of just `str` will let the editor help you detect errors where you could be assuming that a value is always a `str`, when it could actually be `None` too.
|
||||
|
||||
`Optional[Something]` is actually a shortcut for `Union[Something, None]`, they are equivalent.
|
||||
|
||||
|
||||
@@ -7,6 +7,315 @@ hide:
|
||||
|
||||
## Latest Changes
|
||||
|
||||
## 0.115.0
|
||||
|
||||
### Highlights
|
||||
|
||||
Now you can declare `Query`, `Header`, and `Cookie` parameters with Pydantic models. 🎉
|
||||
|
||||
#### `Query` Parameter Models
|
||||
|
||||
Use Pydantic models for `Query` parameters:
|
||||
|
||||
```python
|
||||
from typing import Annotated, Literal
|
||||
|
||||
from fastapi import FastAPI, Query
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class FilterParams(BaseModel):
|
||||
limit: int = Field(100, gt=0, le=100)
|
||||
offset: int = Field(0, ge=0)
|
||||
order_by: Literal["created_at", "updated_at"] = "created_at"
|
||||
tags: list[str] = []
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items(filter_query: Annotated[FilterParams, Query()]):
|
||||
return filter_query
|
||||
```
|
||||
|
||||
Read the new docs: [Query Parameter Models](https://fastapi.tiangolo.com/tutorial/query-param-models/).
|
||||
|
||||
#### `Header` Parameter Models
|
||||
|
||||
Use Pydantic models for `Header` parameters:
|
||||
|
||||
```python
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import FastAPI, Header
|
||||
from pydantic import BaseModel
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class CommonHeaders(BaseModel):
|
||||
host: str
|
||||
save_data: bool
|
||||
if_modified_since: str | None = None
|
||||
traceparent: str | None = None
|
||||
x_tag: list[str] = []
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items(headers: Annotated[CommonHeaders, Header()]):
|
||||
return headers
|
||||
```
|
||||
|
||||
Read the new docs: [Header Parameter Models](https://fastapi.tiangolo.com/tutorial/header-param-models/).
|
||||
|
||||
#### `Cookie` Parameter Models
|
||||
|
||||
Use Pydantic models for `Cookie` parameters:
|
||||
|
||||
```python
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import Cookie, FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class Cookies(BaseModel):
|
||||
session_id: str
|
||||
fatebook_tracker: str | None = None
|
||||
googall_tracker: str | None = None
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items(cookies: Annotated[Cookies, Cookie()]):
|
||||
return cookies
|
||||
```
|
||||
|
||||
Read the new docs: [Cookie Parameter Models](https://fastapi.tiangolo.com/tutorial/cookie-param-models/).
|
||||
|
||||
#### Forbid Extra Query (Cookie, Header) Parameters
|
||||
|
||||
Use Pydantic models to restrict extra values for `Query` parameters (also applies to `Header` and `Cookie` parameters).
|
||||
|
||||
To achieve it, use Pydantic's `model_config = {"extra": "forbid"}`:
|
||||
|
||||
```python
|
||||
from typing import Annotated, Literal
|
||||
|
||||
from fastapi import FastAPI, Query
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class FilterParams(BaseModel):
|
||||
model_config = {"extra": "forbid"}
|
||||
|
||||
limit: int = Field(100, gt=0, le=100)
|
||||
offset: int = Field(0, ge=0)
|
||||
order_by: Literal["created_at", "updated_at"] = "created_at"
|
||||
tags: list[str] = []
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items(filter_query: Annotated[FilterParams, Query()]):
|
||||
return filter_query
|
||||
```
|
||||
|
||||
This applies to `Query`, `Header`, and `Cookie` parameters, read the new docs:
|
||||
|
||||
* [Forbid Extra Query Parameters](https://fastapi.tiangolo.com/tutorial/query-param-models/#forbid-extra-query-parameters)
|
||||
* [Forbid Extra Headers](https://fastapi.tiangolo.com/tutorial/header-param-models/#forbid-extra-headers)
|
||||
* [Forbid Extra Cookies](https://fastapi.tiangolo.com/tutorial/cookie-param-models/#forbid-extra-cookies)
|
||||
|
||||
### Features
|
||||
|
||||
* ✨ Add support for Pydantic models for parameters using `Query`, `Cookie`, `Header`. PR [#12199](https://github.com/fastapi/fastapi/pull/12199) by [@tiangolo](https://github.com/tiangolo).
|
||||
|
||||
### Translations
|
||||
|
||||
* 🌐 Add Portuguese translation for `docs/pt/docs/advanced/security/http-basic-auth.md`. PR [#12195](https://github.com/fastapi/fastapi/pull/12195) by [@ceb10n](https://github.com/ceb10n).
|
||||
|
||||
### Internal
|
||||
|
||||
* ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#12204](https://github.com/fastapi/fastapi/pull/12204) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci).
|
||||
|
||||
## 0.114.2
|
||||
|
||||
### Fixes
|
||||
|
||||
* 🐛 Fix form field regression with `alias`. PR [#12194](https://github.com/fastapi/fastapi/pull/12194) by [@Wurstnase](https://github.com/Wurstnase).
|
||||
|
||||
### Translations
|
||||
|
||||
* 🌐 Add Portuguese translation for `docs/pt/docs/tutorial/request-form-models.md`. PR [#12175](https://github.com/fastapi/fastapi/pull/12175) by [@ceb10n](https://github.com/ceb10n).
|
||||
* 🌐 Add Chinese translation for `docs/zh/docs/project-generation.md`. PR [#12170](https://github.com/fastapi/fastapi/pull/12170) by [@waketzheng](https://github.com/waketzheng).
|
||||
* 🌐 Add Dutch translation for `docs/nl/docs/python-types.md`. PR [#12158](https://github.com/fastapi/fastapi/pull/12158) by [@maxscheijen](https://github.com/maxscheijen).
|
||||
|
||||
### Internal
|
||||
|
||||
* 💡 Add comments with instructions for Playwright screenshot scripts. PR [#12193](https://github.com/fastapi/fastapi/pull/12193) by [@tiangolo](https://github.com/tiangolo).
|
||||
* ➕ Add inline-snapshot for tests. PR [#12189](https://github.com/fastapi/fastapi/pull/12189) by [@tiangolo](https://github.com/tiangolo).
|
||||
|
||||
## 0.114.1
|
||||
|
||||
### Refactors
|
||||
|
||||
* ⚡️ Improve performance in request body parsing with a cache for internal model fields. PR [#12184](https://github.com/fastapi/fastapi/pull/12184) by [@tiangolo](https://github.com/tiangolo).
|
||||
|
||||
### Docs
|
||||
|
||||
* 📝 Remove duplicate line in docs for `docs/en/docs/environment-variables.md`. PR [#12169](https://github.com/fastapi/fastapi/pull/12169) by [@prometek](https://github.com/prometek).
|
||||
|
||||
### Translations
|
||||
|
||||
* 🌐 Add Portuguese translation for `docs/pt/docs/virtual-environments.md`. PR [#12163](https://github.com/fastapi/fastapi/pull/12163) by [@marcelomarkus](https://github.com/marcelomarkus).
|
||||
* 🌐 Add Portuguese translation for `docs/pt/docs/environment-variables.md`. PR [#12162](https://github.com/fastapi/fastapi/pull/12162) by [@marcelomarkus](https://github.com/marcelomarkus).
|
||||
* 🌐 Add Portuguese translation for `docs/pt/docs/tutorial/testing.md`. PR [#12164](https://github.com/fastapi/fastapi/pull/12164) by [@marcelomarkus](https://github.com/marcelomarkus).
|
||||
* 🌐 Add Portuguese translation for `docs/pt/docs/tutorial/debugging.md`. PR [#12165](https://github.com/fastapi/fastapi/pull/12165) by [@marcelomarkus](https://github.com/marcelomarkus).
|
||||
* 🌐 Add Korean translation for `docs/ko/docs/project-generation.md`. PR [#12157](https://github.com/fastapi/fastapi/pull/12157) by [@BORA040126](https://github.com/BORA040126).
|
||||
|
||||
### Internal
|
||||
|
||||
* ⬆ Bump tiangolo/issue-manager from 0.5.0 to 0.5.1. PR [#12173](https://github.com/fastapi/fastapi/pull/12173) by [@dependabot[bot]](https://github.com/apps/dependabot).
|
||||
* ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#12176](https://github.com/fastapi/fastapi/pull/12176) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci).
|
||||
* 👷 Update `issue-manager.yml`. PR [#12159](https://github.com/fastapi/fastapi/pull/12159) by [@tiangolo](https://github.com/tiangolo).
|
||||
* ✏️ Fix typo in `fastapi/params.py`. PR [#12143](https://github.com/fastapi/fastapi/pull/12143) by [@surreal30](https://github.com/surreal30).
|
||||
|
||||
## 0.114.0
|
||||
|
||||
You can restrict form fields to only include those declared in a Pydantic model and forbid any extra field sent in the request using Pydantic's `model_config = {"extra": "forbid"}`:
|
||||
|
||||
```python
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import FastAPI, Form
|
||||
from pydantic import BaseModel
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class FormData(BaseModel):
|
||||
username: str
|
||||
password: str
|
||||
model_config = {"extra": "forbid"}
|
||||
|
||||
|
||||
@app.post("/login/")
|
||||
async def login(data: Annotated[FormData, Form()]):
|
||||
return data
|
||||
```
|
||||
|
||||
Read the new docs: [Form Models - Forbid Extra Form Fields](https://fastapi.tiangolo.com/tutorial/request-form-models/#forbid-extra-form-fields).
|
||||
|
||||
### Features
|
||||
|
||||
* ✨ Add support for forbidding extra form fields with Pydantic models. PR [#12134](https://github.com/fastapi/fastapi/pull/12134) by [@tiangolo](https://github.com/tiangolo).
|
||||
|
||||
### Docs
|
||||
|
||||
* 📝 Update docs, Form Models section title, to match config name. PR [#12152](https://github.com/fastapi/fastapi/pull/12152) by [@tiangolo](https://github.com/tiangolo).
|
||||
|
||||
### Internal
|
||||
|
||||
* ✅ Update internal tests for latest Pydantic, including CI tweaks to install the latest Pydantic. PR [#12147](https://github.com/fastapi/fastapi/pull/12147) by [@tiangolo](https://github.com/tiangolo).
|
||||
|
||||
## 0.113.0
|
||||
|
||||
Now you can declare form fields with Pydantic models:
|
||||
|
||||
```python
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import FastAPI, Form
|
||||
from pydantic import BaseModel
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class FormData(BaseModel):
|
||||
username: str
|
||||
password: str
|
||||
|
||||
|
||||
@app.post("/login/")
|
||||
async def login(data: Annotated[FormData, Form()]):
|
||||
return data
|
||||
```
|
||||
|
||||
Read the new docs: [Form Models](https://fastapi.tiangolo.com/tutorial/request-form-models/).
|
||||
|
||||
### Features
|
||||
|
||||
* ✨ Add support for Pydantic models in `Form` parameters. PR [#12129](https://github.com/fastapi/fastapi/pull/12129) by [@tiangolo](https://github.com/tiangolo).
|
||||
|
||||
### Internal
|
||||
|
||||
* 🔧 Update sponsors: Coherence link. PR [#12130](https://github.com/fastapi/fastapi/pull/12130) by [@tiangolo](https://github.com/tiangolo).
|
||||
|
||||
## 0.112.4
|
||||
|
||||
This release is mainly a big internal refactor to enable adding support for Pydantic models for `Form` fields, but that feature comes in the next release.
|
||||
|
||||
This release shouldn't affect apps using FastAPI in any way. You don't even have to upgrade to this version yet. It's just a checkpoint. 🤓
|
||||
|
||||
### Refactors
|
||||
|
||||
* ♻️ Refactor deciding if `embed` body fields, do not overwrite fields, compute once per router, refactor internals in preparation for Pydantic models in `Form`, `Query` and others. PR [#12117](https://github.com/fastapi/fastapi/pull/12117) by [@tiangolo](https://github.com/tiangolo).
|
||||
|
||||
### Internal
|
||||
|
||||
* ⏪️ Temporarily revert "✨ Add support for Pydantic models in `Form` parameters" to make a checkpoint release. PR [#12128](https://github.com/fastapi/fastapi/pull/12128) by [@tiangolo](https://github.com/tiangolo). Restored by PR [#12129](https://github.com/fastapi/fastapi/pull/12129).
|
||||
* ✨ Add support for Pydantic models in `Form` parameters. PR [#12127](https://github.com/fastapi/fastapi/pull/12127) by [@tiangolo](https://github.com/tiangolo). Reverted by PR [#12128](https://github.com/fastapi/fastapi/pull/12128) to make a checkpoint release with only refactors. Restored by PR [#12129](https://github.com/fastapi/fastapi/pull/12129).
|
||||
|
||||
## 0.112.3
|
||||
|
||||
This release is mainly internal refactors, it shouldn't affect apps using FastAPI in any way. You don't even have to upgrade to this version yet. There are a few bigger releases coming right after. 🚀
|
||||
|
||||
### Refactors
|
||||
|
||||
* ♻️ Refactor internal `check_file_field()`, rename to `ensure_multipart_is_installed()` to clarify its purpose. PR [#12106](https://github.com/fastapi/fastapi/pull/12106) by [@tiangolo](https://github.com/tiangolo).
|
||||
* ♻️ Rename internal `create_response_field()` to `create_model_field()` as it's used for more than response models. PR [#12103](https://github.com/fastapi/fastapi/pull/12103) by [@tiangolo](https://github.com/tiangolo).
|
||||
* ♻️ Refactor and simplify internal data from `solve_dependencies()` using dataclasses. PR [#12100](https://github.com/fastapi/fastapi/pull/12100) by [@tiangolo](https://github.com/tiangolo).
|
||||
* ♻️ Refactor and simplify internal `analyze_param()` to structure data with dataclasses instead of tuple. PR [#12099](https://github.com/fastapi/fastapi/pull/12099) by [@tiangolo](https://github.com/tiangolo).
|
||||
* ♻️ Refactor and simplify dependencies data structures with dataclasses. PR [#12098](https://github.com/fastapi/fastapi/pull/12098) by [@tiangolo](https://github.com/tiangolo).
|
||||
|
||||
### Docs
|
||||
|
||||
* 📝 Add External Link: Techniques and applications of SQLAlchemy global filters in FastAPI. PR [#12109](https://github.com/fastapi/fastapi/pull/12109) by [@TheShubhendra](https://github.com/TheShubhendra).
|
||||
* 📝 Add note about `time.perf_counter()` in middlewares. PR [#12095](https://github.com/fastapi/fastapi/pull/12095) by [@tiangolo](https://github.com/tiangolo).
|
||||
* 📝 Tweak middleware code sample `time.time()` to `time.perf_counter()`. PR [#11957](https://github.com/fastapi/fastapi/pull/11957) by [@domdent](https://github.com/domdent).
|
||||
* 🔧 Update sponsors: Coherence. PR [#12093](https://github.com/fastapi/fastapi/pull/12093) by [@tiangolo](https://github.com/tiangolo).
|
||||
* 📝 Fix async test example not to trigger DeprecationWarning. PR [#12084](https://github.com/fastapi/fastapi/pull/12084) by [@marcinsulikowski](https://github.com/marcinsulikowski).
|
||||
* 📝 Update `docs_src/path_params_numeric_validations/tutorial006.py`. PR [#11478](https://github.com/fastapi/fastapi/pull/11478) by [@MuhammadAshiqAmeer](https://github.com/MuhammadAshiqAmeer).
|
||||
* 📝 Update comma in `docs/en/docs/async.md`. PR [#12062](https://github.com/fastapi/fastapi/pull/12062) by [@Alec-Gillis](https://github.com/Alec-Gillis).
|
||||
* 📝 Update docs about serving FastAPI: ASGI servers, Docker containers, etc.. PR [#12069](https://github.com/fastapi/fastapi/pull/12069) by [@tiangolo](https://github.com/tiangolo).
|
||||
* 📝 Clarify `response_class` parameter, validations, and returning a response directly. PR [#12067](https://github.com/fastapi/fastapi/pull/12067) by [@tiangolo](https://github.com/tiangolo).
|
||||
* 📝 Fix minor typos and issues in the documentation. PR [#12063](https://github.com/fastapi/fastapi/pull/12063) by [@svlandeg](https://github.com/svlandeg).
|
||||
* 📝 Add note in Docker docs about ensuring graceful shutdowns and lifespan events with `CMD` exec form. PR [#11960](https://github.com/fastapi/fastapi/pull/11960) by [@GPla](https://github.com/GPla).
|
||||
|
||||
### Translations
|
||||
|
||||
* 🌐 Add Dutch translation for `docs/nl/docs/features.md`. PR [#12101](https://github.com/fastapi/fastapi/pull/12101) by [@maxscheijen](https://github.com/maxscheijen).
|
||||
* 🌐 Add Portuguese translation for `docs/pt/docs/advanced/testing-events.md`. PR [#12108](https://github.com/fastapi/fastapi/pull/12108) by [@ceb10n](https://github.com/ceb10n).
|
||||
* 🌐 Add Portuguese translation for `docs/pt/docs/advanced/security/index.md`. PR [#12114](https://github.com/fastapi/fastapi/pull/12114) by [@ceb10n](https://github.com/ceb10n).
|
||||
* 🌐 Add Dutch translation for `docs/nl/docs/index.md`. PR [#12042](https://github.com/fastapi/fastapi/pull/12042) by [@svlandeg](https://github.com/svlandeg).
|
||||
* 🌐 Update Chinese translation for `docs/zh/docs/how-to/index.md`. PR [#12070](https://github.com/fastapi/fastapi/pull/12070) by [@synthpop123](https://github.com/synthpop123).
|
||||
|
||||
### Internal
|
||||
|
||||
* ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#12115](https://github.com/fastapi/fastapi/pull/12115) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci).
|
||||
* ⬆ Bump pypa/gh-action-pypi-publish from 1.10.0 to 1.10.1. PR [#12120](https://github.com/fastapi/fastapi/pull/12120) by [@dependabot[bot]](https://github.com/apps/dependabot).
|
||||
* ⬆ Bump pillow from 10.3.0 to 10.4.0. PR [#12105](https://github.com/fastapi/fastapi/pull/12105) by [@dependabot[bot]](https://github.com/apps/dependabot).
|
||||
* 💚 Set `include-hidden-files` to `True` when using the `upload-artifact` GH action. PR [#12118](https://github.com/fastapi/fastapi/pull/12118) by [@svlandeg](https://github.com/svlandeg).
|
||||
* ⬆ Bump pypa/gh-action-pypi-publish from 1.9.0 to 1.10.0. PR [#12112](https://github.com/fastapi/fastapi/pull/12112) by [@dependabot[bot]](https://github.com/apps/dependabot).
|
||||
* 🔧 Update sponsors link: Coherence. PR [#12097](https://github.com/fastapi/fastapi/pull/12097) by [@tiangolo](https://github.com/tiangolo).
|
||||
* 🔧 Update labeler config to handle sponsorships data. PR [#12096](https://github.com/fastapi/fastapi/pull/12096) by [@tiangolo](https://github.com/tiangolo).
|
||||
* 🔧 Update sponsors, remove Kong. PR [#12085](https://github.com/fastapi/fastapi/pull/12085) by [@tiangolo](https://github.com/tiangolo).
|
||||
* ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#12076](https://github.com/fastapi/fastapi/pull/12076) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci).
|
||||
* 👷 Update `latest-changes` GitHub Action. PR [#12073](https://github.com/fastapi/fastapi/pull/12073) by [@tiangolo](https://github.com/tiangolo).
|
||||
|
||||
## 0.112.2
|
||||
|
||||
### Fixes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Bigger Applications - Multiple Files
|
||||
|
||||
If you are building an application or a web API, it's rarely the case that you can put everything on a single file.
|
||||
If you are building an application or a web API, it's rarely the case that you can put everything in a single file.
|
||||
|
||||
**FastAPI** provides a convenience tool to structure your application while keeping all the flexibility.
|
||||
|
||||
@@ -478,7 +478,7 @@ We can declare all that without having to modify the original `APIRouter` by pas
|
||||
{!../../../docs_src/bigger_applications/app/main.py!}
|
||||
```
|
||||
|
||||
That way, the original `APIRouter` will keep unmodified, so we can still share that same `app/internal/admin.py` file with other projects in the organization.
|
||||
That way, the original `APIRouter` will stay unmodified, so we can still share that same `app/internal/admin.py` file with other projects in the organization.
|
||||
|
||||
The result is that in our app, each of the *path operations* from the `admin` module will have:
|
||||
|
||||
@@ -519,12 +519,12 @@ As we cannot just isolate them and "mount" them independently of the rest, the *
|
||||
|
||||
## Check the automatic API docs
|
||||
|
||||
Now, run `uvicorn`, using the module `app.main` and the variable `app`:
|
||||
Now, run your app:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ uvicorn app.main:app --reload
|
||||
$ fastapi dev app/main.py
|
||||
|
||||
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
|
||||
```
|
||||
|
||||
@@ -97,7 +97,7 @@ But you can also declare multiple body parameters, e.g. `item` and `user`:
|
||||
|
||||
////
|
||||
|
||||
In this case, **FastAPI** will notice that there are more than one body parameters in the function (two parameters that are Pydantic models).
|
||||
In this case, **FastAPI** will notice that there is more than one body parameter in the function (there are two parameters that are Pydantic models).
|
||||
|
||||
So, it will then use the parameter names as keys (field names) in the body, and expect a body like:
|
||||
|
||||
|
||||
@@ -155,7 +155,7 @@ In summary, to apply partial updates you would:
|
||||
* Put that data in a Pydantic model.
|
||||
* Generate a `dict` without default values from the input model (using `exclude_unset`).
|
||||
* This way you can update only the values actually set by the user, instead of overriding values already stored with default values in your model.
|
||||
* Create a copy of the stored model, updating it's attributes with the received partial updates (using the `update` parameter).
|
||||
* Create a copy of the stored model, updating its attributes with the received partial updates (using the `update` parameter).
|
||||
* Convert the copied model to something that can be stored in your DB (for example, using the `jsonable_encoder`).
|
||||
* This is comparable to using the model's `.model_dump()` method again, but it makes sure (and converts) the values to data types that can be converted to JSON, for example, `datetime` to `str`.
|
||||
* Save the data to your DB.
|
||||
|
||||
@@ -123,7 +123,7 @@ The JSON Schemas of your models will be part of your OpenAPI generated schema, a
|
||||
|
||||
<img src="/img/tutorial/body/image01.png">
|
||||
|
||||
And will be also used in the API docs inside each *path operation* that needs them:
|
||||
And will also be used in the API docs inside each *path operation* that needs them:
|
||||
|
||||
<img src="/img/tutorial/body/image02.png">
|
||||
|
||||
|
||||
154
docs/en/docs/tutorial/cookie-param-models.md
Normal file
154
docs/en/docs/tutorial/cookie-param-models.md
Normal file
@@ -0,0 +1,154 @@
|
||||
# Cookie Parameter Models
|
||||
|
||||
If you have a group of **cookies** that are related, you can create a **Pydantic model** to declare them. 🍪
|
||||
|
||||
This would allow you to **re-use the model** in **multiple places** and also to declare validations and metadata for all the parameters at once. 😎
|
||||
|
||||
/// note
|
||||
|
||||
This is supported since FastAPI version `0.115.0`. 🤓
|
||||
|
||||
///
|
||||
|
||||
/// tip
|
||||
|
||||
This same technique applies to `Query`, `Cookie`, and `Header`. 😎
|
||||
|
||||
///
|
||||
|
||||
## Cookies with a Pydantic Model
|
||||
|
||||
Declare the **cookie** parameters that you need in a **Pydantic model**, and then declare the parameter as `Cookie`:
|
||||
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python hl_lines="9-12 16"
|
||||
{!> ../../../docs_src/cookie_param_models/tutorial001_an_py310.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python hl_lines="9-12 16"
|
||||
{!> ../../../docs_src/cookie_param_models/tutorial001_an_py39.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+
|
||||
|
||||
```Python hl_lines="10-13 17"
|
||||
{!> ../../../docs_src/cookie_param_models/tutorial001_an.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.10+ non-Annotated
|
||||
|
||||
/// tip
|
||||
|
||||
Prefer to use the `Annotated` version if possible.
|
||||
|
||||
///
|
||||
|
||||
```Python hl_lines="7-10 14"
|
||||
{!> ../../../docs_src/cookie_param_models/tutorial001_py310.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+ non-Annotated
|
||||
|
||||
/// tip
|
||||
|
||||
Prefer to use the `Annotated` version if possible.
|
||||
|
||||
///
|
||||
|
||||
```Python hl_lines="9-12 16"
|
||||
{!> ../../../docs_src/cookie_param_models/tutorial001.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
**FastAPI** will **extract** the data for **each field** from the **cookies** received in the request and give you the Pydantic model you defined.
|
||||
|
||||
## Check the Docs
|
||||
|
||||
You can see the defined cookies in the docs UI at `/docs`:
|
||||
|
||||
<div class="screenshot">
|
||||
<img src="/img/tutorial/cookie-param-models/image01.png">
|
||||
</div>
|
||||
|
||||
/// info
|
||||
|
||||
Have in mind that, as **browsers handle cookies** in special ways and behind the scenes, they **don't** easily allow **JavaScript** to touch them.
|
||||
|
||||
If you go to the **API docs UI** at `/docs` you will be able to see the **documentation** for cookies for your *path operations*.
|
||||
|
||||
But even if you **fill the data** and click "Execute", because the docs UI works with **JavaScript**, the cookies won't be sent, and you will see an **error** message as if you didn't write any values.
|
||||
|
||||
///
|
||||
|
||||
## Forbid Extra Cookies
|
||||
|
||||
In some special use cases (probably not very common), you might want to **restrict** the cookies that you want to receive.
|
||||
|
||||
Your API now has the power to control its own <abbr title="This is a joke, just in case. It has nothing to do with cookie consents, but it's funny that even the API can now reject the poor cookies. Have a cookie. 🍪">cookie consent</abbr>. 🤪🍪
|
||||
|
||||
You can use Pydantic's model configuration to `forbid` any `extra` fields:
|
||||
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python hl_lines="10"
|
||||
{!> ../../../docs_src/cookie_param_models/tutorial002_an_py39.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+
|
||||
|
||||
```Python hl_lines="11"
|
||||
{!> ../../../docs_src/cookie_param_models/tutorial002_an.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+ non-Annotated
|
||||
|
||||
/// tip
|
||||
|
||||
Prefer to use the `Annotated` version if possible.
|
||||
|
||||
///
|
||||
|
||||
```Python hl_lines="10"
|
||||
{!> ../../../docs_src/cookie_param_models/tutorial002.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
If a client tries to send some **extra cookies**, they will receive an **error** response.
|
||||
|
||||
Poor cookie banners with all their effort to get your consent for the <abbr title="This is another joke. Don't pay attention to me. Have some coffee for your cookie. ☕">API to reject it</abbr>. 🍪
|
||||
|
||||
For example, if the client tries to send a `santa_tracker` cookie with a value of `good-list-please`, the client will receive an **error** response telling them that the `santa_tracker` <abbr title="Santa disapproves the lack of cookies. 🎅 Okay, no more cookie jokes.">cookie is not allowed</abbr>:
|
||||
|
||||
```json
|
||||
{
|
||||
"detail": [
|
||||
{
|
||||
"type": "extra_forbidden",
|
||||
"loc": ["cookie", "santa_tracker"],
|
||||
"msg": "Extra inputs are not permitted",
|
||||
"input": "good-list-please",
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Summary
|
||||
|
||||
You can use **Pydantic models** to declare <abbr title="Have a last cookie before you go. 🍪">**cookies**</abbr> in **FastAPI**. 😎
|
||||
@@ -40,7 +40,7 @@ You can configure it in your **FastAPI** application using the `CORSMiddleware`.
|
||||
* Create a list of allowed origins (as strings).
|
||||
* Add it as a "middleware" to your **FastAPI** application.
|
||||
|
||||
You can also specify if your backend allows:
|
||||
You can also specify whether your backend allows:
|
||||
|
||||
* Credentials (Authorization headers, Cookies, etc).
|
||||
* Specific HTTP methods (`POST`, `PUT`) or all of them with the wildcard `"*"`.
|
||||
|
||||
@@ -381,7 +381,7 @@ The last `CommonQueryParams`, in:
|
||||
|
||||
...is what **FastAPI** will actually use to know what is the dependency.
|
||||
|
||||
From it is that FastAPI will extract the declared parameters and that is what FastAPI will actually call.
|
||||
It is from this one that FastAPI will extract the declared parameters and that is what FastAPI will actually call.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ Here are some of the additional data types you can use:
|
||||
* `Decimal`:
|
||||
* Standard Python `Decimal`.
|
||||
* In requests and responses, handled the same as a `float`.
|
||||
* You can check all the valid pydantic data types here: <a href="https://docs.pydantic.dev/latest/usage/types/types/" class="external-link" target="_blank">Pydantic data types</a>.
|
||||
* You can check all the valid Pydantic data types here: <a href="https://docs.pydantic.dev/latest/usage/types/types/" class="external-link" target="_blank">Pydantic data types</a>.
|
||||
|
||||
## Example
|
||||
|
||||
|
||||
@@ -176,7 +176,7 @@ These are technical details that you might skip if it's not important for you no
|
||||
|
||||
**FastAPI** uses it so that, if you use a Pydantic model in `response_model`, and your data has an error, you will see the error in your log.
|
||||
|
||||
But the client/user will not see it. Instead, the client will receive an "Internal Server Error" with a HTTP status code `500`.
|
||||
But the client/user will not see it. Instead, the client will receive an "Internal Server Error" with an HTTP status code `500`.
|
||||
|
||||
It should be this way because if you have a Pydantic `ValidationError` in your *response* or anywhere in your code (not in the client's *request*), it's actually a bug in your code.
|
||||
|
||||
@@ -262,7 +262,7 @@ from starlette.exceptions import HTTPException as StarletteHTTPException
|
||||
|
||||
### Reuse **FastAPI**'s exception handlers
|
||||
|
||||
If you want to use the exception along with the same default exception handlers from **FastAPI**, You can import and reuse the default exception handlers from `fastapi.exception_handlers`:
|
||||
If you want to use the exception along with the same default exception handlers from **FastAPI**, you can import and reuse the default exception handlers from `fastapi.exception_handlers`:
|
||||
|
||||
```Python hl_lines="2-5 15 21"
|
||||
{!../../../docs_src/handling_errors/tutorial006.py!}
|
||||
|
||||
184
docs/en/docs/tutorial/header-param-models.md
Normal file
184
docs/en/docs/tutorial/header-param-models.md
Normal file
@@ -0,0 +1,184 @@
|
||||
# Header Parameter Models
|
||||
|
||||
If you have a group of related **header parameters**, you can create a **Pydantic model** to declare them.
|
||||
|
||||
This would allow you to **re-use the model** in **multiple places** and also to declare validations and metadata for all the parameters at once. 😎
|
||||
|
||||
/// note
|
||||
|
||||
This is supported since FastAPI version `0.115.0`. 🤓
|
||||
|
||||
///
|
||||
|
||||
## Header Parameters with a Pydantic Model
|
||||
|
||||
Declare the **header parameters** that you need in a **Pydantic model**, and then declare the parameter as `Header`:
|
||||
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python hl_lines="9-14 18"
|
||||
{!> ../../../docs_src/header_param_models/tutorial001_an_py310.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python hl_lines="9-14 18"
|
||||
{!> ../../../docs_src/header_param_models/tutorial001_an_py39.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+
|
||||
|
||||
```Python hl_lines="10-15 19"
|
||||
{!> ../../../docs_src/header_param_models/tutorial001_an.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.10+ non-Annotated
|
||||
|
||||
/// tip
|
||||
|
||||
Prefer to use the `Annotated` version if possible.
|
||||
|
||||
///
|
||||
|
||||
```Python hl_lines="7-12 16"
|
||||
{!> ../../../docs_src/header_param_models/tutorial001_py310.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+ non-Annotated
|
||||
|
||||
/// tip
|
||||
|
||||
Prefer to use the `Annotated` version if possible.
|
||||
|
||||
///
|
||||
|
||||
```Python hl_lines="9-14 18"
|
||||
{!> ../../../docs_src/header_param_models/tutorial001_py39.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+ non-Annotated
|
||||
|
||||
/// tip
|
||||
|
||||
Prefer to use the `Annotated` version if possible.
|
||||
|
||||
///
|
||||
|
||||
```Python hl_lines="7-12 16"
|
||||
{!> ../../../docs_src/header_param_models/tutorial001_py310.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
**FastAPI** will **extract** the data for **each field** from the **headers** in the request and give you the Pydantic model you defined.
|
||||
|
||||
## Check the Docs
|
||||
|
||||
You can see the required headers in the docs UI at `/docs`:
|
||||
|
||||
<div class="screenshot">
|
||||
<img src="/img/tutorial/header-param-models/image01.png">
|
||||
</div>
|
||||
|
||||
## Forbid Extra Headers
|
||||
|
||||
In some special use cases (probably not very common), you might want to **restrict** the headers that you want to receive.
|
||||
|
||||
You can use Pydantic's model configuration to `forbid` any `extra` fields:
|
||||
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python hl_lines="10"
|
||||
{!> ../../../docs_src/header_param_models/tutorial002_an_py310.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python hl_lines="10"
|
||||
{!> ../../../docs_src/header_param_models/tutorial002_an_py39.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+
|
||||
|
||||
```Python hl_lines="11"
|
||||
{!> ../../../docs_src/header_param_models/tutorial002_an.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.10+ non-Annotated
|
||||
|
||||
/// tip
|
||||
|
||||
Prefer to use the `Annotated` version if possible.
|
||||
|
||||
///
|
||||
|
||||
```Python hl_lines="8"
|
||||
{!> ../../../docs_src/header_param_models/tutorial002_py310.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+ non-Annotated
|
||||
|
||||
/// tip
|
||||
|
||||
Prefer to use the `Annotated` version if possible.
|
||||
|
||||
///
|
||||
|
||||
```Python hl_lines="10"
|
||||
{!> ../../../docs_src/header_param_models/tutorial002_py39.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+ non-Annotated
|
||||
|
||||
/// tip
|
||||
|
||||
Prefer to use the `Annotated` version if possible.
|
||||
|
||||
///
|
||||
|
||||
```Python hl_lines="10"
|
||||
{!> ../../../docs_src/header_param_models/tutorial002.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
If a client tries to send some **extra headers**, they will receive an **error** response.
|
||||
|
||||
For example, if the client tries to send a `tool` header with a value of `plumbus`, they will receive an **error** response telling them that the header parameter `tool` is not allowed:
|
||||
|
||||
```json
|
||||
{
|
||||
"detail": [
|
||||
{
|
||||
"type": "extra_forbidden",
|
||||
"loc": ["header", "tool"],
|
||||
"msg": "Extra inputs are not permitted",
|
||||
"input": "plumbus",
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Summary
|
||||
|
||||
You can use **Pydantic models** to declare **headers** in **FastAPI**. 😎
|
||||
@@ -95,7 +95,7 @@ If you don't want to have those optional dependencies, you can instead install `
|
||||
|
||||
There is also an **Advanced User Guide** that you can read later after this **Tutorial - User guide**.
|
||||
|
||||
The **Advanced User Guide**, builds on this, uses the same concepts, and teaches you some extra features.
|
||||
The **Advanced User Guide** builds on this one, uses the same concepts, and teaches you some extra features.
|
||||
|
||||
But you should first read the **Tutorial - User Guide** (what you are reading right now).
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ The middleware function receives:
|
||||
* A function `call_next` that will receive the `request` as a parameter.
|
||||
* This function will pass the `request` to the corresponding *path operation*.
|
||||
* Then it returns the `response` generated by the corresponding *path operation*.
|
||||
* You can then modify further the `response` before returning it.
|
||||
* You can then further modify the `response` before returning it.
|
||||
|
||||
```Python hl_lines="8-9 11 14"
|
||||
{!../../../docs_src/middleware/tutorial001.py!}
|
||||
@@ -63,6 +63,12 @@ For example, you could add a custom header `X-Process-Time` containing the time
|
||||
{!../../../docs_src/middleware/tutorial001.py!}
|
||||
```
|
||||
|
||||
/// tip
|
||||
|
||||
Here we use <a href="https://docs.python.org/3/library/time.html#time.perf_counter" class="external-link" target="_blank">`time.perf_counter()`</a> instead of `time.time()` because it can be more precise for these use cases. 🤓
|
||||
|
||||
///
|
||||
|
||||
## Other middlewares
|
||||
|
||||
You can later read more about other middlewares in the [Advanced User Guide: Advanced Middleware](../advanced/middleware.md){.internal-link target=_blank}.
|
||||
|
||||
196
docs/en/docs/tutorial/query-param-models.md
Normal file
196
docs/en/docs/tutorial/query-param-models.md
Normal file
@@ -0,0 +1,196 @@
|
||||
# Query Parameter Models
|
||||
|
||||
If you have a group of **query parameters** that are related, you can create a **Pydantic model** to declare them.
|
||||
|
||||
This would allow you to **re-use the model** in **multiple places** and also to declare validations and metadata for all the parameters at once. 😎
|
||||
|
||||
/// note
|
||||
|
||||
This is supported since FastAPI version `0.115.0`. 🤓
|
||||
|
||||
///
|
||||
|
||||
## Query Parameters with a Pydantic Model
|
||||
|
||||
Declare the **query parameters** that you need in a **Pydantic model**, and then declare the parameter as `Query`:
|
||||
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python hl_lines="9-13 17"
|
||||
{!> ../../../docs_src/query_param_models/tutorial001_an_py310.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python hl_lines="8-12 16"
|
||||
{!> ../../../docs_src/query_param_models/tutorial001_an_py39.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+
|
||||
|
||||
```Python hl_lines="10-14 18"
|
||||
{!> ../../../docs_src/query_param_models/tutorial001_an.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.10+ non-Annotated
|
||||
|
||||
/// tip
|
||||
|
||||
Prefer to use the `Annotated` version if possible.
|
||||
|
||||
///
|
||||
|
||||
```Python hl_lines="9-13 17"
|
||||
{!> ../../../docs_src/query_param_models/tutorial001_py310.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+ non-Annotated
|
||||
|
||||
/// tip
|
||||
|
||||
Prefer to use the `Annotated` version if possible.
|
||||
|
||||
///
|
||||
|
||||
```Python hl_lines="8-12 16"
|
||||
{!> ../../../docs_src/query_param_models/tutorial001_py39.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+ non-Annotated
|
||||
|
||||
/// tip
|
||||
|
||||
Prefer to use the `Annotated` version if possible.
|
||||
|
||||
///
|
||||
|
||||
```Python hl_lines="9-13 17"
|
||||
{!> ../../../docs_src/query_param_models/tutorial001_py310.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
**FastAPI** will **extract** the data for **each field** from the **query parameters** in the request and give you the Pydantic model you defined.
|
||||
|
||||
## Check the Docs
|
||||
|
||||
You can see the query parameters in the docs UI at `/docs`:
|
||||
|
||||
<div class="screenshot">
|
||||
<img src="/img/tutorial/query-param-models/image01.png">
|
||||
</div>
|
||||
|
||||
## Forbid Extra Query Parameters
|
||||
|
||||
In some special use cases (probably not very common), you might want to **restrict** the query parameters that you want to receive.
|
||||
|
||||
You can use Pydantic's model configuration to `forbid` any `extra` fields:
|
||||
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python hl_lines="10"
|
||||
{!> ../../../docs_src/query_param_models/tutorial002_an_py310.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python hl_lines="9"
|
||||
{!> ../../../docs_src/query_param_models/tutorial002_an_py39.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+
|
||||
|
||||
```Python hl_lines="11"
|
||||
{!> ../../../docs_src/query_param_models/tutorial002_an.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.10+ non-Annotated
|
||||
|
||||
/// tip
|
||||
|
||||
Prefer to use the `Annotated` version if possible.
|
||||
|
||||
///
|
||||
|
||||
```Python hl_lines="10"
|
||||
{!> ../../../docs_src/query_param_models/tutorial002_py310.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+ non-Annotated
|
||||
|
||||
/// tip
|
||||
|
||||
Prefer to use the `Annotated` version if possible.
|
||||
|
||||
///
|
||||
|
||||
```Python hl_lines="9"
|
||||
{!> ../../../docs_src/query_param_models/tutorial002_py39.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+ non-Annotated
|
||||
|
||||
/// tip
|
||||
|
||||
Prefer to use the `Annotated` version if possible.
|
||||
|
||||
///
|
||||
|
||||
```Python hl_lines="11"
|
||||
{!> ../../../docs_src/query_param_models/tutorial002.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
If a client tries to send some **extra** data in the **query parameters**, they will receive an **error** response.
|
||||
|
||||
For example, if the client tries to send a `tool` query parameter with a value of `plumbus`, like:
|
||||
|
||||
```http
|
||||
https://example.com/items/?limit=10&tool=plumbus
|
||||
```
|
||||
|
||||
They will receive an **error** response telling them that the query parameter `tool` is not allowed:
|
||||
|
||||
```json
|
||||
{
|
||||
"detail": [
|
||||
{
|
||||
"type": "extra_forbidden",
|
||||
"loc": ["query", "tool"],
|
||||
"msg": "Extra inputs are not permitted",
|
||||
"input": "plumbus"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Summary
|
||||
|
||||
You can use **Pydantic models** to declare **query parameters** in **FastAPI**. 😎
|
||||
|
||||
/// tip
|
||||
|
||||
Spoiler alert: you can also use Pydantic models to declare cookies and headers, but you will read about that later in the tutorial. 🤫
|
||||
|
||||
///
|
||||
@@ -273,7 +273,7 @@ The **default** value of the **function parameter** is the **actual default** va
|
||||
|
||||
You could **call** that same function in **other places** without FastAPI, and it would **work as expected**. If there's a **required** parameter (without a default value), your **editor** will let you know with an error, **Python** will also complain if you run it without passing the required parameter.
|
||||
|
||||
When you don't use `Annotated` and instead use the **(old) default value style**, if you call that function without FastAPI in **other place**, you have to **remember** to pass the arguments to the function for it to work correctly, otherwise the values will be different from what you expect (e.g. `QueryInfo` or something similar instead of `str`). And your editor won't complain, and Python won't complain running that function, only when the operations inside error out.
|
||||
When you don't use `Annotated` and instead use the **(old) default value style**, if you call that function without FastAPI in **other places**, you have to **remember** to pass the arguments to the function for it to work correctly, otherwise the values will be different from what you expect (e.g. `QueryInfo` or something similar instead of `str`). And your editor won't complain, and Python won't complain running that function, only when the operations inside error out.
|
||||
|
||||
Because `Annotated` can have more than one metadata annotation, you could now even use the same function with other tools, like <a href="https://typer.tiangolo.com/" class="external-link" target="_blank">Typer</a>. 🚀
|
||||
|
||||
@@ -645,7 +645,7 @@ Remember that in most of the cases, when something is required, you can simply o
|
||||
|
||||
## 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 other way, to receive 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.
|
||||
|
||||
For example, to declare a query parameter `q` that can appear multiple times in the URL, you can write:
|
||||
|
||||
@@ -1182,4 +1182,4 @@ Validations specific for strings:
|
||||
|
||||
In these examples you saw how to declare validations for `str` values.
|
||||
|
||||
See the next chapters to see how to declare validations for other types, like numbers.
|
||||
See the next chapters to learn how to declare validations for other types, like numbers.
|
||||
|
||||
134
docs/en/docs/tutorial/request-form-models.md
Normal file
134
docs/en/docs/tutorial/request-form-models.md
Normal file
@@ -0,0 +1,134 @@
|
||||
# Form Models
|
||||
|
||||
You can use **Pydantic models** to declare **form fields** in FastAPI.
|
||||
|
||||
/// info
|
||||
|
||||
To use forms, first install <a href="https://github.com/Kludex/python-multipart" class="external-link" target="_blank">`python-multipart`</a>.
|
||||
|
||||
Make sure you create a [virtual environment](../virtual-environments.md){.internal-link target=_blank}, activate it, and then install it, for example:
|
||||
|
||||
```console
|
||||
$ pip install python-multipart
|
||||
```
|
||||
|
||||
///
|
||||
|
||||
/// note
|
||||
|
||||
This is supported since FastAPI version `0.113.0`. 🤓
|
||||
|
||||
///
|
||||
|
||||
## Pydantic Models for Forms
|
||||
|
||||
You just need to declare a **Pydantic model** with the fields you want to receive as **form fields**, and then declare the parameter as `Form`:
|
||||
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python hl_lines="9-11 15"
|
||||
{!> ../../../docs_src/request_form_models/tutorial001_an_py39.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+
|
||||
|
||||
```Python hl_lines="8-10 14"
|
||||
{!> ../../../docs_src/request_form_models/tutorial001_an.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+ non-Annotated
|
||||
|
||||
/// tip
|
||||
|
||||
Prefer to use the `Annotated` version if possible.
|
||||
|
||||
///
|
||||
|
||||
```Python hl_lines="7-9 13"
|
||||
{!> ../../../docs_src/request_form_models/tutorial001.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
**FastAPI** will **extract** the data for **each field** from the **form data** in the request and give you the Pydantic model you defined.
|
||||
|
||||
## Check the Docs
|
||||
|
||||
You can verify it in the docs UI at `/docs`:
|
||||
|
||||
<div class="screenshot">
|
||||
<img src="/img/tutorial/request-form-models/image01.png">
|
||||
</div>
|
||||
|
||||
## Forbid Extra Form Fields
|
||||
|
||||
In some special use cases (probably not very common), you might want to **restrict** the form fields to only those declared in the Pydantic model. And **forbid** any **extra** fields.
|
||||
|
||||
/// note
|
||||
|
||||
This is supported since FastAPI version `0.114.0`. 🤓
|
||||
|
||||
///
|
||||
|
||||
You can use Pydantic's model configuration to `forbid` any `extra` fields:
|
||||
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python hl_lines="12"
|
||||
{!> ../../../docs_src/request_form_models/tutorial002_an_py39.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+
|
||||
|
||||
```Python hl_lines="11"
|
||||
{!> ../../../docs_src/request_form_models/tutorial002_an.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+ non-Annotated
|
||||
|
||||
/// tip
|
||||
|
||||
Prefer to use the `Annotated` version if possible.
|
||||
|
||||
///
|
||||
|
||||
```Python hl_lines="10"
|
||||
{!> ../../../docs_src/request_form_models/tutorial002.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
If a client tries to send some extra data, they will receive an **error** response.
|
||||
|
||||
For example, if the client tries to send the form fields:
|
||||
|
||||
* `username`: `Rick`
|
||||
* `password`: `Portal Gun`
|
||||
* `extra`: `Mr. Poopybutthole`
|
||||
|
||||
They will receive an error response telling them that the field `extra` is not allowed:
|
||||
|
||||
```json
|
||||
{
|
||||
"detail": [
|
||||
{
|
||||
"type": "extra_forbidden",
|
||||
"loc": ["body", "extra"],
|
||||
"msg": "Extra inputs are not permitted",
|
||||
"input": "Mr. Poopybutthole"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Summary
|
||||
|
||||
You can use Pydantic models to declare form fields in FastAPI. 😎
|
||||
@@ -347,7 +347,7 @@ If the ideas above already work for you, that might be enough, and you probably
|
||||
|
||||
Before OpenAPI 3.1.0, OpenAPI used an older and modified version of **JSON Schema**.
|
||||
|
||||
JSON Schema didn't have `examples`, so OpenAPI added it's own `example` field to its own modified version.
|
||||
JSON Schema didn't have `examples`, so OpenAPI added its own `example` field to its own modified version.
|
||||
|
||||
OpenAPI also added `example` and `examples` fields to other parts of the specification:
|
||||
|
||||
|
||||
@@ -152,7 +152,7 @@ A "bearer" token is not the only option.
|
||||
|
||||
But it's the best one for our use case.
|
||||
|
||||
And it might be the best for most use cases, unless you are an OAuth2 expert and know exactly why there's another option that suits better your needs.
|
||||
And it might be the best for most use cases, unless you are an OAuth2 expert and know exactly why there's another option that better suits your needs.
|
||||
|
||||
In that case, **FastAPI** also provides you with the tools to build it.
|
||||
|
||||
|
||||
@@ -316,7 +316,7 @@ And you can make it as complex as you want. And still, have it written only once
|
||||
|
||||
But you can have thousands of endpoints (*path operations*) using the same security system.
|
||||
|
||||
And all of them (or any portion of them that you want) can take the advantage of re-using these dependencies or any other dependencies you create.
|
||||
And all of them (or any portion of them that you want) can take advantage of re-using these dependencies or any other dependencies you create.
|
||||
|
||||
And all these thousands of *path operations* can be as small as 3 lines:
|
||||
|
||||
|
||||
@@ -118,6 +118,7 @@ nav:
|
||||
- tutorial/body.md
|
||||
- tutorial/query-params-str-validations.md
|
||||
- tutorial/path-params-numeric-validations.md
|
||||
- tutorial/query-param-models.md
|
||||
- tutorial/body-multiple-params.md
|
||||
- tutorial/body-fields.md
|
||||
- tutorial/body-nested-models.md
|
||||
@@ -125,10 +126,13 @@ nav:
|
||||
- tutorial/extra-data-types.md
|
||||
- tutorial/cookie-params.md
|
||||
- tutorial/header-params.md
|
||||
- tutorial/cookie-param-models.md
|
||||
- tutorial/header-param-models.md
|
||||
- tutorial/response-model.md
|
||||
- tutorial/extra-models.md
|
||||
- tutorial/response-status-code.md
|
||||
- tutorial/request-forms.md
|
||||
- tutorial/request-form-models.md
|
||||
- tutorial/request-files.md
|
||||
- tutorial/request-forms-and-files.md
|
||||
- tutorial/handling-errors.md
|
||||
@@ -363,6 +367,8 @@ extra:
|
||||
name: ja - 日本語
|
||||
- link: /ko/
|
||||
name: ko - 한국어
|
||||
- link: /nl/
|
||||
name: nl - Nederlands
|
||||
- link: /pl/
|
||||
name: pl - Polski
|
||||
- link: /pt/
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
</a>
|
||||
</div>
|
||||
<div class="item">
|
||||
<a title="Coherence" style="display: block; position: relative;" href="https://docs.withcoherence.com/configuration/frameworks/?utm_medium=advertising&utm_source=fastapi&utm_campaign=docs#fastapi-example" target="_blank">
|
||||
<a title="Coherence" style="display: block; position: relative;" href="https://www.withcoherence.com/?utm_medium=advertising&utm_source=fastapi&utm_campaign=website" target="_blank">
|
||||
<span class="sponsor-badge">sponsor</span>
|
||||
<img class="sponsor-image" src="/img/sponsors/coherence-banner.png" />
|
||||
</a>
|
||||
@@ -70,12 +70,6 @@
|
||||
<img class="sponsor-image" src="/img/sponsors/mongodb-banner.png" />
|
||||
</a>
|
||||
</div>
|
||||
<div class="item">
|
||||
<a title="Kong Konnect - API management platform" style="display: block; position: relative;" href="https://konghq.com/products/kong-konnect?utm_medium=referral&utm_source=github&utm_campaign=platform&utm_content=fast-api" target="_blank">
|
||||
<span class="sponsor-badge">sponsor</span>
|
||||
<img class="sponsor-image" src="/img/sponsors/kong-banner.png" />
|
||||
</a>
|
||||
</div>
|
||||
<div class="item">
|
||||
<a title="Zuplo: Scale, Protect, Document, and Monetize your FastAPI" style="display: block; position: relative;" href="https://zuplo.link/fastapi-web" target="_blank">
|
||||
<span class="sponsor-badge">sponsor</span>
|
||||
|
||||
28
docs/ko/docs/project-generation.md
Normal file
28
docs/ko/docs/project-generation.md
Normal file
@@ -0,0 +1,28 @@
|
||||
# Full Stack FastAPI 템플릿
|
||||
|
||||
템플릿은 일반적으로 특정 설정과 함께 제공되지만, 유연하고 커스터마이징이 가능하게 디자인 되었습니다. 이 특성들은 여러분이 프로젝트의 요구사항에 맞춰 수정, 적용을 할 수 있게 해주고, 템플릿이 완벽한 시작점이 되게 해줍니다. 🏁
|
||||
|
||||
많은 초기 설정, 보안, 데이터베이스 및 일부 API 엔드포인트가 이미 준비되어 있으므로, 여러분은 이 템플릿을 (프로젝트를) 시작하는 데 사용할 수 있습니다.
|
||||
|
||||
GitHub 저장소: <a href="https://github.com/tiangolo/full-stack-fastapi-template" class="external-link" target="_blank">Full Stack FastAPI 템플릿</a>
|
||||
|
||||
## Full Stack FastAPI 템플릿 - 기술 스택과 기능들
|
||||
|
||||
- ⚡ [**FastAPI**](https://fastapi.tiangolo.com): Python 백엔드 API.
|
||||
- 🧰 [SQLModel](https://sqlmodel.tiangolo.com): Python SQL 데이터 상호작용을 위한 (ORM).
|
||||
- 🔍 [Pydantic](https://docs.pydantic.dev): FastAPI에 의해 사용되는, 데이터 검증과 설정관리.
|
||||
- 💾 [PostgreSQL](https://www.postgresql.org): SQL 데이터베이스.
|
||||
- 🚀 [React](https://react.dev): 프론트엔드.
|
||||
- 💃 TypeScript, hooks, Vite 및 기타 현대적인 프론트엔드 스택을 사용.
|
||||
- 🎨 [Chakra UI](https://chakra-ui.com): 프론트엔드 컴포넌트.
|
||||
- 🤖 자동으로 생성된 프론트엔드 클라이언트.
|
||||
- 🧪 E2E 테스트를 위한 Playwright.
|
||||
- 🦇 다크 모드 지원.
|
||||
- 🐋 [Docker Compose](https://www.docker.com): 개발 환경과 프로덕션(운영).
|
||||
- 🔒 기본으로 지원되는 안전한 비밀번호 해싱.
|
||||
- 🔑 JWT 토큰 인증.
|
||||
- 📫 이메일 기반 비밀번호 복구.
|
||||
- ✅ [Pytest]를 이용한 테스트(https://pytest.org).
|
||||
- 📞 [Traefik](https://traefik.io): 리버스 프록시 / 로드 밸런서.
|
||||
- 🚢 Docker Compose를 이용한 배포 지침: 자동 HTTPS 인증서를 처리하기 위한 프론트엔드 Traefik 프록시 설정 방법을 포함.
|
||||
- 🏭 GitHub Actions를 기반으로 CI (지속적인 통합) 및 CD (지속적인 배포).
|
||||
201
docs/nl/docs/features.md
Normal file
201
docs/nl/docs/features.md
Normal file
@@ -0,0 +1,201 @@
|
||||
# Functionaliteit
|
||||
|
||||
## FastAPI functionaliteit
|
||||
|
||||
**FastAPI** biedt je het volgende:
|
||||
|
||||
### Gebaseerd op open standaarden
|
||||
|
||||
* <a href="https://github.com/OAI/OpenAPI-Specification" class="external-link" target="_blank"><strong>OpenAPI</strong></a> voor het maken van API's, inclusief declaraties van <abbr title="ook bekend als: endpoints, routess">pad</abbr><abbr title="ook bekend als HTTP-methoden, zoals POST, GET, PUT, DELETE">bewerkingen</abbr>, parameters, request bodies, beveiliging, enz.
|
||||
* Automatische datamodel documentatie met <a href="https://json-schema.org/" class="external-link" target="_blank"><strong>JSON Schema</strong></a> (aangezien OpenAPI zelf is gebaseerd op JSON Schema).
|
||||
* Ontworpen op basis van deze standaarden, na zorgvuldig onderzoek. In plaats van achteraf deze laag er bovenop te bouwen.
|
||||
* Dit maakt het ook mogelijk om automatisch **clientcode te genereren** in verschillende programmeertalen.
|
||||
|
||||
### Automatische documentatie
|
||||
|
||||
Interactieve API-documentatie en verkenning van webgebruikersinterfaces. Aangezien dit framework is gebaseerd op OpenAPI, zijn er meerdere documentatie opties mogelijk, waarvan er standaard 2 zijn inbegrepen.
|
||||
|
||||
* <a href="https://github.com/swagger-api/swagger-ui" class="external-link" target="_blank"><strong>Swagger UI</strong></a>, met interactieve interface, maakt het mogelijk je API rechtstreeks vanuit de browser aan te roepen en te testen.
|
||||
|
||||

|
||||
|
||||
* Alternatieve API-documentatie met <a href="https://github.com/Rebilly/ReDoc" class="external-link" target="_blank"><strong>ReDoc</strong></a>.
|
||||
|
||||

|
||||
|
||||
### Gewoon Moderne Python
|
||||
|
||||
Het is allemaal gebaseerd op standaard **Python type** declaraties (dankzij Pydantic). Je hoeft dus geen nieuwe syntax te leren. Het is gewoon standaard moderne Python.
|
||||
|
||||
Als je een opfriscursus van 2 minuten nodig hebt over het gebruik van Python types (zelfs als je FastAPI niet gebruikt), bekijk dan deze korte tutorial: [Python Types](python-types.md){.internal-link target=_blank}.
|
||||
|
||||
Je schrijft gewoon standaard Python met types:
|
||||
|
||||
```Python
|
||||
from datetime import date
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
# Declareer een variabele als een str
|
||||
# en krijg editorondersteuning in de functie
|
||||
def main(user_id: str):
|
||||
return user_id
|
||||
|
||||
|
||||
# Een Pydantic model
|
||||
class User(BaseModel):
|
||||
id: int
|
||||
name: str
|
||||
joined: date
|
||||
```
|
||||
|
||||
Vervolgens kan je het op deze manier gebruiken:
|
||||
|
||||
```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` betekent:
|
||||
|
||||
Geef de sleutels (keys) en waarden (values) van de `second_user_data` dict direct door als sleutel-waarden argumenten, gelijk aan: `User(id=4, name=“Mary”, joined=“2018-11-30”)`
|
||||
|
||||
///
|
||||
|
||||
### Editor-ondersteuning
|
||||
|
||||
Het gehele framework is ontworpen om eenvoudig en intuïtief te zijn in gebruik. Alle beslissingen zijn getest op meerdere code-editors nog voordat het daadwerkelijke ontwikkelen begon, om zo de beste ontwikkelervaring te garanderen.
|
||||
|
||||
Uit enquêtes onder Python ontwikkelaars blijkt maar al te duidelijk dat "(automatische) code aanvulling" <a href="https://www.jetbrains.com/research/python-developers-survey-2017/#tools-and-features" class="external-link" target="_blank">een van de meest gebruikte functionaliteiten is</a>.
|
||||
|
||||
Het hele **FastAPI** framework is daarop gebaseerd. Automatische code aanvulling werkt overal.
|
||||
|
||||
Je hoeft zelden terug te vallen op de documentatie.
|
||||
|
||||
Zo kan je editor je helpen:
|
||||
|
||||
* in <a href="https://code.visualstudio.com/" class="external-link" target="_blank">Visual Studio Code</a>:
|
||||
|
||||

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

|
||||
|
||||
Je krijgt autocomletion die je voorheen misschien zelfs voor onmogelijk had gehouden. Zoals bijvoorbeeld de `price` key in een JSON body (die genest had kunnen zijn) die afkomstig is van een request.
|
||||
|
||||
Je hoeft niet langer de verkeerde keys in te typen, op en neer te gaan tussen de documentatie, of heen en weer te scrollen om te checken of je `username` of toch `user_name` had gebruikt.
|
||||
|
||||
### Kort
|
||||
|
||||
Dit framework heeft voor alles verstandige **standaardinstellingen**, met overal optionele configuraties. Alle parameters kunnen worden verfijnd zodat het past bij wat je nodig hebt, om zo de API te kunnen definiëren die jij nodig hebt.
|
||||
|
||||
Maar standaard werkt alles **“gewoon”**.
|
||||
|
||||
### Validatie
|
||||
|
||||
* Validatie voor de meeste (of misschien wel alle?) Python **datatypes**, inclusief:
|
||||
* JSON objecten (`dict`).
|
||||
* JSON array (`list`) die itemtypes definiëren.
|
||||
* String (`str`) velden, die min en max lengtes hebben.
|
||||
* Getallen (`int`, `float`) met min en max waarden, enz.
|
||||
|
||||
* Validatie voor meer exotische typen, zoals:
|
||||
* URL.
|
||||
* E-mail.
|
||||
* UUID.
|
||||
* ...en anderen.
|
||||
|
||||
Alle validatie wordt uitgevoerd door het beproefde en robuuste **Pydantic**.
|
||||
|
||||
### Beveiliging en authenticatie
|
||||
|
||||
Beveiliging en authenticatie is geïntegreerd. Zonder compromissen te doen naar databases of datamodellen.
|
||||
|
||||
Alle beveiligingsschema's gedefinieerd in OpenAPI, inclusief:
|
||||
|
||||
* HTTP Basic.
|
||||
* **OAuth2** (ook met **JWT tokens**). Bekijk de tutorial over [OAuth2 with JWT](tutorial/security/oauth2-jwt.md){.internal-link target=_blank}.
|
||||
* API keys in:
|
||||
* Headers.
|
||||
* Query parameters.
|
||||
* Cookies, enz.
|
||||
|
||||
Plus alle beveiligingsfuncties van Starlette (inclusief **sessiecookies**).
|
||||
|
||||
Gebouwd als een herbruikbare tool met componenten die makkelijk te integreren zijn in en met je systemen, datastores, relationele en NoSQL databases, enz.
|
||||
|
||||
### Dependency Injection
|
||||
|
||||
FastAPI bevat een uiterst eenvoudig, maar uiterst krachtig <abbr title='ook bekend als "componenten", "bronnen", "diensten", "aanbieders"'><strong>Dependency Injection</strong></abbr> systeem.
|
||||
|
||||
* Zelfs dependencies kunnen dependencies hebben, waardoor een hiërarchie of **“graph” van dependencies** ontstaat.
|
||||
* Allemaal **automatisch afgehandeld** door het framework.
|
||||
* Alle dependencies kunnen data nodig hebben van request, de vereiste **padoperaties veranderen** en automatische documentatie verstrekken.
|
||||
* **Automatische validatie** zelfs voor *padoperatie* parameters gedefinieerd in dependencies.
|
||||
* Ondersteuning voor complexe gebruikersauthenticatiesystemen, **databaseverbindingen**, enz.
|
||||
* **Geen compromisen** met databases, gebruikersinterfaces, enz. Maar eenvoudige integratie met ze allemaal.
|
||||
|
||||
### Ongelimiteerde "plug-ins"
|
||||
|
||||
Of anders gezegd, je hebt ze niet nodig, importeer en gebruik de code die je nodig hebt.
|
||||
|
||||
Elke integratie is ontworpen om eenvoudig te gebruiken (met afhankelijkheden), zodat je een “plug-in" kunt maken in 2 regels code, met dezelfde structuur en syntax die wordt gebruikt voor je *padbewerkingen*.
|
||||
|
||||
### Getest
|
||||
|
||||
* 100% <abbr title="De hoeveelheid code die automatisch wordt getest">van de code is getest</abbr>.
|
||||
* 100% <abbr title="Python type annotaties, hiermee kunnen je editor en externe tools je beter ondersteunen">type geannoteerde</abbr> codebase.
|
||||
* Wordt gebruikt in productietoepassingen.
|
||||
|
||||
## Starlette functies
|
||||
|
||||
**FastAPI** is volledig verenigbaar met (en gebaseerd op) <a href="https://www.starlette.io/" class="external-link" target="_blank"><strong>Starlette</strong></a>.
|
||||
|
||||
`FastAPI` is eigenlijk een subklasse van `Starlette`. Dus als je Starlette al kent of gebruikt, zal de meeste functionaliteit op dezelfde manier werken.
|
||||
|
||||
Met **FastAPI** krijg je alle functies van **Starlette** (FastAPI is gewoon Starlette op steroïden):
|
||||
|
||||
* Zeer indrukwekkende prestaties. Het is <a href="https://github.com/encode/starlette#performance" class="external-link" target="_blank">een van de snelste Python frameworks, vergelijkbaar met **NodeJS** en **Go**</a>.
|
||||
* **WebSocket** ondersteuning.
|
||||
* Taken in de achtergrond tijdens het proces.
|
||||
* Opstart- en afsluit events.
|
||||
* Test client gebouwd op HTTPX.
|
||||
* **CORS**, GZip, Statische bestanden, Streaming reacties.
|
||||
* **Sessie en Cookie** ondersteuning.
|
||||
* 100% van de code is getest.
|
||||
* 100% type geannoteerde codebase.
|
||||
|
||||
## Pydantic functionaliteit
|
||||
|
||||
**FastAPI** is volledig verenigbaar met (en gebaseerd op) Pydantic. Dus alle extra <a href="https://docs.pydantic.dev/" class="external-link" target="_blank"><strong>Pydantic</strong></a> code die je nog hebt werkt ook.
|
||||
|
||||
Inclusief externe pakketten die ook gebaseerd zijn op Pydantic, zoals <abbr title="Object-Relational Mapper">ORM</abbr>s, <abbr title="Object-Document Mapper">ODM</abbr>s voor databases.
|
||||
|
||||
Dit betekent ook dat je in veel gevallen het object dat je van een request krijgt **direct naar je database** kunt sturen, omdat alles automatisch wordt gevalideerd.
|
||||
|
||||
Hetzelfde geldt ook andersom, in veel gevallen kun je dus het object dat je krijgt van de database **direct doorgeven aan de client**.
|
||||
|
||||
Met **FastAPI** krijg je alle functionaliteit van **Pydantic** (omdat FastAPI is gebaseerd op Pydantic voor alle dataverwerking):
|
||||
|
||||
* **Geen brainfucks**:
|
||||
* Je hoeft geen nieuwe microtaal voor schemadefinities te leren.
|
||||
* Als je bekend bent Python types, weet je hoe je Pydantic moet gebruiken.
|
||||
* Werkt goed samen met je **<abbr title=“Integrated Development Environment, vergelijkbaar met een code editor”>IDE</abbr>/<abbr title=“Een programma dat controleert op fouten in de code”>linter</abbr>/hersenen**:
|
||||
* Doordat pydantic's datastructuren enkel instanties zijn van klassen, die je definieert, werkt automatische aanvulling, linting, mypy en je intuïtie allemaal goed met je gevalideerde data.
|
||||
* Valideer **complexe structuren**:
|
||||
* Gebruik van hiërarchische Pydantic modellen, Python `typing`'s `List` en `Dict`, enz.
|
||||
* Met validators kunnen complexe dataschema's duidelijk en eenvoudig worden gedefinieerd, gecontroleerd en gedocumenteerd als JSON Schema.
|
||||
* Je kunt diep **geneste JSON** objecten laten valideren en annoteren.
|
||||
* **Uitbreidbaar**:
|
||||
* Met Pydantic kunnen op maat gemaakte datatypen worden gedefinieerd of je kunt validatie uitbreiden met methoden op een model dat is ingericht met de decorator validator.
|
||||
* 100% van de code is getest.
|
||||
494
docs/nl/docs/index.md
Normal file
494
docs/nl/docs/index.md
Normal file
@@ -0,0 +1,494 @@
|
||||
# FastAPI
|
||||
|
||||
<style>
|
||||
.md-content .md-typeset h1 { display: none; }
|
||||
</style>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://fastapi.tiangolo.com"><img src="https://fastapi.tiangolo.com/img/logo-margin/logo-teal.png" alt="FastAPI"></a>
|
||||
</p>
|
||||
<p align="center">
|
||||
<em>FastAPI framework, zeer goede prestaties, eenvoudig te leren, snel te programmeren, klaar voor productie</em>
|
||||
</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">
|
||||
</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">
|
||||
</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>
|
||||
|
||||
---
|
||||
|
||||
**Documentatie**: <a href="https://fastapi.tiangolo.com" target="_blank">https://fastapi.tiangolo.com</a>
|
||||
|
||||
**Broncode**: <a href="https://github.com/fastapi/fastapi" target="_blank">https://github.com/tiangolo/fastapi</a>
|
||||
|
||||
---
|
||||
|
||||
FastAPI is een modern, snel (zeer goede prestaties), web framework voor het bouwen van API's in Python, gebruikmakend van standaard Python type-hints.
|
||||
|
||||
De belangrijkste kenmerken zijn:
|
||||
|
||||
* **Snel**: Zeer goede prestaties, vergelijkbaar met **NodeJS** en **Go** (dankzij Starlette en Pydantic). [Een van de snelste beschikbare Python frameworks](#prestaties).
|
||||
* **Snel te programmeren**: Verhoog de snelheid om functionaliteit te ontwikkelen met ongeveer 200% tot 300%. *
|
||||
* **Minder bugs**: Verminder ongeveer 40% van de door mensen (ontwikkelaars) veroorzaakte fouten. *
|
||||
* **Intuïtief**: Buitengewoon goede ondersteuning voor editors. <abbr title="ook bekend als automatisch aanvullen, automatisch aanvullen, IntelliSense">Overal automische code aanvulling</abbr>. Minder tijd kwijt aan debuggen.
|
||||
* **Eenvoudig**: Ontworpen om gemakkelijk te gebruiken en te leren. Minder tijd nodig om documentatie te lezen.
|
||||
* **Kort**: Minimaliseer codeduplicatie. Elke parameterdeclaratie ondersteunt meerdere functionaliteiten. Minder bugs.
|
||||
* **Robust**: Code gereed voor productie. Met automatische interactieve documentatie.
|
||||
* **Standards-based**: Gebaseerd op (en volledig verenigbaar met) open standaarden voor API's: <a href="https://github.com/OAI/OpenAPI-Specification" class="external-link" target="_blank">OpenAPI</a> (voorheen bekend als Swagger) en <a href="https://json-schema.org/" class="external-link" target="_blank">JSON Schema</a>.
|
||||
|
||||
<small>* schatting op basis van testen met een intern ontwikkelteam en bouwen van productieapplicaties.</small>
|
||||
|
||||
## Sponsors
|
||||
|
||||
<!-- sponsors -->
|
||||
|
||||
{% if sponsors %}
|
||||
{% for sponsor in sponsors.gold -%}
|
||||
<a href="{{ sponsor.url }}" target="_blank" title="{{ sponsor.title }}"><img src="{{ sponsor.img }}" style="border-radius:15px"></a>
|
||||
{% endfor -%}
|
||||
{%- for sponsor in sponsors.silver -%}
|
||||
<a href="{{ sponsor.url }}" target="_blank" title="{{ sponsor.title }}"><img src="{{ sponsor.img }}" style="border-radius:15px"></a>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
<!-- /sponsors -->
|
||||
|
||||
<a href="https://fastapi.tiangolo.com/fastapi-people/#sponsors" class="external-link" target="_blank">Overige sponsoren</a>
|
||||
|
||||
## Meningen
|
||||
|
||||
"_[...] Ik gebruik **FastAPI** heel vaak tegenwoordig. [...] Ik ben van plan om het te gebruiken voor alle **ML-services van mijn team bij Microsoft**. Sommige van deze worden geïntegreerd in het kernproduct van **Windows** en sommige **Office**-producten._"
|
||||
|
||||
<div style="text-align: right; margin-right: 10%;">Kabir Khan - <strong>Microsoft</strong> <a href="https://github.com/fastapi/fastapi/pull/26" target="_blank"><small>(ref)</small></a></div>
|
||||
|
||||
---
|
||||
|
||||
"_We hebben de **FastAPI** library gebruikt om een **REST** server te maken die bevraagd kan worden om **voorspellingen** te maken. [voor Ludwig]_"
|
||||
|
||||
<div style="text-align: right; margin-right: 10%;">Piero Molino, Yaroslav Dudin en Sai Sumanth Miryala - <strong>Uber</strong> <a href="https://eng.uber.com/ludwig-v0-2/" target="_blank"><small>(ref)</small></a></div>
|
||||
|
||||
---
|
||||
|
||||
"_**Netflix** is verheugd om een open-source release aan te kondigen van ons **crisismanagement**-orkestratieframework: **Dispatch**! [gebouwd met **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>
|
||||
|
||||
---
|
||||
|
||||
"_Ik ben super enthousiast over **FastAPI**. Het is zo leuk!_"
|
||||
|
||||
<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> podcast presentator</strong> <a href="https://twitter.com/brianokken/status/1112220079972728832" target="_blank"><small>(ref)</small></a></div>
|
||||
|
||||
---
|
||||
|
||||
"_Wat je hebt gebouwd ziet er echt super solide en gepolijst uit. In veel opzichten is het wat ik wilde dat **Hug** kon zijn - het is echt inspirerend om iemand dit te zien bouwen._"
|
||||
|
||||
<div style="text-align: right; margin-right: 10%;">Timothy Crosley - <strong><a href="https://www.hug.rest/" target="_blank">Hug</a> creator</strong> <a href="https://news.ycombinator.com/item?id=19455465" target="_blank"><small>(ref)</small></a></div>
|
||||
|
||||
---
|
||||
|
||||
"Wie geïnteresseerd is in een **modern framework** voor het bouwen van REST API's, bekijkt best eens **FastAPI** [...] Het is snel, gebruiksvriendelijk en gemakkelijk te leren [...]_"
|
||||
|
||||
"_We zijn overgestapt naar **FastAPI** voor onze **API's** [...] Het gaat jou vast ook bevallen [...]_"
|
||||
|
||||
<div style="text-align: right; margin-right: 10%;">Ines Montani - Matthew Honnibal - <strong><a href="https://explosion.ai" target="_blank">Explosion AI</a> oprichters - <a href="https://spacy.io" target="_blank">spaCy</a> ontwikkelaars</strong> <a href="https://twitter.com/_inesmontani/status/1144173225322143744" target="_blank"><small>(ref)</small></a> - <a href="https://twitter.com/honnibal/status/1144031421859655680" target="_blank"><small>(ref)</small></a></div>
|
||||
|
||||
---
|
||||
|
||||
"_Wie een Python API wil bouwen voor productie, kan ik ten stelligste **FastAPI** aanraden. Het is **prachtig ontworpen**, **eenvoudig te gebruiken** en **gemakkelijk schaalbaar**, het is een **cruciale component** geworden in onze strategie om API's centraal te zetten, en het vereenvoudigt automatisering en diensten zoals onze Virtual TAC Engineer._"
|
||||
|
||||
<div style="text-align: right; margin-right: 10%;">Deon Pillsbury - <strong>Cisco</strong> <a href="https://www.linkedin.com/posts/deonpillsbury_cisco-cx-python-activity-6963242628536487936-trAp/" target="_blank"><small>(ref)</small></a></div>
|
||||
|
||||
---
|
||||
|
||||
## **Typer**, de FastAPI van CLIs
|
||||
|
||||
<a href="https://typer.tiangolo.com" target="_blank"><img src="https://typer.tiangolo.com/img/logo-margin/logo-margin-vector.svg" style="width: 20%;"></a>
|
||||
|
||||
Als je een <abbr title="Command Line Interface">CLI</abbr>-app bouwt die in de terminal moet worden gebruikt in plaats van een web-API, gebruik dan <a href="https://typer.tiangolo.com/ " class="external-link" target="_blank">**Typer**</a>.
|
||||
|
||||
**Typer** is het kleine broertje van FastAPI. En het is bedoeld als de **FastAPI van CLI's**. ️
|
||||
|
||||
## Vereisten
|
||||
|
||||
FastAPI staat op de schouders van reuzen:
|
||||
|
||||
* <a href="https://www.starlette.io/" class="external-link" target="_blank">Starlette</a> voor de webonderdelen.
|
||||
* <a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic</a> voor de datadelen.
|
||||
|
||||
## Installatie
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ pip install "fastapi[standard]"
|
||||
|
||||
---> 100%
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
**Opmerking**: Zet `"fastapi[standard]"` tussen aanhalingstekens om ervoor te zorgen dat het werkt in alle terminals.
|
||||
|
||||
## Voorbeeld
|
||||
|
||||
### Creëer het
|
||||
|
||||
* Maak het bestand `main.py` aan met daarin:
|
||||
|
||||
```Python
|
||||
from typing import Union
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/")
|
||||
def read_root():
|
||||
return {"Hello": "World"}
|
||||
|
||||
|
||||
@app.get("/items/{item_id}")
|
||||
def read_item(item_id: int, q: Union[str, None] = None):
|
||||
return {"item_id": item_id, "q": q}
|
||||
```
|
||||
|
||||
<details markdown="1">
|
||||
<summary>Of maak gebruik van <code>async def</code>...</summary>
|
||||
|
||||
Als je code gebruik maakt van `async` / `await`, gebruik dan `async def`:
|
||||
|
||||
```Python hl_lines="9 14"
|
||||
from typing import Union
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/")
|
||||
async def read_root():
|
||||
return {"Hello": "World"}
|
||||
|
||||
|
||||
@app.get("/items/{item_id}")
|
||||
async def read_item(item_id: int, q: Union[str, None] = None):
|
||||
return {"item_id": item_id, "q": q}
|
||||
```
|
||||
|
||||
**Opmerking**:
|
||||
|
||||
Als je het niet weet, kijk dan in het gedeelte _"Heb je haast?"_ over <a href="https://fastapi.tiangolo.com/async/#in-a-hurry" target="_blank">`async` en `await` in de documentatie</a>.
|
||||
|
||||
</details>
|
||||
|
||||
### Voer het uit
|
||||
|
||||
Run de server met:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ 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 [2248755] using WatchFiles
|
||||
INFO: Started server process [2248757]
|
||||
INFO: Waiting for application startup.
|
||||
INFO: Application startup complete.
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
<details markdown="1">
|
||||
<summary>Over het commando <code>fastapi dev main.py</code>...</summary>
|
||||
|
||||
Het commando `fastapi dev` leest het `main.py` bestand, detecteert de **FastAPI** app, en start een server met <a href="https://www.uvicorn.org" class="external-link" target="_blank">Uvicorn</a>.
|
||||
|
||||
Standaard zal dit commando `fastapi dev` starten met "auto-reload" geactiveerd voor ontwikkeling op het lokale systeem.
|
||||
|
||||
Je kan hier meer over lezen in de <a href="https://fastapi.tiangolo.com/fastapi-cli/" target="_blank">FastAPI CLI documentatie</a>.
|
||||
|
||||
</details>
|
||||
|
||||
### Controleer het
|
||||
|
||||
Open je browser op <a href="http://127.0.0.1:8000/items/5?q=somequery" class="external-link" target="_blank">http://127.0.0.1:8000/items/5?q=somequery</a>.
|
||||
|
||||
Je zult een JSON response zien:
|
||||
|
||||
```JSON
|
||||
{"item_id": 5, "q": "somequery"}
|
||||
```
|
||||
|
||||
Je hebt een API gemaakt die:
|
||||
|
||||
* HTTP verzoeken kan ontvangen op de _paden_ `/` en `/items/{item_id}`.
|
||||
* Beide _paden_ hebben `GET` <em>operaties</em> (ook bekend als HTTP _methoden_).
|
||||
* Het _pad_ `/items/{item_id}` heeft een _pad parameter_ `item_id` dat een `int` moet zijn.
|
||||
* Het _pad_ `/items/{item_id}` heeft een optionele `str` _query parameter_ `q`.
|
||||
|
||||
### Interactieve API documentatie
|
||||
|
||||
Ga naar <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
|
||||
|
||||
Je ziet de automatische interactieve API documentatie (verstrekt door <a href="https://github.com/swagger-api/swagger-ui" class="external-link" target="_blank">Swagger UI</a>):
|
||||
|
||||

|
||||
|
||||
### Alternatieve API documentatie
|
||||
|
||||
Ga vervolgens naar <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>.
|
||||
|
||||
Je ziet de automatische interactieve API documentatie (verstrekt door <a href="https://github.com/Rebilly/ReDoc" class="external-link" target="_blank">ReDoc</a>):
|
||||
|
||||

|
||||
|
||||
## Voorbeeld upgrade
|
||||
|
||||
Pas nu het bestand `main.py` aan om de body van een `PUT` request te ontvangen.
|
||||
|
||||
Dankzij Pydantic kunnen we de body declareren met standaard Python types.
|
||||
|
||||
```Python hl_lines="4 9-12 25-27"
|
||||
from typing import Union
|
||||
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
name: str
|
||||
price: float
|
||||
is_offer: Union[bool, None] = None
|
||||
|
||||
|
||||
@app.get("/")
|
||||
def read_root():
|
||||
return {"Hello": "World"}
|
||||
|
||||
|
||||
@app.get("/items/{item_id}")
|
||||
def read_item(item_id: int, q: Union[str, None] = None):
|
||||
return {"item_id": item_id, "q": q}
|
||||
|
||||
|
||||
@app.put("/items/{item_id}")
|
||||
def update_item(item_id: int, item: Item):
|
||||
return {"item_name": item.name, "item_id": item_id}
|
||||
```
|
||||
|
||||
De `fastapi dev` server zou automatisch moeten herladen.
|
||||
|
||||
### Interactieve API documentatie upgrade
|
||||
|
||||
Ga nu naar <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
|
||||
|
||||
* De interactieve API-documentatie wordt automatisch bijgewerkt, inclusief de nieuwe body:
|
||||
|
||||

|
||||
|
||||
* Klik op de knop "Try it out", hiermee kan je de parameters invullen en direct met de API interacteren:
|
||||
|
||||

|
||||
|
||||
* Klik vervolgens op de knop "Execute", de gebruikersinterface zal communiceren met jouw API, de parameters verzenden, de resultaten ophalen en deze op het scherm tonen:
|
||||
|
||||

|
||||
|
||||
### Alternatieve API documentatie upgrade
|
||||
|
||||
Ga vervolgens naar <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>.
|
||||
|
||||
* De alternatieve documentatie zal ook de nieuwe queryparameter en body weergeven:
|
||||
|
||||

|
||||
|
||||
### Samenvatting
|
||||
|
||||
Samengevat declareer je **eenmalig** de types van parameters, body, etc. als functieparameters.
|
||||
|
||||
Dat doe je met standaard moderne Python types.
|
||||
|
||||
Je hoeft geen nieuwe syntax te leren, de methods of klassen van een specifieke bibliotheek, etc.
|
||||
|
||||
Gewoon standaard **Python**.
|
||||
|
||||
Bijvoorbeeld, voor een `int`:
|
||||
|
||||
```Python
|
||||
item_id: int
|
||||
```
|
||||
|
||||
of voor een complexer `Item` model:
|
||||
|
||||
```Python
|
||||
item: Item
|
||||
```
|
||||
|
||||
...en met die ene verklaring krijg je:
|
||||
|
||||
* Editor ondersteuning, inclusief:
|
||||
* Code aanvulling.
|
||||
* Type validatie.
|
||||
* Validatie van data:
|
||||
* Automatische en duidelijke foutboodschappen wanneer de data ongeldig is.
|
||||
* Validatie zelfs voor diep geneste JSON objecten.
|
||||
* <abbr title="ook bekend als: serialisatie, parsing, marshalling">Conversie</abbr> van invoergegevens: afkomstig van het netwerk naar Python-data en -types. Zoals:
|
||||
* JSON.
|
||||
* Pad parameters.
|
||||
* Query parameters.
|
||||
* Cookies.
|
||||
* Headers.
|
||||
* Formulieren.
|
||||
* Bestanden.
|
||||
* <abbr title="ook bekend als: serialisatie, parsing, marshalling">Conversie</abbr> van uitvoergegevens: converstie van Python-data en -types naar netwerkgegevens (zoals JSON):
|
||||
* Converteer Python types (`str`, `int`, `float`, `bool`, `list`, etc).
|
||||
* `datetime` objecten.
|
||||
* `UUID` objecten.
|
||||
* Database modellen.
|
||||
* ...en nog veel meer.
|
||||
* Automatische interactieve API-documentatie, inclusief 2 alternatieve gebruikersinterfaces:
|
||||
* Swagger UI.
|
||||
* ReDoc.
|
||||
|
||||
---
|
||||
|
||||
Terugkomend op het vorige code voorbeeld, **FastAPI** zal:
|
||||
|
||||
* Valideren dat er een `item_id` bestaat in het pad voor `GET` en `PUT` verzoeken.
|
||||
* Valideren dat het `item_id` van het type `int` is voor `GET` en `PUT` verzoeken.
|
||||
* Wanneer dat niet het geval is, krijgt de cliënt een nuttige, duidelijke foutmelding.
|
||||
* Controleren of er een optionele query parameter is met de naam `q` (zoals in `http://127.0.0.1:8000/items/foo?q=somequery`) voor `GET` verzoeken.
|
||||
* Aangezien de `q` parameter werd gedeclareerd met `= None`, is deze optioneel.
|
||||
* Zonder de `None` declaratie zou deze verplicht zijn (net als bij de body in het geval met `PUT`).
|
||||
* Voor `PUT` verzoeken naar `/items/{item_id}`, lees de body als JSON:
|
||||
* Controleer of het een verplicht attribuut `naam` heeft en dat dat een `str` is.
|
||||
* Controleer of het een verplicht attribuut `price` heeft en dat dat een`float` is.
|
||||
* Controleer of het een optioneel attribuut `is_offer` heeft, dat een `bool` is wanneer het aanwezig is.
|
||||
* Dit alles werkt ook voor diep geneste JSON objecten.
|
||||
* Converteer automatisch van en naar JSON.
|
||||
* Documenteer alles met OpenAPI, dat gebruikt kan worden door:
|
||||
* Interactieve documentatiesystemen.
|
||||
* Automatische client code generatie systemen, voor vele talen.
|
||||
* Biedt 2 interactieve documentatie-webinterfaces aan.
|
||||
|
||||
---
|
||||
|
||||
Dit was nog maar een snel overzicht, maar je zou nu toch al een idee moeten hebben over hoe het allemaal werkt.
|
||||
|
||||
Probeer deze regel te veranderen:
|
||||
|
||||
```Python
|
||||
return {"item_name": item.name, "item_id": item_id}
|
||||
```
|
||||
|
||||
...van:
|
||||
|
||||
```Python
|
||||
... "item_name": item.name ...
|
||||
```
|
||||
|
||||
...naar:
|
||||
|
||||
```Python
|
||||
... "item_price": item.price ...
|
||||
```
|
||||
|
||||
...en zie hoe je editor de attributen automatisch invult en hun types herkent:
|
||||
|
||||

|
||||
|
||||
Voor een vollediger voorbeeld met meer mogelijkheden, zie de <a href="https://fastapi.tiangolo.com/tutorial/">Tutorial - Gebruikershandleiding</a>.
|
||||
|
||||
**Spoiler alert**: de tutorial - gebruikershandleiding bevat:
|
||||
|
||||
* Declaratie van **parameters** op andere plaatsen zoals: **headers**, **cookies**, **formuliervelden** en **bestanden**.
|
||||
* Hoe stel je **validatie restricties** in zoals `maximum_length` of een `regex`.
|
||||
* Een zeer krachtig en eenvoudig te gebruiken **<abbr title="ook bekend als componenten, middelen, verstrekkers, diensten, injectables">Dependency Injection</abbr>** systeem.
|
||||
* Beveiliging en authenticatie, inclusief ondersteuning voor **OAuth2** met **JWT-tokens** en **HTTP Basic** auth.
|
||||
* Meer geavanceerde (maar even eenvoudige) technieken voor het declareren van **diep geneste JSON modellen** (dankzij Pydantic).
|
||||
* **GraphQL** integratie met <a href="https://strawberry.rocks" class="external-link" target="_blank">Strawberry</a> en andere packages.
|
||||
* Veel extra functies (dankzij Starlette) zoals:
|
||||
* **WebSockets**
|
||||
* uiterst gemakkelijke tests gebaseerd op HTTPX en `pytest`
|
||||
* **CORS**
|
||||
* **Cookie Sessions**
|
||||
* ...en meer.
|
||||
|
||||
## Prestaties
|
||||
|
||||
Onafhankelijke TechEmpower benchmarks tonen **FastAPI** applicaties draaiend onder Uvicorn aan als <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">een van de snelste Python frameworks beschikbaar</a>, alleen onder Starlette en Uvicorn zelf (intern gebruikt door FastAPI). (*)
|
||||
|
||||
Zie de sectie <a href="https://fastapi.tiangolo.com/benchmarks/" class="internal-link" target="_blank">Benchmarks</a> om hier meer over te lezen.
|
||||
|
||||
## Afhankelijkheden
|
||||
|
||||
FastAPI maakt gebruik van Pydantic en Starlette.
|
||||
|
||||
### `standard` Afhankelijkheden
|
||||
|
||||
Wanneer je FastAPI installeert met `pip install "fastapi[standard]"`, worden de volgende `standard` optionele afhankelijkheden geïnstalleerd:
|
||||
|
||||
Gebruikt door Pydantic:
|
||||
|
||||
* <a href="https://github.com/JoshData/python-email-validator" target="_blank"><code>email_validator</code></a> - voor email validatie.
|
||||
|
||||
Gebruikt door Starlette:
|
||||
|
||||
* <a href="https://www.python-httpx.org" target="_blank"><code>httpx</code></a> - Vereist indien je de `TestClient` wil gebruiken.
|
||||
* <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - Vereist als je de standaard templateconfiguratie wil gebruiken.
|
||||
* <a href="https://github.com/Kludex/python-multipart" target="_blank"><code>python-multipart</code></a> - Vereist indien je <abbr title="het omzetten van de string die uit een HTTP verzoek komt in Python data">"parsen"</abbr> van formulieren wil ondersteunen met `requests.form()`.
|
||||
|
||||
Gebruikt door FastAPI / Starlette:
|
||||
|
||||
* <a href="https://www.uvicorn.org" target="_blank"><code>uvicorn</code></a> - voor de server die jouw applicatie laadt en bedient.
|
||||
* `fastapi-cli` - om het `fastapi` commando te voorzien.
|
||||
|
||||
### Zonder `standard` Afhankelijkheden
|
||||
|
||||
Indien je de optionele `standard` afhankelijkheden niet wenst te installeren, kan je installeren met `pip install fastapi` in plaats van `pip install "fastapi[standard]"`.
|
||||
|
||||
### Bijkomende Optionele Afhankelijkheden
|
||||
|
||||
Er zijn nog een aantal bijkomende afhankelijkheden die je eventueel kan installeren.
|
||||
|
||||
Bijkomende optionele afhankelijkheden voor Pydantic:
|
||||
|
||||
* <a href="https://docs.pydantic.dev/latest/usage/pydantic_settings/" target="_blank"><code>pydantic-settings</code></a> - voor het beheren van settings.
|
||||
* <a href="https://docs.pydantic.dev/latest/usage/types/extra_types/extra_types/" target="_blank"><code>pydantic-extra-types</code></a> - voor extra data types die gebruikt kunnen worden met Pydantic.
|
||||
|
||||
Bijkomende optionele afhankelijkheden voor FastAPI:
|
||||
|
||||
* <a href="https://github.com/ijl/orjson" target="_blank"><code>orjson</code></a> - Vereist indien je `ORJSONResponse` wil gebruiken.
|
||||
* <a href="https://github.com/esnme/ultrajson" target="_blank"><code>ujson</code></a> - Vereist indien je `UJSONResponse` wil gebruiken.
|
||||
|
||||
## Licentie
|
||||
|
||||
Dit project is gelicenseerd onder de voorwaarden van de MIT licentie.
|
||||
597
docs/nl/docs/python-types.md
Normal file
597
docs/nl/docs/python-types.md
Normal file
@@ -0,0 +1,597 @@
|
||||
# Introductie tot Python Types
|
||||
|
||||
Python biedt ondersteuning voor optionele "type hints" (ook wel "type annotaties" genoemd).
|
||||
|
||||
Deze **"type hints"** of annotaties zijn een speciale syntax waarmee het <abbr title="bijvoorbeeld: str, int, float, bool">type</abbr> van een variabele kan worden gedeclareerd.
|
||||
|
||||
Door types voor je variabelen te declareren, kunnen editors en hulpmiddelen je beter ondersteunen.
|
||||
|
||||
Dit is slechts een **korte tutorial/opfrisser** over Python type hints. Het behandelt enkel het minimum dat nodig is om ze te gebruiken met **FastAPI**... en dat is relatief weinig.
|
||||
|
||||
**FastAPI** is helemaal gebaseerd op deze type hints, ze geven veel voordelen.
|
||||
|
||||
Maar zelfs als je **FastAPI** nooit gebruikt, heb je er baat bij om er iets over te leren.
|
||||
|
||||
/// note
|
||||
|
||||
Als je een Python expert bent en alles al weet over type hints, sla dan dit hoofdstuk over.
|
||||
|
||||
///
|
||||
|
||||
## Motivatie
|
||||
|
||||
Laten we beginnen met een eenvoudig voorbeeld:
|
||||
|
||||
```Python
|
||||
{!../../../docs_src/python_types/tutorial001.py!}
|
||||
```
|
||||
|
||||
Het aanroepen van dit programma leidt tot het volgende resultaat:
|
||||
|
||||
```
|
||||
John Doe
|
||||
```
|
||||
|
||||
De functie voert het volgende uit:
|
||||
|
||||
* Neem een `first_name` en een `last_name`
|
||||
* Converteer de eerste letter van elk naar een hoofdletter met `title()`.
|
||||
``
|
||||
* <abbr title="Voegt ze samen, als één. Met de inhoud van de een na de ander.">Voeg samen</abbr> met een spatie in het midden.
|
||||
|
||||
```Python hl_lines="2"
|
||||
{!../../../docs_src/python_types/tutorial001.py!}
|
||||
```
|
||||
|
||||
### Bewerk het
|
||||
|
||||
Dit is een heel eenvoudig programma.
|
||||
|
||||
Maar stel je nu voor dat je het vanaf nul zou moeten maken.
|
||||
|
||||
Op een gegeven moment zou je aan de definitie van de functie zijn begonnen, je had de parameters klaar...
|
||||
|
||||
Maar dan moet je “die methode die de eerste letter naar hoofdletters converteert” aanroepen.
|
||||
|
||||
Was het `upper`? Was het `uppercase`? `first_uppercase`? `capitalize`?
|
||||
|
||||
Dan roep je de hulp in van je oude programmeursvriend, (automatische) code aanvulling in je editor.
|
||||
|
||||
Je typt de eerste parameter van de functie, `first_name`, dan een punt (`.`) en drukt dan op `Ctrl+Spatie` om de aanvulling te activeren.
|
||||
|
||||
Maar helaas krijg je niets bruikbaars:
|
||||
|
||||
<img src="/img/python-types/image01.png">
|
||||
|
||||
### Types toevoegen
|
||||
|
||||
Laten we een enkele regel uit de vorige versie aanpassen.
|
||||
|
||||
We zullen precies dit fragment, de parameters van de functie, wijzigen van:
|
||||
|
||||
```Python
|
||||
first_name, last_name
|
||||
```
|
||||
|
||||
naar:
|
||||
|
||||
```Python
|
||||
first_name: str, last_name: str
|
||||
```
|
||||
|
||||
Dat is alles.
|
||||
|
||||
Dat zijn de "type hints":
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!../../../docs_src/python_types/tutorial002.py!}
|
||||
```
|
||||
|
||||
Dit is niet hetzelfde als het declareren van standaardwaarden zoals bij:
|
||||
|
||||
```Python
|
||||
first_name="john", last_name="doe"
|
||||
```
|
||||
|
||||
Het is iets anders.
|
||||
|
||||
We gebruiken dubbele punten (`:`), geen gelijkheidstekens (`=`).
|
||||
|
||||
Het toevoegen van type hints verandert normaal gesproken niet wat er gebeurt in je programma t.o.v. wat er zonder type hints zou gebeuren.
|
||||
|
||||
Maar stel je voor dat je weer bezig bent met het maken van een functie, maar deze keer met type hints.
|
||||
|
||||
Op hetzelfde moment probeer je de automatische aanvulling te activeren met `Ctrl+Spatie` en je ziet:
|
||||
|
||||
<img src="/img/python-types/image02.png">
|
||||
|
||||
Nu kun je de opties bekijken en er doorheen scrollen totdat je de optie vindt die “een belletje doet rinkelen”:
|
||||
|
||||
<img src="/img/python-types/image03.png">
|
||||
|
||||
### Meer motivatie
|
||||
|
||||
Bekijk deze functie, deze heeft al type hints:
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!../../../docs_src/python_types/tutorial003.py!}
|
||||
```
|
||||
|
||||
Omdat de editor de types van de variabelen kent, krijgt u niet alleen aanvulling, maar ook controles op fouten:
|
||||
|
||||
<img src="/img/python-types/image04.png">
|
||||
|
||||
Nu weet je hoe je het moet oplossen, converteer `age` naar een string met `str(age)`:
|
||||
|
||||
```Python hl_lines="2"
|
||||
{!../../../docs_src/python_types/tutorial004.py!}
|
||||
```
|
||||
|
||||
## Types declareren
|
||||
|
||||
Je hebt net de belangrijkste plek om type hints te declareren gezien. Namelijk als functieparameters.
|
||||
|
||||
Dit is ook de belangrijkste plek waar je ze gebruikt met **FastAPI**.
|
||||
|
||||
### Eenvoudige types
|
||||
|
||||
Je kunt alle standaard Python types declareren, niet alleen `str`.
|
||||
|
||||
Je kunt bijvoorbeeld het volgende gebruiken:
|
||||
|
||||
* `int`
|
||||
* `float`
|
||||
* `bool`
|
||||
* `bytes`
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!../../../docs_src/python_types/tutorial005.py!}
|
||||
```
|
||||
|
||||
### Generieke types met typeparameters
|
||||
|
||||
Er zijn enkele datastructuren die andere waarden kunnen bevatten, zoals `dict`, `list`, `set` en `tuple` en waar ook de interne waarden hun eigen type kunnen hebben.
|
||||
|
||||
Deze types die interne types hebben worden “**generieke**” types genoemd. Het is mogelijk om ze te declareren, zelfs met hun interne types.
|
||||
|
||||
Om deze types en de interne types te declareren, kun je de standaard Python module `typing` gebruiken. Deze module is speciaal gemaakt om deze type hints te ondersteunen.
|
||||
|
||||
#### Nieuwere versies van Python
|
||||
|
||||
De syntax met `typing` is **verenigbaar** met alle versies, van Python 3.6 tot aan de nieuwste, inclusief Python 3.9, Python 3.10, enz.
|
||||
|
||||
Naarmate Python zich ontwikkelt, worden **nieuwere versies**, met verbeterde ondersteuning voor deze type annotaties, beschikbaar. In veel gevallen hoef je niet eens de `typing` module te importeren en te gebruiken om de type annotaties te declareren.
|
||||
|
||||
Als je een recentere versie van Python kunt kiezen voor je project, kun je profiteren van die extra eenvoud.
|
||||
|
||||
In alle documentatie staan voorbeelden die compatibel zijn met elke versie van Python (als er een verschil is).
|
||||
|
||||
Bijvoorbeeld “**Python 3.6+**” betekent dat het compatibel is met Python 3.6 of hoger (inclusief 3.7, 3.8, 3.9, 3.10, etc). En “**Python 3.9+**” betekent dat het compatibel is met Python 3.9 of hoger (inclusief 3.10, etc).
|
||||
|
||||
Als je de **laatste versies van Python** kunt gebruiken, gebruik dan de voorbeelden voor de laatste versie, die hebben de **beste en eenvoudigste syntax**, bijvoorbeeld “**Python 3.10+**”.
|
||||
|
||||
#### List
|
||||
|
||||
Laten we bijvoorbeeld een variabele definiëren als een `list` van `str`.
|
||||
|
||||
//// tab | Python 3.9+
|
||||
|
||||
Declareer de variabele met dezelfde dubbele punt (`:`) syntax.
|
||||
|
||||
Als type, vul `list` in.
|
||||
|
||||
Doordat de list een type is dat enkele interne types bevat, zet je ze tussen vierkante haakjes:
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!> ../../../docs_src/python_types/tutorial006_py39.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+
|
||||
|
||||
Van `typing`, importeer `List` (met een hoofdletter `L`):
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!> ../../../docs_src/python_types/tutorial006.py!}
|
||||
```
|
||||
|
||||
Declareer de variabele met dezelfde dubbele punt (`:`) syntax.
|
||||
|
||||
Zet als type de `List` die je hebt geïmporteerd uit `typing`.
|
||||
|
||||
Doordat de list een type is dat enkele interne types bevat, zet je ze tussen vierkante haakjes:
|
||||
|
||||
```Python hl_lines="4"
|
||||
{!> ../../../docs_src/python_types/tutorial006.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
/// info
|
||||
|
||||
De interne types tussen vierkante haakjes worden “typeparameters” genoemd.
|
||||
|
||||
In dit geval is `str` de typeparameter die wordt doorgegeven aan `List` (of `list` in Python 3.9 en hoger).
|
||||
|
||||
///
|
||||
|
||||
Dat betekent: “de variabele `items` is een `list`, en elk van de items in deze list is een `str`”.
|
||||
|
||||
/// tip
|
||||
|
||||
Als je Python 3.9 of hoger gebruikt, hoef je `List` niet te importeren uit `typing`, je kunt in plaats daarvan hetzelfde reguliere `list` type gebruiken.
|
||||
|
||||
///
|
||||
|
||||
Door dat te doen, kan je editor ondersteuning bieden, zelfs tijdens het verwerken van items uit de list:
|
||||
|
||||
<img src="/img/python-types/image05.png">
|
||||
|
||||
Zonder types is dat bijna onmogelijk om te bereiken.
|
||||
|
||||
Merk op dat de variabele `item` een van de elementen is in de lijst `items`.
|
||||
|
||||
Toch weet de editor dat het een `str` is, en biedt daar vervolgens ondersteuning voor aan.
|
||||
|
||||
#### Tuple en Set
|
||||
|
||||
Je kunt hetzelfde doen om `tuple`s en `set`s te declareren:
|
||||
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!> ../../../docs_src/python_types/tutorial007_py39.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!> ../../../docs_src/python_types/tutorial007.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
Dit betekent:
|
||||
|
||||
* De variabele `items_t` is een `tuple` met 3 items, een `int`, nog een `int`, en een `str`.
|
||||
* De variabele `items_s` is een `set`, en elk van de items is van het type `bytes`.
|
||||
|
||||
#### Dict
|
||||
|
||||
Om een `dict` te definiëren, geef je 2 typeparameters door, gescheiden door komma's.
|
||||
|
||||
De eerste typeparameter is voor de sleutels (keys) van de `dict`.
|
||||
|
||||
De tweede typeparameter is voor de waarden (values) van het `dict`:
|
||||
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!> ../../../docs_src/python_types/tutorial008_py39.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!> ../../../docs_src/python_types/tutorial008.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
Dit betekent:
|
||||
|
||||
* De variabele `prices` is een `dict`:
|
||||
* De sleutels van dit `dict` zijn van het type `str` (bijvoorbeeld de naam van elk item).
|
||||
* De waarden van dit `dict` zijn van het type `float` (bijvoorbeeld de prijs van elk item).
|
||||
|
||||
#### Union
|
||||
|
||||
Je kunt een variable declareren die van **verschillende types** kan zijn, bijvoorbeeld een `int` of een `str`.
|
||||
|
||||
In Python 3.6 en hoger (inclusief Python 3.10) kun je het `Union`-type van `typing` gebruiken en de mogelijke types die je wilt accepteren, tussen de vierkante haakjes zetten.
|
||||
|
||||
In Python 3.10 is er ook een **nieuwe syntax** waarin je de mogelijke types kunt scheiden door een <abbr title='ook wel "bitwise of operator" genoemd, maar die betekenis is hier niet relevant'>verticale balk (`|`)</abbr>.
|
||||
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!> ../../../docs_src/python_types/tutorial008b_py310.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!> ../../../docs_src/python_types/tutorial008b.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
In beide gevallen betekent dit dat `item` een `int` of een `str` kan zijn.
|
||||
|
||||
#### Mogelijk `None`
|
||||
|
||||
Je kunt declareren dat een waarde een type kan hebben, zoals `str`, maar dat het ook `None` kan zijn.
|
||||
|
||||
In Python 3.6 en hoger (inclusief Python 3.10) kun je het declareren door `Optional` te importeren en te gebruiken vanuit de `typing`-module.
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!../../../docs_src/python_types/tutorial009.py!}
|
||||
```
|
||||
|
||||
Door `Optional[str]` te gebruiken in plaats van alleen `str`, kan de editor je helpen fouten te detecteren waarbij je ervan uit zou kunnen gaan dat een waarde altijd een `str` is, terwijl het in werkelijkheid ook `None` zou kunnen zijn.
|
||||
|
||||
`Optional[EenType]` is eigenlijk een snelkoppeling voor `Union[EenType, None]`, ze zijn equivalent.
|
||||
|
||||
Dit betekent ook dat je in Python 3.10 `EenType | None` kunt gebruiken:
|
||||
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!> ../../../docs_src/python_types/tutorial009_py310.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!> ../../../docs_src/python_types/tutorial009.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+ alternative
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!> ../../../docs_src/python_types/tutorial009b.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
#### Gebruik van `Union` of `Optional`
|
||||
|
||||
Als je een Python versie lager dan 3.10 gebruikt, is dit een tip vanuit mijn **subjectieve** standpunt:
|
||||
|
||||
* 🚨 Vermijd het gebruik van `Optional[EenType]`.
|
||||
* Gebruik in plaats daarvan **`Union[EenType, None]`** ✨.
|
||||
|
||||
Beide zijn gelijkwaardig en onderliggend zijn ze hetzelfde, maar ik zou `Union` aanraden in plaats van `Optional` omdat het woord “**optional**” lijkt te impliceren dat de waarde optioneel is, en het eigenlijk betekent “het kan `None` zijn”, zelfs als het niet optioneel is en nog steeds vereist is.
|
||||
|
||||
Ik denk dat `Union[SomeType, None]` explicieter is over wat het betekent.
|
||||
|
||||
Het gaat alleen om de woorden en naamgeving. Maar die naamgeving kan invloed hebben op hoe jij en je teamgenoten over de code denken.
|
||||
|
||||
Laten we als voorbeeld deze functie nemen:
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!../../../docs_src/python_types/tutorial009c.py!}
|
||||
```
|
||||
|
||||
De parameter `name` is gedefinieerd als `Optional[str]`, maar is **niet optioneel**, je kunt de functie niet aanroepen zonder de parameter:
|
||||
|
||||
```Python
|
||||
say_hi() # Oh, nee, dit geeft een foutmelding! 😱
|
||||
```
|
||||
|
||||
De `name` parameter is **nog steeds vereist** (niet *optioneel*) omdat het geen standaardwaarde heeft. Toch accepteert `name` `None` als waarde:
|
||||
|
||||
```Python
|
||||
say_hi(name=None) # Dit werkt, None is geldig 🎉
|
||||
```
|
||||
|
||||
Het goede nieuws is dat als je eenmaal Python 3.10 gebruikt, je je daar geen zorgen meer over hoeft te maken, omdat je dan gewoon `|` kunt gebruiken om unions van types te definiëren:
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!../../../docs_src/python_types/tutorial009c_py310.py!}
|
||||
```
|
||||
|
||||
Dan hoef je je geen zorgen te maken over namen als `Optional` en `Union`. 😎
|
||||
|
||||
#### Generieke typen
|
||||
|
||||
De types die typeparameters in vierkante haakjes gebruiken, worden **Generieke types** of **Generics** genoemd, bijvoorbeeld:
|
||||
|
||||
//// tab | Python 3.10+
|
||||
|
||||
Je kunt dezelfde ingebouwde types gebruiken als generics (met vierkante haakjes en types erin):
|
||||
|
||||
* `list`
|
||||
* `tuple`
|
||||
* `set`
|
||||
* `dict`
|
||||
|
||||
Hetzelfde als bij Python 3.8, uit de `typing`-module:
|
||||
|
||||
* `Union`
|
||||
* `Optional` (hetzelfde als bij Python 3.8)
|
||||
* ...en anderen.
|
||||
|
||||
In Python 3.10 kun je , als alternatief voor de generieke `Union` en `Optional`, de <abbr title='ook wel "bitwise or operator" genoemd, maar die betekenis is hier niet relevant'>verticale lijn (`|`)</abbr> gebruiken om unions van typen te voorzien, dat is veel beter en eenvoudiger.
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+
|
||||
|
||||
Je kunt dezelfde ingebouwde types gebruiken als generieke types (met vierkante haakjes en types erin):
|
||||
|
||||
* `list`
|
||||
* `tuple`
|
||||
* `set`
|
||||
* `dict`
|
||||
|
||||
En hetzelfde als met Python 3.8, vanuit de `typing`-module:
|
||||
|
||||
* `Union`
|
||||
* `Optional`
|
||||
* ...en anderen.
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+
|
||||
|
||||
* `List`
|
||||
* `Tuple`
|
||||
* `Set`
|
||||
* `Dict`
|
||||
* `Union`
|
||||
* `Optional`
|
||||
* ...en anderen.
|
||||
|
||||
////
|
||||
|
||||
### Klassen als types
|
||||
|
||||
Je kunt een klasse ook declareren als het type van een variabele.
|
||||
|
||||
Stel dat je een klasse `Person` hebt, met een naam:
|
||||
|
||||
```Python hl_lines="1-3"
|
||||
{!../../../docs_src/python_types/tutorial010.py!}
|
||||
```
|
||||
|
||||
Vervolgens kun je een variabele van het type `Persoon` declareren:
|
||||
|
||||
```Python hl_lines="6"
|
||||
{!../../../docs_src/python_types/tutorial010.py!}
|
||||
```
|
||||
|
||||
Dan krijg je ook nog eens volledige editorondersteuning:
|
||||
|
||||
<img src="/img/python-types/image06.png">
|
||||
|
||||
Merk op dat dit betekent dat "`one_person` een **instantie** is van de klasse `Person`".
|
||||
|
||||
Dit betekent niet dat `one_person` de **klasse** is met de naam `Person`.
|
||||
|
||||
## Pydantic modellen
|
||||
|
||||
<a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic</a> is een Python-pakket voor het uitvoeren van datavalidatie.
|
||||
|
||||
Je declareert de "vorm" van de data als klassen met attributen.
|
||||
|
||||
Elk attribuut heeft een type.
|
||||
|
||||
Vervolgens maak je een instantie van die klasse met een aantal waarden en het valideert de waarden, converteert ze naar het juiste type (als dat het geval is) en geeft je een object met alle data terug.
|
||||
|
||||
Daarnaast krijg je volledige editorondersteuning met dat resulterende object.
|
||||
|
||||
Een voorbeeld uit de officiële Pydantic-documentatie:
|
||||
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python
|
||||
{!> ../../../docs_src/python_types/tutorial011_py310.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python
|
||||
{!> ../../../docs_src/python_types/tutorial011_py39.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+
|
||||
|
||||
```Python
|
||||
{!> ../../../docs_src/python_types/tutorial011.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
/// info
|
||||
|
||||
Om meer te leren over <a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic, bekijk de documentatie</a>.
|
||||
|
||||
///
|
||||
|
||||
**FastAPI** is volledig gebaseerd op Pydantic.
|
||||
|
||||
Je zult veel meer van dit alles in de praktijk zien in de [Tutorial - Gebruikershandleiding](tutorial/index.md){.internal-link target=_blank}.
|
||||
|
||||
/// tip
|
||||
|
||||
Pydantic heeft een speciaal gedrag wanneer je `Optional` of `Union[EenType, None]` gebruikt zonder een standaardwaarde, je kunt er meer over lezen in de Pydantic-documentatie over <a href="https://docs.pydantic.dev/2.3/usage/models/#required-fields" class="external-link" target="_blank">Verplichte optionele velden</a>.
|
||||
|
||||
///
|
||||
|
||||
## Type Hints met Metadata Annotaties
|
||||
|
||||
Python heeft ook een functie waarmee je **extra <abbr title="Data over de data, in dit geval informatie over het type, bijvoorbeeld een beschrijving.">metadata</abbr>** in deze type hints kunt toevoegen met behulp van `Annotated`.
|
||||
|
||||
//// tab | Python 3.9+
|
||||
|
||||
In Python 3.9 is `Annotated` onderdeel van de standaardpakket, dus je kunt het importeren vanuit `typing`.
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!> ../../../docs_src/python_types/tutorial013_py39.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+
|
||||
|
||||
In versies lager dan Python 3.9 importeer je `Annotated` vanuit `typing_extensions`.
|
||||
|
||||
Het wordt al geïnstalleerd met **FastAPI**.
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!> ../../../docs_src/python_types/tutorial013.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
Python zelf doet niets met deze `Annotated` en voor editors en andere hulpmiddelen is het type nog steeds een `str`.
|
||||
|
||||
Maar je kunt deze ruimte in `Annotated` gebruiken om **FastAPI** te voorzien van extra metadata over hoe je wilt dat je applicatie zich gedraagt.
|
||||
|
||||
Het belangrijkste om te onthouden is dat **de eerste *typeparameter*** die je doorgeeft aan `Annotated` het **werkelijke type** is. De rest is gewoon metadata voor andere hulpmiddelen.
|
||||
|
||||
Voor nu hoef je alleen te weten dat `Annotated` bestaat en dat het standaard Python is. 😎
|
||||
|
||||
Later zul je zien hoe **krachtig** het kan zijn.
|
||||
|
||||
/// tip
|
||||
|
||||
Het feit dat dit **standaard Python** is, betekent dat je nog steeds de **best mogelijke ontwikkelaarservaring** krijgt in je editor, met de hulpmiddelen die je gebruikt om je code te analyseren en te refactoren, enz. ✨
|
||||
|
||||
Daarnaast betekent het ook dat je code zeer verenigbaar zal zijn met veel andere Python-hulpmiddelen en -pakketten. 🚀
|
||||
|
||||
///
|
||||
|
||||
## Type hints in **FastAPI**
|
||||
|
||||
**FastAPI** maakt gebruik van type hints om verschillende dingen te doen.
|
||||
|
||||
Met **FastAPI** declareer je parameters met type hints en krijg je:
|
||||
|
||||
* **Editor ondersteuning**.
|
||||
* **Type checks**.
|
||||
|
||||
...en **FastAPI** gebruikt dezelfde declaraties om:
|
||||
|
||||
* **Vereisten te definïeren **: van request pad parameters, query parameters, headers, bodies, dependencies, enz.
|
||||
* **Data te converteren**: van de request naar het vereiste type.
|
||||
* **Data te valideren**: afkomstig van elke request:
|
||||
* **Automatische foutmeldingen** te genereren die naar de client worden geretourneerd wanneer de data ongeldig is.
|
||||
* De API met OpenAPI te **documenteren**:
|
||||
* die vervolgens wordt gebruikt door de automatische interactieve documentatie gebruikersinterfaces.
|
||||
|
||||
Dit klinkt misschien allemaal abstract. Maak je geen zorgen. Je ziet dit allemaal in actie in de [Tutorial - Gebruikershandleiding](tutorial/index.md){.internal-link target=_blank}.
|
||||
|
||||
Het belangrijkste is dat door standaard Python types te gebruiken, op één plek (in plaats van meer klassen, decorators, enz. toe te voegen), **FastAPI** een groot deel van het werk voor je doet.
|
||||
|
||||
/// info
|
||||
|
||||
Als je de hele tutorial al hebt doorgenomen en terug bent gekomen om meer te weten te komen over types, is een goede bron <a href="https://mypy.readthedocs.io/en/latest/cheat_sheet_py3.html" class="external-link" target="_blank">het "cheat sheet" van `mypy`</a>.
|
||||
|
||||
///
|
||||
1
docs/nl/mkdocs.yml
Normal file
1
docs/nl/mkdocs.yml
Normal file
@@ -0,0 +1 @@
|
||||
INHERIT: ../en/mkdocs.yml
|
||||
@@ -72,7 +72,7 @@ Note que a função de teste é `async def` agora, no lugar de apenas `def` como
|
||||
|
||||
Então podemos criar um `AsyncClient` com a aplicação, e enviar requisições assíncronas para ela utilizando `await`.
|
||||
|
||||
```Python hl_lines="9-10"
|
||||
```Python hl_lines="9-12"
|
||||
{!../../../docs_src/async_tests/test_main.py!}
|
||||
```
|
||||
|
||||
|
||||
192
docs/pt/docs/advanced/security/http-basic-auth.md
Normal file
192
docs/pt/docs/advanced/security/http-basic-auth.md
Normal file
@@ -0,0 +1,192 @@
|
||||
# HTTP Basic Auth
|
||||
|
||||
Para os casos mais simples, você pode utilizar o HTTP Basic Auth.
|
||||
|
||||
No HTTP Basic Auth, a aplicação espera um cabeçalho que contém um usuário e uma senha.
|
||||
|
||||
Caso ela não receba, ela retorna um erro HTTP 401 "Unauthorized" (*Não Autorizado*).
|
||||
|
||||
E retorna um cabeçalho `WWW-Authenticate` com o valor `Basic`, e um parâmetro opcional `realm`.
|
||||
|
||||
Isso sinaliza ao navegador para mostrar o prompt integrado para um usuário e senha.
|
||||
|
||||
Então, quando você digitar o usuário e senha, o navegador os envia automaticamente no cabeçalho.
|
||||
|
||||
## HTTP Basic Auth Simples
|
||||
|
||||
* Importe `HTTPBasic` e `HTTPBasicCredentials`.
|
||||
* Crie um "esquema `security`" utilizando `HTTPBasic`.
|
||||
* Utilize o `security` com uma dependência em sua *operação de rota*.
|
||||
* Isso retorna um objeto do tipo `HTTPBasicCredentials`:
|
||||
* Isto contém o `username` e o `password` enviado.
|
||||
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python hl_lines="4 8 12"
|
||||
{!> ../../../docs_src/security/tutorial006_an_py39.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+
|
||||
|
||||
```Python hl_lines="2 7 11"
|
||||
{!> ../../../docs_src/security/tutorial006_an.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+ non-Annotated
|
||||
|
||||
/// tip | "Dica"
|
||||
|
||||
Prefira utilizar a versão `Annotated` se possível.
|
||||
|
||||
///
|
||||
|
||||
```Python hl_lines="2 6 10"
|
||||
{!> ../../../docs_src/security/tutorial006.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
Quando você tentar abrir a URL pela primeira vez (ou clicar no botão "Executar" nos documentos) o navegador vai pedir pelo seu usuário e senha:
|
||||
|
||||
<img src="/img/tutorial/security/image12.png">
|
||||
|
||||
## Verifique o usuário
|
||||
|
||||
Aqui está um exemplo mais completo.
|
||||
|
||||
Utilize uma dependência para verificar se o usuário e a senha estão corretos.
|
||||
|
||||
Para isso, utilize o módulo padrão do Python <a href="https://docs.python.org/3/library/secrets.html" class="external-link" target="_blank">`secrets`</a> para verificar o usuário e senha.
|
||||
|
||||
O `secrets.compare_digest()` necessita receber `bytes` ou `str` que possuem apenas caracteres ASCII (os em Inglês). Isso significa que não funcionaria com caracteres como o `á`, como em `Sebastián`.
|
||||
|
||||
Para lidar com isso, primeiramente nós convertemos o `username` e o `password` para `bytes`, codificando-os com UTF-8.
|
||||
|
||||
Então nós podemos utilizar o `secrets.compare_digest()` para garantir que o `credentials.username` é `"stanleyjobson"`, e que o `credentials.password` é `"swordfish"`.
|
||||
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python hl_lines="1 12-24"
|
||||
{!> ../../../docs_src/security/tutorial007_an_py39.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+
|
||||
|
||||
```Python hl_lines="1 12-24"
|
||||
{!> ../../../docs_src/security/tutorial007_an.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+ non-Annotated
|
||||
|
||||
/// tip | "Dica"
|
||||
|
||||
Prefira utilizar a versão `Annotated` se possível.
|
||||
|
||||
///
|
||||
|
||||
```Python hl_lines="1 11-21"
|
||||
{!> ../../../docs_src/security/tutorial007.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
Isso seria parecido com:
|
||||
|
||||
```Python
|
||||
if not (credentials.username == "stanleyjobson") or not (credentials.password == "swordfish"):
|
||||
# Return some error
|
||||
...
|
||||
```
|
||||
|
||||
Porém ao utilizar o `secrets.compare_digest()`, isso estará seguro contra um tipo de ataque chamado "ataque de temporização (timing attacks)".
|
||||
|
||||
### Ataques de Temporização
|
||||
|
||||
Mas o que é um "ataque de temporização"?
|
||||
|
||||
Vamos imaginar que alguns invasores estão tentando adivinhar o usuário e a senha.
|
||||
|
||||
E eles enviam uma requisição com um usuário `johndoe` e uma senha `love123`.
|
||||
|
||||
Então o código Python em sua aplicação seria equivalente a algo como:
|
||||
|
||||
```Python
|
||||
if "johndoe" == "stanleyjobson" and "love123" == "swordfish":
|
||||
...
|
||||
```
|
||||
|
||||
Mas no exato momento que o Python compara o primeiro `j` em `johndoe` contra o primeiro `s` em `stanleyjobson`, ele retornará `False`, porque ele já sabe que aquelas duas strings não são a mesma, pensando que "não existe a necessidade de desperdiçar mais poder computacional comparando o resto das letras". E a sua aplicação dirá "Usuário ou senha incorretos".
|
||||
|
||||
Mas então os invasores vão tentar com o usuário `stanleyjobsox` e a senha `love123`.
|
||||
|
||||
E a sua aplicação faz algo como:
|
||||
|
||||
```Python
|
||||
if "stanleyjobsox" == "stanleyjobson" and "love123" == "swordfish":
|
||||
...
|
||||
```
|
||||
|
||||
O Python terá que comparar todo o `stanleyjobso` tanto em `stanleyjobsox` como em `stanleyjobson` antes de perceber que as strings não são a mesma. Então isso levará alguns microsegundos a mais para retornar "Usuário ou senha incorretos".
|
||||
|
||||
#### O tempo para responder ajuda os invasores
|
||||
|
||||
Neste ponto, ao perceber que o servidor demorou alguns microsegundos a mais para enviar o retorno "Usuário ou senha incorretos", os invasores irão saber que eles acertaram _alguma coisa_, algumas das letras iniciais estavam certas.
|
||||
|
||||
E eles podem tentar de novo sabendo que provavelmente é algo mais parecido com `stanleyjobsox` do que com `johndoe`.
|
||||
|
||||
#### Um ataque "profissional"
|
||||
|
||||
Claro, os invasores não tentariam tudo isso de forma manual, eles escreveriam um programa para fazer isso, possivelmente com milhares ou milhões de testes por segundo. E obteriam apenas uma letra a mais por vez.
|
||||
|
||||
Mas fazendo isso, em alguns minutos ou horas os invasores teriam adivinhado o usuário e senha corretos, com a "ajuda" da nossa aplicação, apenas usando o tempo levado para responder.
|
||||
|
||||
#### Corrija com o `secrets.compare_digest()`
|
||||
|
||||
Mas em nosso código nós estamos utilizando o `secrets.compare_digest()`.
|
||||
|
||||
Resumindo, levará o mesmo tempo para comparar `stanleyjobsox` com `stanleyjobson` do que comparar `johndoe` com `stanleyjobson`. E o mesmo para a senha.
|
||||
|
||||
Deste modo, ao utilizar `secrets.compare_digest()` no código de sua aplicação, ela esterá a salvo contra toda essa gama de ataques de segurança.
|
||||
|
||||
|
||||
### Retorne o erro
|
||||
|
||||
Depois de detectar que as credenciais estão incorretas, retorne um `HTTPException` com o status 401 (o mesmo retornado quando nenhuma credencial foi informada) e adicione o cabeçalho `WWW-Authenticate` para fazer com que o navegador mostre o prompt de login novamente:
|
||||
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python hl_lines="26-30"
|
||||
{!> ../../../docs_src/security/tutorial007_an_py39.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+
|
||||
|
||||
```Python hl_lines="26-30"
|
||||
{!> ../../../docs_src/security/tutorial007_an.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+ non-Annotated
|
||||
|
||||
/// tip | "Dica"
|
||||
|
||||
Prefira utilizar a versão `Annotated` se possível.
|
||||
|
||||
///
|
||||
|
||||
```Python hl_lines="23-27"
|
||||
{!> ../../../docs_src/security/tutorial007.py!}
|
||||
```
|
||||
|
||||
////
|
||||
19
docs/pt/docs/advanced/security/index.md
Normal file
19
docs/pt/docs/advanced/security/index.md
Normal file
@@ -0,0 +1,19 @@
|
||||
# Segurança Avançada
|
||||
|
||||
## Funcionalidades Adicionais
|
||||
|
||||
Existem algumas funcionalidades adicionais para lidar com segurança além das cobertas em [Tutorial - Guia de Usuário: Segurança](../../tutorial/security/index.md){.internal-link target=_blank}.
|
||||
|
||||
/// tip | "Dica"
|
||||
|
||||
As próximas seções **não são necessariamente "avançadas"**.
|
||||
|
||||
E é possível que para o seu caso de uso, a solução está em uma delas.
|
||||
|
||||
///
|
||||
|
||||
## Leia o Tutorial primeiro
|
||||
|
||||
As próximas seções pressupõem que você já leu o principal [Tutorial - Guia de Usuário: Segurança](../../tutorial/security/index.md){.internal-link target=_blank}.
|
||||
|
||||
Todas elas são baseadas nos mesmos conceitos, mas permitem algumas funcionalidades extras.
|
||||
7
docs/pt/docs/advanced/testing-events.md
Normal file
7
docs/pt/docs/advanced/testing-events.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# Testando Eventos: inicialização - encerramento
|
||||
|
||||
Quando você precisa que os seus manipuladores de eventos (`startup` e `shutdown`) sejam executados em seus testes, você pode utilizar o `TestClient` usando a instrução `with`:
|
||||
|
||||
```Python hl_lines="9-12 20-24"
|
||||
{!../../../docs_src/app_testing/tutorial003.py!}
|
||||
```
|
||||
298
docs/pt/docs/environment-variables.md
Normal file
298
docs/pt/docs/environment-variables.md
Normal file
@@ -0,0 +1,298 @@
|
||||
# Variáveis de Ambiente
|
||||
|
||||
/// tip | "Dica"
|
||||
|
||||
Se você já sabe o que são "variáveis de ambiente" e como usá-las, pode pular esta seção.
|
||||
|
||||
///
|
||||
|
||||
Uma variável de ambiente (também conhecida como "**env var**") é uma variável que existe **fora** do código Python, no **sistema operacional**, e pode ser lida pelo seu código Python (ou por outros programas também).
|
||||
|
||||
Variáveis de ambiente podem ser úteis para lidar com **configurações** do aplicativo, como parte da **instalação** do Python, etc.
|
||||
|
||||
## Criar e Usar Variáveis de Ambiente
|
||||
|
||||
Você pode **criar** e usar variáveis de ambiente no **shell (terminal)**, sem precisar do Python:
|
||||
|
||||
//// tab | Linux, macOS, Windows Bash
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
// Você pode criar uma variável de ambiente MY_NAME com
|
||||
$ export MY_NAME="Wade Wilson"
|
||||
|
||||
// Então você pode usá-la com outros programas, como
|
||||
$ echo "Hello $MY_NAME"
|
||||
|
||||
Hello Wade Wilson
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
////
|
||||
|
||||
//// tab | Windows PowerShell
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
// Criar uma variável de ambiente MY_NAME
|
||||
$ $Env:MY_NAME = "Wade Wilson"
|
||||
|
||||
// Usá-la com outros programas, como
|
||||
$ echo "Hello $Env:MY_NAME"
|
||||
|
||||
Hello Wade Wilson
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
////
|
||||
|
||||
## Ler Variáveis de Ambiente no Python
|
||||
|
||||
Você também pode criar variáveis de ambiente **fora** do Python, no terminal (ou com qualquer outro método) e depois **lê-las no Python**.
|
||||
|
||||
Por exemplo, você poderia ter um arquivo `main.py` com:
|
||||
|
||||
```Python hl_lines="3"
|
||||
import os
|
||||
|
||||
name = os.getenv("MY_NAME", "World")
|
||||
print(f"Hello {name} from Python")
|
||||
```
|
||||
|
||||
/// tip | "Dica"
|
||||
|
||||
O segundo argumento para <a href="https://docs.python.org/3.8/library/os.html#os.getenv" class="external-link" target="_blank">`os.getenv()`</a> é o valor padrão a ser retornado.
|
||||
|
||||
Se não for fornecido, é `None` por padrão, Aqui fornecemos `"World"` como o valor padrão a ser usado.
|
||||
|
||||
///
|
||||
|
||||
Então você poderia chamar esse programa Python:
|
||||
|
||||
//// tab | Linux, macOS, Windows Bash
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
// Aqui ainda não definimos a variável de ambiente
|
||||
$ python main.py
|
||||
|
||||
// Como não definimos a variável de ambiente, obtemos o valor padrão
|
||||
|
||||
Hello World from Python
|
||||
|
||||
// Mas se criarmos uma variável de ambiente primeiro
|
||||
$ export MY_NAME="Wade Wilson"
|
||||
|
||||
// E então chamar o programa novamente
|
||||
$ python main.py
|
||||
|
||||
// Agora ele pode ler a variável de ambiente
|
||||
|
||||
Hello Wade Wilson from Python
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
////
|
||||
|
||||
//// tab | Windows PowerShell
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
// Aqui ainda não definimos a variável de ambiente
|
||||
$ python main.py
|
||||
|
||||
// Como não definimos a variável de ambiente, obtemos o valor padrão
|
||||
|
||||
Hello World from Python
|
||||
|
||||
// Mas se criarmos uma variável de ambiente primeiro
|
||||
$ $Env:MY_NAME = "Wade Wilson"
|
||||
|
||||
// E então chamar o programa novamente
|
||||
$ python main.py
|
||||
|
||||
// Agora ele pode ler a variável de ambiente
|
||||
|
||||
Hello Wade Wilson from Python
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
////
|
||||
|
||||
Como as variáveis de ambiente podem ser definidas fora do código, mas podem ser lidas pelo código e não precisam ser armazenadas (com versão no `git`) com o restante dos arquivos, é comum usá-las para configurações ou **definições**.
|
||||
|
||||
Você também pode criar uma variável de ambiente apenas para uma **invocação específica do programa**, que só está disponível para aquele programa e apenas pela duração dele.
|
||||
|
||||
Para fazer isso, crie-a na mesma linha, antes do próprio programa:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
// Criar uma variável de ambiente MY_NAME para esta chamada de programa
|
||||
$ MY_NAME="Wade Wilson" python main.py
|
||||
|
||||
// Agora ele pode ler a variável de ambiente
|
||||
|
||||
Hello Wade Wilson from Python
|
||||
|
||||
// A variável de ambiente não existe mais depois
|
||||
$ python main.py
|
||||
|
||||
Hello World from Python
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
/// tip | "Dica"
|
||||
|
||||
Você pode ler mais sobre isso em <a href="https://12factor.net/config" class="external-link" target="_blank">The Twelve-Factor App: Config</a>.
|
||||
|
||||
///
|
||||
|
||||
## Tipos e Validação
|
||||
|
||||
Essas variáveis de ambiente só podem lidar com **strings de texto**, pois são externas ao Python e precisam ser compatíveis com outros programas e com o resto do sistema (e até mesmo com diferentes sistemas operacionais, como Linux, Windows, macOS).
|
||||
|
||||
Isso significa que **qualquer valor** lido em Python de uma variável de ambiente **será uma `str`**, e qualquer conversão para um tipo diferente ou qualquer validação precisa ser feita no código.
|
||||
|
||||
Você aprenderá mais sobre como usar variáveis de ambiente para lidar com **configurações do aplicativo** no [Guia do Usuário Avançado - Configurações e Variáveis de Ambiente](./advanced/settings.md){.internal-link target=_blank}.
|
||||
|
||||
## Variável de Ambiente `PATH`
|
||||
|
||||
Existe uma variável de ambiente **especial** chamada **`PATH`** que é usada pelos sistemas operacionais (Linux, macOS, Windows) para encontrar programas para executar.
|
||||
|
||||
O valor da variável `PATH` é uma longa string composta por diretórios separados por dois pontos `:` no Linux e macOS, e por ponto e vírgula `;` no Windows.
|
||||
|
||||
Por exemplo, a variável de ambiente `PATH` poderia ter esta aparência:
|
||||
|
||||
//// tab | Linux, macOS
|
||||
|
||||
```plaintext
|
||||
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
|
||||
```
|
||||
|
||||
Isso significa que o sistema deve procurar programas nos diretórios:
|
||||
|
||||
* `/usr/local/bin`
|
||||
* `/usr/bin`
|
||||
* `/bin`
|
||||
* `/usr/sbin`
|
||||
* `/sbin`
|
||||
|
||||
////
|
||||
|
||||
//// tab | Windows
|
||||
|
||||
```plaintext
|
||||
C:\Program Files\Python312\Scripts;C:\Program Files\Python312;C:\Windows\System32
|
||||
```
|
||||
|
||||
Isso significa que o sistema deve procurar programas nos diretórios:
|
||||
|
||||
* `C:\Program Files\Python312\Scripts`
|
||||
* `C:\Program Files\Python312`
|
||||
* `C:\Windows\System32`
|
||||
|
||||
////
|
||||
|
||||
Quando você digita um **comando** no terminal, o sistema operacional **procura** o programa em **cada um dos diretórios** listados na variável de ambiente `PATH`.
|
||||
|
||||
Por exemplo, quando você digita `python` no terminal, o sistema operacional procura um programa chamado `python` no **primeiro diretório** dessa lista.
|
||||
|
||||
Se ele o encontrar, então ele o **usará**. Caso contrário, ele continua procurando nos **outros diretórios**.
|
||||
|
||||
### Instalando o Python e Atualizando o `PATH`
|
||||
|
||||
Durante a instalação do Python, você pode ser questionado sobre a atualização da variável de ambiente `PATH`.
|
||||
|
||||
//// tab | Linux, macOS
|
||||
|
||||
Vamos supor que você instale o Python e ele fique em um diretório `/opt/custompython/bin`.
|
||||
|
||||
Se você concordar em atualizar a variável de ambiente `PATH`, o instalador adicionará `/opt/custompython/bin` para a variável de ambiente `PATH`.
|
||||
|
||||
Poderia parecer assim:
|
||||
|
||||
```plaintext
|
||||
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/custompython/bin
|
||||
```
|
||||
|
||||
Dessa forma, ao digitar `python` no terminal, o sistema encontrará o programa Python em `/opt/custompython/bin` (último diretório) e o utilizará.
|
||||
|
||||
////
|
||||
|
||||
//// tab | Windows
|
||||
|
||||
Digamos que você instala o Python e ele acaba em um diretório `C:\opt\custompython\bin`.
|
||||
|
||||
Se você disser sim para atualizar a variável de ambiente `PATH`, o instalador adicionará `C:\opt\custompython\bin` à variável de ambiente `PATH`.
|
||||
|
||||
```plaintext
|
||||
C:\Program Files\Python312\Scripts;C:\Program Files\Python312;C:\Windows\System32;C:\opt\custompython\bin
|
||||
```
|
||||
|
||||
Dessa forma, quando você digitar `python` no terminal, o sistema encontrará o programa Python em `C:\opt\custompython\bin` (o último diretório) e o utilizará.
|
||||
|
||||
////
|
||||
|
||||
Então, se você digitar:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ python
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
//// tab | Linux, macOS
|
||||
|
||||
O sistema **encontrará** o programa `python` em `/opt/custompython/bin` e o executará.
|
||||
|
||||
Seria aproximadamente equivalente a digitar:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ /opt/custompython/bin/python
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
////
|
||||
|
||||
//// tab | Windows
|
||||
|
||||
O sistema **encontrará** o programa `python` em `C:\opt\custompython\bin\python` e o executará.
|
||||
|
||||
Seria aproximadamente equivalente a digitar:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ C:\opt\custompython\bin\python
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
////
|
||||
|
||||
Essas informações serão úteis ao aprender sobre [Ambientes Virtuais](virtual-environments.md){.internal-link target=_blank}.
|
||||
|
||||
## Conclusão
|
||||
|
||||
Com isso, você deve ter uma compreensão básica do que são **variáveis de ambiente** e como usá-las em Python.
|
||||
|
||||
Você também pode ler mais sobre elas na <a href="https://en.wikipedia.org/wiki/Environment_variable" class="external-link" target="_blank">Wikipedia para Variáveis de Ambiente</a>.
|
||||
|
||||
Em muitos casos, não é muito óbvio como as variáveis de ambiente seriam úteis e aplicáveis imediatamente. Mas elas continuam aparecendo em muitos cenários diferentes quando você está desenvolvendo, então é bom saber sobre elas.
|
||||
|
||||
Por exemplo, você precisará dessas informações na próxima seção, sobre [Ambientes Virtuais](virtual-environments.md).
|
||||
115
docs/pt/docs/tutorial/debugging.md
Normal file
115
docs/pt/docs/tutorial/debugging.md
Normal file
@@ -0,0 +1,115 @@
|
||||
# Depuração
|
||||
|
||||
Você pode conectar o depurador no seu editor, por exemplo, com o Visual Studio Code ou PyCharm.
|
||||
|
||||
## Chamar `uvicorn`
|
||||
|
||||
Em seu aplicativo FastAPI, importe e execute `uvicorn` diretamente:
|
||||
|
||||
```Python hl_lines="1 15"
|
||||
{!../../../docs_src/debugging/tutorial001.py!}
|
||||
```
|
||||
|
||||
### Sobre `__name__ == "__main__"`
|
||||
|
||||
O objetivo principal de `__name__ == "__main__"` é ter algum código que seja executado quando seu arquivo for chamado com:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ python myapp.py
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
mas não é chamado quando outro arquivo o importa, como em:
|
||||
|
||||
```Python
|
||||
from myapp import app
|
||||
```
|
||||
|
||||
#### Mais detalhes
|
||||
|
||||
Digamos que seu arquivo se chama `myapp.py`.
|
||||
|
||||
Se você executá-lo com:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ python myapp.py
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
então a variável interna `__name__` no seu arquivo, criada automaticamente pelo Python, terá como valor a string `"__main__"`.
|
||||
|
||||
Então, a seção:
|
||||
|
||||
```Python
|
||||
uvicorn.run(app, host="0.0.0.0", port=8000)
|
||||
```
|
||||
|
||||
vai executar.
|
||||
|
||||
---
|
||||
|
||||
Isso não acontecerá se você importar esse módulo (arquivo).
|
||||
|
||||
Então, se você tiver outro arquivo `importer.py` com:
|
||||
|
||||
```Python
|
||||
from myapp import app
|
||||
|
||||
# Mais um pouco de código
|
||||
```
|
||||
|
||||
nesse caso, a variável criada automaticamente dentro de `myapp.py` não terá a variável `__name__` com o valor `"__main__"`.
|
||||
|
||||
Então, a linha:
|
||||
|
||||
```Python
|
||||
uvicorn.run(app, host="0.0.0.0", port=8000)
|
||||
```
|
||||
|
||||
não será executada.
|
||||
|
||||
/// info | "Informação"
|
||||
|
||||
Para mais informações, consulte <a href="https://docs.python.org/3/library/__main__.html" class="external-link" target="_blank">a documentação oficial do Python</a>.
|
||||
|
||||
///
|
||||
|
||||
## Execute seu código com seu depurador
|
||||
|
||||
Como você está executando o servidor Uvicorn diretamente do seu código, você pode chamar seu programa Python (seu aplicativo FastAPI) diretamente do depurador.
|
||||
|
||||
---
|
||||
|
||||
Por exemplo, no Visual Studio Code, você pode:
|
||||
|
||||
* Ir para o painel "Debug".
|
||||
* "Add configuration...".
|
||||
* Selecionar "Python"
|
||||
* Executar o depurador com a opção "`Python: Current File (Integrated Terminal)`".
|
||||
|
||||
Em seguida, ele iniciará o servidor com seu código **FastAPI**, parará em seus pontos de interrupção, etc.
|
||||
|
||||
Veja como pode parecer:
|
||||
|
||||
<img src="/img/tutorial/debugging/image01.png">
|
||||
|
||||
---
|
||||
|
||||
Se você usar o Pycharm, você pode:
|
||||
|
||||
* Abrir o menu "Executar".
|
||||
* Selecionar a opção "Depurar...".
|
||||
* Então um menu de contexto aparece.
|
||||
* Selecionar o arquivo para depurar (neste caso, `main.py`).
|
||||
|
||||
Em seguida, ele iniciará o servidor com seu código **FastAPI**, parará em seus pontos de interrupção, etc.
|
||||
|
||||
Veja como pode parecer:
|
||||
|
||||
<img src="/img/tutorial/debugging/image02.png">
|
||||
134
docs/pt/docs/tutorial/request-form-models.md
Normal file
134
docs/pt/docs/tutorial/request-form-models.md
Normal file
@@ -0,0 +1,134 @@
|
||||
# Modelos de Formulários
|
||||
|
||||
Você pode utilizar **Modelos Pydantic** para declarar **campos de formulários** no FastAPI.
|
||||
|
||||
/// info | "Informação"
|
||||
|
||||
Para utilizar formulários, instale primeiramente o <a href="https://github.com/Kludex/python-multipart" class="external-link" target="_blank">`python-multipart`</a>.
|
||||
|
||||
Certifique-se de criar um [ambiente virtual](../virtual-environments.md){.internal-link target=_blank}, ativá-lo, e então instalar. Por exemplo:
|
||||
|
||||
```console
|
||||
$ pip install python-multipart
|
||||
```
|
||||
|
||||
///
|
||||
|
||||
/// note | "Nota"
|
||||
|
||||
Isto é suportado desde a versão `0.113.0` do FastAPI. 🤓
|
||||
|
||||
///
|
||||
|
||||
## Modelos Pydantic para Formulários
|
||||
|
||||
Você precisa apenas declarar um **modelo Pydantic** com os campos que deseja receber como **campos de formulários**, e então declarar o parâmetro como um `Form`:
|
||||
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python hl_lines="9-11 15"
|
||||
{!> ../../../docs_src/request_form_models/tutorial001_an_py39.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+
|
||||
|
||||
```Python hl_lines="8-10 14"
|
||||
{!> ../../../docs_src/request_form_models/tutorial001_an.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+ non-Annotated
|
||||
|
||||
/// tip | "Dica"
|
||||
|
||||
Prefira utilizar a versão `Annotated` se possível.
|
||||
|
||||
///
|
||||
|
||||
```Python hl_lines="7-9 13"
|
||||
{!> ../../../docs_src/request_form_models/tutorial001.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
O **FastAPI** irá **extrair** as informações para **cada campo** dos **dados do formulário** na requisição e dar para você o modelo Pydantic que você definiu.
|
||||
|
||||
## Confira os Documentos
|
||||
|
||||
Você pode verificar na UI de documentação em `/docs`:
|
||||
|
||||
<div class="screenshot">
|
||||
<img src="/img/tutorial/request-form-models/image01.png">
|
||||
</div>
|
||||
|
||||
## Proibir Campos Extras de Formulários
|
||||
|
||||
Em alguns casos de uso especiais (provavelmente não muito comum), você pode desejar **restringir** os campos do formulário para aceitar apenas os declarados no modelo Pydantic. E **proibir** qualquer campo **extra**.
|
||||
|
||||
/// note | "Nota"
|
||||
|
||||
Isso é suportado deste a versão `0.114.0` do FastAPI. 🤓
|
||||
|
||||
///
|
||||
|
||||
Você pode utilizar a configuração de modelo do Pydantic para `proibir` qualquer campo `extra`:
|
||||
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python hl_lines="12"
|
||||
{!> ../../../docs_src/request_form_models/tutorial002_an_py39.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+
|
||||
|
||||
```Python hl_lines="11"
|
||||
{!> ../../../docs_src/request_form_models/tutorial002_an.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+ non-Annotated
|
||||
|
||||
/// tip
|
||||
|
||||
Prefira utilizar a versão `Annotated` se possível.
|
||||
|
||||
///
|
||||
|
||||
```Python hl_lines="10"
|
||||
{!> ../../../docs_src/request_form_models/tutorial002.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
Caso um cliente tente enviar informações adicionais, ele receberá um retorno de **erro**.
|
||||
|
||||
Por exemplo, se o cliente tentar enviar os campos de formulário:
|
||||
|
||||
* `username`: `Rick`
|
||||
* `password`: `Portal Gun`
|
||||
* `extra`: `Mr. Poopybutthole`
|
||||
|
||||
Ele receberá um retorno de erro informando-o que o campo `extra` não é permitido:
|
||||
|
||||
```json
|
||||
{
|
||||
"detail": [
|
||||
{
|
||||
"type": "extra_forbidden",
|
||||
"loc": ["body", "extra"],
|
||||
"msg": "Extra inputs are not permitted",
|
||||
"input": "Mr. Poopybutthole"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Resumo
|
||||
|
||||
Você pode utilizar modelos Pydantic para declarar campos de formulários no FastAPI. 😎
|
||||
249
docs/pt/docs/tutorial/testing.md
Normal file
249
docs/pt/docs/tutorial/testing.md
Normal file
@@ -0,0 +1,249 @@
|
||||
# Testando
|
||||
|
||||
Graças ao <a href="https://www.starlette.io/testclient/" class="external-link" target="_blank">Starlette</a>, testar aplicativos **FastAPI** é fácil e agradável.
|
||||
|
||||
Ele é baseado no <a href="https://www.python-httpx.org" class="external-link" target="_blank">HTTPX</a>, que por sua vez é projetado com base em Requests, por isso é muito familiar e intuitivo.
|
||||
|
||||
Com ele, você pode usar o <a href="https://docs.pytest.org/" class="external-link" target="_blank">pytest</a> diretamente com **FastAPI**.
|
||||
|
||||
## Usando `TestClient`
|
||||
|
||||
/// info | "Informação"
|
||||
|
||||
Para usar o `TestClient`, primeiro instale o <a href="https://www.python-httpx.org" class="external-link" target="_blank">`httpx`</a>.
|
||||
|
||||
Certifique-se de criar um [ambiente virtual](../virtual-environments.md){.internal-link target=_blank}, ativá-lo e instalá-lo, por exemplo:
|
||||
|
||||
```console
|
||||
$ pip install httpx
|
||||
```
|
||||
|
||||
///
|
||||
|
||||
Importe `TestClient`.
|
||||
|
||||
Crie um `TestClient` passando seu aplicativo **FastAPI** para ele.
|
||||
|
||||
Crie funções com um nome que comece com `test_` (essa é a convenção padrão do `pytest`).
|
||||
|
||||
Use o objeto `TestClient` da mesma forma que você faz com `httpx`.
|
||||
|
||||
Escreva instruções `assert` simples com as expressões Python padrão que você precisa verificar (novamente, `pytest` padrão).
|
||||
|
||||
```Python hl_lines="2 12 15-18"
|
||||
{!../../../docs_src/app_testing/tutorial001.py!}
|
||||
```
|
||||
|
||||
/// tip | "Dica"
|
||||
|
||||
Observe que as funções de teste são `def` normais, não `async def`.
|
||||
|
||||
E as chamadas para o cliente também são chamadas normais, não usando `await`.
|
||||
|
||||
Isso permite que você use `pytest` diretamente sem complicações.
|
||||
|
||||
///
|
||||
|
||||
/// note | "Detalhes técnicos"
|
||||
|
||||
Você também pode usar `from starlette.testclient import TestClient`.
|
||||
|
||||
**FastAPI** fornece o mesmo `starlette.testclient` que `fastapi.testclient` apenas como uma conveniência para você, o desenvolvedor. Mas ele vem diretamente da Starlette.
|
||||
|
||||
///
|
||||
|
||||
/// tip | "Dica"
|
||||
|
||||
Se você quiser chamar funções `async` em seus testes além de enviar solicitações ao seu aplicativo FastAPI (por exemplo, funções de banco de dados assíncronas), dê uma olhada em [Testes assíncronos](../advanced/async-tests.md){.internal-link target=_blank} no tutorial avançado.
|
||||
|
||||
///
|
||||
|
||||
## Separando testes
|
||||
|
||||
Em uma aplicação real, você provavelmente teria seus testes em um arquivo diferente.
|
||||
|
||||
E seu aplicativo **FastAPI** também pode ser composto de vários arquivos/módulos, etc.
|
||||
|
||||
### Arquivo do aplicativo **FastAPI**
|
||||
|
||||
Digamos que você tenha uma estrutura de arquivo conforme descrito em [Aplicativos maiores](bigger-applications.md){.internal-link target=_blank}:
|
||||
|
||||
```
|
||||
.
|
||||
├── app
|
||||
│ ├── __init__.py
|
||||
│ └── main.py
|
||||
```
|
||||
|
||||
No arquivo `main.py` você tem seu aplicativo **FastAPI**:
|
||||
|
||||
|
||||
```Python
|
||||
{!../../../docs_src/app_testing/main.py!}
|
||||
```
|
||||
|
||||
### Arquivo de teste
|
||||
|
||||
Então você poderia ter um arquivo `test_main.py` com seus testes. Ele poderia estar no mesmo pacote Python (o mesmo diretório com um arquivo `__init__.py`):
|
||||
|
||||
``` hl_lines="5"
|
||||
.
|
||||
├── app
|
||||
│ ├── __init__.py
|
||||
│ ├── main.py
|
||||
│ └── test_main.py
|
||||
```
|
||||
|
||||
Como esse arquivo está no mesmo pacote, você pode usar importações relativas para importar o objeto `app` do módulo `main` (`main.py`):
|
||||
|
||||
```Python hl_lines="3"
|
||||
{!../../../docs_src/app_testing/test_main.py!}
|
||||
```
|
||||
|
||||
...e ter o código para os testes como antes.
|
||||
|
||||
## Testando: exemplo estendido
|
||||
|
||||
Agora vamos estender este exemplo e adicionar mais detalhes para ver como testar diferentes partes.
|
||||
|
||||
### Arquivo de aplicativo **FastAPI** estendido
|
||||
|
||||
Vamos continuar com a mesma estrutura de arquivo de antes:
|
||||
|
||||
```
|
||||
.
|
||||
├── app
|
||||
│ ├── __init__.py
|
||||
│ ├── main.py
|
||||
│ └── test_main.py
|
||||
```
|
||||
|
||||
Digamos que agora o arquivo `main.py` com seu aplicativo **FastAPI** tenha algumas outras **operações de rotas**.
|
||||
|
||||
Ele tem uma operação `GET` que pode retornar um erro.
|
||||
|
||||
Ele tem uma operação `POST` que pode retornar vários erros.
|
||||
|
||||
Ambas as *operações de rotas* requerem um cabeçalho `X-Token`.
|
||||
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python
|
||||
{!> ../../../docs_src/app_testing/app_b_an_py310/main.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python
|
||||
{!> ../../../docs_src/app_testing/app_b_an_py39/main.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+
|
||||
|
||||
```Python
|
||||
{!> ../../../docs_src/app_testing/app_b_an/main.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.10+ non-Annotated
|
||||
|
||||
/// tip | "Dica"
|
||||
|
||||
Prefira usar a versão `Annotated` se possível.
|
||||
|
||||
///
|
||||
|
||||
```Python
|
||||
{!> ../../../docs_src/app_testing/app_b_py310/main.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+ non-Annotated
|
||||
|
||||
/// tip | "Dica"
|
||||
|
||||
Prefira usar a versão `Annotated` se possível.
|
||||
|
||||
///
|
||||
|
||||
```Python
|
||||
{!> ../../../docs_src/app_testing/app_b/main.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
### Arquivo de teste estendido
|
||||
|
||||
Você pode então atualizar `test_main.py` com os testes estendidos:
|
||||
|
||||
```Python
|
||||
{!> ../../../docs_src/app_testing/app_b/test_main.py!}
|
||||
```
|
||||
|
||||
Sempre que você precisar que o cliente passe informações na requisição e não souber como, você pode pesquisar (no Google) como fazer isso no `httpx`, ou até mesmo como fazer isso com `requests`, já que o design do HTTPX é baseado no design do Requests.
|
||||
|
||||
Depois é só fazer o mesmo nos seus testes.
|
||||
|
||||
Por exemplo:
|
||||
|
||||
* Para passar um parâmetro *path* ou *query*, adicione-o à própria URL.
|
||||
* Para passar um corpo JSON, passe um objeto Python (por exemplo, um `dict`) para o parâmetro `json`.
|
||||
* Se você precisar enviar *Dados de Formulário* em vez de JSON, use o parâmetro `data`.
|
||||
* Para passar *headers*, use um `dict` no parâmetro `headers`.
|
||||
* Para *cookies*, um `dict` no parâmetro `cookies`.
|
||||
|
||||
Para mais informações sobre como passar dados para o backend (usando `httpx` ou `TestClient`), consulte a <a href="https://www.python-httpx.org" class="external-link" target="_blank">documentação do HTTPX</a>.
|
||||
|
||||
/// info | "Informação"
|
||||
|
||||
Observe que o `TestClient` recebe dados que podem ser convertidos para JSON, não para modelos Pydantic.
|
||||
|
||||
Se você tiver um modelo Pydantic em seu teste e quiser enviar seus dados para o aplicativo durante o teste, poderá usar o `jsonable_encoder` descrito em [Codificador compatível com JSON](encoder.md){.internal-link target=_blank}.
|
||||
|
||||
///
|
||||
|
||||
## Execute-o
|
||||
|
||||
Depois disso, você só precisa instalar o `pytest`.
|
||||
|
||||
Certifique-se de criar um [ambiente virtual](../virtual-environments.md){.internal-link target=_blank}, ativá-lo e instalá-lo, por exemplo:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ pip install pytest
|
||||
|
||||
---> 100%
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
Ele detectará os arquivos e os testes automaticamente, os executará e informará os resultados para você.
|
||||
|
||||
Execute os testes com:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ pytest
|
||||
|
||||
================ test session starts ================
|
||||
platform linux -- Python 3.6.9, pytest-5.3.5, py-1.8.1, pluggy-0.13.1
|
||||
rootdir: /home/user/code/superawesome-cli/app
|
||||
plugins: forked-1.1.3, xdist-1.31.0, cov-2.8.1
|
||||
collected 6 items
|
||||
|
||||
---> 100%
|
||||
|
||||
test_main.py <span style="color: green; white-space: pre;">...... [100%]</span>
|
||||
|
||||
<span style="color: green;">================= 1 passed in 0.03s =================</span>
|
||||
```
|
||||
|
||||
</div>
|
||||
844
docs/pt/docs/virtual-environments.md
Normal file
844
docs/pt/docs/virtual-environments.md
Normal file
@@ -0,0 +1,844 @@
|
||||
# Ambientes Virtuais
|
||||
|
||||
Ao trabalhar em projetos Python, você provavelmente deve usar um **ambiente virtual** (ou um mecanismo similar) para isolar os pacotes que você instala para cada projeto.
|
||||
|
||||
/// info | "Informação"
|
||||
|
||||
Se você já sabe sobre ambientes virtuais, como criá-los e usá-los, talvez seja melhor pular esta seção. 🤓
|
||||
|
||||
///
|
||||
|
||||
/// tip | "Dica"
|
||||
|
||||
Um **ambiente virtual** é diferente de uma **variável de ambiente**.
|
||||
|
||||
Uma **variável de ambiente** é uma variável no sistema que pode ser usada por programas.
|
||||
|
||||
Um **ambiente virtual** é um diretório com alguns arquivos.
|
||||
|
||||
///
|
||||
|
||||
/// info | "Informação"
|
||||
|
||||
Esta página lhe ensinará como usar **ambientes virtuais** e como eles funcionam.
|
||||
|
||||
Se você estiver pronto para adotar uma **ferramenta que gerencia tudo** para você (incluindo a instalação do Python), experimente <a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">uv</a>.
|
||||
|
||||
///
|
||||
|
||||
## Criar um Projeto
|
||||
|
||||
Primeiro, crie um diretório para seu projeto.
|
||||
|
||||
O que normalmente faço é criar um diretório chamado `code` dentro do meu diretório home/user.
|
||||
|
||||
E dentro disso eu crio um diretório por projeto.
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
// Vá para o diretório inicial
|
||||
$ cd
|
||||
// Crie um diretório para todos os seus projetos de código
|
||||
$ mkdir code
|
||||
// Entre nesse diretório de código
|
||||
$ cd code
|
||||
// Crie um diretório para este projeto
|
||||
$ mkdir awesome-project
|
||||
// Entre no diretório do projeto
|
||||
$ cd awesome-project
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
## Crie um ambiente virtual
|
||||
|
||||
Ao começar a trabalhar em um projeto Python **pela primeira vez**, crie um ambiente virtual **<abbr title="existem outras opções, esta é uma diretriz simples">dentro do seu projeto</abbr>**.
|
||||
|
||||
/// tip | "Dica"
|
||||
|
||||
Você só precisa fazer isso **uma vez por projeto**, não toda vez que trabalhar.
|
||||
|
||||
///
|
||||
|
||||
//// tab | `venv`
|
||||
|
||||
Para criar um ambiente virtual, você pode usar o módulo `venv` que vem com o Python.
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ python -m venv .venv
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
/// details | O que esse comando significa
|
||||
|
||||
* `python`: usa o programa chamado `python`
|
||||
* `-m`: chama um módulo como um script, nós diremos a ele qual módulo vem em seguida
|
||||
* `venv`: usa o módulo chamado `venv` que normalmente vem instalado com o Python
|
||||
* `.venv`: cria o ambiente virtual no novo diretório `.venv`
|
||||
|
||||
///
|
||||
|
||||
////
|
||||
|
||||
//// tab | `uv`
|
||||
|
||||
Se você tiver o <a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">`uv`</a> instalado, poderá usá-lo para criar um ambiente virtual.
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ uv venv
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
/// tip | "Dica"
|
||||
|
||||
Por padrão, `uv` criará um ambiente virtual em um diretório chamado `.venv`.
|
||||
|
||||
Mas você pode personalizá-lo passando um argumento adicional com o nome do diretório.
|
||||
|
||||
///
|
||||
|
||||
////
|
||||
|
||||
Esse comando cria um novo ambiente virtual em um diretório chamado `.venv`.
|
||||
|
||||
/// details | `.venv` ou outro nome
|
||||
|
||||
Você pode criar o ambiente virtual em um diretório diferente, mas há uma convenção para chamá-lo de `.venv`.
|
||||
|
||||
///
|
||||
|
||||
## Ative o ambiente virtual
|
||||
|
||||
Ative o novo ambiente virtual para que qualquer comando Python que você executar ou pacote que você instalar o utilize.
|
||||
|
||||
/// tip | "Dica"
|
||||
|
||||
Faça isso **toda vez** que iniciar uma **nova sessão de terminal** para trabalhar no projeto.
|
||||
|
||||
///
|
||||
|
||||
//// 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
|
||||
|
||||
Ou se você usa o Bash para Windows (por exemplo, <a href="https://gitforwindows.org/" class="external-link" target="_blank">Git Bash</a>):
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ source .venv/Scripts/activate
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
////
|
||||
|
||||
/// tip | "Dica"
|
||||
|
||||
Toda vez que você instalar um **novo pacote** naquele ambiente, **ative** o ambiente novamente.
|
||||
|
||||
Isso garante que, se você usar um **programa de terminal (<abbr title="interface de linha de comando">CLI</abbr>)** instalado por esse pacote, você usará aquele do seu ambiente virtual e não qualquer outro que possa ser instalado globalmente, provavelmente com uma versão diferente do que você precisa.
|
||||
|
||||
///
|
||||
|
||||
## Verifique se o ambiente virtual está ativo
|
||||
|
||||
Verifique se o ambiente virtual está ativo (o comando anterior funcionou).
|
||||
|
||||
/// tip | "Dica"
|
||||
|
||||
Isso é **opcional**, mas é uma boa maneira de **verificar** se tudo está funcionando conforme o esperado e se você está usando o ambiente virtual pretendido.
|
||||
|
||||
///
|
||||
|
||||
//// tab | Linux, macOS, Windows Bash
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ which python
|
||||
|
||||
/home/user/code/awesome-project/.venv/bin/python
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
Se ele mostrar o binário `python` em `.venv/bin/python`, dentro do seu projeto (neste caso `awesome-project`), então funcionou. 🎉
|
||||
|
||||
////
|
||||
|
||||
//// tab | Windows PowerShell
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ Get-Command python
|
||||
|
||||
C:\Users\user\code\awesome-project\.venv\Scripts\python
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
Se ele mostrar o binário `python` em `.venv\Scripts\python`, dentro do seu projeto (neste caso `awesome-project`), então funcionou. 🎉
|
||||
|
||||
////
|
||||
|
||||
## Atualizar `pip`
|
||||
|
||||
/// tip | "Dica"
|
||||
|
||||
Se você usar <a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">`uv`</a>, você o usará para instalar coisas em vez do `pip`, então não precisará atualizar o `pip`. 😎
|
||||
|
||||
///
|
||||
|
||||
Se você estiver usando `pip` para instalar pacotes (ele vem por padrão com o Python), você deve **atualizá-lo** para a versão mais recente.
|
||||
|
||||
Muitos erros exóticos durante a instalação de um pacote são resolvidos apenas atualizando o `pip` primeiro.
|
||||
|
||||
/// tip | "Dica"
|
||||
|
||||
Normalmente, você faria isso **uma vez**, logo após criar o ambiente virtual.
|
||||
|
||||
///
|
||||
|
||||
Certifique-se de que o ambiente virtual esteja ativo (com o comando acima) e execute:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ python -m pip install --upgrade pip
|
||||
|
||||
---> 100%
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
## Adicionar `.gitignore`
|
||||
|
||||
Se você estiver usando **Git** (você deveria), adicione um arquivo `.gitignore` para excluir tudo em seu `.venv` do Git.
|
||||
|
||||
/// tip | "Dica"
|
||||
|
||||
Se você usou <a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">`uv`</a> para criar o ambiente virtual, ele já fez isso para você, você pode pular esta etapa. 😎
|
||||
|
||||
///
|
||||
|
||||
/// tip | "Dica"
|
||||
|
||||
Faça isso **uma vez**, logo após criar o ambiente virtual.
|
||||
|
||||
///
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ echo "*" > .venv/.gitignore
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
/// details | O que esse comando significa
|
||||
|
||||
* `echo "*"`: irá "imprimir" o texto `*` no terminal (a próxima parte muda isso um pouco)
|
||||
* `>`: qualquer coisa impressa no terminal pelo comando à esquerda de `>` não deve ser impressa, mas sim escrita no arquivo que vai à direita de `>`
|
||||
* `.gitignore`: o nome do arquivo onde o texto deve ser escrito
|
||||
|
||||
E `*` para Git significa "tudo". Então, ele ignorará tudo no diretório `.venv`.
|
||||
|
||||
Esse comando criará um arquivo `.gitignore` com o conteúdo:
|
||||
|
||||
```gitignore
|
||||
*
|
||||
```
|
||||
|
||||
///
|
||||
|
||||
## Instalar Pacotes
|
||||
|
||||
Após ativar o ambiente, você pode instalar pacotes nele.
|
||||
|
||||
/// tip | "Dica"
|
||||
|
||||
Faça isso **uma vez** ao instalar ou atualizar os pacotes que seu projeto precisa.
|
||||
|
||||
Se precisar atualizar uma versão ou adicionar um novo pacote, você **fará isso novamente**.
|
||||
|
||||
///
|
||||
|
||||
### Instalar pacotes diretamente
|
||||
|
||||
Se estiver com pressa e não quiser usar um arquivo para declarar os requisitos de pacote do seu projeto, você pode instalá-los diretamente.
|
||||
|
||||
/// tip | "Dica"
|
||||
|
||||
É uma (muito) boa ideia colocar os pacotes e versões que seu programa precisa em um arquivo (por exemplo `requirements.txt` ou `pyproject.toml`).
|
||||
|
||||
///
|
||||
|
||||
//// tab | `pip`
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ pip install "fastapi[standard]"
|
||||
|
||||
---> 100%
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
////
|
||||
|
||||
//// tab | `uv`
|
||||
|
||||
Se você tem o <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>
|
||||
|
||||
////
|
||||
|
||||
### Instalar a partir de `requirements.txt`
|
||||
|
||||
Se você tiver um `requirements.txt`, agora poderá usá-lo para instalar seus pacotes.
|
||||
|
||||
//// tab | `pip`
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ pip install -r requirements.txt
|
||||
---> 100%
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
////
|
||||
|
||||
//// tab | `uv`
|
||||
|
||||
Se você tem o <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`
|
||||
|
||||
Um `requirements.txt` com alguns pacotes poderia se parecer com:
|
||||
|
||||
```requirements.txt
|
||||
fastapi[standard]==0.113.0
|
||||
pydantic==2.8.0
|
||||
```
|
||||
|
||||
///
|
||||
|
||||
## Execute seu programa
|
||||
|
||||
Depois de ativar o ambiente virtual, você pode executar seu programa, e ele usará o Python dentro do seu ambiente virtual com os pacotes que você instalou lá.
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ python main.py
|
||||
|
||||
Hello World
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
## Configure seu editor
|
||||
|
||||
Você provavelmente usaria um editor. Certifique-se de configurá-lo para usar o mesmo ambiente virtual que você criou (ele provavelmente o detectará automaticamente) para que você possa obter erros de preenchimento automático e em linha.
|
||||
|
||||
Por exemplo:
|
||||
|
||||
* <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 | "Dica"
|
||||
|
||||
Normalmente, você só precisa fazer isso **uma vez**, ao criar o ambiente virtual.
|
||||
|
||||
///
|
||||
|
||||
## Desativar o ambiente virtual
|
||||
|
||||
Quando terminar de trabalhar no seu projeto, você pode **desativar** o ambiente virtual.
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ deactivate
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
Dessa forma, quando você executar `python`, ele não tentará executá-lo naquele ambiente virtual com os pacotes instalados nele.
|
||||
|
||||
## Pronto para trabalhar
|
||||
|
||||
Agora você está pronto para começar a trabalhar no seu projeto.
|
||||
|
||||
|
||||
|
||||
/// tip | "Dica"
|
||||
|
||||
Você quer entender o que é tudo isso acima?
|
||||
|
||||
Continue lendo. 👇🤓
|
||||
|
||||
///
|
||||
|
||||
## Por que ambientes virtuais
|
||||
|
||||
Para trabalhar com o FastAPI, você precisa instalar o <a href="https://www.python.org/" class="external-link" target="_blank">Python</a>.
|
||||
|
||||
Depois disso, você precisará **instalar** o FastAPI e quaisquer outros **pacotes** que queira usar.
|
||||
|
||||
Para instalar pacotes, você normalmente usaria o comando `pip` que vem com o Python (ou alternativas semelhantes).
|
||||
|
||||
No entanto, se você usar `pip` diretamente, os pacotes serão instalados no seu **ambiente Python global** (a instalação global do Python).
|
||||
|
||||
### O Problema
|
||||
|
||||
Então, qual é o problema em instalar pacotes no ambiente global do Python?
|
||||
|
||||
Em algum momento, você provavelmente acabará escrevendo muitos programas diferentes que dependem de **pacotes diferentes**. E alguns desses projetos em que você trabalha dependerão de **versões diferentes** do mesmo pacote. 😱
|
||||
|
||||
Por exemplo, você pode criar um projeto chamado `philosophers-stone`, este programa depende de outro pacote chamado **`harry`, usando a versão `1`**. Então, você precisa instalar `harry`.
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
stone(philosophers-stone) -->|requires| harry-1[harry v1]
|
||||
```
|
||||
|
||||
Então, em algum momento depois, você cria outro projeto chamado `prisoner-of-azkaban`, e esse projeto também depende de `harry`, mas esse projeto precisa do **`harry` versão `3`**.
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
azkaban(prisoner-of-azkaban) --> |requires| harry-3[harry v3]
|
||||
```
|
||||
|
||||
Mas agora o problema é que, se você instalar os pacotes globalmente (no ambiente global) em vez de em um **ambiente virtual** local, você terá que escolher qual versão do `harry` instalar.
|
||||
|
||||
Se você quiser executar `philosophers-stone`, precisará primeiro instalar `harry` versão `1`, por exemplo com:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ pip install "harry==1"
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
E então você acabaria com `harry` versão `1` instalado em seu ambiente Python global.
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph global[global env]
|
||||
harry-1[harry v1]
|
||||
end
|
||||
subgraph stone-project[philosophers-stone project]
|
||||
stone(philosophers-stone) -->|requires| harry-1
|
||||
end
|
||||
```
|
||||
|
||||
Mas se você quiser executar `prisoner-of-azkaban`, você precisará desinstalar `harry` versão `1` e instalar `harry` versão `3` (ou apenas instalar a versão `3` desinstalaria automaticamente a versão `1`).
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ pip install "harry==3"
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
E então você acabaria com `harry` versão `3` instalado em seu ambiente Python global.
|
||||
|
||||
E se você tentar executar `philosophers-stone` novamente, há uma chance de que **não funcione** porque ele precisa de `harry` versão `1`.
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph global[global env]
|
||||
harry-1[<strike>harry v1</strike>]
|
||||
style harry-1 fill:#ccc,stroke-dasharray: 5 5
|
||||
harry-3[harry v3]
|
||||
end
|
||||
subgraph stone-project[philosophers-stone project]
|
||||
stone(philosophers-stone) -.-x|⛔️| harry-1
|
||||
end
|
||||
subgraph azkaban-project[prisoner-of-azkaban project]
|
||||
azkaban(prisoner-of-azkaban) --> |requires| harry-3
|
||||
end
|
||||
```
|
||||
|
||||
/// tip | "Dica"
|
||||
|
||||
É muito comum em pacotes Python tentar ao máximo **evitar alterações drásticas** em **novas versões**, mas é melhor prevenir do que remediar e instalar versões mais recentes intencionalmente e, quando possível, executar os testes para verificar se tudo está funcionando corretamente.
|
||||
|
||||
///
|
||||
|
||||
Agora, imagine isso com **muitos** outros **pacotes** dos quais todos os seus **projetos dependem**. Isso é muito difícil de gerenciar. E você provavelmente acabaria executando alguns projetos com algumas **versões incompatíveis** dos pacotes, e não saberia por que algo não está funcionando.
|
||||
|
||||
Além disso, dependendo do seu sistema operacional (por exemplo, Linux, Windows, macOS), ele pode ter vindo com o Python já instalado. E, nesse caso, provavelmente tinha alguns pacotes pré-instalados com algumas versões específicas **necessárias para o seu sistema**. Se você instalar pacotes no ambiente global do Python, poderá acabar **quebrando** alguns dos programas que vieram com seu sistema operacional.
|
||||
|
||||
## Onde os pacotes são instalados
|
||||
|
||||
Quando você instala o Python, ele cria alguns diretórios com alguns arquivos no seu computador.
|
||||
|
||||
Alguns desses diretórios são os responsáveis por ter todos os pacotes que você instala.
|
||||
|
||||
Quando você executa:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
// Não execute isso agora, é apenas um exemplo 🤓
|
||||
$ pip install "fastapi[standard]"
|
||||
---> 100%
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
Isso fará o download de um arquivo compactado com o código FastAPI, normalmente do <a href="https://pypi.org/project/fastapi/" class="external-link" target="_blank">PyPI</a>.
|
||||
|
||||
Ele também fará o **download** de arquivos para outros pacotes dos quais o FastAPI depende.
|
||||
|
||||
Em seguida, ele **extrairá** todos esses arquivos e os colocará em um diretório no seu computador.
|
||||
|
||||
Por padrão, ele colocará os arquivos baixados e extraídos no diretório que vem com a instalação do Python, que é o **ambiente global**.
|
||||
|
||||
## O que são ambientes virtuais
|
||||
|
||||
A solução para os problemas de ter todos os pacotes no ambiente global é usar um **ambiente virtual para cada projeto** em que você trabalha.
|
||||
|
||||
Um ambiente virtual é um **diretório**, muito semelhante ao global, onde você pode instalar os pacotes para um projeto.
|
||||
|
||||
Dessa forma, cada projeto terá seu próprio ambiente virtual (diretório `.venv`) com seus próprios pacotes.
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
subgraph stone-project[philosophers-stone project]
|
||||
stone(philosophers-stone) --->|requires| harry-1
|
||||
subgraph venv1[.venv]
|
||||
harry-1[harry v1]
|
||||
end
|
||||
end
|
||||
subgraph azkaban-project[prisoner-of-azkaban project]
|
||||
azkaban(prisoner-of-azkaban) --->|requires| harry-3
|
||||
subgraph venv2[.venv]
|
||||
harry-3[harry v3]
|
||||
end
|
||||
end
|
||||
stone-project ~~~ azkaban-project
|
||||
```
|
||||
|
||||
## O que significa ativar um ambiente virtual
|
||||
|
||||
Quando você ativa um ambiente virtual, por exemplo com:
|
||||
|
||||
//// 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
|
||||
|
||||
Ou se você usa o Bash para Windows (por exemplo, <a href="https://gitforwindows.org/" class="external-link" target="_blank">Git Bash</a>):
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ source .venv/Scripts/activate
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
////
|
||||
|
||||
Esse comando criará ou modificará algumas [variáveis de ambiente](environment-variables.md){.internal-link target=_blank} que estarão disponíveis para os próximos comandos.
|
||||
|
||||
Uma dessas variáveis é a variável `PATH`.
|
||||
|
||||
/// tip | "Dica"
|
||||
|
||||
Você pode aprender mais sobre a variável de ambiente `PATH` na seção [Variáveis de ambiente](environment-variables.md#path-environment-variable){.internal-link target=_blank}.
|
||||
|
||||
///
|
||||
|
||||
A ativação de um ambiente virtual adiciona seu caminho `.venv/bin` (no Linux e macOS) ou `.venv\Scripts` (no Windows) à variável de ambiente `PATH`.
|
||||
|
||||
Digamos que antes de ativar o ambiente, a variável `PATH` estava assim:
|
||||
|
||||
//// tab | Linux, macOS
|
||||
|
||||
```plaintext
|
||||
/usr/bin:/bin:/usr/sbin:/sbin
|
||||
```
|
||||
|
||||
Isso significa que o sistema procuraria programas em:
|
||||
|
||||
* `/usr/bin`
|
||||
* `/bin`
|
||||
* `/usr/sbin`
|
||||
* `/sbin`
|
||||
|
||||
////
|
||||
|
||||
//// tab | Windows
|
||||
|
||||
```plaintext
|
||||
C:\Windows\System32
|
||||
```
|
||||
|
||||
Isso significa que o sistema procuraria programas em:
|
||||
|
||||
* `C:\Windows\System32`
|
||||
|
||||
////
|
||||
|
||||
Após ativar o ambiente virtual, a variável `PATH` ficaria mais ou menos assim:
|
||||
|
||||
//// tab | Linux, macOS
|
||||
|
||||
```plaintext
|
||||
/home/user/code/awesome-project/.venv/bin:/usr/bin:/bin:/usr/sbin:/sbin
|
||||
```
|
||||
|
||||
Isso significa que o sistema agora começará a procurar primeiro por programas em:
|
||||
|
||||
```plaintext
|
||||
/home/user/code/awesome-project/.venv/bin
|
||||
```
|
||||
|
||||
antes de procurar nos outros diretórios.
|
||||
|
||||
Então, quando você digita `python` no terminal, o sistema encontrará o programa Python em
|
||||
|
||||
```plaintext
|
||||
/home/user/code/awesome-project/.venv/bin/python
|
||||
```
|
||||
|
||||
e usa esse.
|
||||
|
||||
////
|
||||
|
||||
//// tab | Windows
|
||||
|
||||
```plaintext
|
||||
C:\Users\user\code\awesome-project\.venv\Scripts;C:\Windows\System32
|
||||
```
|
||||
|
||||
Isso significa que o sistema agora começará a procurar primeiro por programas em:
|
||||
|
||||
```plaintext
|
||||
C:\Users\user\code\awesome-project\.venv\Scripts
|
||||
```
|
||||
|
||||
antes de procurar nos outros diretórios.
|
||||
|
||||
Então, quando você digita `python` no terminal, o sistema encontrará o programa Python em
|
||||
|
||||
```plaintext
|
||||
C:\Users\user\code\awesome-project\.venv\Scripts\python
|
||||
```
|
||||
|
||||
e usa esse.
|
||||
|
||||
////
|
||||
|
||||
Um detalhe importante é que ele colocará o caminho do ambiente virtual no **início** da variável `PATH`. O sistema o encontrará **antes** de encontrar qualquer outro Python disponível. Dessa forma, quando você executar `python`, ele usará o Python **do ambiente virtual** em vez de qualquer outro `python` (por exemplo, um `python` de um ambiente global).
|
||||
|
||||
Ativar um ambiente virtual também muda algumas outras coisas, mas esta é uma das mais importantes.
|
||||
|
||||
## Verificando um ambiente virtual
|
||||
|
||||
Ao verificar se um ambiente virtual está ativo, por exemplo com:
|
||||
|
||||
//// 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>
|
||||
|
||||
////
|
||||
|
||||
Isso significa que o programa `python` que será usado é aquele **no ambiente virtual**.
|
||||
|
||||
você usa `which` no Linux e macOS e `Get-Command` no Windows PowerShell.
|
||||
|
||||
A maneira como esse comando funciona é que ele vai e verifica na variável de ambiente `PATH`, passando por **cada caminho em ordem**, procurando pelo programa chamado `python`. Uma vez que ele o encontre, ele **mostrará o caminho** para esse programa.
|
||||
|
||||
A parte mais importante é que quando você chama ``python`, esse é exatamente o "`python`" que será executado.
|
||||
|
||||
Assim, você pode confirmar se está no ambiente virtual correto.
|
||||
|
||||
/// tip | "Dica"
|
||||
|
||||
É fácil ativar um ambiente virtual, obter um Python e então **ir para outro projeto**.
|
||||
|
||||
E o segundo projeto **não funcionaria** porque você está usando o **Python incorreto**, de um ambiente virtual para outro projeto.
|
||||
|
||||
É útil poder verificar qual `python` está sendo usado. 🤓
|
||||
|
||||
///
|
||||
|
||||
## Por que desativar um ambiente virtual
|
||||
|
||||
Por exemplo, você pode estar trabalhando em um projeto `philosophers-stone`, **ativar esse ambiente virtual**, instalar pacotes e trabalhar com esse ambiente.
|
||||
|
||||
E então você quer trabalhar em **outro projeto** `prisoner-of-azkaban`.
|
||||
|
||||
Você vai para aquele projeto:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ cd ~/code/prisoner-of-azkaban
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
Se você não desativar o ambiente virtual para `philosophers-stone`, quando você executar `python` no terminal, ele tentará usar o Python de `philosophers-stone`.
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ cd ~/code/prisoner-of-azkaban
|
||||
|
||||
$ python main.py
|
||||
|
||||
// Erro ao importar o Sirius, ele não está instalado 😱
|
||||
Traceback (most recent call last):
|
||||
File "main.py", line 1, in <module>
|
||||
import sirius
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
Mas se você desativar o ambiente virtual e ativar o novo para `prisoner-of-askaban`, quando você executar `python`, ele usará o Python do ambiente virtual em `prisoner-of-azkaban`.
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ cd ~/code/prisoner-of-azkaban
|
||||
|
||||
// Você não precisa estar no diretório antigo para desativar, você pode fazer isso de onde estiver, mesmo depois de ir para o outro projeto 😎
|
||||
$ deactivate
|
||||
|
||||
// Ative o ambiente virtual em prisoner-of-azkaban/.venv 🚀
|
||||
$ source .venv/bin/activate
|
||||
|
||||
// Agora, quando você executar o python, ele encontrará o pacote sirius instalado neste ambiente virtual ✨
|
||||
$ python main.py
|
||||
|
||||
Eu juro solenemente 🐺
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
## Alternativas
|
||||
|
||||
Este é um guia simples para você começar e lhe ensinar como tudo funciona **por baixo**.
|
||||
|
||||
Existem muitas **alternativas** para gerenciar ambientes virtuais, dependências de pacotes (requisitos) e projetos.
|
||||
|
||||
Quando estiver pronto e quiser usar uma ferramenta para **gerenciar todo o projeto**, dependências de pacotes, ambientes virtuais, etc., sugiro que você experimente o <a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">uv</a>.
|
||||
|
||||
`uv` pode fazer muitas coisas, ele pode:
|
||||
|
||||
* **Instalar o Python** para você, incluindo versões diferentes
|
||||
* Gerenciar o **ambiente virtual** para seus projetos
|
||||
* Instalar **pacotes**
|
||||
* Gerenciar **dependências e versões** de pacotes para seu projeto
|
||||
* Certifique-se de ter um conjunto **exato** de pacotes e versões para instalar, incluindo suas dependências, para que você possa ter certeza de que pode executar seu projeto em produção exatamente da mesma forma que em seu computador durante o desenvolvimento, isso é chamado de **bloqueio**
|
||||
* E muitas outras coisas
|
||||
|
||||
## Conclusão
|
||||
|
||||
Se você leu e entendeu tudo isso, agora **você sabe muito mais** sobre ambientes virtuais do que muitos desenvolvedores por aí. 🤓
|
||||
|
||||
Saber esses detalhes provavelmente será útil no futuro, quando você estiver depurando algo que parece complexo, mas você saberá **como tudo funciona**. 😎
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
如果某些内容看起来对你的项目有用,请继续查阅,否则请直接跳过它们。
|
||||
|
||||
/// 小技巧
|
||||
/// tip | 小技巧
|
||||
|
||||
如果你想以系统的方式**学习 FastAPI**(推荐),请阅读 [教程 - 用户指南](../tutorial/index.md){.internal-link target=_blank} 的每一章节。
|
||||
|
||||
|
||||
@@ -1,84 +1,28 @@
|
||||
# 项目生成 - 模板
|
||||
# FastAPI全栈模板
|
||||
|
||||
项目生成器一般都会提供很多初始设置、安全措施、数据库,甚至还准备好了第一个 API 端点,能帮助您快速上手。
|
||||
模板通常带有特定的设置,而且被设计为灵活和可定制的。这允许您根据项目的需求修改和调整它们,使它们成为一个很好的起点。🏁
|
||||
|
||||
项目生成器的设置通常都很主观,您可以按需更新或修改,但对于您的项目来说,它是非常好的起点。
|
||||
您可以使用此模板开始,因为它包含了许多已经为您完成的初始设置、安全性、数据库和一些API端点。
|
||||
|
||||
## 全栈 FastAPI + PostgreSQL
|
||||
代码仓: <a href="https://github.com/fastapi/full-stack-fastapi-template" class="external-link" target="_blank">Full Stack FastAPI Template</a>
|
||||
|
||||
GitHub:<a href="https://github.com/tiangolo/full-stack-fastapi-postgresql" class="external-link" target="_blank">https://github.com/tiangolo/full-stack-fastapi-postgresql</a>
|
||||
## FastAPI全栈模板 - 技术栈和特性
|
||||
|
||||
### 全栈 FastAPI + PostgreSQL - 功能
|
||||
|
||||
* 完整的 **Docker** 集成(基于 Docker)
|
||||
* Docker Swarm 开发模式
|
||||
* **Docker Compose** 本地开发集成与优化
|
||||
* **生产可用**的 Python 网络服务器,使用 Uvicorn 或 Gunicorn
|
||||
* Python <a href="https://github.com/fastapi/fastapi" class="external-link" target="_blank">**FastAPI**</a> 后端:
|
||||
* * **速度快**:可与 **NodeJS** 和 **Go** 比肩的极高性能(归功于 Starlette 和 Pydantic)
|
||||
* **直观**:强大的编辑器支持,处处皆可<abbr title="也叫自动完成、智能感知">自动补全</abbr>,减少调试时间
|
||||
* **简单**:易学、易用,阅读文档所需时间更短
|
||||
* **简短**:代码重复最小化,每次参数声明都可以实现多个功能
|
||||
* **健壮**: 生产级别的代码,还有自动交互文档
|
||||
* **基于标准**:完全兼容并基于 API 开放标准:<a href="https://github.com/OAI/OpenAPI-Specification" class="external-link" target="_blank">OpenAPI</a> 和 <a href="https://json-schema.org/" class="external-link" target="_blank">JSON Schema</a>
|
||||
* <a href="https://fastapi.tiangolo.com/features/" class="external-link" target="_blank">**更多功能**</a>包括自动验证、序列化、交互文档、OAuth2 JWT 令牌身份验证等
|
||||
* **安全密码**,默认使用密码哈希
|
||||
* **JWT 令牌**身份验证
|
||||
* **SQLAlchemy** 模型(独立于 Flask 扩展,可直接用于 Celery Worker)
|
||||
* 基础的用户模型(可按需修改或删除)
|
||||
* **Alembic** 迁移
|
||||
* **CORS**(跨域资源共享)
|
||||
* **Celery** Worker 可从后端其它部分有选择地导入并使用模型和代码
|
||||
* REST 后端测试基于 Pytest,并与 Docker 集成,可独立于数据库实现完整的 API 交互测试。因为是在 Docker 中运行,每次都可从头构建新的数据存储(使用 ElasticSearch、MongoDB、CouchDB 等数据库,仅测试 API 运行)
|
||||
* Python 与 **Jupyter Kernels** 集成,用于远程或 Docker 容器内部开发,使用 Atom Hydrogen 或 Visual Studio Code 的 Jupyter 插件
|
||||
* **Vue** 前端:
|
||||
* 由 Vue CLI 生成
|
||||
* **JWT 身份验证**处理
|
||||
* 登录视图
|
||||
* 登录后显示主仪表盘视图
|
||||
* 主仪表盘支持用户创建与编辑
|
||||
* 用户信息编辑
|
||||
* **Vuex**
|
||||
* **Vue-router**
|
||||
* **Vuetify** 美化组件
|
||||
* **TypeScript**
|
||||
* 基于 **Nginx** 的 Docker 服务器(优化了 Vue-router 配置)
|
||||
* Docker 多阶段构建,无需保存或提交编译的代码
|
||||
* 在构建时运行前端测试(可禁用)
|
||||
* 尽量模块化,开箱即用,但仍可使用 Vue CLI 重新生成或创建所需项目,或复用所需内容
|
||||
* 使用 **PGAdmin** 管理 PostgreSQL 数据库,可轻松替换为 PHPMyAdmin 或 MySQL
|
||||
* 使用 **Flower** 监控 Celery 任务
|
||||
* 使用 **Traefik** 处理前后端负载平衡,可把前后端放在同一个域下,按路径分隔,但在不同容器中提供服务
|
||||
* Traefik 集成,包括自动生成 Let's Encrypt **HTTPS** 凭证
|
||||
* GitLab **CI**(持续集成),包括前后端测试
|
||||
|
||||
## 全栈 FastAPI + Couchbase
|
||||
|
||||
GitHub:<a href="https://github.com/tiangolo/full-stack-fastapi-couchbase" class="external-link" target="_blank">https://github.com/tiangolo/full-stack-fastapi-couchbase</a>
|
||||
|
||||
⚠️ **警告** ⚠️
|
||||
|
||||
如果您想从头开始创建新项目,建议使用以下备选方案。
|
||||
|
||||
例如,项目生成器<a href="https://github.com/tiangolo/full-stack-fastapi-postgresql" class="external-link" target="_blank">全栈 FastAPI + PostgreSQL </a>会更适用,这个项目的维护积极,用的人也多,还包括了所有新功能和改进内容。
|
||||
|
||||
当然,您也可以放心使用这个基于 Couchbase 的生成器,它也能正常使用。就算用它生成项目也没有任何问题(为了更好地满足需求,您可以自行更新这个项目)。
|
||||
|
||||
详见资源仓库中的文档。
|
||||
|
||||
## 全栈 FastAPI + MongoDB
|
||||
|
||||
……敬请期待,得看我有没有时间做这个项目。😅 🎉
|
||||
|
||||
## FastAPI + spaCy 机器学习模型
|
||||
|
||||
GitHub:<a href="https://github.com/microsoft/cookiecutter-spacy-fastapi" class="external-link" target="_blank">https://github.com/microsoft/cookiecutter-spacy-fastapi</a>
|
||||
|
||||
### FastAPI + spaCy 机器学习模型 - 功能
|
||||
|
||||
* 集成 **spaCy** NER 模型
|
||||
* 内置 **Azure 认知搜索**请求格式
|
||||
* **生产可用**的 Python 网络服务器,使用 Uvicorn 与 Gunicorn
|
||||
* 内置 **Azure DevOps** Kubernetes (AKS) CI/CD 开发
|
||||
* **多语**支持,可在项目设置时选择 spaCy 内置的语言
|
||||
* 不仅局限于 spaCy,可**轻松扩展**至其它模型框架(Pytorch、TensorFlow)
|
||||
- ⚡ [**FastAPI**](https://fastapi.tiangolo.com) 用于Python后端API.
|
||||
- 🧰 [SQLModel](https://sqlmodel.tiangolo.com) 用于Python和SQL数据库的集成(ORM)。
|
||||
- 🔍 [Pydantic](https://docs.pydantic.dev) FastAPI的依赖项之一,用于数据验证和配置管理。
|
||||
- 💾 [PostgreSQL](https://www.postgresql.org) 作为SQL数据库。
|
||||
- 🚀 [React](https://react.dev) 用于前端。
|
||||
- 💃 使用了TypeScript、hooks、Vite和其他一些现代化的前端技术栈。
|
||||
- 🎨 [Chakra UI](https://chakra-ui.com) 用于前端组件。
|
||||
- 🤖 一个自动化生成的前端客户端。
|
||||
- 🧪 Playwright用于端到端测试。
|
||||
- 🦇 支持暗黑主题(Dark mode)。
|
||||
- 🐋 [Docker Compose](https://www.docker.com) 用于开发环境和生产环境。
|
||||
- 🔒 默认使用密码哈希来保证安全。
|
||||
- 🔑 JWT令牌用于权限验证。
|
||||
- 📫 使用邮箱来进行密码恢复。
|
||||
- ✅ 单元测试用了[Pytest](https://pytest.org).
|
||||
- 📞 [Traefik](https://traefik.io) 用于反向代理和负载均衡。
|
||||
- 🚢 部署指南(Docker Compose)包含了如何起一个Traefik前端代理来自动化HTTPS认证。
|
||||
- 🏭 CI(持续集成)和 CD(持续部署)基于GitHub Actions。
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import pytest
|
||||
from httpx import AsyncClient
|
||||
from httpx import ASGITransport, AsyncClient
|
||||
|
||||
from .main import app
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_root():
|
||||
async with AsyncClient(app=app, base_url="http://test") as ac:
|
||||
async with AsyncClient(
|
||||
transport=ASGITransport(app=app), base_url="http://test"
|
||||
) as ac:
|
||||
response = await ac.get("/")
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {"message": "Tomato"}
|
||||
|
||||
17
docs_src/cookie_param_models/tutorial001.py
Normal file
17
docs_src/cookie_param_models/tutorial001.py
Normal file
@@ -0,0 +1,17 @@
|
||||
from typing import Union
|
||||
|
||||
from fastapi import Cookie, FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class Cookies(BaseModel):
|
||||
session_id: str
|
||||
fatebook_tracker: Union[str, None] = None
|
||||
googall_tracker: Union[str, None] = None
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items(cookies: Cookies = Cookie()):
|
||||
return cookies
|
||||
18
docs_src/cookie_param_models/tutorial001_an.py
Normal file
18
docs_src/cookie_param_models/tutorial001_an.py
Normal file
@@ -0,0 +1,18 @@
|
||||
from typing import Union
|
||||
|
||||
from fastapi import Cookie, FastAPI
|
||||
from pydantic import BaseModel
|
||||
from typing_extensions import Annotated
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class Cookies(BaseModel):
|
||||
session_id: str
|
||||
fatebook_tracker: Union[str, None] = None
|
||||
googall_tracker: Union[str, None] = None
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items(cookies: Annotated[Cookies, Cookie()]):
|
||||
return cookies
|
||||
17
docs_src/cookie_param_models/tutorial001_an_py310.py
Normal file
17
docs_src/cookie_param_models/tutorial001_an_py310.py
Normal file
@@ -0,0 +1,17 @@
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import Cookie, FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class Cookies(BaseModel):
|
||||
session_id: str
|
||||
fatebook_tracker: str | None = None
|
||||
googall_tracker: str | None = None
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items(cookies: Annotated[Cookies, Cookie()]):
|
||||
return cookies
|
||||
17
docs_src/cookie_param_models/tutorial001_an_py39.py
Normal file
17
docs_src/cookie_param_models/tutorial001_an_py39.py
Normal file
@@ -0,0 +1,17 @@
|
||||
from typing import Annotated, Union
|
||||
|
||||
from fastapi import Cookie, FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class Cookies(BaseModel):
|
||||
session_id: str
|
||||
fatebook_tracker: Union[str, None] = None
|
||||
googall_tracker: Union[str, None] = None
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items(cookies: Annotated[Cookies, Cookie()]):
|
||||
return cookies
|
||||
15
docs_src/cookie_param_models/tutorial001_py310.py
Normal file
15
docs_src/cookie_param_models/tutorial001_py310.py
Normal file
@@ -0,0 +1,15 @@
|
||||
from fastapi import Cookie, FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class Cookies(BaseModel):
|
||||
session_id: str
|
||||
fatebook_tracker: str | None = None
|
||||
googall_tracker: str | None = None
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items(cookies: Cookies = Cookie()):
|
||||
return cookies
|
||||
19
docs_src/cookie_param_models/tutorial002.py
Normal file
19
docs_src/cookie_param_models/tutorial002.py
Normal file
@@ -0,0 +1,19 @@
|
||||
from typing import Union
|
||||
|
||||
from fastapi import Cookie, FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class Cookies(BaseModel):
|
||||
model_config = {"extra": "forbid"}
|
||||
|
||||
session_id: str
|
||||
fatebook_tracker: Union[str, None] = None
|
||||
googall_tracker: Union[str, None] = None
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items(cookies: Cookies = Cookie()):
|
||||
return cookies
|
||||
20
docs_src/cookie_param_models/tutorial002_an.py
Normal file
20
docs_src/cookie_param_models/tutorial002_an.py
Normal file
@@ -0,0 +1,20 @@
|
||||
from typing import Union
|
||||
|
||||
from fastapi import Cookie, FastAPI
|
||||
from pydantic import BaseModel
|
||||
from typing_extensions import Annotated
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class Cookies(BaseModel):
|
||||
model_config = {"extra": "forbid"}
|
||||
|
||||
session_id: str
|
||||
fatebook_tracker: Union[str, None] = None
|
||||
googall_tracker: Union[str, None] = None
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items(cookies: Annotated[Cookies, Cookie()]):
|
||||
return cookies
|
||||
19
docs_src/cookie_param_models/tutorial002_an_py310.py
Normal file
19
docs_src/cookie_param_models/tutorial002_an_py310.py
Normal file
@@ -0,0 +1,19 @@
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import Cookie, FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class Cookies(BaseModel):
|
||||
model_config = {"extra": "forbid"}
|
||||
|
||||
session_id: str
|
||||
fatebook_tracker: str | None = None
|
||||
googall_tracker: str | None = None
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items(cookies: Annotated[Cookies, Cookie()]):
|
||||
return cookies
|
||||
19
docs_src/cookie_param_models/tutorial002_an_py39.py
Normal file
19
docs_src/cookie_param_models/tutorial002_an_py39.py
Normal file
@@ -0,0 +1,19 @@
|
||||
from typing import Annotated, Union
|
||||
|
||||
from fastapi import Cookie, FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class Cookies(BaseModel):
|
||||
model_config = {"extra": "forbid"}
|
||||
|
||||
session_id: str
|
||||
fatebook_tracker: Union[str, None] = None
|
||||
googall_tracker: Union[str, None] = None
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items(cookies: Annotated[Cookies, Cookie()]):
|
||||
return cookies
|
||||
20
docs_src/cookie_param_models/tutorial002_pv1.py
Normal file
20
docs_src/cookie_param_models/tutorial002_pv1.py
Normal file
@@ -0,0 +1,20 @@
|
||||
from typing import Union
|
||||
|
||||
from fastapi import Cookie, FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class Cookies(BaseModel):
|
||||
class Config:
|
||||
extra = "forbid"
|
||||
|
||||
session_id: str
|
||||
fatebook_tracker: Union[str, None] = None
|
||||
googall_tracker: Union[str, None] = None
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items(cookies: Cookies = Cookie()):
|
||||
return cookies
|
||||
21
docs_src/cookie_param_models/tutorial002_pv1_an.py
Normal file
21
docs_src/cookie_param_models/tutorial002_pv1_an.py
Normal file
@@ -0,0 +1,21 @@
|
||||
from typing import Union
|
||||
|
||||
from fastapi import Cookie, FastAPI
|
||||
from pydantic import BaseModel
|
||||
from typing_extensions import Annotated
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class Cookies(BaseModel):
|
||||
class Config:
|
||||
extra = "forbid"
|
||||
|
||||
session_id: str
|
||||
fatebook_tracker: Union[str, None] = None
|
||||
googall_tracker: Union[str, None] = None
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items(cookies: Annotated[Cookies, Cookie()]):
|
||||
return cookies
|
||||
20
docs_src/cookie_param_models/tutorial002_pv1_an_py310.py
Normal file
20
docs_src/cookie_param_models/tutorial002_pv1_an_py310.py
Normal file
@@ -0,0 +1,20 @@
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import Cookie, FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class Cookies(BaseModel):
|
||||
class Config:
|
||||
extra = "forbid"
|
||||
|
||||
session_id: str
|
||||
fatebook_tracker: str | None = None
|
||||
googall_tracker: str | None = None
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items(cookies: Annotated[Cookies, Cookie()]):
|
||||
return cookies
|
||||
20
docs_src/cookie_param_models/tutorial002_pv1_an_py39.py
Normal file
20
docs_src/cookie_param_models/tutorial002_pv1_an_py39.py
Normal file
@@ -0,0 +1,20 @@
|
||||
from typing import Annotated, Union
|
||||
|
||||
from fastapi import Cookie, FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class Cookies(BaseModel):
|
||||
class Config:
|
||||
extra = "forbid"
|
||||
|
||||
session_id: str
|
||||
fatebook_tracker: Union[str, None] = None
|
||||
googall_tracker: Union[str, None] = None
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items(cookies: Annotated[Cookies, Cookie()]):
|
||||
return cookies
|
||||
18
docs_src/cookie_param_models/tutorial002_pv1_py310.py
Normal file
18
docs_src/cookie_param_models/tutorial002_pv1_py310.py
Normal file
@@ -0,0 +1,18 @@
|
||||
from fastapi import Cookie, FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class Cookies(BaseModel):
|
||||
class Config:
|
||||
extra = "forbid"
|
||||
|
||||
session_id: str
|
||||
fatebook_tracker: str | None = None
|
||||
googall_tracker: str | None = None
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items(cookies: Cookies = Cookie()):
|
||||
return cookies
|
||||
17
docs_src/cookie_param_models/tutorial002_py310.py
Normal file
17
docs_src/cookie_param_models/tutorial002_py310.py
Normal file
@@ -0,0 +1,17 @@
|
||||
from fastapi import Cookie, FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class Cookies(BaseModel):
|
||||
model_config = {"extra": "forbid"}
|
||||
|
||||
session_id: str
|
||||
fatebook_tracker: str | None = None
|
||||
googall_tracker: str | None = None
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items(cookies: Cookies = Cookie()):
|
||||
return cookies
|
||||
19
docs_src/header_param_models/tutorial001.py
Normal file
19
docs_src/header_param_models/tutorial001.py
Normal file
@@ -0,0 +1,19 @@
|
||||
from typing import List, Union
|
||||
|
||||
from fastapi import FastAPI, Header
|
||||
from pydantic import BaseModel
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class CommonHeaders(BaseModel):
|
||||
host: str
|
||||
save_data: bool
|
||||
if_modified_since: Union[str, None] = None
|
||||
traceparent: Union[str, None] = None
|
||||
x_tag: List[str] = []
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items(headers: CommonHeaders = Header()):
|
||||
return headers
|
||||
20
docs_src/header_param_models/tutorial001_an.py
Normal file
20
docs_src/header_param_models/tutorial001_an.py
Normal file
@@ -0,0 +1,20 @@
|
||||
from typing import List, Union
|
||||
|
||||
from fastapi import FastAPI, Header
|
||||
from pydantic import BaseModel
|
||||
from typing_extensions import Annotated
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class CommonHeaders(BaseModel):
|
||||
host: str
|
||||
save_data: bool
|
||||
if_modified_since: Union[str, None] = None
|
||||
traceparent: Union[str, None] = None
|
||||
x_tag: List[str] = []
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items(headers: Annotated[CommonHeaders, Header()]):
|
||||
return headers
|
||||
19
docs_src/header_param_models/tutorial001_an_py310.py
Normal file
19
docs_src/header_param_models/tutorial001_an_py310.py
Normal file
@@ -0,0 +1,19 @@
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import FastAPI, Header
|
||||
from pydantic import BaseModel
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class CommonHeaders(BaseModel):
|
||||
host: str
|
||||
save_data: bool
|
||||
if_modified_since: str | None = None
|
||||
traceparent: str | None = None
|
||||
x_tag: list[str] = []
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items(headers: Annotated[CommonHeaders, Header()]):
|
||||
return headers
|
||||
19
docs_src/header_param_models/tutorial001_an_py39.py
Normal file
19
docs_src/header_param_models/tutorial001_an_py39.py
Normal file
@@ -0,0 +1,19 @@
|
||||
from typing import Annotated, Union
|
||||
|
||||
from fastapi import FastAPI, Header
|
||||
from pydantic import BaseModel
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class CommonHeaders(BaseModel):
|
||||
host: str
|
||||
save_data: bool
|
||||
if_modified_since: Union[str, None] = None
|
||||
traceparent: Union[str, None] = None
|
||||
x_tag: list[str] = []
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items(headers: Annotated[CommonHeaders, Header()]):
|
||||
return headers
|
||||
17
docs_src/header_param_models/tutorial001_py310.py
Normal file
17
docs_src/header_param_models/tutorial001_py310.py
Normal file
@@ -0,0 +1,17 @@
|
||||
from fastapi import FastAPI, Header
|
||||
from pydantic import BaseModel
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class CommonHeaders(BaseModel):
|
||||
host: str
|
||||
save_data: bool
|
||||
if_modified_since: str | None = None
|
||||
traceparent: str | None = None
|
||||
x_tag: list[str] = []
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items(headers: CommonHeaders = Header()):
|
||||
return headers
|
||||
19
docs_src/header_param_models/tutorial001_py39.py
Normal file
19
docs_src/header_param_models/tutorial001_py39.py
Normal file
@@ -0,0 +1,19 @@
|
||||
from typing import Union
|
||||
|
||||
from fastapi import FastAPI, Header
|
||||
from pydantic import BaseModel
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class CommonHeaders(BaseModel):
|
||||
host: str
|
||||
save_data: bool
|
||||
if_modified_since: Union[str, None] = None
|
||||
traceparent: Union[str, None] = None
|
||||
x_tag: list[str] = []
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items(headers: CommonHeaders = Header()):
|
||||
return headers
|
||||
21
docs_src/header_param_models/tutorial002.py
Normal file
21
docs_src/header_param_models/tutorial002.py
Normal file
@@ -0,0 +1,21 @@
|
||||
from typing import List, Union
|
||||
|
||||
from fastapi import FastAPI, Header
|
||||
from pydantic import BaseModel
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class CommonHeaders(BaseModel):
|
||||
model_config = {"extra": "forbid"}
|
||||
|
||||
host: str
|
||||
save_data: bool
|
||||
if_modified_since: Union[str, None] = None
|
||||
traceparent: Union[str, None] = None
|
||||
x_tag: List[str] = []
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items(headers: CommonHeaders = Header()):
|
||||
return headers
|
||||
22
docs_src/header_param_models/tutorial002_an.py
Normal file
22
docs_src/header_param_models/tutorial002_an.py
Normal file
@@ -0,0 +1,22 @@
|
||||
from typing import List, Union
|
||||
|
||||
from fastapi import FastAPI, Header
|
||||
from pydantic import BaseModel
|
||||
from typing_extensions import Annotated
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class CommonHeaders(BaseModel):
|
||||
model_config = {"extra": "forbid"}
|
||||
|
||||
host: str
|
||||
save_data: bool
|
||||
if_modified_since: Union[str, None] = None
|
||||
traceparent: Union[str, None] = None
|
||||
x_tag: List[str] = []
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items(headers: Annotated[CommonHeaders, Header()]):
|
||||
return headers
|
||||
21
docs_src/header_param_models/tutorial002_an_py310.py
Normal file
21
docs_src/header_param_models/tutorial002_an_py310.py
Normal file
@@ -0,0 +1,21 @@
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import FastAPI, Header
|
||||
from pydantic import BaseModel
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class CommonHeaders(BaseModel):
|
||||
model_config = {"extra": "forbid"}
|
||||
|
||||
host: str
|
||||
save_data: bool
|
||||
if_modified_since: str | None = None
|
||||
traceparent: str | None = None
|
||||
x_tag: list[str] = []
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items(headers: Annotated[CommonHeaders, Header()]):
|
||||
return headers
|
||||
21
docs_src/header_param_models/tutorial002_an_py39.py
Normal file
21
docs_src/header_param_models/tutorial002_an_py39.py
Normal file
@@ -0,0 +1,21 @@
|
||||
from typing import Annotated, Union
|
||||
|
||||
from fastapi import FastAPI, Header
|
||||
from pydantic import BaseModel
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class CommonHeaders(BaseModel):
|
||||
model_config = {"extra": "forbid"}
|
||||
|
||||
host: str
|
||||
save_data: bool
|
||||
if_modified_since: Union[str, None] = None
|
||||
traceparent: Union[str, None] = None
|
||||
x_tag: list[str] = []
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items(headers: Annotated[CommonHeaders, Header()]):
|
||||
return headers
|
||||
22
docs_src/header_param_models/tutorial002_pv1.py
Normal file
22
docs_src/header_param_models/tutorial002_pv1.py
Normal file
@@ -0,0 +1,22 @@
|
||||
from typing import List, Union
|
||||
|
||||
from fastapi import FastAPI, Header
|
||||
from pydantic import BaseModel
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class CommonHeaders(BaseModel):
|
||||
class Config:
|
||||
extra = "forbid"
|
||||
|
||||
host: str
|
||||
save_data: bool
|
||||
if_modified_since: Union[str, None] = None
|
||||
traceparent: Union[str, None] = None
|
||||
x_tag: List[str] = []
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items(headers: CommonHeaders = Header()):
|
||||
return headers
|
||||
23
docs_src/header_param_models/tutorial002_pv1_an.py
Normal file
23
docs_src/header_param_models/tutorial002_pv1_an.py
Normal file
@@ -0,0 +1,23 @@
|
||||
from typing import List, Union
|
||||
|
||||
from fastapi import FastAPI, Header
|
||||
from pydantic import BaseModel
|
||||
from typing_extensions import Annotated
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class CommonHeaders(BaseModel):
|
||||
class Config:
|
||||
extra = "forbid"
|
||||
|
||||
host: str
|
||||
save_data: bool
|
||||
if_modified_since: Union[str, None] = None
|
||||
traceparent: Union[str, None] = None
|
||||
x_tag: List[str] = []
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items(headers: Annotated[CommonHeaders, Header()]):
|
||||
return headers
|
||||
22
docs_src/header_param_models/tutorial002_pv1_an_py310.py
Normal file
22
docs_src/header_param_models/tutorial002_pv1_an_py310.py
Normal file
@@ -0,0 +1,22 @@
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import FastAPI, Header
|
||||
from pydantic import BaseModel
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class CommonHeaders(BaseModel):
|
||||
class Config:
|
||||
extra = "forbid"
|
||||
|
||||
host: str
|
||||
save_data: bool
|
||||
if_modified_since: str | None = None
|
||||
traceparent: str | None = None
|
||||
x_tag: list[str] = []
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items(headers: Annotated[CommonHeaders, Header()]):
|
||||
return headers
|
||||
22
docs_src/header_param_models/tutorial002_pv1_an_py39.py
Normal file
22
docs_src/header_param_models/tutorial002_pv1_an_py39.py
Normal file
@@ -0,0 +1,22 @@
|
||||
from typing import Annotated, Union
|
||||
|
||||
from fastapi import FastAPI, Header
|
||||
from pydantic import BaseModel
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class CommonHeaders(BaseModel):
|
||||
class Config:
|
||||
extra = "forbid"
|
||||
|
||||
host: str
|
||||
save_data: bool
|
||||
if_modified_since: Union[str, None] = None
|
||||
traceparent: Union[str, None] = None
|
||||
x_tag: list[str] = []
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items(headers: Annotated[CommonHeaders, Header()]):
|
||||
return headers
|
||||
20
docs_src/header_param_models/tutorial002_pv1_py310.py
Normal file
20
docs_src/header_param_models/tutorial002_pv1_py310.py
Normal file
@@ -0,0 +1,20 @@
|
||||
from fastapi import FastAPI, Header
|
||||
from pydantic import BaseModel
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class CommonHeaders(BaseModel):
|
||||
class Config:
|
||||
extra = "forbid"
|
||||
|
||||
host: str
|
||||
save_data: bool
|
||||
if_modified_since: str | None = None
|
||||
traceparent: str | None = None
|
||||
x_tag: list[str] = []
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items(headers: CommonHeaders = Header()):
|
||||
return headers
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user