mirror of
https://github.com/fastapi/fastapi.git
synced 2025-12-25 07:08:11 -05:00
Compare commits
235 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a9673f145a | ||
|
|
aa28784f6b | ||
|
|
7b3319ddab | ||
|
|
6a20078259 | ||
|
|
d0ab909544 | ||
|
|
13da029dca | ||
|
|
91fe90e8e6 | ||
|
|
a0c8f93231 | ||
|
|
cad6a6e0c1 | ||
|
|
fd5ba77b83 | ||
|
|
cb1410426e | ||
|
|
7b31e52766 | ||
|
|
4151616681 | ||
|
|
462e24e864 | ||
|
|
9f9ed7a6bd | ||
|
|
b6ea9ea2ca | ||
|
|
b85b2e3942 | ||
|
|
08fc2a41ca | ||
|
|
8d3dcbcd1b | ||
|
|
861ed37c97 | ||
|
|
7a445402d4 | ||
|
|
04c8502cc7 | ||
|
|
c7c69586ae | ||
|
|
4e09feda9e | ||
|
|
73260971b5 | ||
|
|
b36bfff56e | ||
|
|
83d04df8a6 | ||
|
|
7bc78c5fd3 | ||
|
|
ae8fa3aacd | ||
|
|
08bc120771 | ||
|
|
a39efb029f | ||
|
|
58ca98285f | ||
|
|
3f5f81bbdc | ||
|
|
90236c8135 | ||
|
|
c200bc2240 | ||
|
|
e9861cd918 | ||
|
|
202fa11d50 | ||
|
|
4b6e09296c | ||
|
|
9bd0d6fa96 | ||
|
|
35510a5ea7 | ||
|
|
c1788a25c7 | ||
|
|
19c77e35bd | ||
|
|
cc4c13e4ae | ||
|
|
4f3764faa9 | ||
|
|
c27ad0dc26 | ||
|
|
6d0caf7522 | ||
|
|
06df32e84c | ||
|
|
28c089c029 | ||
|
|
44b26bb64c | ||
|
|
e04bae2286 | ||
|
|
23f5940e8b | ||
|
|
4915cf0561 | ||
|
|
a1c9eff041 | ||
|
|
57cb3f3089 | ||
|
|
bd6b3b07c5 | ||
|
|
3cf8b86dc1 | ||
|
|
55165f292a | ||
|
|
f3ddc7bdeb | ||
|
|
4356cc9588 | ||
|
|
cd9e87e60e | ||
|
|
4834d87dcd | ||
|
|
7781cc0936 | ||
|
|
23459d4a35 | ||
|
|
ab2b86fe2c | ||
|
|
90a5796b94 | ||
|
|
bb8a630fc3 | ||
|
|
f5a503afae | ||
|
|
49fba853c2 | ||
|
|
bac2f587b7 | ||
|
|
e1fd6785aa | ||
|
|
4e50f53459 | ||
|
|
933d4327fb | ||
|
|
c7902dd23a | ||
|
|
c5f5e63810 | ||
|
|
c3cc077fa9 | ||
|
|
c6f98c009f | ||
|
|
e4206772cb | ||
|
|
723ef07ccf | ||
|
|
8609beb9ab | ||
|
|
65536cbf63 | ||
|
|
0192eab557 | ||
|
|
3f9f4a0f8f | ||
|
|
380e3731a8 | ||
|
|
d6d99b86cb | ||
|
|
5592fa0f6f | ||
|
|
b65be5d496 | ||
|
|
6c7da43e51 | ||
|
|
dfec2d7644 | ||
|
|
8c3ef76139 | ||
|
|
7a504a721c | ||
|
|
dd963511d6 | ||
|
|
fdb6d43e10 | ||
|
|
a7c718e968 | ||
|
|
f4d753620b | ||
|
|
fadfe4c586 | ||
|
|
5fd83c5fa4 | ||
|
|
14daaf409f | ||
|
|
c7dc26b760 | ||
|
|
f5ccb3c35d | ||
|
|
4cea311e6e | ||
|
|
f8718072a0 | ||
|
|
3dbbecdd16 | ||
|
|
6d5530ec1c | ||
|
|
0761f11d1a | ||
|
|
f2e7ef7056 | ||
|
|
d5d9a20937 | ||
|
|
96f092179f | ||
|
|
8505b716af | ||
|
|
78272ac1f3 | ||
|
|
f1bee9a271 | ||
|
|
b20b2218cd | ||
|
|
b9cf69cd42 | ||
|
|
f803c77515 | ||
|
|
0c67022048 | ||
|
|
d8fe307d61 | ||
|
|
580cf8f4e2 | ||
|
|
af390af77c | ||
|
|
4642f63a1e | ||
|
|
203e10596f | ||
|
|
5a2278d09a | ||
|
|
47a8387a04 | ||
|
|
27ca0c9dca | ||
|
|
9418d78de6 | ||
|
|
4b74aef429 | ||
|
|
fc7d123347 | ||
|
|
53da56146e | ||
|
|
3799b9027e | ||
|
|
c70f3f1198 | ||
|
|
58dddc5e4f | ||
|
|
c90c4fb6c1 | ||
|
|
5b3df28f0c | ||
|
|
6c6bdb6233 | ||
|
|
f156f45193 | ||
|
|
f24d744a3b | ||
|
|
937b462cdd | ||
|
|
3025a368c6 | ||
|
|
c218e0d560 | ||
|
|
1ed5aa23e6 | ||
|
|
106d2171d8 | ||
|
|
c5817912d2 | ||
|
|
a7a92bc637 | ||
|
|
68d1fea961 | ||
|
|
8c6b2d5804 | ||
|
|
19c53b21c1 | ||
|
|
44d63cd555 | ||
|
|
55c4b5fb0b | ||
|
|
c32e800c23 | ||
|
|
73dbbeab55 | ||
|
|
417a3ab140 | ||
|
|
a3235ed8de | ||
|
|
38495fffa5 | ||
|
|
b77a43bcac | ||
|
|
483eb73b26 | ||
|
|
51a928d3f5 | ||
|
|
e71636e381 | ||
|
|
f7f17fcfd6 | ||
|
|
033bc2a6c9 | ||
|
|
28d3b9f783 | ||
|
|
0c55553328 | ||
|
|
b66056aa34 | ||
|
|
4f10b8b98d | ||
|
|
06eb421934 | ||
|
|
bf229ad5d8 | ||
|
|
d0319001be | ||
|
|
c4682af13d | ||
|
|
6ca3ce80e4 | ||
|
|
25e85c8522 | ||
|
|
6bf3ab3b7a | ||
|
|
f5ea5eef2a | ||
|
|
46a986cacf | ||
|
|
e620aeb46d | ||
|
|
d1e2e46b80 | ||
|
|
b1c4a8acd5 | ||
|
|
362e2cdc79 | ||
|
|
93e6a08acd | ||
|
|
3ec4342282 | ||
|
|
dc483478eb | ||
|
|
bdd251a05b | ||
|
|
195559ccba | ||
|
|
9a71672a95 | ||
|
|
7e48be1561 | ||
|
|
508f9ce954 | ||
|
|
afbdf2546f | ||
|
|
62df417807 | ||
|
|
09d2747a70 | ||
|
|
d3ea6f7514 | ||
|
|
02187636ea | ||
|
|
687065509b | ||
|
|
b30cca8e9e | ||
|
|
3906777065 | ||
|
|
d60a10fa59 | ||
|
|
54368e7b22 | ||
|
|
acc556e416 | ||
|
|
700585f99d | ||
|
|
4c2993f353 | ||
|
|
ea9277aab4 | ||
|
|
8d86fca027 | ||
|
|
fc0716a7dd | ||
|
|
1e593dc4d4 | ||
|
|
dcc1e1bcf8 | ||
|
|
06eb775c63 | ||
|
|
ab77c069d4 | ||
|
|
46fffc0e94 | ||
|
|
1c2cdb97e9 | ||
|
|
76b6fd5c18 | ||
|
|
a2fb716035 | ||
|
|
aa84ac8e3e | ||
|
|
4ed2bd1fea | ||
|
|
87b7a63ff2 | ||
|
|
06d0918c3d | ||
|
|
5b3adfe449 | ||
|
|
bdd794a0e6 | ||
|
|
f0df79aa91 | ||
|
|
c26f1760d4 | ||
|
|
e5fa4b0af6 | ||
|
|
a33c299fd7 | ||
|
|
6939621730 | ||
|
|
120ab08360 | ||
|
|
3f5521fdfb | ||
|
|
7244e4b612 | ||
|
|
d329745064 | ||
|
|
5f7fe926ab | ||
|
|
c8eea09664 | ||
|
|
5700d65188 | ||
|
|
46178a5347 | ||
|
|
bff5dbbf5d | ||
|
|
09cd7c47a1 | ||
|
|
e2fadcbc90 | ||
|
|
b3bb29afa8 | ||
|
|
c7db2ff858 | ||
|
|
2a7ef5504a | ||
|
|
27964c5ffd | ||
|
|
d262f6e929 | ||
|
|
d61f5e4b55 | ||
|
|
3ed112e8a9 |
56
.github/ISSUE_TEMPLATE/bug_report.md
vendored
56
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -7,29 +7,48 @@ assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
### Describe the bug
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Create a file with '...'
|
||||
2. Add a path operation function with '....'
|
||||
3. Open the browser and call it with a payload of '....'
|
||||
4. See error
|
||||
Write here a clear and concise description of what the bug is.
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
### To Reproduce
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
Steps to reproduce the behavior with a minimum self-contained file.
|
||||
|
||||
**Environment:**
|
||||
- OS: [e.g. Linux / Windows / macOS]
|
||||
- FastAPI Version [e.g. 0.3.0], get it with:
|
||||
Replace each part with your own scenario:
|
||||
|
||||
1. Create a file with:
|
||||
|
||||
```Python
|
||||
import fastapi
|
||||
print(fastapi.__version__)
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/")
|
||||
def read_root():
|
||||
return {"Hello": "World"}
|
||||
```
|
||||
|
||||
3. Open the browser and call the endpoint `/`.
|
||||
4. It returns a JSON with `{"Hello": "World"}`.
|
||||
5. But I expected it to return `{"Hello": "Sara"}`.
|
||||
|
||||
### Expected behavior
|
||||
|
||||
Add a clear and concise description of what you expected to happen.
|
||||
|
||||
### Screenshots
|
||||
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
### Environment
|
||||
|
||||
- OS: [e.g. Linux / Windows / macOS]
|
||||
- FastAPI Version [e.g. 0.3.0], get it with:
|
||||
|
||||
```bash
|
||||
python -c "import fastapi; print(fastapi.__version__)"
|
||||
```
|
||||
|
||||
- Python version, get it with:
|
||||
@@ -38,5 +57,6 @@ print(fastapi.__version__)
|
||||
python --version
|
||||
```
|
||||
|
||||
**Additional context**
|
||||
### Additional context
|
||||
|
||||
Add any other context about the problem here.
|
||||
|
||||
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -7,14 +7,20 @@ assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I want to be able to [...] but I can't because [...]
|
||||
### Is your feature request related to a problem
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
Is your feature request related to a problem?
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
Add a clear and concise description of what the problem is. Ex. I want to be able to [...] but I can't because [...]
|
||||
|
||||
### The solution you would like
|
||||
|
||||
Add a clear and concise description of what you want to happen.
|
||||
|
||||
### Describe alternatives you've considered
|
||||
|
||||
Add a clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
### Additional context
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
|
||||
11
.github/ISSUE_TEMPLATE/question.md
vendored
11
.github/ISSUE_TEMPLATE/question.md
vendored
@@ -7,11 +7,18 @@ assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Description**
|
||||
### First check
|
||||
|
||||
* [ ] I used the GitHub search to find a similar issue and didn't find it.
|
||||
* [ ] I searched the FastAPI documentation, with the integrated search.
|
||||
* [ ] I already searched in Google "How to X in FastAPI" and didn't find any information.
|
||||
|
||||
### Description
|
||||
|
||||
How can I [...]?
|
||||
|
||||
Is it possible to [...]?
|
||||
|
||||
**Additional context**
|
||||
### Additional context
|
||||
|
||||
Add any other context or screenshots about the feature request here.
|
||||
|
||||
19
.github/workflows/main.yml
vendored
Normal file
19
.github/workflows/main.yml
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 0 * * *"
|
||||
|
||||
jobs:
|
||||
issue-manager:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: tiangolo/issue-manager@master
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
config: >
|
||||
{
|
||||
"answered": {
|
||||
"users": ["tiangolo", "dmontagu"],
|
||||
"delay": 864000,
|
||||
"message": "Assuming the original issue was solved, it will be automatically closed now. But feel free to add more comments or create new issues."
|
||||
}
|
||||
}
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -12,3 +12,4 @@ coverage.xml
|
||||
.netlify
|
||||
test.db
|
||||
log.txt
|
||||
Pipfile.lock
|
||||
|
||||
16
.travis.yml
16
.travis.yml
@@ -7,6 +7,12 @@ cache: pip
|
||||
python:
|
||||
- "3.6"
|
||||
- "3.7"
|
||||
- "3.8"
|
||||
- "nightly"
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
- python: "nightly"
|
||||
|
||||
install:
|
||||
- pip install flit
|
||||
@@ -19,8 +25,8 @@ after_script:
|
||||
- bash <(curl -s https://codecov.io/bash)
|
||||
|
||||
deploy:
|
||||
provider: script
|
||||
script: bash scripts/deploy.sh
|
||||
on:
|
||||
tags: true
|
||||
python: "3.6"
|
||||
provider: script
|
||||
script: bash scripts/deploy.sh
|
||||
on:
|
||||
tags: true
|
||||
python: "3.6"
|
||||
|
||||
5
Pipfile
5
Pipfile
@@ -25,10 +25,11 @@ sqlalchemy = "*"
|
||||
uvicorn = "*"
|
||||
|
||||
[packages]
|
||||
starlette = "==0.12.0"
|
||||
pydantic = "==0.26.0"
|
||||
starlette = "==0.12.9"
|
||||
pydantic = "==1.0.0"
|
||||
databases = {extras = ["sqlite"],version = "*"}
|
||||
hypercorn = "*"
|
||||
orjson = "*"
|
||||
|
||||
[requires]
|
||||
python_version = "3.6"
|
||||
|
||||
936
Pipfile.lock
generated
936
Pipfile.lock
generated
@@ -1,936 +0,0 @@
|
||||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "4a33b47e814fa75533548874ffadbc6163b3058db4d1615ff633512366d72ccb"
|
||||
},
|
||||
"pipfile-spec": 6,
|
||||
"requires": {
|
||||
"python_version": "3.6"
|
||||
},
|
||||
"sources": [
|
||||
{
|
||||
"name": "pypi",
|
||||
"url": "https://pypi.org/simple",
|
||||
"verify_ssl": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"default": {
|
||||
"aiocontextvars": {
|
||||
"hashes": [
|
||||
"sha256:885daf8261818767d8f7cbd79f9d4482d118f024b6586ef6e67980236a27bfa3",
|
||||
"sha256:f027372dc48641f683c559f247bd84962becaacdc9ba711d583c3871fb5652aa"
|
||||
],
|
||||
"version": "==0.2.2"
|
||||
},
|
||||
"aiosqlite": {
|
||||
"hashes": [
|
||||
"sha256:ad84fbd7516ca7065d799504fc41d6845c938e5306d1b7dd960caaeda12e22a9"
|
||||
],
|
||||
"version": "==0.10.0"
|
||||
},
|
||||
"contextvars": {
|
||||
"hashes": [
|
||||
"sha256:f38c908aaa59c14335eeea12abea5f443646216c4e29380d7bf34d2018e2c39e"
|
||||
],
|
||||
"markers": "python_version < '3.7'",
|
||||
"version": "==2.4"
|
||||
},
|
||||
"databases": {
|
||||
"extras": [
|
||||
"sqlite"
|
||||
],
|
||||
"hashes": [
|
||||
"sha256:d365cff2035c5177ef5fd8c5abf6671da01189521da64848a01251c870daf48f"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.2.2"
|
||||
},
|
||||
"dataclasses": {
|
||||
"hashes": [
|
||||
"sha256:454a69d788c7fda44efd71e259be79577822f5e3f53f029a22d08004e951dc9f",
|
||||
"sha256:6988bd2b895eef432d562370bb707d540f32f7360ab13da45340101bc2307d84"
|
||||
],
|
||||
"markers": "python_version < '3.7'",
|
||||
"version": "==0.6"
|
||||
},
|
||||
"h11": {
|
||||
"hashes": [
|
||||
"sha256:acca6a44cb52a32ab442b1779adf0875c443c689e9e028f8d831a3769f9c5208",
|
||||
"sha256:f2b1ca39bfed357d1f19ac732913d5f9faa54a5062eca7d2ec3a916cfb7ae4c7"
|
||||
],
|
||||
"version": "==0.8.1"
|
||||
},
|
||||
"h2": {
|
||||
"hashes": [
|
||||
"sha256:c8f387e0e4878904d4978cd688a3195f6b169d49b1ffa572a3d347d7adc5e09f",
|
||||
"sha256:fd07e865a3272ac6ef195d8904de92dc7b38dc28297ec39cfa22716b6d62e6eb"
|
||||
],
|
||||
"version": "==3.1.0"
|
||||
},
|
||||
"hpack": {
|
||||
"hashes": [
|
||||
"sha256:0edd79eda27a53ba5be2dfabf3b15780928a0dff6eb0c60a3d6767720e970c89",
|
||||
"sha256:8eec9c1f4bfae3408a3f30500261f7e6a65912dc138526ea054f9ad98892e9d2"
|
||||
],
|
||||
"version": "==3.0.0"
|
||||
},
|
||||
"hypercorn": {
|
||||
"hashes": [
|
||||
"sha256:cfe7811a93ab7bc22c8a0d5514a2a7a512e812c1e4ee13b9731b705b79d4d453",
|
||||
"sha256:f2577806223fa44d57d6f136b6c37a046794f961252699aec8afb15f35d226d5"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.5.4"
|
||||
},
|
||||
"hyperframe": {
|
||||
"hashes": [
|
||||
"sha256:5187962cb16dcc078f23cb5a4b110098d546c3f41ff2d4038a9896893bbd0b40",
|
||||
"sha256:a9f5c17f2cc3c719b917c4f33ed1c61bd1f8dfac4b1bd23b7c80b3400971b41f"
|
||||
],
|
||||
"version": "==5.2.0"
|
||||
},
|
||||
"immutables": {
|
||||
"hashes": [
|
||||
"sha256:10861f2a2b86139f0c91d5073392d76117f37e84f912dc47c943c23a64008cc7",
|
||||
"sha256:3e23eeb4bc55d57b2a97bef4c1a2891bbb731050b4167c855545797d45e84e45",
|
||||
"sha256:4373876879f147986808f71e6ca02380192a279e8b8d45832f6fed4e7f717562",
|
||||
"sha256:46f9122da033fecf84d7f4c6257aec780f370b20f3ce6bc521702b63ee3d99f7",
|
||||
"sha256:5104db6102e53702af45c6b0af36e45a80970123b11a80c14e0fce48444cdbe3",
|
||||
"sha256:59274bcb631f4fdc9731e9a4a96d16d96b3a17e29fd5e46516518f38406f678f",
|
||||
"sha256:65a9c624e50ca5c50464dbf432996b5c4f056a411bcff5690ef4cab59f913f99",
|
||||
"sha256:b64e0672497b884d21170ca61c693da8488d77f043650efa7911378cbbad0f2c",
|
||||
"sha256:b70655dba00742b033310933066a2202e1cfbbb0f63841b4597cd8787974b242",
|
||||
"sha256:c3d8c238a6f9b60355578579563773348674b6da63c1a0d7394384ed341f3d41",
|
||||
"sha256:cd66bcd11b6a1c1a80fb8d90e25870ff2d5c705ab5eb9666355a33d3fef6ac70",
|
||||
"sha256:d59310fc4f97c1ff8c3660cb98032db266ac0c285a86ca7a512e8e84a95f44c9",
|
||||
"sha256:d71d1c822498646143270580dd6f743bb31ab89ae0ded8b2307c356d3a00f1c0",
|
||||
"sha256:f53da698b42db83cfb1f5073560838051430798c8d8e34a57a27031edbc3041d",
|
||||
"sha256:f958ba15745e30d3a38e3c9fcead8496037135bb21c78c0f925c104abba3a6fa",
|
||||
"sha256:ff95e2aa618eed1a0ef4479938f18f3522c89562b9bbb59d677597c0337569dd"
|
||||
],
|
||||
"version": "==0.9"
|
||||
},
|
||||
"pydantic": {
|
||||
"hashes": [
|
||||
"sha256:b72e0df2463cee746cf42639845d4106c19f30136375e779352d710e69617731",
|
||||
"sha256:dab99d3070e040b8b2e987dfbe237350ab92d5d57a22d4e0e268ede2d85c7964"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.26.0"
|
||||
},
|
||||
"pytoml": {
|
||||
"hashes": [
|
||||
"sha256:ca2d0cb127c938b8b76a9a0d0f855cf930c1d50cc3a0af6d3595b566519a1013"
|
||||
],
|
||||
"version": "==0.1.20"
|
||||
},
|
||||
"sqlalchemy": {
|
||||
"hashes": [
|
||||
"sha256:91c54ca8345008fceaec987e10924bf07dcab36c442925357e5a467b36a38319"
|
||||
],
|
||||
"version": "==1.3.3"
|
||||
},
|
||||
"starlette": {
|
||||
"hashes": [
|
||||
"sha256:d313433ef5cc38e0a276b59688ca2b11b8f031c78808c1afdf9d55cb86f34590"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.12.0"
|
||||
},
|
||||
"typing-extensions": {
|
||||
"hashes": [
|
||||
"sha256:07b2c978670896022a43c4b915df8958bec4a6b84add7f2c87b2b728bda3ba64",
|
||||
"sha256:f3f0e67e1d42de47b5c67c32c9b26641642e9170fe7e292991793705cd5fef7c",
|
||||
"sha256:fb2cd053238d33a8ec939190f30cfd736c00653a85a2919415cecf7dc3d9da71"
|
||||
],
|
||||
"version": "==3.7.2"
|
||||
},
|
||||
"wsproto": {
|
||||
"hashes": [
|
||||
"sha256:55c3da870460e8838b2fbe4d10f3accc0cea3a13d5e8dbbdc6da5d537d6d44dc",
|
||||
"sha256:c7f35e0af250b9f25583b090039eb2159a079fbe71b7daf86cc3ddcd2f3a70b3"
|
||||
],
|
||||
"version": "==0.14.0"
|
||||
}
|
||||
},
|
||||
"develop": {
|
||||
"appdirs": {
|
||||
"hashes": [
|
||||
"sha256:9e5896d1372858f8dd3344faf4e5014d21849c756c8d5701f78f8a103b372d92",
|
||||
"sha256:d8b24664561d0d34ddfaec54636d502d7cea6e29c3eaf68f3df6180863e2166e"
|
||||
],
|
||||
"version": "==1.4.3"
|
||||
},
|
||||
"atomicwrites": {
|
||||
"hashes": [
|
||||
"sha256:03472c30eb2c5d1ba9227e4c2ca66ab8287fbfbbda3888aa93dc2e28fc6811b4",
|
||||
"sha256:75a9445bac02d8d058d5e1fe689654ba5a6556a1dfd8ce6ec55a0ed79866cfa6"
|
||||
],
|
||||
"version": "==1.3.0"
|
||||
},
|
||||
"attrs": {
|
||||
"hashes": [
|
||||
"sha256:69c0dbf2ed392de1cb5ec704444b08a5ef81680a61cb899dc08127123af36a79",
|
||||
"sha256:f0b870f674851ecbfbbbd364d6b5cbdff9dcedbc7f3f5e18a6891057f21fe399"
|
||||
],
|
||||
"version": "==19.1.0"
|
||||
},
|
||||
"autoflake": {
|
||||
"hashes": [
|
||||
"sha256:6b59e5b9b82e30077499578856282debb81186d10b4f899e8c2e1d616cdef973"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.3"
|
||||
},
|
||||
"backcall": {
|
||||
"hashes": [
|
||||
"sha256:38ecd85be2c1e78f77fd91700c76e14667dc21e2713b63876c0eb901196e01e4",
|
||||
"sha256:bbbf4b1e5cd2bdb08f915895b51081c041bac22394fdfcfdfbe9f14b77c08bf2"
|
||||
],
|
||||
"version": "==0.1.0"
|
||||
},
|
||||
"better-exceptions": {
|
||||
"hashes": [
|
||||
"sha256:bf79c87659bc849989d726bf0e4a2100edefe7eded112d201f54fe08467fdf63",
|
||||
"sha256:c196cad849de615abb9f6eb67ca1b83f33b938818f0e2fe8fa157b22aeb7b992"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.2.2"
|
||||
},
|
||||
"black": {
|
||||
"hashes": [
|
||||
"sha256:09a9dcb7c46ed496a9850b76e4e825d6049ecd38b611f1224857a79bd985a8cf",
|
||||
"sha256:68950ffd4d9169716bcb8719a56c07a2f4485354fec061cdd5910aa07369731c"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==19.3b0"
|
||||
},
|
||||
"bleach": {
|
||||
"hashes": [
|
||||
"sha256:213336e49e102af26d9cde77dd2d0397afabc5a6bf2fed985dc35b5d1e285a16",
|
||||
"sha256:3fdf7f77adcf649c9911387df51254b813185e32b2c6619f690b593a617e19fa"
|
||||
],
|
||||
"version": "==3.1.0"
|
||||
},
|
||||
"certifi": {
|
||||
"hashes": [
|
||||
"sha256:59b7658e26ca9c7339e00f8f4636cdfe59d34fa37b9b04f6f9e9926b3cece1a5",
|
||||
"sha256:b26104d6835d1f5e49452a26eb2ff87fe7090b89dfcaee5ea2212697e1e1d7ae"
|
||||
],
|
||||
"version": "==2019.3.9"
|
||||
},
|
||||
"chardet": {
|
||||
"hashes": [
|
||||
"sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae",
|
||||
"sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"
|
||||
],
|
||||
"version": "==3.0.4"
|
||||
},
|
||||
"click": {
|
||||
"hashes": [
|
||||
"sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13",
|
||||
"sha256:5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7"
|
||||
],
|
||||
"version": "==7.0"
|
||||
},
|
||||
"coverage": {
|
||||
"hashes": [
|
||||
"sha256:0402b1822d513d0231589494bceddb067d20581f5083598c451b56c684b0e5d6",
|
||||
"sha256:0644e28e8aea9d9d563607ee8b7071b07dd57a4a3de11f8684cd33c51c0d1b93",
|
||||
"sha256:0874a283686803884ec0665018881130604956dbaa344f2539c46d82cbe29eda",
|
||||
"sha256:0988c3837df4bc371189bb3425d5232cf150055452034c232dda9cbe04f9c38e",
|
||||
"sha256:20bc3205b3100956bb72293fabb97f0ed972c81fed10b3251c90c70dcb0599ab",
|
||||
"sha256:2cc9142a3367e74eb6b19d58c53ebb1dfd7336b91cdcc91a6a2888bf8c7af984",
|
||||
"sha256:3ae9a0a59b058ce0761c3bd2c2d66ecb2ee2b8ac592620184370577f7a546fb3",
|
||||
"sha256:3b2e30b835df58cb973f478d09f3d82e90c98c8e5059acc245a8e4607e023801",
|
||||
"sha256:401e9b04894eb1498c639c6623ee78a646990ce5f095248e2440968aafd6e90e",
|
||||
"sha256:41ec5812d5decdaa72708be3018e7443e90def4b5a71294236a4df192cf9eab9",
|
||||
"sha256:475769b638a055e75b3d3219e054fe2a023c0b077ff15bff6c95aba7e93e6cac",
|
||||
"sha256:61424f4e2e82c4129a4ba71e10ebacb32a9ecd6f80de2cd05bdead6ba75ed736",
|
||||
"sha256:811969904d4dd0bee7d958898be8d9d75cef672d9b7e7db819dfeac3d20d2d0c",
|
||||
"sha256:86224bb99abfd672bf2f9fcecad5e8d7a3fa94f7f71513f2210460a0350307cd",
|
||||
"sha256:9a238a20a3af00665f8381f7e53e9c606f9bb652d2423f6b822f6cb790d887e8",
|
||||
"sha256:a23b3fbc14d4e6182ecebfd22f3729beef0636d151d94764a1c28330d185e4e5",
|
||||
"sha256:ac162b4ebe51b7a2b7f5e462c4402802633eb81e77c94f8a7c1ed8a556e72c75",
|
||||
"sha256:b6187378726c84365bf297b5dcdae8789b6a5823b200bea23797777e5a63be09",
|
||||
"sha256:bcd5723d905ed4a825f17410a53535f880b6d7548ae3d89078db7b1ceefcd853",
|
||||
"sha256:c48a4f9c5fb385269bb7fbaf9c1326a94863b65ec7f5c96b2ea56b252f01ad08",
|
||||
"sha256:cd40199d6f1c29c85b170d25589be9a97edff8ee7e62be180a2a137823896030",
|
||||
"sha256:d1bc331a7d069485ac1d8c25a0ea1f6aab6cb2a87146fb652222481c1bddc9ff",
|
||||
"sha256:d7e0cdc249aa0f94aa2e531b03999ddaf03a10b4fa090a894712d4c8066abd89",
|
||||
"sha256:e9ee8fcd8e067fcc5d7276d46e07e863102b70a52545ef4254df1ff0893ce75f",
|
||||
"sha256:eb313c23d983b7810504f42104e8dcd1c7ccdda8fbaab82aab92ab79fea19345",
|
||||
"sha256:f9cfd478654b509941b85ed70f870f5e3c74678f566bec12fd26545e5340ba47",
|
||||
"sha256:fae1fa144034d021a52cb9ea200eb8dedf91869c6df8202ad5d149b41ed91cc8"
|
||||
],
|
||||
"version": "==5.0a5"
|
||||
},
|
||||
"decorator": {
|
||||
"hashes": [
|
||||
"sha256:86156361c50488b84a3f148056ea716ca587df2f0de1d34750d35c21312725de",
|
||||
"sha256:f069f3a01830ca754ba5258fde2278454a0b5b79e0d7f5c13b3b97e57d4acff6"
|
||||
],
|
||||
"version": "==4.4.0"
|
||||
},
|
||||
"defusedxml": {
|
||||
"hashes": [
|
||||
"sha256:6687150770438374ab581bb7a1b327a847dd9c5749e396102de3fad4e8a3ef93",
|
||||
"sha256:f684034d135af4c6cbb949b8a4d2ed61634515257a67299e5f940fbaa34377f5"
|
||||
],
|
||||
"version": "==0.6.0"
|
||||
},
|
||||
"dnspython": {
|
||||
"hashes": [
|
||||
"sha256:36c5e8e38d4369a08b6780b7f27d790a292b2b08eea01607865bf0936c558e01",
|
||||
"sha256:f69c21288a962f4da86e56c4905b49d11aba7938d3d740e80d9e366ee4f1632d"
|
||||
],
|
||||
"version": "==1.16.0"
|
||||
},
|
||||
"docutils": {
|
||||
"hashes": [
|
||||
"sha256:02aec4bd92ab067f6ff27a38a38a41173bf01bed8f89157768c1573f53e474a6",
|
||||
"sha256:51e64ef2ebfb29cae1faa133b3710143496eca21c530f3f71424d77687764274",
|
||||
"sha256:7a4bd47eaf6596e1295ecb11361139febe29b084a87bf005bf899f9a42edc3c6"
|
||||
],
|
||||
"version": "==0.14"
|
||||
},
|
||||
"email-validator": {
|
||||
"hashes": [
|
||||
"sha256:79966e318d6d68fed359c90f8f19d242bcc178b724011f1c07145bd093da6cc7"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.0.4"
|
||||
},
|
||||
"entrypoints": {
|
||||
"hashes": [
|
||||
"sha256:589f874b313739ad35be6e0cd7efde2a4e9b6fea91edcc34e58ecbb8dbe56d19",
|
||||
"sha256:c70dd71abe5a8c85e55e12c19bd91ccfeec11a6e99044204511f9ed547d48451"
|
||||
],
|
||||
"version": "==0.3"
|
||||
},
|
||||
"flake8": {
|
||||
"hashes": [
|
||||
"sha256:859996073f341f2670741b51ec1e67a01da142831aa1fdc6242dbf88dffbe661",
|
||||
"sha256:a796a115208f5c03b18f332f7c11729812c8c3ded6c46319c59b53efd3819da8"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==3.7.7"
|
||||
},
|
||||
"flit": {
|
||||
"hashes": [
|
||||
"sha256:1d93f7a833ed8a6e120ddc40db5c4763bc39bccc75c05081ec8285ece718aefb",
|
||||
"sha256:6f6f0fb83c51ffa3a150fa41b5ac118df9ea4a87c2c06dff4ebf9adbe7b52b36"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.3"
|
||||
},
|
||||
"h11": {
|
||||
"hashes": [
|
||||
"sha256:acca6a44cb52a32ab442b1779adf0875c443c689e9e028f8d831a3769f9c5208",
|
||||
"sha256:f2b1ca39bfed357d1f19ac732913d5f9faa54a5062eca7d2ec3a916cfb7ae4c7"
|
||||
],
|
||||
"version": "==0.8.1"
|
||||
},
|
||||
"httptools": {
|
||||
"hashes": [
|
||||
"sha256:e00cbd7ba01ff748e494248183abc6e153f49181169d8a3d41bb49132ca01dfc"
|
||||
],
|
||||
"version": "==0.0.13"
|
||||
},
|
||||
"idna": {
|
||||
"hashes": [
|
||||
"sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407",
|
||||
"sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c"
|
||||
],
|
||||
"version": "==2.8"
|
||||
},
|
||||
"ipykernel": {
|
||||
"hashes": [
|
||||
"sha256:346189536b88859937b5f4848a6fd85d1ad0729f01724a411de5cae9b618819c",
|
||||
"sha256:f0e962052718068ad3b1d8bcc703794660858f58803c3798628817f492a8769c"
|
||||
],
|
||||
"version": "==5.1.1"
|
||||
},
|
||||
"ipython": {
|
||||
"hashes": [
|
||||
"sha256:54c5a8aa1eadd269ac210b96923688ccf01ebb2d0f21c18c3c717909583579a8",
|
||||
"sha256:e840810029224b56cd0d9e7719dc3b39cf84d577f8ac686547c8ba7a06eeab26"
|
||||
],
|
||||
"markers": "python_version >= '3.3'",
|
||||
"version": "==7.5.0"
|
||||
},
|
||||
"ipython-genutils": {
|
||||
"hashes": [
|
||||
"sha256:72dd37233799e619666c9f639a9da83c34013a73e8bbc79a7a6348d93c61fab8",
|
||||
"sha256:eb2e116e75ecef9d4d228fdc66af54269afa26ab4463042e33785b887c628ba8"
|
||||
],
|
||||
"version": "==0.2.0"
|
||||
},
|
||||
"ipywidgets": {
|
||||
"hashes": [
|
||||
"sha256:0f2b5cde9f272cb49d52f3f0889fdd1a7ae1e74f37b48dac35a83152780d2b7b",
|
||||
"sha256:a3e224f430163f767047ab9a042fc55adbcab0c24bbe6cf9f306c4f89fdf0ba3"
|
||||
],
|
||||
"version": "==7.4.2"
|
||||
},
|
||||
"isort": {
|
||||
"hashes": [
|
||||
"sha256:c40744b6bc5162bbb39c1257fe298b7a393861d50978b565f3ccd9cb9de0182a",
|
||||
"sha256:f57abacd059dc3bd666258d1efb0377510a89777fda3e3274e3c01f7c03ae22d"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==4.3.20"
|
||||
},
|
||||
"jedi": {
|
||||
"hashes": [
|
||||
"sha256:2bb0603e3506f708e792c7f4ad8fc2a7a9d9c2d292a358fbbd58da531695595b",
|
||||
"sha256:2c6bcd9545c7d6440951b12b44d373479bf18123a401a52025cf98563fbd826c"
|
||||
],
|
||||
"version": "==0.13.3"
|
||||
},
|
||||
"jinja2": {
|
||||
"hashes": [
|
||||
"sha256:065c4f02ebe7f7cf559e49ee5a95fb800a9e4528727aec6f24402a5374c65013",
|
||||
"sha256:14dd6caf1527abb21f08f86c784eac40853ba93edb79552aa1e4b8aef1b61c7b"
|
||||
],
|
||||
"version": "==2.10.1"
|
||||
},
|
||||
"jsonschema": {
|
||||
"hashes": [
|
||||
"sha256:0c0a81564f181de3212efa2d17de1910f8732fa1b71c42266d983cd74304e20d",
|
||||
"sha256:a5f6559964a3851f59040d3b961de5e68e70971afb88ba519d27e6a039efff1a"
|
||||
],
|
||||
"version": "==3.0.1"
|
||||
},
|
||||
"jupyter": {
|
||||
"hashes": [
|
||||
"sha256:3e1f86076bbb7c8c207829390305a2b1fe836d471ed54be66a3b8c41e7f46cc7",
|
||||
"sha256:5b290f93b98ffbc21c0c7e749f054b3267782166d72fa5e3ed1ed4eaf34a2b78",
|
||||
"sha256:d9dc4b3318f310e34c82951ea5d6683f67bed7def4b259fafbfe4f1beb1d8e5f"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.0.0"
|
||||
},
|
||||
"jupyter-client": {
|
||||
"hashes": [
|
||||
"sha256:b5f9cb06105c1d2d30719db5ffb3ea67da60919fb68deaefa583deccd8813551",
|
||||
"sha256:c44411eb1463ed77548bc2d5ec0d744c9b81c4a542d9637c7a52824e2121b987"
|
||||
],
|
||||
"version": "==5.2.4"
|
||||
},
|
||||
"jupyter-console": {
|
||||
"hashes": [
|
||||
"sha256:308ce876354924fb6c540b41d5d6d08acfc946984bf0c97777c1ddcb42e0b2f5",
|
||||
"sha256:cc80a97a5c389cbd30252ffb5ce7cefd4b66bde98219edd16bf5cb6f84bb3568"
|
||||
],
|
||||
"version": "==6.0.0"
|
||||
},
|
||||
"jupyter-core": {
|
||||
"hashes": [
|
||||
"sha256:927d713ffa616ea11972534411544589976b2493fc7e09ad946e010aa7eb9970",
|
||||
"sha256:ba70754aa680300306c699790128f6fbd8c306ee5927976cbe48adacf240c0b7"
|
||||
],
|
||||
"version": "==4.4.0"
|
||||
},
|
||||
"livereload": {
|
||||
"hashes": [
|
||||
"sha256:78d55f2c268a8823ba499305dcac64e28ddeb9a92571e12d543cd304faf5817b",
|
||||
"sha256:89254f78d7529d7ea0a3417d224c34287ebfe266b05e67e51facaf82c27f0f66"
|
||||
],
|
||||
"version": "==2.6.1"
|
||||
},
|
||||
"markdown": {
|
||||
"hashes": [
|
||||
"sha256:2e50876bcdd74517e7b71f3e7a76102050edec255b3983403f1a63e7c8a41e7a",
|
||||
"sha256:56a46ac655704b91e5b7e6326ce43d5ef72411376588afa1dd90e881b83c7e8c"
|
||||
],
|
||||
"version": "==3.1.1"
|
||||
},
|
||||
"markdown-include": {
|
||||
"hashes": [
|
||||
"sha256:72a45461b589489a088753893bc95c5fa5909936186485f4ed55caa57d10250f"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.5.1"
|
||||
},
|
||||
"markupsafe": {
|
||||
"hashes": [
|
||||
"sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473",
|
||||
"sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161",
|
||||
"sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235",
|
||||
"sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5",
|
||||
"sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff",
|
||||
"sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b",
|
||||
"sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1",
|
||||
"sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e",
|
||||
"sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183",
|
||||
"sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66",
|
||||
"sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1",
|
||||
"sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1",
|
||||
"sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e",
|
||||
"sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b",
|
||||
"sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905",
|
||||
"sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735",
|
||||
"sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d",
|
||||
"sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e",
|
||||
"sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d",
|
||||
"sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c",
|
||||
"sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21",
|
||||
"sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2",
|
||||
"sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5",
|
||||
"sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b",
|
||||
"sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6",
|
||||
"sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f",
|
||||
"sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f",
|
||||
"sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7"
|
||||
],
|
||||
"version": "==1.1.1"
|
||||
},
|
||||
"mccabe": {
|
||||
"hashes": [
|
||||
"sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42",
|
||||
"sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"
|
||||
],
|
||||
"version": "==0.6.1"
|
||||
},
|
||||
"mistune": {
|
||||
"hashes": [
|
||||
"sha256:59a3429db53c50b5c6bcc8a07f8848cb00d7dc8bdb431a4ab41920d201d4756e",
|
||||
"sha256:88a1051873018da288eee8538d476dffe1262495144b33ecb586c4ab266bb8d4"
|
||||
],
|
||||
"version": "==0.8.4"
|
||||
},
|
||||
"mkdocs": {
|
||||
"hashes": [
|
||||
"sha256:17d34329aad75d5de604b9ed4e31df3a4d235afefdc46ce7b1964fddb2e1e939",
|
||||
"sha256:8cc8b38325456b9e942c981a209eaeb1e9f3f77b493ad755bfef889b9c8d356a"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.0.4"
|
||||
},
|
||||
"mkdocs-material": {
|
||||
"hashes": [
|
||||
"sha256:1c39b6af13a900d9f47ab2b8ac67b3258799f4570b552573e9d6868ad6a438e9",
|
||||
"sha256:22073941cff7176e810b719aced6a90381e64a96d346b8a6803a06b7192b7ad5"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==4.3.0"
|
||||
},
|
||||
"more-itertools": {
|
||||
"hashes": [
|
||||
"sha256:2112d2ca570bb7c3e53ea1a35cd5df42bb0fd10c45f0fb97178679c3c03d64c7",
|
||||
"sha256:c3e4748ba1aad8dba30a4886b0b1a2004f9a863837b8654e7059eebf727afa5a"
|
||||
],
|
||||
"markers": "python_version > '2.7'",
|
||||
"version": "==7.0.0"
|
||||
},
|
||||
"mypy": {
|
||||
"hashes": [
|
||||
"sha256:2afe51527b1f6cdc4a5f34fc90473109b22bf7f21086ba3e9451857cf11489e6",
|
||||
"sha256:56a16df3e0abb145d8accd5dbb70eba6c4bd26e2f89042b491faa78c9635d1e2",
|
||||
"sha256:5764f10d27b2e93c84f70af5778941b8f4aa1379b2430f85c827e0f5464e8714",
|
||||
"sha256:5bbc86374f04a3aa817622f98e40375ccb28c4836f36b66706cf3c6ccce86eda",
|
||||
"sha256:6a9343089f6377e71e20ca734cd8e7ac25d36478a9df580efabfe9059819bf82",
|
||||
"sha256:6c9851bc4a23dc1d854d3f5dfd5f20a016f8da86bcdbb42687879bb5f86434b0",
|
||||
"sha256:b8e85956af3fcf043d6f87c91cbe8705073fc67029ba6e22d3468bfee42c4823",
|
||||
"sha256:b9a0af8fae490306bc112229000aa0c2ccc837b49d29a5c42e088c132a2334dd",
|
||||
"sha256:bbf643528e2a55df2c1587008d6e3bda5c0445f1240dfa85129af22ae16d7a9a",
|
||||
"sha256:c46ab3438bd21511db0f2c612d89d8344154c0c9494afc7fbc932de514cf8d15",
|
||||
"sha256:f7a83d6bd805855ef83ec605eb01ab4fa42bcef254b13631e451cbb44914a9b0"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.701"
|
||||
},
|
||||
"mypy-extensions": {
|
||||
"hashes": [
|
||||
"sha256:37e0e956f41369209a3d5f34580150bcacfabaa57b33a15c0b25f4b5725e0812",
|
||||
"sha256:b16cabe759f55e3409a7d231ebd2841378fb0c27a5d1994719e340e4f429ac3e"
|
||||
],
|
||||
"version": "==0.4.1"
|
||||
},
|
||||
"nbconvert": {
|
||||
"hashes": [
|
||||
"sha256:138381baa41d83584459b5cfecfc38c800ccf1f37d9ddd0bd440783346a4c39c",
|
||||
"sha256:4a978548d8383f6b2cfca4a3b0543afb77bc7cb5a96e8b424337ab58c12da9bc"
|
||||
],
|
||||
"version": "==5.5.0"
|
||||
},
|
||||
"nbformat": {
|
||||
"hashes": [
|
||||
"sha256:b9a0dbdbd45bb034f4f8893cafd6f652ea08c8c1674ba83f2dc55d3955743b0b",
|
||||
"sha256:f7494ef0df60766b7cabe0a3651556345a963b74dbc16bc7c18479041170d402"
|
||||
],
|
||||
"version": "==4.4.0"
|
||||
},
|
||||
"notebook": {
|
||||
"hashes": [
|
||||
"sha256:573e0ae650c5d76b18b6e564ba6d21bf321d00847de1d215b418acb64f056eb8",
|
||||
"sha256:f64fa6624d2323fbef6210a621817d6505a45d0d4a9367f1843b20a38a4666ee"
|
||||
],
|
||||
"version": "==5.7.8"
|
||||
},
|
||||
"pandocfilters": {
|
||||
"hashes": [
|
||||
"sha256:b3dd70e169bb5449e6bc6ff96aea89c5eea8c5f6ab5e207fc2f521a2cf4a0da9"
|
||||
],
|
||||
"version": "==1.4.2"
|
||||
},
|
||||
"parso": {
|
||||
"hashes": [
|
||||
"sha256:17cc2d7a945eb42c3569d4564cdf49bde221bc2b552af3eca9c1aad517dcdd33",
|
||||
"sha256:2e9574cb12e7112a87253e14e2c380ce312060269d04bd018478a3c92ea9a376"
|
||||
],
|
||||
"version": "==0.4.0"
|
||||
},
|
||||
"pexpect": {
|
||||
"hashes": [
|
||||
"sha256:2094eefdfcf37a1fdbfb9aa090862c1a4878e5c7e0e7e7088bdb511c558e5cd1",
|
||||
"sha256:9e2c1fd0e6ee3a49b28f95d4b33bc389c89b20af6a1255906e90ff1262ce62eb"
|
||||
],
|
||||
"markers": "sys_platform != 'win32'",
|
||||
"version": "==4.7.0"
|
||||
},
|
||||
"pickleshare": {
|
||||
"hashes": [
|
||||
"sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca",
|
||||
"sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56"
|
||||
],
|
||||
"version": "==0.7.5"
|
||||
},
|
||||
"pluggy": {
|
||||
"hashes": [
|
||||
"sha256:25a1bc1d148c9a640211872b4ff859878d422bccb59c9965e04eed468a0aa180",
|
||||
"sha256:964cedd2b27c492fbf0b7f58b3284a09cf7f99b0f715941fb24a439b3af1bd1a"
|
||||
],
|
||||
"version": "==0.11.0"
|
||||
},
|
||||
"prometheus-client": {
|
||||
"hashes": [
|
||||
"sha256:1b38b958750f66f208bcd9ab92a633c0c994d8859c831f7abc1f46724fcee490"
|
||||
],
|
||||
"version": "==0.6.0"
|
||||
},
|
||||
"prompt-toolkit": {
|
||||
"hashes": [
|
||||
"sha256:11adf3389a996a6d45cc277580d0d53e8a5afd281d0c9ec71b28e6f121463780",
|
||||
"sha256:2519ad1d8038fd5fc8e770362237ad0364d16a7650fb5724af6997ed5515e3c1",
|
||||
"sha256:977c6583ae813a37dc1c2e1b715892461fcbdaa57f6fc62f33a528c4886c8f55"
|
||||
],
|
||||
"version": "==2.0.9"
|
||||
},
|
||||
"ptyprocess": {
|
||||
"hashes": [
|
||||
"sha256:923f299cc5ad920c68f2bc0bc98b75b9f838b93b599941a6b63ddbc2476394c0",
|
||||
"sha256:d7cc528d76e76342423ca640335bd3633420dc1366f258cb31d05e865ef5ca1f"
|
||||
],
|
||||
"markers": "os_name != 'nt'",
|
||||
"version": "==0.6.0"
|
||||
},
|
||||
"py": {
|
||||
"hashes": [
|
||||
"sha256:64f65755aee5b381cea27766a3a147c3f15b9b6b9ac88676de66ba2ae36793fa",
|
||||
"sha256:dc639b046a6e2cff5bbe40194ad65936d6ba360b52b3c3fe1d08a82dd50b5e53"
|
||||
],
|
||||
"version": "==1.8.0"
|
||||
},
|
||||
"pycodestyle": {
|
||||
"hashes": [
|
||||
"sha256:95a2219d12372f05704562a14ec30bc76b05a5b297b21a5dfe3f6fac3491ae56",
|
||||
"sha256:e40a936c9a450ad81df37f549d676d127b1b66000a6c500caa2b085bc0ca976c"
|
||||
],
|
||||
"version": "==2.5.0"
|
||||
},
|
||||
"pyflakes": {
|
||||
"hashes": [
|
||||
"sha256:17dbeb2e3f4d772725c777fabc446d5634d1038f234e77343108ce445ea69ce0",
|
||||
"sha256:d976835886f8c5b31d47970ed689944a0262b5f3afa00a5a7b4dc81e5449f8a2"
|
||||
],
|
||||
"version": "==2.1.1"
|
||||
},
|
||||
"pygments": {
|
||||
"hashes": [
|
||||
"sha256:31cba6ffb739f099a85e243eff8cb717089fdd3c7300767d9fc34cb8e1b065f5",
|
||||
"sha256:5ad302949b3c98dd73f8d9fcdc7e9cb592f120e32a18e23efd7f3dc51194472b"
|
||||
],
|
||||
"version": "==2.4.0"
|
||||
},
|
||||
"pymdown-extensions": {
|
||||
"hashes": [
|
||||
"sha256:25b0a7967fa697b5035e23340a48594e3e93acb10b06d74574218ace3347d1df",
|
||||
"sha256:6cf0cf36b5a03b291ace22dc2f320f4789ce56fbdb6635a3be5fadbf5d7694dd"
|
||||
],
|
||||
"version": "==6.0"
|
||||
},
|
||||
"pyrsistent": {
|
||||
"hashes": [
|
||||
"sha256:16692ee739d42cf5e39cef8d27649a8c1fdb7aa99887098f1460057c5eb75c3a"
|
||||
],
|
||||
"version": "==0.15.2"
|
||||
},
|
||||
"pytest": {
|
||||
"hashes": [
|
||||
"sha256:1a8aa4fa958f8f451ac5441f3ac130d9fc86ea38780dd2715e6d5c5882700b24",
|
||||
"sha256:b8bf138592384bd4e87338cb0f256bf5f615398a649d4bd83915f0e4047a5ca6"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==4.5.0"
|
||||
},
|
||||
"pytest-cov": {
|
||||
"hashes": [
|
||||
"sha256:2b097cde81a302e1047331b48cadacf23577e431b61e9c6f49a1170bbe3d3da6",
|
||||
"sha256:e00ea4fdde970725482f1f35630d12f074e121a23801aabf2ae154ec6bdd343a"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==2.7.1"
|
||||
},
|
||||
"python-dateutil": {
|
||||
"hashes": [
|
||||
"sha256:7e6584c74aeed623791615e26efd690f29817a27c73085b78e4bad02493df2fb",
|
||||
"sha256:c89805f6f4d64db21ed966fda138f8a5ed7a4fdbc1a8ee329ce1b74e3c74da9e"
|
||||
],
|
||||
"version": "==2.8.0"
|
||||
},
|
||||
"python-multipart": {
|
||||
"hashes": [
|
||||
"sha256:f7bb5f611fc600d15fa47b3974c8aa16e93724513b49b5f95c81e6624c83fa43"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.0.5"
|
||||
},
|
||||
"pytoml": {
|
||||
"hashes": [
|
||||
"sha256:ca2d0cb127c938b8b76a9a0d0f855cf930c1d50cc3a0af6d3595b566519a1013"
|
||||
],
|
||||
"version": "==0.1.20"
|
||||
},
|
||||
"pyyaml": {
|
||||
"hashes": [
|
||||
"sha256:1adecc22f88d38052fb787d959f003811ca858b799590a5eaa70e63dca50308c",
|
||||
"sha256:436bc774ecf7c103814098159fbb84c2715d25980175292c648f2da143909f95",
|
||||
"sha256:460a5a4248763f6f37ea225d19d5c205677d8d525f6a83357ca622ed541830c2",
|
||||
"sha256:5a22a9c84653debfbf198d02fe592c176ea548cccce47553f35f466e15cf2fd4",
|
||||
"sha256:7a5d3f26b89d688db27822343dfa25c599627bc92093e788956372285c6298ad",
|
||||
"sha256:9372b04a02080752d9e6f990179a4ab840227c6e2ce15b95e1278456664cf2ba",
|
||||
"sha256:a5dcbebee834eaddf3fa7366316b880ff4062e4bcc9787b78c7fbb4a26ff2dd1",
|
||||
"sha256:aee5bab92a176e7cd034e57f46e9df9a9862a71f8f37cad167c6fc74c65f5b4e",
|
||||
"sha256:c51f642898c0bacd335fc119da60baae0824f2cde95b0330b56c0553439f0673",
|
||||
"sha256:c68ea4d3ba1705da1e0d85da6684ac657912679a649e8868bd850d2c299cce13",
|
||||
"sha256:e23d0cc5299223dcc37885dae624f382297717e459ea24053709675a976a3e19"
|
||||
],
|
||||
"version": "==5.1"
|
||||
},
|
||||
"pyzmq": {
|
||||
"hashes": [
|
||||
"sha256:1651e52ed91f0736afd6d94ef9f3259b5534ce8beddb054f3d5ca989c4ef7c4f",
|
||||
"sha256:5ccb9b3d4cd20c000a9b75689d5add8cd3bce67fcbd0f8ae1b59345247d803af",
|
||||
"sha256:5e120c4cd3872e332fb35d255ad5998ebcee32ace4387b1b337416b6b90436c7",
|
||||
"sha256:5e2a3707c69a7281a9957f83718815fd74698cba31f6d69f9ed359921f662221",
|
||||
"sha256:63d51add9af8d0442dc90f916baf98fdc04e3b0a32afec4bfc83f8d85e72959f",
|
||||
"sha256:65c5a0bdc49e20f7d6b03a661f71e2fda7a99c51270cafe71598146d09810d0d",
|
||||
"sha256:66828fabe911aa545d919028441a585edb7c9c77969a5fea6722ef6e6ece38ab",
|
||||
"sha256:7d79427e82d9dad6e9b47c0b3e7ae5f9d489b1601e3a36ea629bb49501a4daf3",
|
||||
"sha256:824ee5d3078c4eae737ffc500fbf32f2b14e6ec89b26b435b7834febd70120cf",
|
||||
"sha256:89dc0a83cccec19ff3c62c091e43e66e0183d1e6b4658c16ee4e659518131494",
|
||||
"sha256:8b319805f6f7c907b101c864c3ca6cefc9db8ce0791356f180b1b644c7347e4c",
|
||||
"sha256:90facfb379ab47f94b19519c1ecc8ec8d10813b69d9c163117944948bdec5d15",
|
||||
"sha256:a0a178c7420021fc0730180a914a4b4b3092ce9696ceb8e72d0f60f8ce1655dd",
|
||||
"sha256:a7a89591ae315baccb8072f216614b3e59aed7385aef4393a6c741783d6ee9cf",
|
||||
"sha256:ba2578f0ae582452c02ed9fac2dc477b08e80ce05d2c0885becf5fff6651ccb0",
|
||||
"sha256:c69b0055c55702f5b0b6b354133e8325b9a56dbc80e1be2d240bead253fb9825",
|
||||
"sha256:ca434e1858fe222380221ddeb81e86f45522773344c9da63c311d17161df5e06",
|
||||
"sha256:d4b8ecfc3d92f114f04d5c40f60a65e5196198b827503341521dda12d8b14939",
|
||||
"sha256:d706025c47b09a54f005953ebe206f6d07a22516776faa4f509aaff681cc5468",
|
||||
"sha256:d8f27e958f8a2c0c8ffd4d8855c3ce8ac3fa1e105f0491ce31729aa2b3229740",
|
||||
"sha256:dbd264298f76b9060ce537008eb989317ca787c857e23cbd1b3ddf89f190a9b1",
|
||||
"sha256:e926d66f0df8fdbf03ba20583af0f215e475c667fb033d45fd031c66c63e34c9",
|
||||
"sha256:efc3bd48237f973a749f7312f68062f1b4ca5c2032a0673ca3ea8e46aa77187b",
|
||||
"sha256:f59bc782228777cbfe04555707a9c56d269c787ed25d6d28ed9d0fbb41cb1ad2",
|
||||
"sha256:f8da5322f4ff5f667a0d5a27e871b560c6637153c81e318b35cb012b2a98835c"
|
||||
],
|
||||
"version": "==18.0.1"
|
||||
},
|
||||
"qtconsole": {
|
||||
"hashes": [
|
||||
"sha256:a667558c7b1e1442a2e5bcef1686c55e096efd0b58d8b2a0a8415f4579991ee3",
|
||||
"sha256:fdfc6002d9d2834c88f9c92e0f6f590284ff3740fa53016f188a62d58bcca6d8"
|
||||
],
|
||||
"version": "==4.4.4"
|
||||
},
|
||||
"requests": {
|
||||
"hashes": [
|
||||
"sha256:11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4",
|
||||
"sha256:9cf5292fcd0f598c671cfc1e0d7d1a7f13bb8085e9a590f48c010551dc6c4b31"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==2.22.0"
|
||||
},
|
||||
"send2trash": {
|
||||
"hashes": [
|
||||
"sha256:60001cc07d707fe247c94f74ca6ac0d3255aabcb930529690897ca2a39db28b2",
|
||||
"sha256:f1691922577b6fa12821234aeb57599d887c4900b9ca537948d2dac34aea888b"
|
||||
],
|
||||
"version": "==1.5.0"
|
||||
},
|
||||
"six": {
|
||||
"hashes": [
|
||||
"sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c",
|
||||
"sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73"
|
||||
],
|
||||
"version": "==1.12.0"
|
||||
},
|
||||
"sqlalchemy": {
|
||||
"hashes": [
|
||||
"sha256:91c54ca8345008fceaec987e10924bf07dcab36c442925357e5a467b36a38319"
|
||||
],
|
||||
"version": "==1.3.3"
|
||||
},
|
||||
"terminado": {
|
||||
"hashes": [
|
||||
"sha256:d9d012de63acb8223ac969c17c3043337c2fcfd28f3aea1ee429b345d01ef460",
|
||||
"sha256:de08e141f83c3a0798b050ecb097ab6259c3f0331b2f7b7750c9075ced2c20c2"
|
||||
],
|
||||
"version": "==0.8.2"
|
||||
},
|
||||
"testpath": {
|
||||
"hashes": [
|
||||
"sha256:46c89ebb683f473ffe2aab0ed9f12581d4d078308a3cb3765d79c6b2317b0109",
|
||||
"sha256:b694b3d9288dbd81685c5d2e7140b81365d46c29f5db4bc659de5aa6b98780f8"
|
||||
],
|
||||
"version": "==0.4.2"
|
||||
},
|
||||
"toml": {
|
||||
"hashes": [
|
||||
"sha256:229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c",
|
||||
"sha256:235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e"
|
||||
],
|
||||
"version": "==0.10.0"
|
||||
},
|
||||
"tornado": {
|
||||
"hashes": [
|
||||
"sha256:1174dcb84d08887b55defb2cda1986faeeea715fff189ef3dc44cce99f5fca6b",
|
||||
"sha256:2613fab506bd2aedb3722c8c64c17f8f74f4070afed6eea17f20b2115e445aec",
|
||||
"sha256:44b82bc1146a24e5b9853d04c142576b4e8fa7a92f2e30bc364a85d1f75c4de2",
|
||||
"sha256:457fcbee4df737d2defc181b9073758d73f54a6cfc1f280533ff48831b39f4a8",
|
||||
"sha256:49603e1a6e24104961497ad0c07c799aec1caac7400a6762b687e74c8206677d",
|
||||
"sha256:8c2f40b99a8153893793559919a355d7b74649a11e59f411b0b0a1793e160bc0",
|
||||
"sha256:e1d897889c3b5a829426b7d52828fb37b28bc181cd598624e65c8be40ee3f7fa"
|
||||
],
|
||||
"version": "==6.0.2"
|
||||
},
|
||||
"traitlets": {
|
||||
"hashes": [
|
||||
"sha256:9c4bd2d267b7153df9152698efb1050a5d84982d3384a37b2c1f7723ba3e7835",
|
||||
"sha256:c6cb5e6f57c5a9bdaa40fa71ce7b4af30298fbab9ece9815b5d995ab6217c7d9"
|
||||
],
|
||||
"version": "==4.3.2"
|
||||
},
|
||||
"typed-ast": {
|
||||
"hashes": [
|
||||
"sha256:132eae51d6ef3ff4a8c47c393a4ef5ebf0d1aecc96880eb5d6c8ceab7017cc9b",
|
||||
"sha256:18141c1484ab8784006c839be8b985cfc82a2e9725837b0ecfa0203f71c4e39d",
|
||||
"sha256:2baf617f5bbbfe73fd8846463f5aeafc912b5ee247f410700245d68525ec584a",
|
||||
"sha256:3d90063f2cbbe39177e9b4d888e45777012652d6110156845b828908c51ae462",
|
||||
"sha256:4304b2218b842d610aa1a1d87e1dc9559597969acc62ce717ee4dfeaa44d7eee",
|
||||
"sha256:4983ede548ffc3541bae49a82675996497348e55bafd1554dc4e4a5d6eda541a",
|
||||
"sha256:5315f4509c1476718a4825f45a203b82d7fdf2a6f5f0c8f166435975b1c9f7d4",
|
||||
"sha256:6cdfb1b49d5345f7c2b90d638822d16ba62dc82f7616e9b4caa10b72f3f16649",
|
||||
"sha256:7b325f12635598c604690efd7a0197d0b94b7d7778498e76e0710cd582fd1c7a",
|
||||
"sha256:8d3b0e3b8626615826f9a626548057c5275a9733512b137984a68ba1598d3d2f",
|
||||
"sha256:8f8631160c79f53081bd23446525db0bc4c5616f78d04021e6e434b286493fd7",
|
||||
"sha256:912de10965f3dc89da23936f1cc4ed60764f712e5fa603a09dd904f88c996760",
|
||||
"sha256:b010c07b975fe853c65d7bbe9d4ac62f1c69086750a574f6292597763781ba18",
|
||||
"sha256:c908c10505904c48081a5415a1e295d8403e353e0c14c42b6d67f8f97fae6616",
|
||||
"sha256:c94dd3807c0c0610f7c76f078119f4ea48235a953512752b9175f9f98f5ae2bd",
|
||||
"sha256:ce65dee7594a84c466e79d7fb7d3303e7295d16a83c22c7c4037071b059e2c21",
|
||||
"sha256:eaa9cfcb221a8a4c2889be6f93da141ac777eb8819f077e1d09fb12d00a09a93",
|
||||
"sha256:f3376bc31bad66d46d44b4e6522c5c21976bf9bca4ef5987bb2bf727f4506cbb",
|
||||
"sha256:f9202fa138544e13a4ec1a6792c35834250a85958fde1251b6a22e07d1260ae7"
|
||||
],
|
||||
"version": "==1.3.5"
|
||||
},
|
||||
"ujson": {
|
||||
"hashes": [
|
||||
"sha256:f66073e5506e91d204ab0c614a148d5aa938bdbf104751be66f8ad7a222f5f86"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.35"
|
||||
},
|
||||
"urllib3": {
|
||||
"hashes": [
|
||||
"sha256:a53063d8b9210a7bdec15e7b272776b9d42b2fd6816401a0d43006ad2f9902db",
|
||||
"sha256:d363e3607d8de0c220d31950a8f38b18d5ba7c0830facd71a1c6b1036b7ce06c"
|
||||
],
|
||||
"version": "==1.25.2"
|
||||
},
|
||||
"uvicorn": {
|
||||
"hashes": [
|
||||
"sha256:c10da7a54a6552279870900c881a2f1726314e2dd6270d4d3f9251683c643783"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.7.1"
|
||||
},
|
||||
"uvloop": {
|
||||
"hashes": [
|
||||
"sha256:0fcd894f6fc3226a962ee7ad895c4f52e3f5c3c55098e21efb17c071849a0573",
|
||||
"sha256:2f31de1742c059c96cb76b91c5275b22b22b965c886ee1fced093fa27dde9e64",
|
||||
"sha256:459e4649fcd5ff719523de33964aa284898e55df62761e7773d088823ccbd3e0",
|
||||
"sha256:67867aafd6e0bc2c30a079603a85d83b94f23c5593b3cc08ec7e58ac18bf48e5",
|
||||
"sha256:8c200457e6847f28d8bb91c5e5039d301716f5f2fce25646f5fb3fd65eda4a26",
|
||||
"sha256:958906b9ca39eb158414fbb7d6b8ef1b7aee4db5c8e8e5d00fcbb69a1ce9dca7",
|
||||
"sha256:ac1dca3d8f3ef52806059e81042ee397ac939e5a86c8a3cea55d6b087db66115",
|
||||
"sha256:b284c22d8938866318e3b9d178142b8be316c52d16fcfe1560685a686718a021",
|
||||
"sha256:c48692bf4587ce281d641087658eca275a5ad3b63c78297bbded96570ae9ce8f",
|
||||
"sha256:fefc3b2b947c99737c348887db2c32e539160dcbeb7af9aa6b53db7a283538fe"
|
||||
],
|
||||
"version": "==0.12.2"
|
||||
},
|
||||
"wcwidth": {
|
||||
"hashes": [
|
||||
"sha256:3df37372226d6e63e1b1e1eda15c594bca98a22d33a23832a90998faa96bc65e",
|
||||
"sha256:f4ebe71925af7b40a864553f761ed559b43544f8f71746c2d756c7fe788ade7c"
|
||||
],
|
||||
"version": "==0.1.7"
|
||||
},
|
||||
"webencodings": {
|
||||
"hashes": [
|
||||
"sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78",
|
||||
"sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"
|
||||
],
|
||||
"version": "==0.5.1"
|
||||
},
|
||||
"websockets": {
|
||||
"hashes": [
|
||||
"sha256:04b42a1b57096ffa5627d6a78ea1ff7fad3bc2c0331ffc17bc32a4024da7fea0",
|
||||
"sha256:08e3c3e0535befa4f0c4443824496c03ecc25062debbcf895874f8a0b4c97c9f",
|
||||
"sha256:10d89d4326045bf5e15e83e9867c85d686b612822e4d8f149cf4840aab5f46e0",
|
||||
"sha256:232fac8a1978fc1dead4b1c2fa27c7756750fb393eb4ac52f6bc87ba7242b2fa",
|
||||
"sha256:4bf4c8097440eff22bc78ec76fe2a865a6e658b6977a504679aaf08f02c121da",
|
||||
"sha256:51642ea3a00772d1e48fb0c492f0d3ae3b6474f34d20eca005a83f8c9c06c561",
|
||||
"sha256:55d86102282a636e195dad68aaaf85b81d0bef449d7e2ef2ff79ac450bb25d53",
|
||||
"sha256:564d2675682bd497b59907d2205031acbf7d3fadf8c763b689b9ede20300b215",
|
||||
"sha256:5d13bf5197a92149dc0badcc2b699267ff65a867029f465accfca8abab95f412",
|
||||
"sha256:5eda665f6789edb9b57b57a159b9c55482cbe5b046d7db458948370554b16439",
|
||||
"sha256:5edb2524d4032be4564c65dc4f9d01e79fe8fad5f966e5b552f4e5164fef0885",
|
||||
"sha256:79691794288bc51e2a3b8de2bc0272ca8355d0b8503077ea57c0716e840ebaef",
|
||||
"sha256:7fcc8681e9981b9b511cdee7c580d5b005f3bb86b65bde2188e04a29f1d63317",
|
||||
"sha256:8e447e05ec88b1b408a4c9cde85aa6f4b04f06aa874b9f0b8e8319faf51b1fee",
|
||||
"sha256:90ea6b3e7787620bb295a4ae050d2811c807d65b1486749414f78cfd6fb61489",
|
||||
"sha256:9e13239952694b8b831088431d15f771beace10edfcf9ef230cefea14f18508f",
|
||||
"sha256:d40f081187f7b54d7a99d8a5c782eaa4edc335a057aa54c85059272ed826dc09",
|
||||
"sha256:e1df1a58ed2468c7b7ce9a2f9752a32ad08eac2bcd56318625c3647c2cd2da6f",
|
||||
"sha256:e98d0cec437097f09c7834a11c69d79fe6241729b23f656cfc227e93294fc242",
|
||||
"sha256:f8d59627702d2ff27cb495ca1abdea8bd8d581de425c56e93bff6517134e0a9b",
|
||||
"sha256:fc30cdf2e949a2225b012a7911d1d031df3d23e99b7eda7dfc982dc4a860dae9"
|
||||
],
|
||||
"version": "==7.0"
|
||||
},
|
||||
"widgetsnbextension": {
|
||||
"hashes": [
|
||||
"sha256:14b2c65f9940c9a7d3b70adbe713dbd38b5ec69724eebaba034d1036cf3d4740",
|
||||
"sha256:fa618be8435447a017fd1bf2c7ae922d0428056cfc7449f7a8641edf76b48265"
|
||||
],
|
||||
"version": "==3.4.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
24
README.md
24
README.md
@@ -5,8 +5,8 @@
|
||||
<em>FastAPI framework, high performance, easy to learn, fast to code, ready for production</em>
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="https://travis-ci.org/tiangolo/fastapi" target="_blank">
|
||||
<img src="https://travis-ci.org/tiangolo/fastapi.svg?branch=master" alt="Build Status">
|
||||
<a href="https://travis-ci.com/tiangolo/fastapi" target="_blank">
|
||||
<img src="https://travis-ci.com/tiangolo/fastapi.svg?branch=master" alt="Build Status">
|
||||
</a>
|
||||
<a href="https://codecov.io/gh/tiangolo/fastapi" target="_blank">
|
||||
<img src="https://codecov.io/gh/tiangolo/fastapi/branch/master/graph/badge.svg" alt="Coverage">
|
||||
@@ -63,8 +63,19 @@ The key features are:
|
||||
|
||||
---
|
||||
|
||||
"*If you're looking to learn one **modern framework** for building REST APIs, check out **FastAPI** [...] It's fast, easy to use and easy to learn [...]*"
|
||||
|
||||
"*We've switched over to **FastAPI** for our **APIs** [...] I think you'll like it [...]*"
|
||||
|
||||
<div style="text-align: right; margin-right: 10%;">Ines Montani - Matthew Honnibal - <strong><a href="https://explosion.ai" target="_blank">Explosion AI</a> founders - <a href="https://spacy.io" target="_blank">spaCy</a> creators</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>
|
||||
|
||||
---
|
||||
|
||||
"*We adopted the **FastAPI** library to spawn a **REST** server that can be queried to obtain **predictions**. [for Ludwig]*"
|
||||
|
||||
<div style="text-align: right; margin-right: 10%;">Piero Molino, Yaroslav Dudin, and Sai Sumanth Miryala - <strong>Uber</strong> <a href="https://eng.uber.com/ludwig-v0-2/" target="_blank"><small>(ref)</small></a></div>
|
||||
|
||||
---
|
||||
|
||||
## Requirements
|
||||
|
||||
@@ -79,13 +90,13 @@ FastAPI stands on the shoulders of giants:
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
$ pip install fastapi
|
||||
pip install fastapi
|
||||
```
|
||||
|
||||
You will also need an ASGI server, for production such as <a href="http://www.uvicorn.org" target="_blank">Uvicorn</a> or <a href="https://gitlab.com/pgjones/hypercorn" target="_blank">Hypercorn</a>.
|
||||
|
||||
```bash
|
||||
$ pip install uvicorn
|
||||
pip install uvicorn
|
||||
```
|
||||
|
||||
## Example
|
||||
@@ -195,8 +206,7 @@ Now modify the file `main.py` to receive a body from a `PUT` request.
|
||||
|
||||
Declare the body using standard Python types, thanks to Pydantic.
|
||||
|
||||
|
||||
```Python hl_lines="2 7 8 9 10 24"
|
||||
```Python hl_lines="2 7 8 9 10 23 24 25"
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
@@ -396,7 +406,7 @@ Used by FastAPI / Starlette:
|
||||
|
||||
* <a href="http://www.uvicorn.org" target="_blank"><code>uvicorn</code></a> - for the server that loads and serves your application.
|
||||
|
||||
You can install all of these with `pip3 install fastapi[all]`.
|
||||
You can install all of these with `pip install fastapi[all]`.
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -142,12 +142,12 @@ Another big feature required by APIs is <abbr title="reading and converting to P
|
||||
|
||||
Webargs is a tool that was made to provide that on top of several frameworks, including Flask.
|
||||
|
||||
It uses Marshmallow underneath to do the data validation. And it was created by the same guys.
|
||||
It uses Marshmallow underneath to do the data validation. And it was created by the same developers.
|
||||
|
||||
It's a great tool and I have used it a lot too, before having **FastAPI**.
|
||||
|
||||
!!! info
|
||||
Webargs was created by the same Marshmallow guys.
|
||||
Webargs was created by the same Marshmallow developers.
|
||||
|
||||
!!! check "Inspired **FastAPI** to"
|
||||
Have automatic validation of incoming request data.
|
||||
@@ -171,7 +171,7 @@ But then, we have again the problem of having a micro-syntax, inside of a Python
|
||||
The editor can't help much with that. And if we modify parameters or Marshmallow schemas and forget to also modify that YAML docstring, the generated schema would be obsolete.
|
||||
|
||||
!!! info
|
||||
APISpec was created by the same Marshmallow guys.
|
||||
APISpec was created by the same Marshmallow developers.
|
||||
|
||||
|
||||
!!! check "Inspired **FastAPI** to"
|
||||
@@ -198,7 +198,7 @@ Using it led to the creation of several Flask full-stack generators. These are t
|
||||
And these same full-stack generators were the base of the <a href="/project-generation/" target="_blank">**FastAPI** project generator</a>.
|
||||
|
||||
!!! info
|
||||
Flask-apispec was created by the same Marshmallow guys.
|
||||
Flask-apispec was created by the same Marshmallow developers.
|
||||
|
||||
!!! check "Inspired **FastAPI** to"
|
||||
Generate the OpenAPI schema automatically, from the same code that defines serialization and validation.
|
||||
@@ -240,7 +240,7 @@ It was one of the first extremely fast Python frameworks based on `asyncio`. It
|
||||
|
||||
Falcon is another high performance Python framework, it is designed to be minimal, and work as the foundation of other frameworks like Hug.
|
||||
|
||||
It uses the previous standard for Python web frameworks (WSGI) which is synchronous, so it can't handle Websockets and other use cases. Nevertheless, it also has a very good performance.
|
||||
It uses the previous standard for Python web frameworks (WSGI) which is synchronous, so it can't handle WebSockets and other use cases. Nevertheless, it also has a very good performance.
|
||||
|
||||
It is designed to have functions that receive two parameters, one "request" and one "response". Then you "read" parts from the request, and "write" parts to the response. Because of this design, it is not possible to declare request parameters and bodies with standard Python type hints as function parameters.
|
||||
|
||||
@@ -249,6 +249,10 @@ So, data validation, serialization, and documentation, have to be done in code,
|
||||
!!! check "Inspired **FastAPI** to"
|
||||
Find ways to get great performance.
|
||||
|
||||
Along with Hug (as Hug is based on Falcon) inspired **FastAPI** to declare a `response` parameter in functions.
|
||||
|
||||
Although in FastAPI it's optional, and is used mainly to set headers, cookies, and alternative status codes.
|
||||
|
||||
### <a href="https://moltenframework.com/" target="_blank">Molten</a>
|
||||
|
||||
I discovered Molten in the first stages of building **FastAPI**. And it has quite similar ideas:
|
||||
@@ -292,6 +296,7 @@ As it is based on the previous standard for synchronous Python web frameworks (W
|
||||
|
||||
Hug helped inspiring **FastAPI** to use Python type hints to declare parameters, and to generate a schema defining the API automatically.
|
||||
|
||||
Hug inspired **FastAPI** to declare a `response` parameter in functions to set headers and cookies.
|
||||
|
||||
### <a href="https://github.com/encode/apistar" target="_blank">APIStar</a> (<= 0.5)
|
||||
|
||||
|
||||
@@ -170,32 +170,33 @@ Now, from a developer's perspective, here are several things to have in mind whi
|
||||
* So, the certificate and encryption handling is done before HTTP.
|
||||
* TCP doesn't know about "domains". Only about IP addresses.
|
||||
* The information about the specific domain requested goes in the HTTP data.
|
||||
* The HTTPS certificates "certificate" a certain domain, but the protocol and encryption happen at the TCP level, before knowing which domain is being dealt with.
|
||||
* The HTTPS certificates "certify" a certain domain, but the protocol and encryption happen at the TCP level, before knowing which domain is being dealt with.
|
||||
* By default, that would mean that you can only have one HTTPS certificate per IP address.
|
||||
* No matter how big is your server and how small each application you have there might be. But...
|
||||
* No matter how big your server is or how small each application you have on it might be.
|
||||
* There is a solution to this, however.
|
||||
* There's an extension to the TLS protocol (the one handling the encryption at the TCP level, before HTTP) called <a href="https://en.wikipedia.org/wiki/Server_Name_Indication" target="_blank"><abbr title="Server Name Indication">SNI</abbr></a>.
|
||||
* This SNI extension allows one single server (with a single IP address) to have several HTTPS certificates and server multiple HTTPS domains/applications.
|
||||
* For this to work, a single component (program) running in the server, listening in the public IP address, must have all the HTTPS certificates in the server.
|
||||
* After having a secure connection, the communication protocol is the same HTTP.
|
||||
* It goes encrypted, but the encrypted contents are the same HTTP protocol.
|
||||
* This SNI extension allows one single server (with a single IP address) to have several HTTPS certificates and serve multiple HTTPS domains/applications.
|
||||
* For this to work, a single component (program) running on the server, listening on the public IP address, must have all the HTTPS certificates in the server.
|
||||
* After obtaining a secure connection, the communication protocol is still HTTP.
|
||||
* The contents are encrypted, even though they are being sent with the HTTP protocol.
|
||||
|
||||
|
||||
It is a common practice to have one program/HTTP server running in the server (the machine, host, etc) and managing all the HTTPS parts, sending the decrypted HTTP requests to the actual HTTP application running in the same server (the **FastAPI** application, in this case), take the HTTP response from the application, encrypt it using the appropriate certificate and sending it back to the client using HTTPS. This server is ofter called a <a href="https://en.wikipedia.org/wiki/TLS_termination_proxy" target="_blank">TLS Termination Proxy</a>.
|
||||
It is a common practice to have one program/HTTP server running on the server (the machine, host, etc.) and managing all the HTTPS parts : sending the decrypted HTTP requests to the actual HTTP application running in the same server (the **FastAPI** application, in this case), take the HTTP response from the application, encrypt it using the appropriate certificate and sending it back to the client using HTTPS. This server is often called a <a href="https://en.wikipedia.org/wiki/TLS_termination_proxy" target="_blank">TLS Termination Proxy</a>.
|
||||
|
||||
|
||||
### Let's Encrypt
|
||||
|
||||
Up to some years ago, these HTTPS certificates were sold by trusted third-parties.
|
||||
Before Let's Encrypt, these HTTPS certificates were sold by trusted third-parties.
|
||||
|
||||
The process to acquire one of these certificates used to be cumbersome, require quite some paperwork and the certificates were quite expensive.
|
||||
|
||||
But then <a href="https://letsencrypt.org/" target="_blank">Let's Encrypt</a> was created.
|
||||
|
||||
It is a project from the Linux Foundation. It provides HTTPS certificates for free. In an automated way. These certificates use all the standard cryptographic security, and are short lived (about 3 months), so, the security is actually increased, by reducing their lifespan.
|
||||
It is a project from the Linux Foundation. It provides HTTPS certificates for free. In an automated way. These certificates use all the standard cryptographic security, and are short lived (about 3 months), so the security is actually better because of their reduced lifespan.
|
||||
|
||||
The domain's are securely verified and the certificates are generated automatically. This also allows automatizing the renewal of these certificates.
|
||||
The domains are securely verified and the certificates are generated automatically. This also allows automating the renewal of these certificates.
|
||||
|
||||
The idea is to automatize the acquisition and renewal of these certificates, so that you can have secure HTTPS, free, forever.
|
||||
The idea is to automate the acquisition and renewal of these certificates, so that you can have secure HTTPS, for free, forever.
|
||||
|
||||
|
||||
### Traefik
|
||||
@@ -204,7 +205,7 @@ The idea is to automatize the acquisition and renewal of these certificates, so
|
||||
|
||||
It has integration with Let's Encrypt. So, it can handle all the HTTPS parts, including certificate acquisition and renewal.
|
||||
|
||||
It also has integrations with Docker. So, you can declare your domains in each application configurations and have it read those configurations, generate the HTTPS certificates and serve HTTPS to your application, all automatically. Without requiring any change in its configuration.
|
||||
It also has integrations with Docker. So, you can declare your domains in each application configurations and have it read those configurations, generate the HTTPS certificates and serve HTTPS to your application automatically, without requiring any change in its configuration.
|
||||
|
||||
---
|
||||
|
||||
@@ -230,7 +231,7 @@ It is designed to be integrated with this Docker Swarm cluster with Traefik and
|
||||
|
||||
You can generate a project in about 2 min.
|
||||
|
||||
The generated project has instructions to deploy it, doing it takes other 2 min.
|
||||
The generated project has instructions to deploy it, doing it takes another 2 min.
|
||||
|
||||
|
||||
## Alternatively, deploy **FastAPI** without Docker
|
||||
|
||||
80
docs/external-links.md
Normal file
80
docs/external-links.md
Normal file
@@ -0,0 +1,80 @@
|
||||
**FastAPI** has a great community constantly growing.
|
||||
|
||||
There are many posts, articles, tools, and projects, related to **FastAPI**.
|
||||
|
||||
Here's an incomplete list of some of them.
|
||||
|
||||
!!! tip
|
||||
If you have an article, project, tool, or anything related to **FastAPI** that is not yet listed here, create a <a href="https://github.com/tiangolo/fastapi/edit/master/docs/external-links.md" target="_blank">Pull Request adding it</a>.
|
||||
|
||||
## Articles
|
||||
|
||||
### English
|
||||
|
||||
* <a href="https://medium.com/@williamhayes/fastapi-starlette-debug-vs-prod-5f7561db3a59" target="_blank">FastAPI/Starlette debug vs prod</a> by <a href="https://medium.com/@williamhayes" target="_blank">William Hayes</a>.
|
||||
|
||||
* <a href="https://medium.com/data-rebels/fastapi-google-as-an-external-authentication-provider-3a527672cf33" target="_blank">FastAPI — Google as an external authentication provider</a> by <a href="https://medium.com/@nils_29588" target="_blank">Nils de Bruin</a>.
|
||||
|
||||
* <a href="https://medium.com/data-rebels/fastapi-how-to-add-basic-and-cookie-authentication-a45c85ef47d3" target="_blank">FastAPI — How to add basic and cookie authentication</a> by <a href="https://medium.com/@nils_29588" target="_blank">Nils de Bruin</a>.
|
||||
|
||||
* <a href="https://dev.to/errietta/introduction-to-the-fastapi-python-framework-2n10" target="_blank">Introduction to the fastapi python framework</a> by <a href="https://dev.to/errietta" target="_blank">Errieta Kostala</a>.
|
||||
|
||||
* <a href="http://nickc1.github.io/api,/scikit-learn/2019/01/10/scikit-fastapi.html" target="_blank">FastAPI and Scikit-Learn: Easily Deploy Models</a> by <a href="http://nickc1.github.io/" target="_blank">Nick Cortale</a>.
|
||||
|
||||
* <a href="https://medium.com/data-rebels/fastapi-authentication-revisited-enabling-api-key-authentication-122dc5975680" target="_blank">FastAPI authentication revisited: Enabling API key authentication</a> by <a href="https://medium.com/@nils_29588" target="_blank">Nils de Bruin</a>.
|
||||
|
||||
* <a href="https://blog.bartab.fr/fastapi-logging-on-the-fly/" target="_blank">FastAPI, a simple use case on logging</a> by <a href="https://blog.bartab.fr/" target="_blank">@euri10</a>.
|
||||
|
||||
* <a href="https://medium.com/@nico.axtmann95/deploying-a-scikit-learn-model-with-onnx-und-fastapi-1af398268915" target="_blank">Deploying a scikit-learn model with ONNX and FastAPI</a> by <a href="https://www.linkedin.com/in/nico-axtmann" target="_blank">Nico Axtmann</a>.
|
||||
|
||||
* <a href="https://geekflare.com/python-asynchronous-web-frameworks/" target="_blank">Top 5 Asynchronous Web Frameworks for Python</a> by <a href="https://geekflare.com/author/ankush/" target="_blank">Ankush Thakur</a> on <a href="https://geekflare.com" target="_blank">GeekFlare</a>.
|
||||
|
||||
* <a href="https://medium.com/@gntrm/jwt-authentication-with-fastapi-and-aws-cognito-1333f7f2729e" target="_blank">JWT Authentication with FastAPI and AWS Cognito</a> by <a href="https://twitter.com/gntrm" target="_blank">Johannes Gontrum</a>.
|
||||
|
||||
* <a href="https://towardsdatascience.com/how-to-deploy-a-machine-learning-model-dc51200fe8cf" target="_blank">How to Deploy a Machine Learning Model</a> by <a href="https://www.linkedin.com/in/mgrootendorst/" target="_blank">Maarten Grootendorst</a> on <a href="https://towardsdatascience.com/" target="_blank">Towards Data Science</a>.
|
||||
|
||||
* <a href="https://eng.uber.com/ludwig-v0-2/" target="_blank">Uber: Ludwig v0.2 Adds New Features and Other Improvements to its Deep Learning Toolbox [including a FastAPI server]</a> on <a href="https://eng.uber.com" target="_blank">Uber Engineering</a>.
|
||||
|
||||
* <a href="https://gitlab.com/euri10/fastapi_cheatsheet" target="_blank">A FastAPI and Swagger UI visual cheatsheet</a> by <a href="https://gitlab.com/euri10" target="_blank">@euri10</a>
|
||||
|
||||
### Japanese
|
||||
|
||||
* <a href="https://qiita.com/mtitg/items/47770e9a562dd150631d" target="_blank">FastAPI|DB接続してCRUDするPython製APIサーバーを構築</a> by <a href="https://qiita.com/mtitg" target="_blank">@mtitg</a>.
|
||||
|
||||
* <a href="https://qiita.com/ryoryomaru/items/59958ed385b3571d50de" target="_blank">python製の最新APIフレームワーク FastAPI を触ってみた</a> by <a href="https://qiita.com/ryoryomaru" target="_blank">@ryoryomaru</a>.
|
||||
|
||||
* <a href="https://qiita.com/angel_katayoku/items/0e1f5dbbe62efc612a78" target="_blank">FastAPIでCORSを回避</a> by <a href="https://qiita.com/angel_katayoku" target="_blank">@angel_katayoku</a>.
|
||||
|
||||
* <a href="https://qiita.com/angel_katayoku/items/4fbc1a4e2b33fa2237d2" target="_blank">FastAPIをMySQLと接続してDockerで管理してみる</a> by <a href="https://qiita.com/angel_katayoku" target="_blank">@angel_katayoku</a>.
|
||||
|
||||
* <a href="https://qiita.com/angel_katayoku/items/8a458a8952f50b73f420" target="_blank">FastAPIでPOSTされたJSONのレスポンスbodyを受け取る</a> by <a href="https://qiita.com/angel_katayoku" target="_blank">@angel_katayoku</a>.
|
||||
|
||||
* <a href="https://qiita.com/hikarut/items/b178af2e2440c67c6ac4" target="_blank">フロントエンド開発者向けのDockerによるPython開発環境構築</a> by <a href="https://qiita.com/hikarut" target="_blank">Hikaru Takahashi</a>.
|
||||
|
||||
* <a href="https://rightcode.co.jp/blog/information-technology/fastapi-tutorial-todo-apps-environment" target="_blank">【第1回】FastAPIチュートリアル: ToDoアプリを作ってみよう【環境構築編】</a> by <a href="https://rightcode.co.jp/author/jun" target="_blank">ライトコードメディア編集部</a>
|
||||
|
||||
* <a href="https://rightcode.co.jp/blog/information-technology/fastapi-tutorial-todo-apps-model-building" target="_blank">【第2回】FastAPIチュートリアル: ToDoアプリを作ってみよう【モデル構築編】</a> by <a href="https://rightcode.co.jp/author/jun" target="_blank">ライトコードメディア編集部</a>
|
||||
|
||||
* <a href="https://rightcode.co.jp/blog/information-technology/fastapi-tutorial-todo-apps-authentication-user-registration" target="_blank">【第3回】FastAPIチュートリアル: toDoアプリを作ってみよう【認証・ユーザ登録編】</a> by <a href="https://rightcode.co.jp/author/jun" target="_blank">ライトコードメディア編集部</a>
|
||||
|
||||
* <a href="https://rightcode.co.jp/blog/information-technology/fastapi-tutorial-todo-apps-admin-page-improvement" target="_blank">【第4回】FastAPIチュートリアル: toDoアプリを作ってみよう【管理者ページ改良編】</a> by <a href="https://rightcode.co.jp/author/jun" target="_blank">ライトコードメディア編集部</a>
|
||||
|
||||
### Chinese
|
||||
|
||||
* <a href="https://cloud.tencent.com/developer/article/1431448" target="_blank">使用FastAPI框架快速构建高性能的api服务</a> by <a href="https://cloud.tencent.com/developer/user/5471722" target="_blank">逍遥散人</a>.
|
||||
|
||||
* <a href="https://wxq0309.github.io/" target="_blank">FastAPI框架中文文档</a> by <a href="https://wxq0309.github.io/" target="_blank">何大仙</a>.
|
||||
|
||||
### Vietnamese
|
||||
|
||||
* <a href="https://fullstackstation.com/fastapi-trien-khai-bang-docker/" target="_blank">FASTAPI: TRIỂN KHAI BẰNG DOCKER</a> by <a href="https://fullstackstation.com/author/figonking/" target="_blank">Nguyễn Nhân</a>.
|
||||
|
||||
### Russian
|
||||
|
||||
* <a href="https://habr.com/ru/post/454440/" target="_blank">Мелкая питонячая радость #2: Starlette - Солидная примочка – FastAPI</a> by <a href="https://habr.com/ru/users/57uff3r/" target="_blank">Andrey Korchak</a>.
|
||||
|
||||
* <a href="https://habr.com/ru/post/478620/" target="_blank">Почему Вы должны попробовать FastAPI?</a> by <a href="https://github.com/prostomarkeloff" target="_blank">prostomarkeloff</a>.
|
||||
|
||||
## Podcasts
|
||||
|
||||
* <a href="https://pythonbytes.fm/episodes/show/123/time-to-right-the-py-wrongs?time_in_sec=855" target="_blank">FastAPI on PythonBytes</a> by <a href="https://pythonbytes.fm/" target="_blank">Python Bytes FM</a>.
|
||||
@@ -37,7 +37,7 @@ from datetime import date
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
# Declare a variable as an str
|
||||
# Declare a variable as a str
|
||||
# and get editor support inside the function
|
||||
def main(user_id: str):
|
||||
return user_id
|
||||
|
||||
@@ -56,7 +56,7 @@ You can:
|
||||
|
||||
## Tweet about **FastAPI**
|
||||
|
||||
<a href="http://twitter.com/home/?status=I'm loving FastAPI because... https://github.com/tiangolo/fastapi cc @tiangolo" target="_blank">Tweet about **FastAPI**</a> and let me and others why you like it.
|
||||
<a href="https://twitter.com/compose/tweet?text=I'm loving FastAPI because... https://github.com/tiangolo/fastapi cc @tiangolo" target="_blank">Tweet about **FastAPI**</a> and let me and others know why you like it.
|
||||
|
||||
## Let me know how are you using **FastAPI**
|
||||
|
||||
@@ -64,16 +64,14 @@ I love to hear about how **FastAPI** is being used, what have you liked in it, i
|
||||
|
||||
You can let me know:
|
||||
|
||||
* <a href="http://twitter.com/home/?status=Hey @tiangolo, I'm using FastAPI at..." target="_blank">On **Twitter**</a>.
|
||||
* <a href="https://twitter.com/compose/tweet?text=Hey @tiangolo, I'm using FastAPI at..." target="_blank">On **Twitter**</a>.
|
||||
* <a href="https://www.linkedin.com/in/tiangolo/" target="_blank">On **Linkedin**</a>.
|
||||
* <a href="https://medium.com/@tiangolo" target="_blank">On **Medium**</a>.
|
||||
|
||||
## Vote for FastAPI
|
||||
|
||||
You can vote to include FastAPI in several "awesome lists":
|
||||
|
||||
* <a href="https://github.com/vinta/awesome-python/pull/1209" target="_blank">Vote to include **FastAPI** in `awesome-python`</a>.
|
||||
* <a href="https://github.com/timofurrer/awesome-asyncio/pull/43" target="_blank">Vote to include **FastAPI** in `awesome-asyncio`</a>.
|
||||
* <a href="https://www.slant.co/options/34241/~fastapi-review" target="_blank">Vote for **FastAPI** in Slant</a>.
|
||||
|
||||
## Help others with issues in GitHub
|
||||
|
||||
|
||||
BIN
docs/img/github-social-preview.png
Normal file
BIN
docs/img/github-social-preview.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 57 KiB |
98
docs/img/github-social-preview.svg
Normal file
98
docs/img/github-social-preview.svg
Normal file
@@ -0,0 +1,98 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
id="svg8"
|
||||
version="1.1"
|
||||
viewBox="0 0 338.66665 169.33332"
|
||||
height="169.33333mm"
|
||||
width="338.66666mm"
|
||||
sodipodi:docname="github-social-preview.svg"
|
||||
inkscape:version="0.92.3 (2405546, 2018-03-11)"
|
||||
inkscape:export-filename="/home/user/code/fastapi/docs/img/github-social-preview.png"
|
||||
inkscape:export-xdpi="96"
|
||||
inkscape:export-ydpi="96">
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1025"
|
||||
id="namedview9"
|
||||
showgrid="false"
|
||||
inkscape:zoom="0.52249777"
|
||||
inkscape:cx="565.37328"
|
||||
inkscape:cy="403.61034"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="27"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg8" />
|
||||
<defs
|
||||
id="defs2" />
|
||||
<metadata
|
||||
id="metadata5">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<rect
|
||||
style="opacity:0.98000004;fill:#ffffff;fill-opacity:1;stroke-width:0.26458332"
|
||||
id="rect853"
|
||||
width="338.66666"
|
||||
height="169.33333"
|
||||
x="-1.0833333e-05"
|
||||
y="0.71613133"
|
||||
inkscape:export-xdpi="96"
|
||||
inkscape:export-ydpi="96" />
|
||||
<g
|
||||
transform="matrix(0.73259569,0,0,0.73259569,64.842852,-4.5763945)"
|
||||
id="layer1">
|
||||
<path
|
||||
style="opacity:0.98000004;fill:#009688;fill-opacity:1;stroke-width:3.20526505"
|
||||
id="path817"
|
||||
d="m 1.4365174,55.50154 c -17.6610514,0 -31.9886064,14.327532 -31.9886064,31.988554 0,17.661036 14.327555,31.988586 31.9886064,31.988586 17.6609756,0 31.9885196,-14.32755 31.9885196,-31.988586 0,-17.661022 -14.327544,-31.988554 -31.9885196,-31.988554 z m -1.66678692,57.63069 V 93.067264 H -11.384533 L 4.6417437,61.847974 V 81.912929 H 15.379405 Z"
|
||||
inkscape:connector-curvature="0" />
|
||||
<text
|
||||
id="text979"
|
||||
y="114.91215"
|
||||
x="52.115433"
|
||||
style="font-style:normal;font-weight:normal;font-size:79.71511078px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#009688;fill-opacity:1;stroke:none;stroke-width:1.99287772"
|
||||
xml:space="preserve"><tspan
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Ubuntu;-inkscape-font-specification:Ubuntu;fill:#009688;fill-opacity:1;stroke-width:1.99287772"
|
||||
y="114.91215"
|
||||
x="52.115433"
|
||||
id="tspan977">FastAPI</tspan></text>
|
||||
</g>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-weight:normal;font-size:10.58333302px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
|
||||
x="169.60979"
|
||||
y="119.20409"
|
||||
id="text851"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan849"
|
||||
x="169.60979"
|
||||
y="119.20409"
|
||||
style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Roboto;-inkscape-font-specification:'Roboto Italic';text-align:center;text-anchor:middle;stroke-width:0.26458332">High performance, easy to learn,</tspan><tspan
|
||||
sodipodi:role="line"
|
||||
x="169.60979"
|
||||
y="132.53661"
|
||||
style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Roboto;-inkscape-font-specification:'Roboto Italic';text-align:center;text-anchor:middle;stroke-width:0.26458332"
|
||||
id="tspan855">fast to code, ready for production</tspan></text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 77 KiB After Width: | Height: | Size: 77 KiB |
BIN
docs/img/tutorial/openapi-callbacks/image01.png
Normal file
BIN
docs/img/tutorial/openapi-callbacks/image01.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 97 KiB |
@@ -5,8 +5,8 @@
|
||||
<em>FastAPI framework, high performance, easy to learn, fast to code, ready for production</em>
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="https://travis-ci.org/tiangolo/fastapi" target="_blank">
|
||||
<img src="https://travis-ci.org/tiangolo/fastapi.svg?branch=master" alt="Build Status">
|
||||
<a href="https://travis-ci.com/tiangolo/fastapi" target="_blank">
|
||||
<img src="https://travis-ci.com/tiangolo/fastapi.svg?branch=master" alt="Build Status">
|
||||
</a>
|
||||
<a href="https://codecov.io/gh/tiangolo/fastapi" target="_blank">
|
||||
<img src="https://codecov.io/gh/tiangolo/fastapi/branch/master/graph/badge.svg" alt="Coverage">
|
||||
@@ -63,8 +63,19 @@ The key features are:
|
||||
|
||||
---
|
||||
|
||||
"*If you're looking to learn one **modern framework** for building REST APIs, check out **FastAPI** [...] It's fast, easy to use and easy to learn [...]*"
|
||||
|
||||
"*We've switched over to **FastAPI** for our **APIs** [...] I think you'll like it [...]*"
|
||||
|
||||
<div style="text-align: right; margin-right: 10%;">Ines Montani - Matthew Honnibal - <strong><a href="https://explosion.ai" target="_blank">Explosion AI</a> founders - <a href="https://spacy.io" target="_blank">spaCy</a> creators</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>
|
||||
|
||||
---
|
||||
|
||||
"*We adopted the **FastAPI** library to spawn a **REST** server that can be queried to obtain **predictions**. [for Ludwig]*"
|
||||
|
||||
<div style="text-align: right; margin-right: 10%;">Piero Molino, Yaroslav Dudin, and Sai Sumanth Miryala - <strong>Uber</strong> <a href="https://eng.uber.com/ludwig-v0-2/" target="_blank"><small>(ref)</small></a></div>
|
||||
|
||||
---
|
||||
|
||||
## Requirements
|
||||
|
||||
@@ -79,13 +90,13 @@ FastAPI stands on the shoulders of giants:
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
$ pip install fastapi
|
||||
pip install fastapi
|
||||
```
|
||||
|
||||
You will also need an ASGI server, for production such as <a href="http://www.uvicorn.org" target="_blank">Uvicorn</a> or <a href="https://gitlab.com/pgjones/hypercorn" target="_blank">Hypercorn</a>.
|
||||
|
||||
```bash
|
||||
$ pip install uvicorn
|
||||
pip install uvicorn
|
||||
```
|
||||
|
||||
## Example
|
||||
@@ -195,8 +206,7 @@ Now modify the file `main.py` to receive a body from a `PUT` request.
|
||||
|
||||
Declare the body using standard Python types, thanks to Pydantic.
|
||||
|
||||
|
||||
```Python hl_lines="2 7 8 9 10 24"
|
||||
```Python hl_lines="2 7 8 9 10 23 24 25"
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
@@ -396,7 +406,7 @@ Used by FastAPI / Starlette:
|
||||
|
||||
* <a href="http://www.uvicorn.org" target="_blank"><code>uvicorn</code></a> - for the server that loads and serves your application.
|
||||
|
||||
You can install all of these with `pip3 install fastapi[all]`.
|
||||
You can install all of these with `pip install fastapi[all]`.
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -1,4 +1,277 @@
|
||||
## Next release
|
||||
## Latest changes
|
||||
|
||||
## 0.46.0
|
||||
|
||||
* Fix typos and tweak configs. PR [#837](https://github.com/tiangolo/fastapi/pull/837).
|
||||
* Add link to Chinese article in [External Links](https://fastapi.tiangolo.com/external-links/). PR [810](https://github.com/tiangolo/fastapi/pull/810) by [@wxq0309](https://github.com/wxq0309).
|
||||
* Implement `OAuth2AuthorizationCodeBearer` class. PR [#797](https://github.com/tiangolo/fastapi/pull/797) by [@kuwv](https://github.com/kuwv).
|
||||
* Update example upgrade in docs main page. PR [#795](https://github.com/tiangolo/fastapi/pull/795) by [@cdeil](https://github.com/cdeil).
|
||||
* Fix callback handling for sub-routers. PR [#792](https://github.com/tiangolo/fastapi/pull/792) by [@jekirl](https://github.com/jekirl).
|
||||
* Fix typos. PR [#784](https://github.com/tiangolo/fastapi/pull/784) by [@kkinder](https://github.com/kkinder).
|
||||
* Add 4 Japanese articles to [External Links](https://fastapi.tiangolo.com/external-links/). PR [#783](https://github.com/tiangolo/fastapi/pull/783) by [@HymanZHAN](https://github.com/HymanZHAN).
|
||||
* Add support for subtypes of main types in `jsonable_encoder`, e.g. asyncpg's UUIDs. PR [#756](https://github.com/tiangolo/fastapi/pull/756) by [@RmStorm](https://github.com/RmStorm).
|
||||
* Fix usage of Pydantic's `HttpUrl` in docs. PR [#832](https://github.com/tiangolo/fastapi/pull/832) by [@Dustyposa](https://github.com/Dustyposa).
|
||||
* Fix Twitter links in docs. PR [#813](https://github.com/tiangolo/fastapi/pull/813) by [@justindujardin](https://github.com/justindujardin).
|
||||
* Add docs for correctly [using FastAPI with Peewee ORM](https://fastapi.tiangolo.com/tutorial/sql-databases-peewee/). Including how to overwrite parts of Peewee to correctly handle async threads. PR [#789](https://github.com/tiangolo/fastapi/pull/789).
|
||||
|
||||
## 0.45.0
|
||||
|
||||
* Add support for OpenAPI Callbacks:
|
||||
* New docs: [OpenAPI Callbacks](https://fastapi.tiangolo.com/tutorial/openapi-callbacks/).
|
||||
* Refactor generation of `operationId`s to be valid Python names (also valid variables in most languages).
|
||||
* Add `default_response_class` parameter to `APIRouter`.
|
||||
* Original PR [#722](https://github.com/tiangolo/fastapi/pull/722) by [@booooh](https://github.com/booooh).
|
||||
* Refactor logging to use the same logger everywhere, update log strings and levels. PR [#781](https://github.com/tiangolo/fastapi/pull/781).
|
||||
* Add article to [External Links](https://fastapi.tiangolo.com/external-links/): [Почему Вы должны попробовать FastAPI?](https://habr.com/ru/post/478620/). PR [#766](https://github.com/tiangolo/fastapi/pull/766) by [@prostomarkeloff](https://github.com/prostomarkeloff).
|
||||
* Remove gender bias in docs for handling errors. PR [#780](https://github.com/tiangolo/fastapi/pull/780). Original idea in PR [#761](https://github.com/tiangolo/fastapi/pull/761) by [@classywhetten](https://github.com/classywhetten).
|
||||
* Rename docs and references to `body-schema` to `body-fields` to keep in line with Pydantic. PR [#746](https://github.com/tiangolo/fastapi/pull/746) by [@prostomarkeloff](https://github.com/prostomarkeloff).
|
||||
|
||||
## 0.44.1
|
||||
|
||||
* Add GitHub social preview images to git. PR [#752](https://github.com/tiangolo/fastapi/pull/752).
|
||||
* Update PyPI "trove classifiers". PR [#751](https://github.com/tiangolo/fastapi/pull/751).
|
||||
* Add full support for Python 3.8. Enable Python 3.8 in full in Travis. PR [749](https://github.com/tiangolo/fastapi/pull/749).
|
||||
* Update "new issue" templates. PR [#749](https://github.com/tiangolo/fastapi/pull/749).
|
||||
* Fix serialization of errors for exotic Pydantic types. PR [#748](https://github.com/tiangolo/fastapi/pull/748) by [@dmontagu](https://github.com/dmontagu).
|
||||
|
||||
## 0.44.0
|
||||
|
||||
* Add GitHub action [Issue Manager](https://github.com/tiangolo/issue-manager). PR [#742](https://github.com/tiangolo/fastapi/pull/742).
|
||||
* Fix typos in docs. PR [734](https://github.com/tiangolo/fastapi/pull/734) by [@bundabrg](https://github.com/bundabrg).
|
||||
* Fix usage of `custom_encoder` in `jsonable_encoder`. PR [#715](https://github.com/tiangolo/fastapi/pull/715) by [@matrixise](https://github.com/matrixise).
|
||||
* Fix invalid XML example. PR [710](https://github.com/tiangolo/fastapi/pull/710) by [@OcasoProtal](https://github.com/OcasoProtal).
|
||||
* Fix typos and update wording in deployment docs. PR [#700](https://github.com/tiangolo/fastapi/pull/700) by [@marier-nico](https://github.com/tiangolo/fastapi/pull/700).
|
||||
* Add note about dependencies in `APIRouter` docs. PR [#698](https://github.com/tiangolo/fastapi/pull/698) by [@marier-nico](https://github.com/marier-nico).
|
||||
* Add support for async class methods as dependencies [#681](https://github.com/tiangolo/fastapi/pull/681) by [@frankie567](https://github.com/frankie567).
|
||||
* Add FastAPI with Swagger UI cheatsheet to external links. PR [#671](https://github.com/tiangolo/fastapi/pull/671) by [@euri10](https://github.com/euri10).
|
||||
* Fix typo in HTTP protocol in CORS example. PR [#647](https://github.com/tiangolo/fastapi/pull/647) by [@forestmonster](https://github.com/forestmonster).
|
||||
* Add support for Pydantic versions `1.0.0` and above, with temporary (deprecated) backwards compatibility for Pydantic `0.32.2`. PR [#646](https://github.com/tiangolo/fastapi/pull/646) by [@dmontagu](https://github.com/dmontagu).
|
||||
|
||||
## 0.43.0
|
||||
|
||||
* Update docs to reduce gender bias. PR [#645](https://github.com/tiangolo/fastapi/pull/645) by [@ticosax](https://github.com/ticosax).
|
||||
* Add docs about [overriding the `operationId` for all the *path operations*](https://fastapi.tiangolo.com/tutorial/path-operation-advanced-configuration/#using-the-path-operation-function-name-as-the-operationid) based on their function name. PR [#642](https://github.com/tiangolo/fastapi/pull/642) by [@SKalt](https://github.com/SKalt).
|
||||
* Fix validators in models generating an incorrect key order. PR [#637](https://github.com/tiangolo/fastapi/pull/637) by [@jaddison](https://github.com/jaddison).
|
||||
* Generate correct OpenAPI docs for responses with no content. PR [#621](https://github.com/tiangolo/fastapi/pull/621) by [@brotskydotcom](https://github.com/brotskydotcom).
|
||||
* Remove `$` from Bash code blocks in docs for consistency. PR [#613](https://github.com/tiangolo/fastapi/pull/613) by [@nstapelbroek](https://github.com/nstapelbroek).
|
||||
* Add docs for [self-serving docs' (Swagger UI) static assets](https://fastapi.tiangolo.com/tutorial/extending-openapi/#self-hosting-javascript-and-css-for-docs), e.g. to use the docs offline, or without Internet. Initial PR [#557](https://github.com/tiangolo/fastapi/pull/557) by [@svalouch](https://github.com/svalouch).
|
||||
* Fix `black` linting after upgrade. PR [#682](https://github.com/tiangolo/fastapi/pull/682) by [@frankie567](https://github.com/frankie567).
|
||||
|
||||
## 0.42.0
|
||||
|
||||
* Add dependencies with `yield`, a.k.a. exit steps, context managers, cleanup, teardown, ...
|
||||
* This allows adding extra code after a dependency is done. It can be used, for example, to close database connections.
|
||||
* Dependencies with `yield` can be normal or `async`, **FastAPI** will run normal dependencies in a threadpool.
|
||||
* They can be combined with normal dependencies.
|
||||
* It's possible to have arbitrary trees/levels of dependencies with `yield` and exit steps are handled in the correct order automatically.
|
||||
* It works by default in Python 3.7 or above. For Python 3.6, it requires the extra backport dependencies:
|
||||
* `async-exit-stack`
|
||||
* `async-generator`
|
||||
* New docs at [Dependencies with `yield`](https://fastapi.tiangolo.com/tutorial/dependencies/dependencies-with-yield/).
|
||||
* Updated database docs [SQL (Relational) Databases: Main **FastAPI** app](https://fastapi.tiangolo.com/tutorial/sql-databases/#main-fastapi-app).
|
||||
* PR [#595](https://github.com/tiangolo/fastapi/pull/595).
|
||||
* Fix `sitemap.xml` in website. PR [#598](https://github.com/tiangolo/fastapi/pull/598) by [@samuelcolvin](https://github.com/samuelcolvin).
|
||||
|
||||
## 0.41.0
|
||||
|
||||
* Upgrade required Starlette to `0.12.9`, the new range is `>=0.12.9,<=0.12.9`.
|
||||
* Add `State` to FastAPI apps at `app.state`.
|
||||
* PR [#593](https://github.com/tiangolo/fastapi/pull/593).
|
||||
* Improve handling of custom classes for `Request`s and `APIRoute`s.
|
||||
* This helps to more easily solve use cases like:
|
||||
* Reading a body before and/or after a request (equivalent to a middleware).
|
||||
* Run middleware-like code only for a subset of *path operations*.
|
||||
* Process a request before passing it to a *path operation function*. E.g. decompressing, deserializing, etc.
|
||||
* Processing a response after being generated by *path operation functions* but before returning it. E.g. adding custom headers, logging, adding extra metadata.
|
||||
* New docs section: [Custom Request and APIRoute class](https://fastapi.tiangolo.com/tutorial/custom-request-and-route/).
|
||||
* PR [#589](https://github.com/tiangolo/fastapi/pull/589) by [@dmontagu](https://github.com/dmontagu).
|
||||
* Fix preserving custom route class in routers when including other sub-routers. PR [#538](https://github.com/tiangolo/fastapi/pull/538) by [@dmontagu](https://github.com/dmontagu).
|
||||
|
||||
## 0.40.0
|
||||
|
||||
* Add notes to docs about installing `python-multipart` when using forms. PR [#574](https://github.com/tiangolo/fastapi/pull/574) by [@sliptonic](https://github.com/sliptonic).
|
||||
* Generate OpenAPI schemas in alphabetical order. PR [#554](https://github.com/tiangolo/fastapi/pull/554) by [@dmontagu](https://github.com/dmontagu).
|
||||
* Add support for truncating docstrings from *path operation functions*.
|
||||
* New docs at [Advanced description from docstring](https://fastapi.tiangolo.com/tutorial/path-operation-advanced-configuration/#advanced-description-from-docstring).
|
||||
* PR [#556](https://github.com/tiangolo/fastapi/pull/556) by [@svalouch](https://github.com/svalouch).
|
||||
* Fix `DOCTYPE` in HTML files generated for Swagger UI and ReDoc. PR [#537](https://github.com/tiangolo/fastapi/pull/537) by [@Trim21](https://github.com/Trim21).
|
||||
* Fix handling `4XX` responses overriding default `422` validation error responses. PR [#517](https://github.com/tiangolo/fastapi/pull/517) by [@tsouvarev](https://github.com/tsouvarev).
|
||||
* Fix typo in documentation for [Simple HTTP Basic Auth](https://fastapi.tiangolo.com/tutorial/security/http-basic-auth/#simple-http-basic-auth). PR [#514](https://github.com/tiangolo/fastapi/pull/514) by [@prostomarkeloff](https://github.com/prostomarkeloff).
|
||||
* Fix incorrect documentation example in [first steps](https://fastapi.tiangolo.com/tutorial/first-steps/). PR [#511](https://github.com/tiangolo/fastapi/pull/511) by [@IgnatovFedor](https://github.com/IgnatovFedor).
|
||||
* Add support for Swagger UI [initOauth](https://github.com/swagger-api/swagger-ui/blob/master/docs/usage/oauth2.md) settings with the parameter `swagger_ui_init_oauth`. PR [#499](https://github.com/tiangolo/fastapi/pull/499) by [@zamiramir](https://github.com/zamiramir).
|
||||
|
||||
## 0.39.0
|
||||
|
||||
* Allow path parameters to have default values (e.g. `None`) and discard them instead of raising an error.
|
||||
* This allows declaring a parameter like `user_id: str = None` that can be taken from a query parameter, but the same path operation can be included in a router with a path `/users/{user_id}`, in which case will be taken from the path and will be required.
|
||||
* PR [#464](https://github.com/tiangolo/fastapi/pull/464) by [@jonathanunderwood](https://github.com/jonathanunderwood).
|
||||
* Add support for setting a `default_response_class` in the `FastAPI` instance or in `include_router`. Initial PR [#467](https://github.com/tiangolo/fastapi/pull/467) by [@toppk](https://github.com/toppk).
|
||||
* Add support for type annotations using strings and `from __future__ import annotations`. PR [#451](https://github.com/tiangolo/fastapi/pull/451) by [@dmontagu](https://github.com/dmontagu).
|
||||
|
||||
## 0.38.1
|
||||
|
||||
* Fix incorrect `Request` class import. PR [#493](https://github.com/tiangolo/fastapi/pull/493) by [@kamalgill](https://github.com/kamalgill).
|
||||
|
||||
## 0.38.0
|
||||
|
||||
* Add recent articles to [External Links](https://fastapi.tiangolo.com/external-links/) and recent opinions. PR [#490](https://github.com/tiangolo/fastapi/pull/490).
|
||||
* Upgrade support range for Starlette to include `0.12.8`. The new range is `>=0.11.1,<=0.12.8"`. PR [#477](https://github.com/tiangolo/fastapi/pull/477) by [@dmontagu](https://github.com/dmontagu).
|
||||
* Upgrade support to Pydantic version 0.32.2 and update internal code to use it (breaking change). PR [#463](https://github.com/tiangolo/fastapi/pull/463) by [@dmontagu](https://github.com/dmontagu).
|
||||
|
||||
## 0.37.0
|
||||
|
||||
* Add support for custom route classes for advanced use cases. PR [#468](https://github.com/tiangolo/fastapi/pull/468) by [@dmontagu](https://github.com/dmontagu).
|
||||
* Allow disabling Google fonts in ReDoc. PR [#481](https://github.com/tiangolo/fastapi/pull/481) by [@b1-luettje](https://github.com/b1-luettje).
|
||||
* Fix security issue: when returning a sub-class of a response model and using `skip_defaults` it could leak information. PR [#485](https://github.com/tiangolo/fastapi/pull/485) by [@dmontagu](https://github.com/dmontagu).
|
||||
* Enable tests for Python 3.8-dev. PR [#465](https://github.com/tiangolo/fastapi/pull/465) by [@Jamim](https://github.com/Jamim).
|
||||
* Add support and tests for Pydantic dataclasses in `response_model`. PR [#454](https://github.com/tiangolo/fastapi/pull/454) by [@dconathan](https://github.com/dconathan).
|
||||
* Fix typo in OAuth2 JWT tutorial. PR [#447](https://github.com/tiangolo/fastapi/pull/447) by [@pablogamboa](https://github.com/pablogamboa).
|
||||
* Use the `media_type` parameter in `Body()` params to set the media type in OpenAPI for `requestBody`. PR [#439](https://github.com/tiangolo/fastapi/pull/439) by [@divums](https://github.com/divums).
|
||||
* Add article [Deploying a scikit-learn model with ONNX and FastAPI](https://medium.com/@nico.axtmann95/deploying-a-scikit-learn-model-with-onnx-und-fastapi-1af398268915) by [https://www.linkedin.com/in/nico-axtmann](Nico Axtmann). PR [#438](https://github.com/tiangolo/fastapi/pull/438) by [@naxty](https://github.com/naxty).
|
||||
* Allow setting custom `422` (validation error) response/schema in OpenAPI.
|
||||
* And use media type from response class instead of fixed `application/json` (the default).
|
||||
* PR [#437](https://github.com/tiangolo/fastapi/pull/437) by [@divums](https://github.com/divums).
|
||||
* Fix using `"default"` extra response with status codes at the same time. PR [#489](https://github.com/tiangolo/fastapi/pull/489).
|
||||
* Allow additional responses to use status code ranges (like `5XX` and `4XX`) and `"default"`. PR [#435](https://github.com/tiangolo/fastapi/pull/435) by [@divums](https://github.com/divums).
|
||||
|
||||
## 0.36.0
|
||||
|
||||
* Fix implementation for `skip_defaults` when returning a Pydantic model. PR [#422](https://github.com/tiangolo/fastapi/pull/422) by [@dmontagu](https://github.com/dmontagu).
|
||||
* Fix OpenAPI generation when using the same dependency in multiple places for the same *path operation*. PR [#417](https://github.com/tiangolo/fastapi/pull/417) by [@dmontagu](https://github.com/dmontagu).
|
||||
* Allow having empty paths in *path operations* used with `include_router` and a `prefix`.
|
||||
* This allows having a router for `/cats` and all its *path operations*, while having one of them for `/cats`.
|
||||
* Now it doesn't have to be only `/cats/` (with a trailing slash).
|
||||
* To use it, declare the path in the *path operation* as the empty string (`""`).
|
||||
* PR [#415](https://github.com/tiangolo/fastapi/pull/415) by [@vitalik](https://github.com/vitalik).
|
||||
* Fix mypy error after merging PR #415. PR [#462](https://github.com/tiangolo/fastapi/pull/462).
|
||||
|
||||
## 0.35.0
|
||||
|
||||
* Fix typo in routing `assert`. PR [#419](https://github.com/tiangolo/fastapi/pull/419) by [@pablogamboa](https://github.com/pablogamboa).
|
||||
* Fix typo in docs. PR [#411](https://github.com/tiangolo/fastapi/pull/411) by [@bronsen](https://github.com/bronsen).
|
||||
* Fix parsing a body type declared with `Union`. PR [#400](https://github.com/tiangolo/fastapi/pull/400) by [@koxudaxi](https://github.com/koxudaxi).
|
||||
|
||||
## 0.34.0
|
||||
|
||||
* Upgrade Starlette supported range to include the latest `0.12.7`. The new range is `0.11.1,<=0.12.7`. PR [#367](https://github.com/tiangolo/fastapi/pull/367) by [@dedsm](https://github.com/dedsm).
|
||||
|
||||
* Add test for OpenAPI schema with duplicate models from PR [#333](https://github.com/tiangolo/fastapi/pull/333) by [@dmontagu](https://github.com/dmontagu). PR [#385](https://github.com/tiangolo/fastapi/pull/385).
|
||||
|
||||
## 0.33.0
|
||||
|
||||
* Upgrade Pydantic version to `0.30.0`. PR [#384](https://github.com/tiangolo/fastapi/pull/384) by [@jekirl](https://github.com/jekirl).
|
||||
|
||||
## 0.32.0
|
||||
|
||||
* Fix typo in docs for features. PR [#380](https://github.com/tiangolo/fastapi/pull/380) by [@MartinoMensio](https://github.com/MartinoMensio).
|
||||
|
||||
* Fix source code `limit` for example in [Query Parameters](https://fastapi.tiangolo.com/tutorial/query-params/). PR [#366](https://github.com/tiangolo/fastapi/pull/366) by [@Smashman](https://github.com/Smashman).
|
||||
|
||||
* Update wording in docs about [OAuth2 scopes](https://fastapi.tiangolo.com/tutorial/security/oauth2-scopes/). PR [#371](https://github.com/tiangolo/fastapi/pull/371) by [@cjw296](https://github.com/cjw296).
|
||||
|
||||
* Update docs for `Enum`s to inherit from `str` and improve Swagger UI rendering. PR [#351](https://github.com/tiangolo/fastapi/pull/351).
|
||||
|
||||
* Fix regression, add Swagger UI deep linking again. PR [#350](https://github.com/tiangolo/fastapi/pull/350).
|
||||
|
||||
* Add test for having path templates in `prefix` of `.include_router`. PR [#349](https://github.com/tiangolo/fastapi/pull/349).
|
||||
|
||||
* Add note to docs: [Include the same router multiple times with different `prefix`](https://fastapi.tiangolo.com/tutorial/bigger-applications/#include-the-same-router-multiple-times-with-different-prefix). PR [#348](https://github.com/tiangolo/fastapi/pull/348).
|
||||
|
||||
* Fix OpenAPI/JSON Schema generation for two functions with the same name (in different modules) with the same composite bodies.
|
||||
* Composite bodies' IDs are now based on path, not only on route name, as the auto-generated name uses the function names, that can be duplicated in different modules.
|
||||
* The same new ID generation applies to response models.
|
||||
* This also changes the generated title for those models.
|
||||
* Only composite bodies and response models are affected because those are generated dynamically, they don't have a module (a Python file).
|
||||
* This also adds the possibility of using `.include_router()` with the same `APIRouter` *multiple* times, with different prefixes, e.g. `/api/v2` and `/api/latest`, and it will now work correctly.
|
||||
* PR [#347](https://github.com/tiangolo/fastapi/pull/347).
|
||||
|
||||
## 0.31.0
|
||||
|
||||
* Upgrade Pydantic supported version to `0.29.0`.
|
||||
* New supported version range is `"pydantic >=0.28,<=0.29.0"`.
|
||||
* This adds support for Pydantic [Generic Models](https://pydantic-docs.helpmanual.io/#generic-models), kudos to [@dmontagu](https://github.com/dmontagu).
|
||||
* PR [#344](https://github.com/tiangolo/fastapi/pull/344).
|
||||
|
||||
## 0.30.1
|
||||
|
||||
* Add section in docs about [External Links and Articles](https://fastapi.tiangolo.com/external-links/). PR [#341](https://github.com/tiangolo/fastapi/pull/341).
|
||||
|
||||
* Remove `Pipfile.lock` from the repository as it is only used by FastAPI contributors (developers of FastAPI itself). See the PR for more details. PR [#340](https://github.com/tiangolo/fastapi/pull/340).
|
||||
|
||||
* Update section about [Help FastAPI - Get Help](https://fastapi.tiangolo.com/help-fastapi/). PR [#339](https://github.com/tiangolo/fastapi/pull/339).
|
||||
|
||||
* Refine internal type declarations to improve/remove Mypy errors in users' code. PR [#338](https://github.com/tiangolo/fastapi/pull/338).
|
||||
|
||||
* Update and clarify [SQL tutorial with SQLAlchemy](https://fastapi.tiangolo.com/tutorial/sql-databases/). PR [#331](https://github.com/tiangolo/fastapi/pull/331) by [@mariacamilagl](https://github.com/mariacamilagl).
|
||||
|
||||
* Add SQLite [online viewers to the docs](https://fastapi.tiangolo.com/tutorial/sql-databases/#interact-with-the-database-directly). PR [#330](https://github.com/tiangolo/fastapi/pull/330) by [@cyrilbois](https://github.com/cyrilbois).
|
||||
|
||||
## 0.30.0
|
||||
|
||||
* Add support for Pydantic's ORM mode:
|
||||
* Updated documentation about SQL with SQLAlchemy, using Pydantic models with ORM mode, SQLAlchemy models with relations, separation of files, simplification of code and other changes. New docs: [SQL (Relational) Databases](https://fastapi.tiangolo.com/tutorial/sql-databases/).
|
||||
* The new support for ORM mode fixes issues/adds features related to ORMs with lazy-loading, hybrid properties, dynamic/getters (using `@property` decorators) and several other use cases.
|
||||
* This applies to ORMs like SQLAlchemy, Peewee, Tortoise ORM, GINO ORM and virtually any other.
|
||||
* If your *path operations* return an arbitrary object with attributes (e.g. `my_item.name` instead of `my_item["name"]`) AND you use a `response_model`, make sure to update the Pydantic models with `orm_mode = True` as described in the docs (link above).
|
||||
* New documentation about receiving plain `dict`s as request bodies: [Bodies of arbitrary `dict`s](https://fastapi.tiangolo.com/tutorial/body-nested-models/#bodies-of-arbitrary-dicts).
|
||||
* New documentation about returning arbitrary `dict`s in responses: [Response with arbitrary `dict`](https://fastapi.tiangolo.com/tutorial/extra-models/#response-with-arbitrary-dict).
|
||||
* **Technical Details**:
|
||||
* When declaring a `response_model` it is used directly to generate the response content, from whatever was returned from the *path operation function*.
|
||||
* Before this, the return content was first passed through `jsonable_encoder` to ensure it was a "jsonable" object, like a `dict`, instead of an arbitrary object with attributes (like an ORM model). That's why you should make sure to update your Pydantic models for objects with attributes to use `orm_mode = True`.
|
||||
* If you don't have a `response_model`, the return object will still be passed through `jsonable_encoder` first.
|
||||
* When a `response_model` is declared, the same `response_model` type declaration won't be used as is, it will be "cloned" to create an new one (a cloned Pydantic `Field` with all the submodels cloned as well).
|
||||
* This avoids/fixes a potential security issue: as the returned object is passed directly to Pydantic, if the returned object was a subclass of the `response_model` (e.g. you return a `UserInDB` that inherits from `User` but contains extra fields, like `hashed_password`, and `User` is used in the `response_model`), it would still pass the validation (because `UserInDB` is a subclass of `User`) and the object would be returned as-is, including the `hashed_password`. To fix this, the declared `response_model` is cloned, if it is a Pydantic model class (or contains Pydantic model classes in it, e.g. in a `List[Item]`), the Pydantic model class(es) will be a different one (the "cloned" one). So, an object that is a subclass won't simply pass the validation and returned as-is, because it is no longer a sub-class of the cloned `response_model`. Instead, a new Pydantic model object will be created with the contents of the returned object. So, it will be a new object (made with the data from the returned one), and will be filtered by the cloned `response_model`, containing only the declared fields as normally.
|
||||
* PR [#322](https://github.com/tiangolo/fastapi/pull/322).
|
||||
|
||||
* Remove/clean unused RegEx code in routing. PR [#314](https://github.com/tiangolo/fastapi/pull/314) by [@dmontagu](https://github.com/dmontagu).
|
||||
|
||||
* Use default response status code descriptions for additional responses. PR [#313](https://github.com/tiangolo/fastapi/pull/313) by [@duxiaoyao](https://github.com/duxiaoyao).
|
||||
|
||||
* Upgrade Pydantic support to `0.28`. PR [#320](https://github.com/tiangolo/fastapi/pull/320) by [@jekirl](https://github.com/jekirl).
|
||||
|
||||
## 0.29.1
|
||||
|
||||
* Fix handling an empty-body request with a required body param. PR [#311](https://github.com/tiangolo/fastapi/pull/311).
|
||||
|
||||
* Fix broken link in docs: [Return a Response directly](https://fastapi.tiangolo.com/tutorial/response-directly/). PR [#306](https://github.com/tiangolo/fastapi/pull/306) by [@dmontagu](https://github.com/dmontagu).
|
||||
|
||||
* Fix docs discrepancy in docs for [Response Model](https://fastapi.tiangolo.com/tutorial/response-model/). PR [#288](https://github.com/tiangolo/fastapi/pull/288) by [@awiddersheim](https://github.com/awiddersheim).
|
||||
|
||||
## 0.29.0
|
||||
|
||||
* Add support for declaring a `Response` parameter:
|
||||
* This allows declaring:
|
||||
* [Response Cookies](https://fastapi.tiangolo.com/tutorial/response-cookies/).
|
||||
* [Response Headers](https://fastapi.tiangolo.com/tutorial/response-headers/).
|
||||
* An HTTP Status Code different than the default: [Response - Change Status Code](https://fastapi.tiangolo.com/tutorial/response-change-status-code/).
|
||||
* All of this while still being able to return arbitrary objects (`dict`, DB model, etc).
|
||||
* Update attribution to Hug, for inspiring the `response` parameter pattern.
|
||||
* PR [#294](https://github.com/tiangolo/fastapi/pull/294).
|
||||
|
||||
## 0.28.0
|
||||
|
||||
* Implement dependency cache per request.
|
||||
* This avoids calling each dependency multiple times for the same request.
|
||||
* This is useful while calling external services, performing costly computation, etc.
|
||||
* This also means that if a dependency was declared as a *path operation decorator* dependency, possibly at the router level (with `.include_router()`) and then it is declared again in a specific *path operation*, the dependency will be called only once.
|
||||
* The cache can be disabled per dependency declaration, using `use_cache=False` as in `Depends(your_dependency, use_cache=False)`.
|
||||
* Updated docs at: [Using the same dependency multiple times](https://fastapi.tiangolo.com/tutorial/dependencies/sub-dependencies/#using-the-same-dependency-multiple-times).
|
||||
* PR [#292](https://github.com/tiangolo/fastapi/pull/292).
|
||||
|
||||
* Implement dependency overrides for testing.
|
||||
* This allows using overrides/mocks of dependencies during tests.
|
||||
* New docs: [Testing Dependencies with Overrides](https://fastapi.tiangolo.com/tutorial/testing-dependencies/).
|
||||
* PR [#291](https://github.com/tiangolo/fastapi/pull/291).
|
||||
|
||||
## 0.27.2
|
||||
|
||||
* Fix path and query parameters receiving `dict` as a valid type. It should be mapped to a body payload. PR [#287](https://github.com/tiangolo/fastapi/pull/287). Updated docs at: [Query parameter list / multiple values with defaults: Using `list`](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#using-list).
|
||||
|
||||
## 0.27.1
|
||||
|
||||
* Fix `auto_error=False` handling in `HTTPBearer` security scheme. Do not `raise` when there's an incorrect `Authorization` header if `auto_error=False`. PR [#282](https://github.com/tiangolo/fastapi/pull/282).
|
||||
|
||||
* Fix type declaration of `HTTPException`. PR [#279](https://github.com/tiangolo/fastapi/pull/279).
|
||||
|
||||
## 0.27.0
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
from fastapi import Body, FastAPI
|
||||
from pydantic import BaseModel, Schema
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
name: str
|
||||
description: str = Schema(None, title="The description of the item", max_length=300)
|
||||
price: float = Schema(..., gt=0, description="The price must be greater than zero")
|
||||
description: str = Field(None, title="The description of the item", max_length=300)
|
||||
price: float = Field(..., gt=0, description="The price must be greater than zero")
|
||||
tax: float = None
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
from typing import Set
|
||||
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel, UrlStr
|
||||
from pydantic import BaseModel, HttpUrl
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class Image(BaseModel):
|
||||
url: UrlStr
|
||||
url: HttpUrl
|
||||
name: str
|
||||
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
from typing import List, Set
|
||||
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel, UrlStr
|
||||
from pydantic import BaseModel, HttpUrl
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class Image(BaseModel):
|
||||
url: UrlStr
|
||||
url: HttpUrl
|
||||
name: str
|
||||
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
from typing import List, Set
|
||||
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel, UrlStr
|
||||
from pydantic import BaseModel, HttpUrl
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class Image(BaseModel):
|
||||
url: UrlStr
|
||||
url: HttpUrl
|
||||
name: str
|
||||
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
from typing import List
|
||||
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel, UrlStr
|
||||
from pydantic import BaseModel, HttpUrl
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class Image(BaseModel):
|
||||
url: UrlStr
|
||||
url: HttpUrl
|
||||
name: str
|
||||
|
||||
|
||||
|
||||
10
docs/src/body_nested_models/tutorial009.py
Normal file
10
docs/src/body_nested_models/tutorial009.py
Normal file
@@ -0,0 +1,10 @@
|
||||
from typing import Dict
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.post("/index-weights/")
|
||||
async def create_index_weights(weights: Dict[int, float]):
|
||||
return weights
|
||||
@@ -31,7 +31,7 @@ async def read_item(item_id: str):
|
||||
async def update_item(item_id: str, item: Item):
|
||||
stored_item_data = items[item_id]
|
||||
stored_item_model = Item(**stored_item_data)
|
||||
update_data = item.dict(skip_defaults=True)
|
||||
update_data = item.dict(exclude_unset=True)
|
||||
updated_item = stored_item_model.copy(update=update_data)
|
||||
items[item_id] = jsonable_encoder(updated_item)
|
||||
return updated_item
|
||||
|
||||
@@ -6,8 +6,8 @@ app = FastAPI()
|
||||
origins = [
|
||||
"http://localhost.tiangolo.com",
|
||||
"https://localhost.tiangolo.com",
|
||||
"http:localhost",
|
||||
"http:localhost:8080",
|
||||
"http://localhost",
|
||||
"http://localhost:8080",
|
||||
]
|
||||
|
||||
app.add_middleware(
|
||||
|
||||
37
docs/src/custom_request_and_route/tutorial001.py
Normal file
37
docs/src/custom_request_and_route/tutorial001.py
Normal file
@@ -0,0 +1,37 @@
|
||||
import gzip
|
||||
from typing import Callable, List
|
||||
|
||||
from fastapi import Body, FastAPI
|
||||
from fastapi.routing import APIRoute
|
||||
from starlette.requests import Request
|
||||
from starlette.responses import Response
|
||||
|
||||
|
||||
class GzipRequest(Request):
|
||||
async def body(self) -> bytes:
|
||||
if not hasattr(self, "_body"):
|
||||
body = await super().body()
|
||||
if "gzip" in self.headers.getlist("Content-Encoding"):
|
||||
body = gzip.decompress(body)
|
||||
self._body = body
|
||||
return self._body
|
||||
|
||||
|
||||
class GzipRoute(APIRoute):
|
||||
def get_route_handler(self) -> Callable:
|
||||
original_route_handler = super().get_route_handler()
|
||||
|
||||
async def custom_route_handler(request: Request) -> Response:
|
||||
request = GzipRequest(request.scope, request.receive)
|
||||
return await original_route_handler(request)
|
||||
|
||||
return custom_route_handler
|
||||
|
||||
|
||||
app = FastAPI()
|
||||
app.router.route_class = GzipRoute
|
||||
|
||||
|
||||
@app.post("/sum")
|
||||
async def sum_numbers(numbers: List[int] = Body(...)):
|
||||
return {"sum": sum(numbers)}
|
||||
31
docs/src/custom_request_and_route/tutorial002.py
Normal file
31
docs/src/custom_request_and_route/tutorial002.py
Normal file
@@ -0,0 +1,31 @@
|
||||
from typing import Callable, List
|
||||
|
||||
from fastapi import Body, FastAPI, HTTPException
|
||||
from fastapi.exceptions import RequestValidationError
|
||||
from fastapi.routing import APIRoute
|
||||
from starlette.requests import Request
|
||||
from starlette.responses import Response
|
||||
|
||||
|
||||
class ValidationErrorLoggingRoute(APIRoute):
|
||||
def get_route_handler(self) -> Callable:
|
||||
original_route_handler = super().get_route_handler()
|
||||
|
||||
async def custom_route_handler(request: Request) -> Response:
|
||||
try:
|
||||
return await original_route_handler(request)
|
||||
except RequestValidationError as exc:
|
||||
body = await request.body()
|
||||
detail = {"errors": exc.errors(), "body": body.decode()}
|
||||
raise HTTPException(status_code=422, detail=detail)
|
||||
|
||||
return custom_route_handler
|
||||
|
||||
|
||||
app = FastAPI()
|
||||
app.router.route_class = ValidationErrorLoggingRoute
|
||||
|
||||
|
||||
@app.post("/")
|
||||
async def sum_numbers(numbers: List[int] = Body(...)):
|
||||
return sum(numbers)
|
||||
41
docs/src/custom_request_and_route/tutorial003.py
Normal file
41
docs/src/custom_request_and_route/tutorial003.py
Normal file
@@ -0,0 +1,41 @@
|
||||
import time
|
||||
from typing import Callable
|
||||
|
||||
from fastapi import APIRouter, FastAPI
|
||||
from fastapi.routing import APIRoute
|
||||
from starlette.requests import Request
|
||||
from starlette.responses import Response
|
||||
|
||||
|
||||
class TimedRoute(APIRoute):
|
||||
def get_route_handler(self) -> Callable:
|
||||
original_route_handler = super().get_route_handler()
|
||||
|
||||
async def custom_route_handler(request: Request) -> Response:
|
||||
before = time.time()
|
||||
response: Response = await original_route_handler(request)
|
||||
duration = time.time() - before
|
||||
response.headers["X-Response-Time"] = str(duration)
|
||||
print(f"route duration: {duration}")
|
||||
print(f"route response: {response}")
|
||||
print(f"route response headers: {response.headers}")
|
||||
return response
|
||||
|
||||
return custom_route_handler
|
||||
|
||||
|
||||
app = FastAPI()
|
||||
router = APIRouter(route_class=TimedRoute)
|
||||
|
||||
|
||||
@app.get("/")
|
||||
async def not_timed():
|
||||
return {"message": "Not timed"}
|
||||
|
||||
|
||||
@router.get("/timed")
|
||||
async def timed():
|
||||
return {"message": "It's the time of my life"}
|
||||
|
||||
|
||||
app.include_router(router)
|
||||
@@ -1,21 +1,6 @@
|
||||
from fastapi import Depends, FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class FixedContentQueryChecker:
|
||||
def __init__(self, fixed_content: str):
|
||||
self.fixed_content = fixed_content
|
||||
|
||||
def __call__(self, q: str = ""):
|
||||
if q:
|
||||
return self.fixed_content in q
|
||||
return False
|
||||
|
||||
|
||||
checker = FixedContentQueryChecker("bar")
|
||||
|
||||
|
||||
@app.get("/query-checker/")
|
||||
async def read_query_check(fixed_content_included: bool = Depends(checker)):
|
||||
return {"fixed_content_in_query": fixed_content_included}
|
||||
async def get_db():
|
||||
db = DBSession()
|
||||
try:
|
||||
yield db
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
25
docs/src/dependencies/tutorial008.py
Normal file
25
docs/src/dependencies/tutorial008.py
Normal file
@@ -0,0 +1,25 @@
|
||||
from fastapi import Depends
|
||||
|
||||
|
||||
async def dependency_a():
|
||||
dep_a = generate_dep_a()
|
||||
try:
|
||||
yield dep_a
|
||||
finally:
|
||||
dep_a.close()
|
||||
|
||||
|
||||
async def dependency_b(dep_a=Depends(dependency_a)):
|
||||
dep_b = generate_dep_b()
|
||||
try:
|
||||
yield dep_b
|
||||
finally:
|
||||
dep_b.close(dep_a)
|
||||
|
||||
|
||||
async def dependency_c(dep_b=Depends(dependency_b)):
|
||||
dep_c = generate_dep_c()
|
||||
try:
|
||||
yield dep_c
|
||||
finally:
|
||||
dep_c.close(dep_b)
|
||||
25
docs/src/dependencies/tutorial009.py
Normal file
25
docs/src/dependencies/tutorial009.py
Normal file
@@ -0,0 +1,25 @@
|
||||
from fastapi import Depends
|
||||
|
||||
|
||||
async def dependency_a():
|
||||
dep_a = generate_dep_a()
|
||||
try:
|
||||
yield dep_a
|
||||
finally:
|
||||
dep_a.close()
|
||||
|
||||
|
||||
async def dependency_b(dep_a=Depends(dependency_a)):
|
||||
dep_b = generate_dep_b()
|
||||
try:
|
||||
yield dep_b
|
||||
finally:
|
||||
dep_b.close(dep_a)
|
||||
|
||||
|
||||
async def dependency_c(dep_b=Depends(dependency_b)):
|
||||
dep_c = generate_dep_c()
|
||||
try:
|
||||
yield dep_c
|
||||
finally:
|
||||
dep_c.close(dep_b)
|
||||
14
docs/src/dependencies/tutorial010.py
Normal file
14
docs/src/dependencies/tutorial010.py
Normal file
@@ -0,0 +1,14 @@
|
||||
class MySuperContextManager:
|
||||
def __init__(self):
|
||||
self.db = DBSession()
|
||||
|
||||
def __enter__(self):
|
||||
return self.db
|
||||
|
||||
def __exit__(self, exc_type, exc_value, traceback):
|
||||
self.db.close()
|
||||
|
||||
|
||||
async def get_db():
|
||||
with MySuperContextManager() as db:
|
||||
yield db
|
||||
21
docs/src/dependencies/tutorial011.py
Normal file
21
docs/src/dependencies/tutorial011.py
Normal file
@@ -0,0 +1,21 @@
|
||||
from fastapi import Depends, FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class FixedContentQueryChecker:
|
||||
def __init__(self, fixed_content: str):
|
||||
self.fixed_content = fixed_content
|
||||
|
||||
def __call__(self, q: str = ""):
|
||||
if q:
|
||||
return self.fixed_content in q
|
||||
return False
|
||||
|
||||
|
||||
checker = FixedContentQueryChecker("bar")
|
||||
|
||||
|
||||
@app.get("/query-checker/")
|
||||
async def read_query_check(fixed_content_included: bool = Depends(checker)):
|
||||
return {"fixed_content_in_query": fixed_content_included}
|
||||
55
docs/src/dependency_testing/tutorial001.py
Normal file
55
docs/src/dependency_testing/tutorial001.py
Normal file
@@ -0,0 +1,55 @@
|
||||
from fastapi import Depends, FastAPI
|
||||
from starlette.testclient import TestClient
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
async def common_parameters(q: str = None, skip: int = 0, limit: int = 100):
|
||||
return {"q": q, "skip": skip, "limit": limit}
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items(commons: dict = Depends(common_parameters)):
|
||||
return {"message": "Hello Items!", "params": commons}
|
||||
|
||||
|
||||
@app.get("/users/")
|
||||
async def read_users(commons: dict = Depends(common_parameters)):
|
||||
return {"message": "Hello Users!", "params": commons}
|
||||
|
||||
|
||||
client = TestClient(app)
|
||||
|
||||
|
||||
async def override_dependency(q: str = None):
|
||||
return {"q": q, "skip": 5, "limit": 10}
|
||||
|
||||
|
||||
app.dependency_overrides[common_parameters] = override_dependency
|
||||
|
||||
|
||||
def test_override_in_items():
|
||||
response = client.get("/items/")
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {
|
||||
"message": "Hello Items!",
|
||||
"params": {"q": None, "skip": 5, "limit": 10},
|
||||
}
|
||||
|
||||
|
||||
def test_override_in_items_with_q():
|
||||
response = client.get("/items/?q=foo")
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {
|
||||
"message": "Hello Items!",
|
||||
"params": {"q": "foo", "skip": 5, "limit": 10},
|
||||
}
|
||||
|
||||
|
||||
def test_override_in_items_with_params():
|
||||
response = client.get("/items/?q=foo&skip=100&limit=200")
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {
|
||||
"message": "Hello Items!",
|
||||
"params": {"q": "foo", "skip": 5, "limit": 10},
|
||||
}
|
||||
41
docs/src/extending_openapi/tutorial002.py
Normal file
41
docs/src/extending_openapi/tutorial002.py
Normal file
@@ -0,0 +1,41 @@
|
||||
from fastapi import FastAPI
|
||||
from fastapi.openapi.docs import (
|
||||
get_redoc_html,
|
||||
get_swagger_ui_html,
|
||||
get_swagger_ui_oauth2_redirect_html,
|
||||
)
|
||||
from starlette.staticfiles import StaticFiles
|
||||
|
||||
app = FastAPI(docs_url=None, redoc_url=None)
|
||||
|
||||
app.mount("/static", StaticFiles(directory="static"), name="static")
|
||||
|
||||
|
||||
@app.get("/docs", include_in_schema=False)
|
||||
async def custom_swagger_ui_html():
|
||||
return get_swagger_ui_html(
|
||||
openapi_url=app.openapi_url,
|
||||
title=app.title + " - Swagger UI",
|
||||
oauth2_redirect_url=app.swagger_ui_oauth2_redirect_url,
|
||||
swagger_js_url="/static/swagger-ui-bundle.js",
|
||||
swagger_css_url="/static/swagger-ui.css",
|
||||
)
|
||||
|
||||
|
||||
@app.get(app.swagger_ui_oauth2_redirect_url, include_in_schema=False)
|
||||
async def swagger_ui_redirect():
|
||||
return get_swagger_ui_oauth2_redirect_html()
|
||||
|
||||
|
||||
@app.get("/redoc", include_in_schema=False)
|
||||
async def redoc_html():
|
||||
return get_redoc_html(
|
||||
openapi_url=app.openapi_url,
|
||||
title=app.title + " - ReDoc",
|
||||
redoc_js_url="/static/redoc.standalone.js",
|
||||
)
|
||||
|
||||
|
||||
@app.get("/users/{username}")
|
||||
async def read_user(username: str):
|
||||
return {"message": f"Hello {username}"}
|
||||
@@ -1,6 +1,5 @@
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
from pydantic.types import EmailStr
|
||||
from pydantic import BaseModel, EmailStr
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
from pydantic.types import EmailStr
|
||||
from pydantic import BaseModel, EmailStr
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
10
docs/src/extra_models/tutorial005.py
Normal file
10
docs/src/extra_models/tutorial005.py
Normal file
@@ -0,0 +1,10 @@
|
||||
from typing import Dict
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/keyword-weights/", response_model=Dict[str, float])
|
||||
async def read_keyword_weights():
|
||||
return {"foo": 2.3, "bar": 3.4}
|
||||
52
docs/src/openapi_callbacks/tutorial001.py
Normal file
52
docs/src/openapi_callbacks/tutorial001.py
Normal file
@@ -0,0 +1,52 @@
|
||||
from fastapi import APIRouter, FastAPI
|
||||
from pydantic import BaseModel, HttpUrl
|
||||
from starlette.responses import JSONResponse
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class Invoice(BaseModel):
|
||||
id: str
|
||||
title: str = None
|
||||
customer: str
|
||||
total: float
|
||||
|
||||
|
||||
class InvoiceEvent(BaseModel):
|
||||
description: str
|
||||
paid: bool
|
||||
|
||||
|
||||
class InvoiceEventReceived(BaseModel):
|
||||
ok: bool
|
||||
|
||||
|
||||
invoices_callback_router = APIRouter(default_response_class=JSONResponse)
|
||||
|
||||
|
||||
@invoices_callback_router.post(
|
||||
"{$callback_url}/invoices/{$request.body.id}", response_model=InvoiceEventReceived,
|
||||
)
|
||||
def invoice_notification(body: InvoiceEvent):
|
||||
pass
|
||||
|
||||
|
||||
@app.post("/invoices/", callbacks=invoices_callback_router.routes)
|
||||
def create_invoice(invoice: Invoice, callback_url: HttpUrl = None):
|
||||
"""
|
||||
Create an invoice.
|
||||
|
||||
This will (let's imagine) let the API user (some external developer) create an
|
||||
invoice.
|
||||
|
||||
And this path operation will:
|
||||
|
||||
* Send the invoice to the client.
|
||||
* Collect the money from the client.
|
||||
* Send a notification back to the API user (the external developer), as a callback.
|
||||
* At this point is that the API will somehow send a POST request to the
|
||||
external API with the notification of the invoice event
|
||||
(e.g. "payment successful").
|
||||
"""
|
||||
# Send the invoice, collect the money, send the notification (the callback)
|
||||
return {"msg": "Invoice received"}
|
||||
@@ -1,8 +1,24 @@
|
||||
from fastapi import FastAPI
|
||||
from fastapi.routing import APIRoute
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/items/", include_in_schema=False)
|
||||
@app.get("/items/")
|
||||
async def read_items():
|
||||
return [{"item_id": "Foo"}]
|
||||
|
||||
|
||||
def use_route_names_as_operation_ids(app: FastAPI) -> None:
|
||||
"""
|
||||
Simplify operation IDs so that generated API clients have simpler function
|
||||
names.
|
||||
|
||||
Should be called only after all routes have been added.
|
||||
"""
|
||||
for route in app.routes:
|
||||
if isinstance(route, APIRoute):
|
||||
route.operation_id = route.name # in this case, 'read_items'
|
||||
|
||||
|
||||
use_route_names_as_operation_ids(app)
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/items/", include_in_schema=False)
|
||||
async def read_items():
|
||||
return [{"item_id": "Foo"}]
|
||||
@@ -0,0 +1,30 @@
|
||||
from typing import Set
|
||||
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
name: str
|
||||
description: str = None
|
||||
price: float
|
||||
tax: float = None
|
||||
tags: Set[str] = []
|
||||
|
||||
|
||||
@app.post("/items/", response_model=Item, summary="Create an item")
|
||||
async def create_item(*, item: Item):
|
||||
"""
|
||||
Create an item with all the information:
|
||||
|
||||
- **name**: each item must have a name
|
||||
- **description**: a long description
|
||||
- **price**: required
|
||||
- **tax**: if the item doesn't have tax, you can omit this
|
||||
- **tags**: a set of unique tag strings for this item
|
||||
\f
|
||||
:param item: User input.
|
||||
"""
|
||||
return item
|
||||
@@ -3,7 +3,7 @@ from enum import Enum
|
||||
from fastapi import FastAPI
|
||||
|
||||
|
||||
class ModelName(Enum):
|
||||
class ModelName(str, Enum):
|
||||
alexnet = "alexnet"
|
||||
resnet = "resnet"
|
||||
lenet = "lenet"
|
||||
|
||||
@@ -6,5 +6,5 @@ fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_item(skip: int = 0, limit: int = 100):
|
||||
async def read_item(skip: int = 0, limit: int = 10):
|
||||
return fake_items_db[skip : skip + limit]
|
||||
|
||||
9
docs/src/query_params_str_validations/tutorial013.py
Normal file
9
docs/src/query_params_str_validations/tutorial013.py
Normal file
@@ -0,0 +1,9 @@
|
||||
from fastapi import FastAPI, Query
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items(q: list = Query(None)):
|
||||
query_items = {"q": q}
|
||||
return query_items
|
||||
15
docs/src/response_change_status_code/tutorial001.py
Normal file
15
docs/src/response_change_status_code/tutorial001.py
Normal file
@@ -0,0 +1,15 @@
|
||||
from fastapi import FastAPI
|
||||
from starlette.responses import Response
|
||||
from starlette.status import HTTP_201_CREATED
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
tasks = {"foo": "Listen to the Bar Fighters"}
|
||||
|
||||
|
||||
@app.put("/get-or-create-task/{task_id}", status_code=200)
|
||||
def get_or_create_task(task_id: str, response: Response):
|
||||
if task_id not in tasks:
|
||||
tasks[task_id] = "This didn't exist before"
|
||||
response.status_code = HTTP_201_CREATED
|
||||
return tasks[task_id]
|
||||
10
docs/src/response_cookies/tutorial002.py
Normal file
10
docs/src/response_cookies/tutorial002.py
Normal file
@@ -0,0 +1,10 @@
|
||||
from fastapi import FastAPI
|
||||
from starlette.responses import Response
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.post("/cookie-and-object/")
|
||||
def create_cookie(response: Response):
|
||||
response.set_cookie(key="fakesession", value="fake-cookie-session-value")
|
||||
return {"message": "Come to the dark side, we have cookies"}
|
||||
@@ -6,12 +6,11 @@ app = FastAPI()
|
||||
|
||||
@app.get("/legacy/")
|
||||
def get_legacy_data():
|
||||
data = """
|
||||
<?xml version="1.0"?>
|
||||
data = """<?xml version="1.0"?>
|
||||
<shampoo>
|
||||
<Header>
|
||||
Apply shampoo here.
|
||||
<Header>
|
||||
</Header>
|
||||
<Body>
|
||||
You'll have to use soap here.
|
||||
</Body>
|
||||
|
||||
10
docs/src/response_headers/tutorial002.py
Normal file
10
docs/src/response_headers/tutorial002.py
Normal file
@@ -0,0 +1,10 @@
|
||||
from fastapi import FastAPI
|
||||
from starlette.responses import Response
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/headers-and-object/")
|
||||
def get_headers(response: Response):
|
||||
response.headers["X-Cat-Dog"] = "alone in the world"
|
||||
return {"message": "Hello World"}
|
||||
@@ -1,6 +1,5 @@
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
from pydantic.types import EmailStr
|
||||
from pydantic import BaseModel, EmailStr
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
from pydantic.types import EmailStr
|
||||
from pydantic import BaseModel, EmailStr
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@@ -21,6 +21,6 @@ items = {
|
||||
}
|
||||
|
||||
|
||||
@app.get("/items/{item_id}", response_model=Item, response_model_skip_defaults=True)
|
||||
@app.get("/items/{item_id}", response_model=Item, response_model_exclude_unset=True)
|
||||
async def read_item(item_id: str):
|
||||
return items[item_id]
|
||||
|
||||
0
docs/src/sql_databases/sql_app/__init__.py
Normal file
0
docs/src/sql_databases/sql_app/__init__.py
Normal file
64
docs/src/sql_databases/sql_app/alt_main.py
Normal file
64
docs/src/sql_databases/sql_app/alt_main.py
Normal file
@@ -0,0 +1,64 @@
|
||||
from typing import List
|
||||
|
||||
from fastapi import Depends, FastAPI, HTTPException
|
||||
from sqlalchemy.orm import Session
|
||||
from starlette.requests import Request
|
||||
from starlette.responses import Response
|
||||
|
||||
from . import crud, models, schemas
|
||||
from .database import SessionLocal, engine
|
||||
|
||||
models.Base.metadata.create_all(bind=engine)
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.middleware("http")
|
||||
async def db_session_middleware(request: Request, call_next):
|
||||
response = Response("Internal server error", status_code=500)
|
||||
try:
|
||||
request.state.db = SessionLocal()
|
||||
response = await call_next(request)
|
||||
finally:
|
||||
request.state.db.close()
|
||||
return response
|
||||
|
||||
|
||||
# Dependency
|
||||
def get_db(request: Request):
|
||||
return request.state.db
|
||||
|
||||
|
||||
@app.post("/users/", response_model=schemas.User)
|
||||
def create_user(user: schemas.UserCreate, db: Session = Depends(get_db)):
|
||||
db_user = crud.get_user_by_email(db, email=user.email)
|
||||
if db_user:
|
||||
raise HTTPException(status_code=400, detail="Email already registered")
|
||||
return crud.create_user(db=db, user=user)
|
||||
|
||||
|
||||
@app.get("/users/", response_model=List[schemas.User])
|
||||
def read_users(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
|
||||
users = crud.get_users(db, skip=skip, limit=limit)
|
||||
return users
|
||||
|
||||
|
||||
@app.get("/users/{user_id}", response_model=schemas.User)
|
||||
def read_user(user_id: int, db: Session = Depends(get_db)):
|
||||
db_user = crud.get_user(db, user_id=user_id)
|
||||
if db_user is None:
|
||||
raise HTTPException(status_code=404, detail="User not found")
|
||||
return db_user
|
||||
|
||||
|
||||
@app.post("/users/{user_id}/items/", response_model=schemas.Item)
|
||||
def create_item_for_user(
|
||||
user_id: int, item: schemas.ItemCreate, db: Session = Depends(get_db)
|
||||
):
|
||||
return crud.create_user_item(db=db, item=item, user_id=user_id)
|
||||
|
||||
|
||||
@app.get("/items/", response_model=List[schemas.Item])
|
||||
def read_items(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
|
||||
items = crud.get_items(db, skip=skip, limit=limit)
|
||||
return items
|
||||
36
docs/src/sql_databases/sql_app/crud.py
Normal file
36
docs/src/sql_databases/sql_app/crud.py
Normal file
@@ -0,0 +1,36 @@
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from . import models, schemas
|
||||
|
||||
|
||||
def get_user(db: Session, user_id: int):
|
||||
return db.query(models.User).filter(models.User.id == user_id).first()
|
||||
|
||||
|
||||
def get_user_by_email(db: Session, email: str):
|
||||
return db.query(models.User).filter(models.User.email == email).first()
|
||||
|
||||
|
||||
def get_users(db: Session, skip: int = 0, limit: int = 100):
|
||||
return db.query(models.User).offset(skip).limit(limit).all()
|
||||
|
||||
|
||||
def create_user(db: Session, user: schemas.UserCreate):
|
||||
fake_hashed_password = user.password + "notreallyhashed"
|
||||
db_user = models.User(email=user.email, hashed_password=fake_hashed_password)
|
||||
db.add(db_user)
|
||||
db.commit()
|
||||
db.refresh(db_user)
|
||||
return db_user
|
||||
|
||||
|
||||
def get_items(db: Session, skip: int = 0, limit: int = 100):
|
||||
return db.query(models.Item).offset(skip).limit(limit).all()
|
||||
|
||||
|
||||
def create_user_item(db: Session, item: schemas.ItemCreate, user_id: int):
|
||||
db_item = models.Item(**item.dict(), owner_id=user_id)
|
||||
db.add(db_item)
|
||||
db.commit()
|
||||
db.refresh(db_item)
|
||||
return db_item
|
||||
13
docs/src/sql_databases/sql_app/database.py
Normal file
13
docs/src/sql_databases/sql_app/database.py
Normal file
@@ -0,0 +1,13 @@
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
|
||||
SQLALCHEMY_DATABASE_URL = "sqlite:///./test.db"
|
||||
# SQLALCHEMY_DATABASE_URL = "postgresql://user:password@postgresserver/db"
|
||||
|
||||
engine = create_engine(
|
||||
SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False}
|
||||
)
|
||||
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
||||
|
||||
Base = declarative_base()
|
||||
55
docs/src/sql_databases/sql_app/main.py
Normal file
55
docs/src/sql_databases/sql_app/main.py
Normal file
@@ -0,0 +1,55 @@
|
||||
from typing import List
|
||||
|
||||
from fastapi import Depends, FastAPI, HTTPException
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from . import crud, models, schemas
|
||||
from .database import SessionLocal, engine
|
||||
|
||||
models.Base.metadata.create_all(bind=engine)
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
# Dependency
|
||||
def get_db():
|
||||
try:
|
||||
db = SessionLocal()
|
||||
yield db
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
@app.post("/users/", response_model=schemas.User)
|
||||
def create_user(user: schemas.UserCreate, db: Session = Depends(get_db)):
|
||||
db_user = crud.get_user_by_email(db, email=user.email)
|
||||
if db_user:
|
||||
raise HTTPException(status_code=400, detail="Email already registered")
|
||||
return crud.create_user(db=db, user=user)
|
||||
|
||||
|
||||
@app.get("/users/", response_model=List[schemas.User])
|
||||
def read_users(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
|
||||
users = crud.get_users(db, skip=skip, limit=limit)
|
||||
return users
|
||||
|
||||
|
||||
@app.get("/users/{user_id}", response_model=schemas.User)
|
||||
def read_user(user_id: int, db: Session = Depends(get_db)):
|
||||
db_user = crud.get_user(db, user_id=user_id)
|
||||
if db_user is None:
|
||||
raise HTTPException(status_code=404, detail="User not found")
|
||||
return db_user
|
||||
|
||||
|
||||
@app.post("/users/{user_id}/items/", response_model=schemas.Item)
|
||||
def create_item_for_user(
|
||||
user_id: int, item: schemas.ItemCreate, db: Session = Depends(get_db)
|
||||
):
|
||||
return crud.create_user_item(db=db, item=item, user_id=user_id)
|
||||
|
||||
|
||||
@app.get("/items/", response_model=List[schemas.Item])
|
||||
def read_items(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
|
||||
items = crud.get_items(db, skip=skip, limit=limit)
|
||||
return items
|
||||
26
docs/src/sql_databases/sql_app/models.py
Normal file
26
docs/src/sql_databases/sql_app/models.py
Normal file
@@ -0,0 +1,26 @@
|
||||
from sqlalchemy import Boolean, Column, ForeignKey, Integer, String
|
||||
from sqlalchemy.orm import relationship
|
||||
|
||||
from .database import Base
|
||||
|
||||
|
||||
class User(Base):
|
||||
__tablename__ = "users"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
email = Column(String, unique=True, index=True)
|
||||
hashed_password = Column(String)
|
||||
is_active = Column(Boolean, default=True)
|
||||
|
||||
items = relationship("Item", back_populates="owner")
|
||||
|
||||
|
||||
class Item(Base):
|
||||
__tablename__ = "items"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
title = Column(String, index=True)
|
||||
description = Column(String, index=True)
|
||||
owner_id = Column(Integer, ForeignKey("users.id"))
|
||||
|
||||
owner = relationship("User", back_populates="items")
|
||||
37
docs/src/sql_databases/sql_app/schemas.py
Normal file
37
docs/src/sql_databases/sql_app/schemas.py
Normal file
@@ -0,0 +1,37 @@
|
||||
from typing import List
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class ItemBase(BaseModel):
|
||||
title: str
|
||||
description: str = None
|
||||
|
||||
|
||||
class ItemCreate(ItemBase):
|
||||
pass
|
||||
|
||||
|
||||
class Item(ItemBase):
|
||||
id: int
|
||||
owner_id: int
|
||||
|
||||
class Config:
|
||||
orm_mode = True
|
||||
|
||||
|
||||
class UserBase(BaseModel):
|
||||
email: str
|
||||
|
||||
|
||||
class UserCreate(UserBase):
|
||||
password: str
|
||||
|
||||
|
||||
class User(UserBase):
|
||||
id: int
|
||||
is_active: bool
|
||||
items: List[Item] = []
|
||||
|
||||
class Config:
|
||||
orm_mode = True
|
||||
@@ -1,76 +0,0 @@
|
||||
from fastapi import Depends, FastAPI
|
||||
from sqlalchemy import Boolean, Column, Integer, String, create_engine
|
||||
from sqlalchemy.ext.declarative import declarative_base, declared_attr
|
||||
from sqlalchemy.orm import Session, sessionmaker
|
||||
from starlette.requests import Request
|
||||
from starlette.responses import Response
|
||||
|
||||
# SQLAlchemy specific code, as with any other app
|
||||
SQLALCHEMY_DATABASE_URI = "sqlite:///./test.db"
|
||||
# SQLALCHEMY_DATABASE_URI = "postgresql://user:password@postgresserver/db"
|
||||
|
||||
engine = create_engine(
|
||||
SQLALCHEMY_DATABASE_URI, connect_args={"check_same_thread": False}
|
||||
)
|
||||
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
||||
|
||||
|
||||
class CustomBase:
|
||||
# Generate __tablename__ automatically
|
||||
@declared_attr
|
||||
def __tablename__(cls):
|
||||
return cls.__name__.lower()
|
||||
|
||||
|
||||
Base = declarative_base(cls=CustomBase)
|
||||
|
||||
|
||||
class User(Base):
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
email = Column(String, unique=True, index=True)
|
||||
hashed_password = Column(String)
|
||||
is_active = Column(Boolean(), default=True)
|
||||
|
||||
|
||||
Base.metadata.create_all(bind=engine)
|
||||
|
||||
db_session = SessionLocal()
|
||||
|
||||
first_user = db_session.query(User).first()
|
||||
if not first_user:
|
||||
u = User(email="johndoe@example.com", hashed_password="notreallyhashed")
|
||||
db_session.add(u)
|
||||
db_session.commit()
|
||||
|
||||
db_session.close()
|
||||
|
||||
|
||||
# Utility
|
||||
def get_user(db_session: Session, user_id: int):
|
||||
return db_session.query(User).filter(User.id == user_id).first()
|
||||
|
||||
|
||||
# Dependency
|
||||
def get_db(request: Request):
|
||||
return request.state.db
|
||||
|
||||
|
||||
# FastAPI specific code
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/users/{user_id}")
|
||||
def read_user(user_id: int, db: Session = Depends(get_db)):
|
||||
user = get_user(db, user_id=user_id)
|
||||
return user
|
||||
|
||||
|
||||
@app.middleware("http")
|
||||
async def db_session_middleware(request: Request, call_next):
|
||||
response = Response("Internal server error", status_code=500)
|
||||
try:
|
||||
request.state.db = SessionLocal()
|
||||
response = await call_next(request)
|
||||
finally:
|
||||
request.state.db.close()
|
||||
return response
|
||||
0
docs/src/sql_databases_peewee/__init__.py
Normal file
0
docs/src/sql_databases_peewee/__init__.py
Normal file
0
docs/src/sql_databases_peewee/sql_app/__init__.py
Normal file
0
docs/src/sql_databases_peewee/sql_app/__init__.py
Normal file
30
docs/src/sql_databases_peewee/sql_app/crud.py
Normal file
30
docs/src/sql_databases_peewee/sql_app/crud.py
Normal file
@@ -0,0 +1,30 @@
|
||||
from . import models, schemas
|
||||
|
||||
|
||||
def get_user(user_id: int):
|
||||
return models.User.filter(models.User.id == user_id).first()
|
||||
|
||||
|
||||
def get_user_by_email(email: str):
|
||||
return models.User.filter(models.User.email == email).first()
|
||||
|
||||
|
||||
def get_users(skip: int = 0, limit: int = 100):
|
||||
return list(models.User.select().offset(skip).limit(limit))
|
||||
|
||||
|
||||
def create_user(user: schemas.UserCreate):
|
||||
fake_hashed_password = user.password + "notreallyhashed"
|
||||
db_user = models.User(email=user.email, hashed_password=fake_hashed_password)
|
||||
db_user.save()
|
||||
return db_user
|
||||
|
||||
|
||||
def get_items(skip: int = 0, limit: int = 100):
|
||||
return list(models.Item.select().offset(skip).limit(limit))
|
||||
|
||||
|
||||
def create_user_item(item: schemas.ItemCreate, user_id: int):
|
||||
db_item = models.Item(**item.dict(), owner_id=user_id)
|
||||
db_item.save()
|
||||
return db_item
|
||||
26
docs/src/sql_databases_peewee/sql_app/database.py
Normal file
26
docs/src/sql_databases_peewee/sql_app/database.py
Normal file
@@ -0,0 +1,26 @@
|
||||
from contextvars import ContextVar
|
||||
|
||||
import peewee
|
||||
|
||||
DATABASE_NAME = "test.db"
|
||||
|
||||
|
||||
class PeeweeConnectionState(peewee._ConnectionState):
|
||||
def __init__(self, **kwargs):
|
||||
super().__setattr__("_state", {})
|
||||
self._state["closed"] = ContextVar("closed", default=True)
|
||||
self._state["conn"] = ContextVar("conn", default=None)
|
||||
self._state["ctx"] = ContextVar("ctx", default=[])
|
||||
self._state["transactions"] = ContextVar("transactions", default=[])
|
||||
super().__init__(**kwargs)
|
||||
|
||||
def __setattr__(self, name, value):
|
||||
self._state[name].set(value)
|
||||
|
||||
def __getattr__(self, name):
|
||||
return self._state[name].get()
|
||||
|
||||
|
||||
db = peewee.SqliteDatabase(DATABASE_NAME, check_same_thread=False)
|
||||
|
||||
db._state = PeeweeConnectionState()
|
||||
70
docs/src/sql_databases_peewee/sql_app/main.py
Normal file
70
docs/src/sql_databases_peewee/sql_app/main.py
Normal file
@@ -0,0 +1,70 @@
|
||||
import time
|
||||
from typing import List
|
||||
|
||||
from fastapi import Depends, FastAPI, HTTPException
|
||||
|
||||
from . import crud, database, models, schemas
|
||||
|
||||
database.db.connect()
|
||||
database.db.create_tables([models.User, models.Item])
|
||||
database.db.close()
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
# Dependency
|
||||
def get_db():
|
||||
try:
|
||||
database.db.connect()
|
||||
yield
|
||||
finally:
|
||||
if not database.db.is_closed():
|
||||
database.db.close()
|
||||
|
||||
|
||||
@app.post("/users/", response_model=schemas.User, dependencies=[Depends(get_db)])
|
||||
def create_user(user: schemas.UserCreate):
|
||||
db_user = crud.get_user_by_email(email=user.email)
|
||||
if db_user:
|
||||
raise HTTPException(status_code=400, detail="Email already registered")
|
||||
return crud.create_user(user=user)
|
||||
|
||||
|
||||
@app.get("/users/", response_model=List[schemas.User], dependencies=[Depends(get_db)])
|
||||
def read_users(skip: int = 0, limit: int = 100):
|
||||
users = crud.get_users(skip=skip, limit=limit)
|
||||
return users
|
||||
|
||||
|
||||
@app.get(
|
||||
"/users/{user_id}", response_model=schemas.User, dependencies=[Depends(get_db)]
|
||||
)
|
||||
def read_user(user_id: int):
|
||||
db_user = crud.get_user(user_id=user_id)
|
||||
if db_user is None:
|
||||
raise HTTPException(status_code=404, detail="User not found")
|
||||
return db_user
|
||||
|
||||
|
||||
@app.post(
|
||||
"/users/{user_id}/items/",
|
||||
response_model=schemas.Item,
|
||||
dependencies=[Depends(get_db)],
|
||||
)
|
||||
def create_item_for_user(user_id: int, item: schemas.ItemCreate):
|
||||
return crud.create_user_item(item=item, user_id=user_id)
|
||||
|
||||
|
||||
@app.get("/items/", response_model=List[schemas.Item], dependencies=[Depends(get_db)])
|
||||
def read_items(skip: int = 0, limit: int = 100):
|
||||
items = crud.get_items(skip=skip, limit=limit)
|
||||
return items
|
||||
|
||||
|
||||
@app.get(
|
||||
"/slowusers/", response_model=List[schemas.User], dependencies=[Depends(get_db)]
|
||||
)
|
||||
def read_slow_users(skip: int = 0, limit: int = 100):
|
||||
time.sleep(15) # Fake long processing request
|
||||
users = crud.get_users(skip=skip, limit=limit)
|
||||
return users
|
||||
21
docs/src/sql_databases_peewee/sql_app/models.py
Normal file
21
docs/src/sql_databases_peewee/sql_app/models.py
Normal file
@@ -0,0 +1,21 @@
|
||||
import peewee
|
||||
|
||||
from .database import db
|
||||
|
||||
|
||||
class User(peewee.Model):
|
||||
email = peewee.CharField(unique=True, index=True)
|
||||
hashed_password = peewee.CharField()
|
||||
is_active = peewee.BooleanField(default=True)
|
||||
|
||||
class Meta:
|
||||
database = db
|
||||
|
||||
|
||||
class Item(peewee.Model):
|
||||
title = peewee.CharField(index=True)
|
||||
description = peewee.CharField(index=True)
|
||||
owner = peewee.ForeignKeyField(User, backref="items")
|
||||
|
||||
class Meta:
|
||||
database = db
|
||||
49
docs/src/sql_databases_peewee/sql_app/schemas.py
Normal file
49
docs/src/sql_databases_peewee/sql_app/schemas.py
Normal file
@@ -0,0 +1,49 @@
|
||||
from typing import Any, List
|
||||
|
||||
import peewee
|
||||
from pydantic import BaseModel
|
||||
from pydantic.utils import GetterDict
|
||||
|
||||
|
||||
class PeeweeGetterDict(GetterDict):
|
||||
def get(self, key: Any, default: Any = None):
|
||||
res = getattr(self._obj, key, default)
|
||||
if isinstance(res, peewee.ModelSelect):
|
||||
return list(res)
|
||||
return res
|
||||
|
||||
|
||||
class ItemBase(BaseModel):
|
||||
title: str
|
||||
description: str = None
|
||||
|
||||
|
||||
class ItemCreate(ItemBase):
|
||||
pass
|
||||
|
||||
|
||||
class Item(ItemBase):
|
||||
id: int
|
||||
owner_id: int
|
||||
|
||||
class Config:
|
||||
orm_mode = True
|
||||
getter_dict = PeeweeGetterDict
|
||||
|
||||
|
||||
class UserBase(BaseModel):
|
||||
email: str
|
||||
|
||||
|
||||
class UserCreate(UserBase):
|
||||
password: str
|
||||
|
||||
|
||||
class User(UserBase):
|
||||
id: int
|
||||
is_active: bool
|
||||
items: List[Item] = []
|
||||
|
||||
class Config:
|
||||
orm_mode = True
|
||||
getter_dict = PeeweeGetterDict
|
||||
@@ -174,6 +174,11 @@ For example, you can add an additional media type of `image/png`, declaring that
|
||||
!!! note
|
||||
Notice that you have to return the image using a `FileResponse` directly.
|
||||
|
||||
!!! info
|
||||
Unless you specify a different media type explicitly in your `responses` parameter, FastAPI will assume the response has the same media type as the main response class (default `application/json`).
|
||||
|
||||
But if you have specified a custom response class with `None` as its media type, FastAPI will use `application/json` for any additional response that has an associated model.
|
||||
|
||||
## Combining information
|
||||
|
||||
You can also combine response information from multiple places, including the `response_model`, `status_code`, and `responses` parameters.
|
||||
|
||||
@@ -41,7 +41,7 @@ Later, for your production application, you might want to use a database server
|
||||
```
|
||||
|
||||
!!! tip
|
||||
If you where connecting to a different database (e.g. PostgreSQL), you would need to change the `DATABASE_URL`.
|
||||
If you were connecting to a different database (e.g. PostgreSQL), you would need to change the `DATABASE_URL`.
|
||||
|
||||
## Create the tables
|
||||
|
||||
|
||||
@@ -174,7 +174,6 @@ from app.routers import items, users
|
||||
|
||||
To learn more about Python Packages and Modules, read <a href="https://docs.python.org/3/tutorial/modules.html" target="_blank">the official Python documentation about Modules</a>.
|
||||
|
||||
|
||||
### Avoid name collisions
|
||||
|
||||
We are importing the submodule `items` directly, instead of importing just its variable `router`.
|
||||
@@ -216,7 +215,6 @@ It will include all the routes from that router as part of it.
|
||||
|
||||
So, behind the scenes, it will actually work as if everything was the same single app.
|
||||
|
||||
|
||||
!!! check
|
||||
You don't have to worry about performance when including routers.
|
||||
|
||||
@@ -248,7 +246,7 @@ We can also add a list of `tags` that will be applied to all the *path operation
|
||||
|
||||
And we can add predefined `responses` that will be included in all the *path operations* too.
|
||||
|
||||
And we can add a list of `dependencies` that will be added to all the *path operations* in the router and will be executed/solved for each request made to them.
|
||||
And we can add a list of `dependencies` that will be added to all the *path operations* in the router and will be executed/solved for each request made to them. Note that, much like dependencies in *path operation decorators*, no value will be passed to your *path operation function*.
|
||||
|
||||
```Python hl_lines="8 9 10 14 15 16 17 18 19 20"
|
||||
{!./src/bigger_applications/app/main.py!}
|
||||
@@ -295,7 +293,6 @@ The end result is that the item paths are now:
|
||||
|
||||
As we cannot just isolate them and "mount" them independently of the rest, the path operations are "cloned" (re-created), not included directly.
|
||||
|
||||
|
||||
## Check the automatic API docs
|
||||
|
||||
Now, run `uvicorn`, using the module `app.main` and the variable `app`:
|
||||
@@ -309,3 +306,11 @@ And open the docs at <a href="http://127.0.0.1:8000/docs" target="_blank">http:/
|
||||
You will see the automatic API docs, including the paths from all the submodules, using the correct paths (and prefixes) and the correct tags:
|
||||
|
||||
<img src="/img/tutorial/bigger-applications/image01.png">
|
||||
|
||||
## Include the same router multiple times with different `prefix`
|
||||
|
||||
You can also use `.include_router()` multiple times with the *same* router using different prefixes.
|
||||
|
||||
This could be useful, for example, to expose the same API under different prefixes, e.g. `/api/v1` and `/api/latest`.
|
||||
|
||||
This is an advanced usage that you might not really need, but it's there in case you do.
|
||||
|
||||
62
docs/tutorial/body-fields.md
Normal file
62
docs/tutorial/body-fields.md
Normal file
@@ -0,0 +1,62 @@
|
||||
The same way you can declare additional validation and metadata in path operation function parameters with `Query`, `Path` and `Body`, you can declare validation and metadata inside of Pydantic models using Pydantic's `Field`.
|
||||
|
||||
## Import `Field`
|
||||
|
||||
First, you have to import it:
|
||||
|
||||
```Python hl_lines="2"
|
||||
{!./src/body_fields/tutorial001.py!}
|
||||
```
|
||||
|
||||
!!! warning
|
||||
Notice that `Field` is imported directly from `pydantic`, not from `fastapi` as are all the rest (`Query`, `Path`, `Body`, etc).
|
||||
|
||||
|
||||
## Declare model attributes
|
||||
|
||||
You can then use `Field` with model attributes:
|
||||
|
||||
```Python hl_lines="9 10"
|
||||
{!./src/body_fields/tutorial001.py!}
|
||||
```
|
||||
|
||||
`Field` works the same way as `Query`, `Path` and `Body`, it has all the same parameters, etc.
|
||||
|
||||
!!! note "Technical Details"
|
||||
Actually, `Query`, `Path` and others you'll see next create objects of subclasses of a common `Param` class, which is itself a subclass of Pydantic's `FieldInfo` class.
|
||||
|
||||
And Pydantic's `Field` returns an instance of `FieldInfo` as well.
|
||||
|
||||
`Body` also returns objects of a subclass of `FieldInfo` directly. And there are others you will see later that are subclasses of the `Body` class.
|
||||
|
||||
Remember that when you import `Query`, `Path`, and others from `fastapi`, <a href="https://fastapi.tiangolo.com/tutorial/path-params-numeric-validations/#recap" target="_blank">those are actually functions that return classes of the same name</a>.
|
||||
|
||||
!!! tip
|
||||
Notice how each model's attribute with a type, default value and `Field` has the same structure as a path operation function's parameter, with `Field` instead of `Path`, `Query` and `Body`.
|
||||
|
||||
## JSON Schema extras
|
||||
|
||||
In `Field`, `Path`, `Query`, `Body` and others you'll see later, you can declare extra parameters apart from those described before.
|
||||
|
||||
Those parameters will be added as-is to the output JSON Schema.
|
||||
|
||||
If you know JSON Schema and want to add extra information apart from what we have discussed here, you can pass that as extra keyword arguments.
|
||||
|
||||
!!! warning
|
||||
Have in mind that extra parameters passed won't add any validation, only annotation, for documentation purposes.
|
||||
|
||||
For example, you can use that functionality to pass a <a href="http://json-schema.org/latest/json-schema-validation.html#rfc.section.8.5" target="_blank">JSON Schema example</a> field to a body request JSON Schema:
|
||||
|
||||
```Python hl_lines="20 21 22 23 24 25"
|
||||
{!./src/body_fields/tutorial002.py!}
|
||||
```
|
||||
|
||||
And it would look in the `/docs` like this:
|
||||
|
||||
<img src="/img/tutorial/body-fields/image01.png">
|
||||
|
||||
## Recap
|
||||
|
||||
You can use Pydantic's `Field` to declare extra validations and metadata for model attributes.
|
||||
|
||||
You can also use the extra keyword arguments to pass additional JSON Schema metadata.
|
||||
@@ -118,7 +118,7 @@ Apart from normal singular types like `str`, `int`, `float`, etc. You can use mo
|
||||
|
||||
To see all the options you have, checkout the docs for <a href="https://pydantic-docs.helpmanual.io/#exotic-types" target="_blank">Pydantic's exotic types</a>. You will see some examples in the next chapter.
|
||||
|
||||
For example, as in the `Image` model we have a `url` field, we can declare it to be instead of a `str`, a Pydantic's `UrlStr`:
|
||||
For example, as in the `Image` model we have a `url` field, we can declare it to be instead of a `str`, a Pydantic's `HttpUrl`:
|
||||
|
||||
```Python hl_lines="4 10"
|
||||
{!./src/body_nested_models/tutorial005.py!}
|
||||
@@ -200,6 +200,35 @@ You couldn't get this kind of editor support if you where working directly with
|
||||
|
||||
But you don't have to worry about them either, incoming dicts are converted automatically and your output is converted automatically to JSON too.
|
||||
|
||||
## Bodies of arbitrary `dict`s
|
||||
|
||||
You can also declare a body as a `dict` with keys of some type and values of other type.
|
||||
|
||||
Without having to know beforehand what are the valid field/attribute names (as would be the case with Pydantic models).
|
||||
|
||||
This would be useful if you want to receive keys that you don't already know.
|
||||
|
||||
---
|
||||
|
||||
Other useful case is when you want to have keys of other type, e.g. `int`.
|
||||
|
||||
That's what we are going to see here.
|
||||
|
||||
In this case, you would accept any `dict` as long as it has `int` keys with `float` values:
|
||||
|
||||
```Python hl_lines="15"
|
||||
{!./src/body_nested_models/tutorial009.py!}
|
||||
```
|
||||
|
||||
!!! tip
|
||||
Have in mind that JSON only supports `str` as keys.
|
||||
|
||||
But Pydantic has automatic data conversion.
|
||||
|
||||
This means that, even though your API clients can only send strings as keys, as long as those strings contain pure integers, Pydantic will convert them and validate them.
|
||||
|
||||
And the `dict` you receive as `weights` will actually have `int` keys and `float` values.
|
||||
|
||||
## Recap
|
||||
|
||||
With **FastAPI** you have the maximum flexibility provided by Pydantic models, while keeping your code simple, short and elegant.
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
The same way you can declare additional validation and metadata in path operation function parameters with `Query`, `Path` and `Body`, you can declare validation and metadata inside of Pydantic models using `Schema`.
|
||||
|
||||
## Import Schema
|
||||
|
||||
First, you have to import it:
|
||||
|
||||
```Python hl_lines="2"
|
||||
{!./src/body_schema/tutorial001.py!}
|
||||
```
|
||||
|
||||
!!! warning
|
||||
Notice that `Schema` is imported directly from `pydantic`, not from `fastapi` as are all the rest (`Query`, `Path`, `Body`, etc).
|
||||
|
||||
|
||||
## Declare model attributes
|
||||
|
||||
You can then use `Schema` with model attributes:
|
||||
|
||||
```Python hl_lines="9 10"
|
||||
{!./src/body_schema/tutorial001.py!}
|
||||
```
|
||||
|
||||
`Schema` works the same way as `Query`, `Path` and `Body`, it has all the same parameters, etc.
|
||||
|
||||
!!! note "Technical Details"
|
||||
Actually, `Query`, `Path` and others you'll see next are subclasses of a common `Param` which is itself a subclass of Pydantic's `Schema`.
|
||||
|
||||
`Body` is also a subclass of `Schema` directly. And there are others you will see later that are subclasses of `Body`.
|
||||
|
||||
But remember that when you import `Query`, `Path` and others from `fastapi`, <a href="https://fastapi.tiangolo.com/tutorial/path-params-numeric-validations/#recap" target="_blank">those are actually functions that return classes of the same name</a>.
|
||||
|
||||
!!! tip
|
||||
Notice how each model's attribute with a type, default value and `Schema` has the same structure as a path operation function's parameter, with `Schema` instead of `Path`, `Query` and `Body`.
|
||||
|
||||
## Schema extras
|
||||
|
||||
In `Schema`, `Path`, `Query`, `Body` and others you'll see later, you can declare extra parameters apart from those described before.
|
||||
|
||||
Those parameters will be added as-is to the output JSON Schema.
|
||||
|
||||
If you know JSON Schema and want to add extra information apart from what we have discussed here, you can pass that as extra keyword arguments.
|
||||
|
||||
!!! warning
|
||||
Have in mind that extra parameters passed won't add any validation, only annotation, for documentation purposes.
|
||||
|
||||
For example, you can use that functionality to pass a <a href="http://json-schema.org/latest/json-schema-validation.html#rfc.section.8.5" target="_blank">JSON Schema example</a> field to a body request JSON Schema:
|
||||
|
||||
```Python hl_lines="20 21 22 23 24 25"
|
||||
{!./src/body_schema/tutorial002.py!}
|
||||
```
|
||||
|
||||
And it would look in the `/docs` like this:
|
||||
|
||||
<img src="/img/tutorial/body-schema/image01.png">
|
||||
|
||||
## Recap
|
||||
|
||||
You can use Pydantic's `Schema` to declare extra validations and metadata for model attributes.
|
||||
|
||||
You can also use the extra keyword arguments to pass additional JSON Schema metadata.
|
||||
@@ -41,15 +41,15 @@ This means that you can send only the data that you want to update, leaving the
|
||||
|
||||
But this guide shows you, more or less, how they are intended to be used.
|
||||
|
||||
### Using Pydantic's `skip_defaults` parameter
|
||||
### Using Pydantic's `exclude_unset` parameter
|
||||
|
||||
If you want to receive partial updates, it's very useful to use the parameter `skip_defaults` in Pydantic's model's `.dict()`.
|
||||
If you want to receive partial updates, it's very useful to use the parameter `exclude_unset` in Pydantic's model's `.dict()`.
|
||||
|
||||
Like `item.dict(skip_defaults=True)`.
|
||||
Like `item.dict(exclude_unset=True)`.
|
||||
|
||||
That would generate a `dict` with only the data that was set when creating the `item` model, excluding default values.
|
||||
|
||||
Then you can use this to generate a `dict` with only the data that was set, omitting default values:
|
||||
Then you can use this to generate a `dict` with only the data that was set (sent in the request), omitting default values:
|
||||
|
||||
```Python hl_lines="34"
|
||||
{!./src/body_updates/tutorial002.py!}
|
||||
@@ -72,7 +72,7 @@ In summary, to apply partial updates you would:
|
||||
* (Optionally) use `PATCH` instead of `PUT`.
|
||||
* Retrieve the stored data.
|
||||
* Put that data in a Pydantic model.
|
||||
* Generate a `dict` without default values from the input model (using `skip_defaults`).
|
||||
* 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).
|
||||
* Convert the copied model to something that can be stored in your DB (for example, using the `jsonable_encoder`).
|
||||
|
||||
100
docs/tutorial/custom-request-and-route.md
Normal file
100
docs/tutorial/custom-request-and-route.md
Normal file
@@ -0,0 +1,100 @@
|
||||
In some cases, you may want to override the logic used by the `Request` and `APIRoute` classes.
|
||||
|
||||
In particular, this may be a good alternative to logic in a middleware.
|
||||
|
||||
For example, if you want to read or manipulate the request body before it is processed by your application.
|
||||
|
||||
!!! danger
|
||||
This is an "advanced" feature.
|
||||
|
||||
If you are just starting with **FastAPI** you might want to skip this section.
|
||||
|
||||
## Use cases
|
||||
|
||||
Some use cases include:
|
||||
|
||||
* Converting non-JSON request bodies to JSON (e.g. [`msgpack`](https://msgpack.org/index.html)).
|
||||
* Decompressing gzip-compressed request bodies.
|
||||
* Automatically logging all request bodies.
|
||||
* Accessing the request body in an exception handler.
|
||||
|
||||
## Handling custom request body encodings
|
||||
|
||||
Let's see how to make use of a custom `Request` subclass to decompress gzip requests.
|
||||
|
||||
And an `APIRoute` subclass to use that custom request class.
|
||||
|
||||
### Create a custom `GzipRequest` class
|
||||
|
||||
First, we create a `GzipRequest` class, which will overwrite the `Request.body()` method to decompress the body in the presence of an appropriate header.
|
||||
|
||||
If there's no `gzip` in the header, it will not try to decompress the body.
|
||||
|
||||
That way, the same route class can handle gzip compressed or uncompressed requests.
|
||||
|
||||
```Python hl_lines="10 11 12 13 14 15 16 17"
|
||||
{!./src/custom_request_and_route/tutorial001.py!}
|
||||
```
|
||||
|
||||
### Create a custom `GzipRoute` class
|
||||
|
||||
Next, we create a custom subclass of `fastapi.routing.APIRoute` that will make use of the `GzipRequest`.
|
||||
|
||||
This time, it will overwrite the method `APIRoute.get_route_handler()`.
|
||||
|
||||
This method returns a function. And that function is what will receive a request and return a response.
|
||||
|
||||
Here we use it to create a `GzipRequest` from the original request.
|
||||
|
||||
```Python hl_lines="20 21 22 23 24 25 26 27 28"
|
||||
{!./src/custom_request_and_route/tutorial001.py!}
|
||||
```
|
||||
|
||||
!!! note "Technical Details"
|
||||
A `Request` has a `request.scope` attribute, that's just a Python `dict` containing the metadata related to the request.
|
||||
|
||||
A `Request` also has a `request.receive`, that's a function to "receive" the body of the request.
|
||||
|
||||
The `scope` `dict` and `receive` function are both part of the ASGI specification.
|
||||
|
||||
And those two things, `scope` and `receive`, are what is needed to create a new `Request` instance.
|
||||
|
||||
To learn more about the `Request` check <a href="https://www.starlette.io/requests/" target="_blank">Starlette's docs about Requests</a>.
|
||||
|
||||
The only thing the function returned by `GzipRequest.get_route_handler` does differently is convert the `Request` to a `GzipRequest`.
|
||||
|
||||
Doing this, our `GzipRequest` will take care of decompressing the data (if necessary) before passing it to our *path operations*.
|
||||
|
||||
After that, all of the processing logic is the same.
|
||||
|
||||
But because of our changes in `GzipRequest.body`, the request body will be automatically decompressed when it is loaded by **FastAPI** when needed.
|
||||
|
||||
## Accessing the request body in an exception handler
|
||||
|
||||
We can also use this same approach to access the request body in an exception handler.
|
||||
|
||||
All we need to do is handle the request inside a `try`/`except` block:
|
||||
|
||||
```Python hl_lines="15 17"
|
||||
{!./src/custom_request_and_route/tutorial002.py!}
|
||||
```
|
||||
|
||||
If an exception occurs, the`Request` instance will still be in scope, so we can read and make use of the request body when handling the error:
|
||||
|
||||
```Python hl_lines="18 19 20"
|
||||
{!./src/custom_request_and_route/tutorial002.py!}
|
||||
```
|
||||
|
||||
## Custom `APIRoute` class in a router
|
||||
|
||||
You can also set the `route_class` parameter of an `APIRouter`:
|
||||
|
||||
```Python hl_lines="25"
|
||||
{!./src/custom_request_and_route/tutorial003.py!}
|
||||
```
|
||||
|
||||
In this example, the *path operations* under the `router` will use the custom `TimedRoute` class, and will have an extra `X-Response-Time` header in the response with the time it took to generate the response:
|
||||
|
||||
```Python hl_lines="15 16 17 18 19"
|
||||
{!./src/custom_request_and_route/tutorial003.py!}
|
||||
```
|
||||
@@ -15,6 +15,9 @@ The contents that you return from your *path operation function* will be put ins
|
||||
|
||||
And if that `Response` has a JSON media type (`application/json`), like is the case with the `JSONResponse` and `UJSONResponse`, the data you return will be automatically converted (and filtered) with any Pydantic `response_model` that you declared in the *path operation decorator*.
|
||||
|
||||
!!! note
|
||||
If you use a response class with no media type, FastAPI will expect your response to have no content, so it will not document the response format in its generated OpenAPI docs.
|
||||
|
||||
## Use `UJSONResponse`
|
||||
|
||||
For example, if you are squeezing performance, you can install and use `ujson` and set the response to be Starlette's `UJSONResponse`.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
!!! danger
|
||||
!!! warning
|
||||
This is, more or less, an "advanced" chapter.
|
||||
|
||||
If you are just starting with **FastAPI** you might want to skip this chapter and come back to it later.
|
||||
@@ -22,7 +22,7 @@ Not the class itself (which is already a callable), but an instance of that clas
|
||||
To do that, we declare a method `__call__`:
|
||||
|
||||
```Python hl_lines="10"
|
||||
{!./src/dependencies/tutorial007.py!}
|
||||
{!./src/dependencies/tutorial011.py!}
|
||||
```
|
||||
|
||||
In this case, this `__call__` is what **FastAPI** will use to check for additional parameters and sub-dependencies, and this is what will be called to pass a value to the parameter in your *path operation function* later.
|
||||
@@ -32,7 +32,7 @@ In this case, this `__call__` is what **FastAPI** will use to check for addition
|
||||
And now, we can use `__init__` to declare the parameters of the instance that we can use to "parameterize" the dependency:
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!./src/dependencies/tutorial007.py!}
|
||||
{!./src/dependencies/tutorial011.py!}
|
||||
```
|
||||
|
||||
In this case, **FastAPI** won't ever touch or care about `__init__`, we will use it directly in our code.
|
||||
@@ -42,7 +42,7 @@ In this case, **FastAPI** won't ever touch or care about `__init__`, we will use
|
||||
We could create an instance of this class with:
|
||||
|
||||
```Python hl_lines="16"
|
||||
{!./src/dependencies/tutorial007.py!}
|
||||
{!./src/dependencies/tutorial011.py!}
|
||||
```
|
||||
|
||||
And that way we are able to "parameterize" our dependency, that now has `"bar"` inside of it, as the attribute `checker.fixed_content`.
|
||||
@@ -60,7 +60,7 @@ checker(q="somequery")
|
||||
...and pass whatever that returns as the value of the dependency in our path operation function as the parameter `fixed_content_included`:
|
||||
|
||||
```Python hl_lines="20"
|
||||
{!./src/dependencies/tutorial007.py!}
|
||||
{!./src/dependencies/tutorial011.py!}
|
||||
```
|
||||
|
||||
!!! tip
|
||||
|
||||
@@ -2,7 +2,7 @@ Before diving deeper into the **Dependency Injection** system, let's upgrade the
|
||||
|
||||
## A `dict` from the previous example
|
||||
|
||||
In the previous example, we where returning a `dict` from our dependency ("dependable"):
|
||||
In the previous example, we are returning a `dict` from our dependency ("dependable"):
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!./src/dependencies/tutorial001.py!}
|
||||
|
||||
153
docs/tutorial/dependencies/dependencies-with-yield.md
Normal file
153
docs/tutorial/dependencies/dependencies-with-yield.md
Normal file
@@ -0,0 +1,153 @@
|
||||
# Dependencies with `yield`
|
||||
|
||||
FastAPI supports dependencies that do some <abbr title='sometimes also called "exit", "cleanup", "teardown", "close", "context managers", ...'>extra steps after finishing</abbr>.
|
||||
|
||||
To do this, use `yield` instead of `return`, and write the extra steps after.
|
||||
|
||||
!!! tip
|
||||
Make sure to use `yield` one single time.
|
||||
|
||||
!!! info
|
||||
For this to work, you need to use **Python 3.7** or above, or in **Python 3.6**, install the "backports":
|
||||
|
||||
```bash
|
||||
pip install async-exit-stack async-generator
|
||||
```
|
||||
|
||||
This installs <a href="https://github.com/sorcio/async_exit_stack" target="_blank">async-exit-stack</a> and <a href="https://github.com/python-trio/async_generator" target="_blank">async-generator</a>.
|
||||
|
||||
!!! note "Technical Details"
|
||||
Any function that is valid to use with:
|
||||
|
||||
* <a href="https://docs.python.org/3/library/contextlib.html#contextlib.contextmanager" target="_blank">`@contextlib.contextmanager`</a> or
|
||||
* <a href="https://docs.python.org/3/library/contextlib.html#contextlib.asynccontextmanager" target="_blank">`@contextlib.asynccontextmanager`</a>
|
||||
|
||||
would be valid to use as a **FastAPI** dependency.
|
||||
|
||||
In fact, FastAPI uses those two decorators internally.
|
||||
|
||||
## A database dependency with `yield`
|
||||
|
||||
For example, you could use this to create a database session and close it after finishing.
|
||||
|
||||
Only the code prior to and including the `yield` statement is executed before sending a response:
|
||||
|
||||
```Python hl_lines="2 3 4"
|
||||
{!./src/dependencies/tutorial007.py!}
|
||||
```
|
||||
|
||||
The yielded value is what is injected into *path operations* and other dependencies:
|
||||
|
||||
```Python hl_lines="4"
|
||||
{!./src/dependencies/tutorial007.py!}
|
||||
```
|
||||
|
||||
The code following the `yield` statement is executed after the response has been delivered:
|
||||
|
||||
```Python hl_lines="5 6"
|
||||
{!./src/dependencies/tutorial007.py!}
|
||||
```
|
||||
|
||||
!!! tip
|
||||
You can use `async` or normal functions.
|
||||
|
||||
**FastAPI** will do the right thing with each, the same as with normal dependencies.
|
||||
|
||||
## A dependency with `yield` and `try`
|
||||
|
||||
If you use a `try` block in a dependency with `yield`, you'll receive any exception that was thrown when using the dependency.
|
||||
|
||||
For example, if some code at some point in the middle, in another dependency or in a *path operation*, made a database transaction "rollback" or create any other error, you will receive the exception in your dependency.
|
||||
|
||||
So, you can look for that specific exception inside the dependency with `except SomeException`.
|
||||
|
||||
In the same way, you can use `finally` to make sure the exit steps are executed, no matter if there was an exception or not.
|
||||
|
||||
```Python hl_lines="3 5"
|
||||
{!./src/dependencies/tutorial007.py!}
|
||||
```
|
||||
|
||||
## Sub-dependencies with `yield`
|
||||
|
||||
You can have sub-dependencies and "trees" of sub-dependencies of any size and shape, and any or all of them can use `yield`.
|
||||
|
||||
**FastAPI** will make sure that the "exit code" in each dependency with `yield` is run in the correct order.
|
||||
|
||||
For example, `dependency_c` can have a dependency on `dependency_b`, and `dependency_b` on `dependency_a`:
|
||||
|
||||
```Python hl_lines="4 12 20"
|
||||
{!./src/dependencies/tutorial008.py!}
|
||||
```
|
||||
|
||||
And all of them can use `yield`.
|
||||
|
||||
In this case `dependency_c`, to execute its exit code, needs the value from `dependency_b` (here named `dep_b`) to still be available.
|
||||
|
||||
And, in turn, `dependency_b` needs the value from `dependency_a` (here named `dep_a`) to be available for its exit code.
|
||||
|
||||
```Python hl_lines="16 17 24 25"
|
||||
{!./src/dependencies/tutorial008.py!}
|
||||
```
|
||||
|
||||
The same way, you could have dependencies with `yield` and `return` mixed.
|
||||
|
||||
And you could have a single dependency that requires several other dependencies with `yield`, etc.
|
||||
|
||||
You can have any combinations of dependencies that you want.
|
||||
|
||||
**FastAPI** will make sure everything is run in the correct order.
|
||||
|
||||
!!! note "Technical Details"
|
||||
This works thanks to Python's <a href="https://docs.python.org/3/library/contextlib.html" target="_blank">Context Managers</a>.
|
||||
|
||||
**FastAPI** uses them internally to achieve this.
|
||||
|
||||
## Context Managers
|
||||
|
||||
### What are "Context Managers"
|
||||
|
||||
"Context Managers" are any of those Python objects that you can use in a `with` statement.
|
||||
|
||||
For example, <a href="https://docs.python.org/3/tutorial/inputoutput.html#reading-and-writing-files" target="_blank">you can use `with` to read a file</a>:
|
||||
|
||||
```Python
|
||||
with open("./somefile.txt") as f:
|
||||
contents = f.read()
|
||||
print(contents)
|
||||
```
|
||||
|
||||
Underneath, the `open("./somefile.txt")` returns an object that is a called a "Context Manager".
|
||||
|
||||
When the `with` block finishes, it makes sure to close the file, even if there were exceptions.
|
||||
|
||||
When you create a dependency with `yield`, **FastAPI** will internally convert it to a context manager, and combine it with some other related tools.
|
||||
|
||||
### Using context managers in dependencies with `yield`
|
||||
|
||||
!!! warning
|
||||
This is, more or less, an "advanced" idea.
|
||||
|
||||
If you are just starting with **FastAPI** you might want to skip it for now.
|
||||
|
||||
In Python, you can create context managers by <a href="https://docs.python.org/3/reference/datamodel.html#context-managers" target="_blank">creating a class with two methods: `__enter__()` and `__exit__()`</a>.
|
||||
|
||||
You can also use them with **FastAPI** dependencies with `yield` by using
|
||||
`with` or `async with` statements inside of the dependency function:
|
||||
|
||||
```Python hl_lines="1 2 3 4 5 6 7 8 9 13"
|
||||
{!./src/dependencies/tutorial010.py!}
|
||||
```
|
||||
|
||||
!!! tip
|
||||
Another way to create a context manager is with:
|
||||
|
||||
* <a href="https://docs.python.org/3/library/contextlib.html#contextlib.contextmanager" target="_blank">`@contextlib.contextmanager`</a> or
|
||||
* <a href="https://docs.python.org/3/library/contextlib.html#contextlib.asynccontextmanager" target="_blank">`@contextlib.asynccontextmanager`</a>
|
||||
|
||||
using them to decorate a function with a single `yield`.
|
||||
|
||||
That's what **FastAPI** uses internally for dependencies with `yield`.
|
||||
|
||||
But you don't have to use the decorators for FastAPI dependencies (and you shouldn't).
|
||||
|
||||
FastAPI will do it for you internally.
|
||||
@@ -17,14 +17,12 @@ This is very useful when you need to:
|
||||
|
||||
All these, while minimizing code repetition.
|
||||
|
||||
|
||||
## First Steps
|
||||
|
||||
Let's see a very simple example. It will be so simple that it is not very useful, for now.
|
||||
|
||||
But this way we can focus on how the **Dependency Injection** system works.
|
||||
|
||||
|
||||
### Create a dependency, or "dependable"
|
||||
|
||||
Let's first focus on the dependency.
|
||||
@@ -151,7 +149,6 @@ The simplicity of the dependency injection system makes **FastAPI** compatible w
|
||||
* response data injection systems
|
||||
* etc.
|
||||
|
||||
|
||||
## Simple and Powerful
|
||||
|
||||
Although the hierarchical dependency injection system is very simple to define and use, it's still very powerful.
|
||||
|
||||
@@ -11,6 +11,7 @@ You could create a first dependency ("dependable") like:
|
||||
```Python hl_lines="6 7"
|
||||
{!./src/dependencies/tutorial005.py!}
|
||||
```
|
||||
|
||||
It declares an optional query parameter `q` as a `str`, and then it just returns it.
|
||||
|
||||
This is quite simple (not very useful), but will help us focus on how the sub-dependencies work.
|
||||
@@ -43,6 +44,18 @@ Then we can use the dependency with:
|
||||
|
||||
But **FastAPI** will know that it has to solve `query_extractor` first, to pass the results of that to `query_or_cookie_extractor` while calling it.
|
||||
|
||||
## Using the same dependency multiple times
|
||||
|
||||
If one of your dependencies is declared multiple times for the same *path operation*, for example, multiple dependencies have a common sub-dependency, **FastAPI** will know to call that sub-dependency only once per request.
|
||||
|
||||
And it will save the returned value in a <abbr title="A utility/system to store computed/generated values, to re-use them instead of computing them again.">"cache"</abbr> and pass it to all the "dependants" that need it in that specific request, instead of calling the dependency multiple times for the same request.
|
||||
|
||||
In an advanced scenario where you know you need the dependency to be called at every step (possibly multiple times) in the same request instead of using the "cached" value, you can set the parameter `use_cache=False` when using `Depends`:
|
||||
|
||||
```Python hl_lines="1"
|
||||
async def needy_dependency(fresh_value: str = Depends(get_value, use_cache=False)):
|
||||
return {"fresh_value": fresh_value}
|
||||
```
|
||||
|
||||
## Recap
|
||||
|
||||
@@ -54,7 +67,7 @@ But still, it is very powerful, and allows you to declare arbitrarily deeply nes
|
||||
|
||||
!!! tip
|
||||
All this might not seem as useful with these simple examples.
|
||||
|
||||
|
||||
But you will see how useful it is in the chapters about **security**.
|
||||
|
||||
And you will also see the amounts of code it will save you.
|
||||
|
||||
@@ -88,3 +88,156 @@ Now you can replace the `.openapi()` method with your new function.
|
||||
Once you go to <a href="http://127.0.0.1:8000/redoc" target="_blank">http://127.0.0.1:8000/redoc</a> you will see that you are using your custom logo (in this example, **FastAPI**'s logo):
|
||||
|
||||
<img src="/img/tutorial/extending-openapi/image01.png">
|
||||
|
||||
## Self-hosting JavaScript and CSS for docs
|
||||
|
||||
The API docs use **Swagger UI** and **ReDoc**, and each of those need some JavaScript and CSS files.
|
||||
|
||||
By default, those files are served from a <abbr title="Content Delivery Network: A service, normally composed of several servers, that provides static files, like JavaScript and CSS. It's commonly used to serve those files from the server closer to the client, improving performance.">CDN</abbr>.
|
||||
|
||||
But it's possible to customize it, you can set a specific CDN, or serve the files yourself.
|
||||
|
||||
That's useful, for example, if you need your app to keep working even while offline, without open Internet access, or in a local network.
|
||||
|
||||
Here you'll see how to serve those files yourself, in the same FastAPI app, and configure the docs to use them.
|
||||
|
||||
### Project file structure
|
||||
|
||||
Let's say your project file structure looks like this:
|
||||
|
||||
```
|
||||
.
|
||||
├── app
|
||||
│ ├── __init__.py
|
||||
│ ├── main.py
|
||||
```
|
||||
|
||||
Now create a directory to store those static files.
|
||||
|
||||
Your new file structure could look like this:
|
||||
|
||||
```
|
||||
.
|
||||
├── app
|
||||
│ ├── __init__.py
|
||||
│ ├── main.py
|
||||
└── static/
|
||||
```
|
||||
|
||||
### Download the files
|
||||
|
||||
Download the static files needed for the docs and put them on that `static/` directory.
|
||||
|
||||
You can probably right-click each link and select an option similar to `Save link as...`.
|
||||
|
||||
**Swagger UI** uses the files:
|
||||
|
||||
* <a href="https://cdn.jsdelivr.net/npm/swagger-ui-dist@3/swagger-ui-bundle.js">`swagger-ui-bundle.js`</a>
|
||||
* <a href="https://cdn.jsdelivr.net/npm/swagger-ui-dist@3/swagger-ui.css" target="_blank">`swagger-ui.css`</a>
|
||||
|
||||
And **ReDoc** uses the file:
|
||||
|
||||
* <a href="https://cdn.jsdelivr.net/npm/redoc@next/bundles/redoc.standalone.js" target="_blank">`redoc.standalone.js`</a>
|
||||
|
||||
After that, your file structure could look like:
|
||||
|
||||
```
|
||||
.
|
||||
├── app
|
||||
│ ├── __init__.py
|
||||
│ ├── main.py
|
||||
└── static
|
||||
├── redoc.standalone.js
|
||||
├── swagger-ui-bundle.js
|
||||
└── swagger-ui.css
|
||||
```
|
||||
|
||||
### Install `aiofiles`
|
||||
|
||||
Now you need to install `aiofiles`:
|
||||
|
||||
```bash
|
||||
pip install aiofiles
|
||||
```
|
||||
|
||||
### Serve the static files
|
||||
|
||||
* Import `StaticFiles` from Starlette.
|
||||
* "Mount" it the same way you would <a href="https://fastapi.tiangolo.com/tutorial/sub-applications-proxy/" target="_blank">mount a Sub-Application</a>.
|
||||
|
||||
```Python hl_lines="7 11"
|
||||
{!./src/extending_openapi/tutorial002.py!}
|
||||
```
|
||||
|
||||
### Test the static files
|
||||
|
||||
Start your application and go to <a href="http://127.0.0.1:8000/static/redoc.standalone.js" target="_blank">http://127.0.0.1:8000/static/redoc.standalone.js</a>.
|
||||
|
||||
You should see a very long JavaScript file for **ReDoc**.
|
||||
|
||||
It could start with something like:
|
||||
|
||||
```JavaScript
|
||||
/*!
|
||||
* ReDoc - OpenAPI/Swagger-generated API Reference Documentation
|
||||
* -------------------------------------------------------------
|
||||
* Version: "2.0.0-rc.18"
|
||||
* Repo: https://github.com/Redocly/redoc
|
||||
*/
|
||||
!function(e,t){"object"==typeof exports&&"object"==typeof m
|
||||
|
||||
...
|
||||
```
|
||||
|
||||
That confirms that you are being able to serve static files from your app, and that you placed the static files for the docs in the correct place.
|
||||
|
||||
Now we can configure the app to use those static files for the docs.
|
||||
|
||||
### Disable the automatic docs
|
||||
|
||||
The first step is to disable the automatic docs, as those use the CDN by default.
|
||||
|
||||
To disable them, set their URLs to `None` when creating your `FastAPI` app:
|
||||
|
||||
```Python hl_lines="9"
|
||||
{!./src/extending_openapi/tutorial002.py!}
|
||||
```
|
||||
|
||||
### Include the custom docs
|
||||
|
||||
Now you can create the *path operations* for the custom docs.
|
||||
|
||||
You can re-use FastAPI's internal functions to create the HTML pages for the docs, and pass them the needed arguments:
|
||||
|
||||
* `openapi_url`: the URL where the HTML page for the docs can get the OpenAPI schema for your API. You can use here the attribute `app.openapi_url`.
|
||||
* `title`: the title of your API.
|
||||
* `oauth2_redirect_url`: you can use `app.swagger_ui_oauth2_redirect_url` here to use the default.
|
||||
* `swagger_js_url`: the URL where the HTML for your Swagger UI docs can get the **JavaScript** file. This is the one that your own app is now serving.
|
||||
* `swagger_css_url`: the URL where the HTML for your Swagger UI docs can get the **CSS** file. This is the one that your own app is now serving.
|
||||
|
||||
And similarly for ReDoc...
|
||||
|
||||
```Python hl_lines="2 3 4 5 6 14 15 16 17 18 19 20 21 22 25 26 27 30 31 32 33 34 35 36"
|
||||
{!./src/extending_openapi/tutorial002.py!}
|
||||
```
|
||||
|
||||
!!! tip
|
||||
The *path operation* for `swagger_ui_redirect` is a helper for when you use OAuth2.
|
||||
|
||||
If you integrate your API with an OAuth2 provider, you will be able to authenticate and come back to the API docs with the acquired credentials. And interact with it using the real OAuth2 authentication.
|
||||
|
||||
Swagger UI will handle it behind the scenes for you, but it needs this "redirect" helper.
|
||||
|
||||
### Create a *path operation* to test it
|
||||
|
||||
Now, to be able to test that everything works, create a path operation:
|
||||
|
||||
```Python hl_lines="39 40 41"
|
||||
{!./src/extending_openapi/tutorial002.py!}
|
||||
```
|
||||
|
||||
### Test it
|
||||
|
||||
Now, you should be able to disconnect your WiFi, go to your docs at <a href="http://127.0.0.1:8000/docs" target="_blank">http://127.0.0.1:8000/docs</a>, and reload the page.
|
||||
|
||||
And even without Internet, you would be able to see the docs for your API and interact with it.
|
||||
|
||||
@@ -15,7 +15,7 @@ This is especially the case for user models, because:
|
||||
|
||||
Here's a general idea of how the models could look like with their password fields and the places where they are used:
|
||||
|
||||
```Python hl_lines="8 10 15 21 23 32 34 39 40"
|
||||
```Python hl_lines="7 9 14 20 22 27 28 31 32 33 38 39"
|
||||
{!./src/extra_models/tutorial001.py!}
|
||||
```
|
||||
|
||||
@@ -148,7 +148,7 @@ All the data conversion, validation, documentation, etc. will still work as norm
|
||||
|
||||
That way, we can declare just the differences between the models (with plaintext `password`, with `hashed_password` and without password):
|
||||
|
||||
```Python hl_lines="8 14 15 18 19 22 23"
|
||||
```Python hl_lines="7 13 14 17 18 21 22"
|
||||
{!./src/extra_models/tutorial002.py!}
|
||||
```
|
||||
|
||||
@@ -174,6 +174,18 @@ For that, use the standard Python `typing.List`:
|
||||
{!./src/extra_models/tutorial004.py!}
|
||||
```
|
||||
|
||||
## Response with arbitrary `dict`
|
||||
|
||||
You can also declare a response using a plain arbitrary `dict`, declaring just the type of the keys and values, without using a Pydantic model.
|
||||
|
||||
This is useful if you don't know the valid field/attribute names (that would be needed for a Pydantic model) beforehand.
|
||||
|
||||
In this case, you can use `typing.Dict`:
|
||||
|
||||
```Python hl_lines="1 8"
|
||||
{!./src/extra_models/tutorial005.py!}
|
||||
```
|
||||
|
||||
## Recap
|
||||
|
||||
Use multiple Pydantic models and inherit freely for each case.
|
||||
|
||||
@@ -37,7 +37,7 @@ Open your browser at <a href="http://127.0.0.1:8000" target="_blank">http://127.
|
||||
You will see the JSON response as:
|
||||
|
||||
```JSON
|
||||
{"hello": "world"}
|
||||
{"message": "Hello World"}
|
||||
```
|
||||
|
||||
### Interactive API docs
|
||||
|
||||
@@ -4,9 +4,9 @@ This client could be a browser with a frontend, the code from someone else, an I
|
||||
|
||||
You could need to tell that client that:
|
||||
|
||||
* He doesn't have enough privileges for that operation.
|
||||
* He doesn't have access to that resource.
|
||||
* The item he was trying to access doesn't exist.
|
||||
* The client doesn't have enough privileges for that operation.
|
||||
* The client doesn't have access to that resource.
|
||||
* The item the client was trying to access doesn't exist.
|
||||
* etc.
|
||||
|
||||
In these cases, you would normally return an **HTTP status code** in the range of **400** (from 400 to 499).
|
||||
|
||||
186
docs/tutorial/openapi-callbacks.md
Normal file
186
docs/tutorial/openapi-callbacks.md
Normal file
@@ -0,0 +1,186 @@
|
||||
You could create an API with a *path operation* that could trigger a request to an *external API* created by someone else (probably the same developer that would be *using* your API).
|
||||
|
||||
The process that happens when your API app calls the *external API* is named a "callback". Because the software that the external developer wrote sends a request to your API and then your API *calls back*, sending a request to an *external API* (that was probably created by the same developer).
|
||||
|
||||
In this case, you could want to document how that external API *should* look like. What *path operation* it should have, what body it should expect, what response it should return, etc.
|
||||
|
||||
## An app with callbacks
|
||||
|
||||
Let's see all this with an example.
|
||||
|
||||
Imagine you develop an app that allows creating invoices.
|
||||
|
||||
These invoices will have an `id`, `title` (optional), `customer`, and `total`.
|
||||
|
||||
The user of your API (an external developer) will create an invoice in your API with a POST request.
|
||||
|
||||
Then your API will (let's imagine):
|
||||
|
||||
* Send the invoice to some customer of the external developer.
|
||||
* Collect the money.
|
||||
* Send a notification back to the API user (the external developer).
|
||||
* This will be done by sending a POST request (from *your API*) to some *external API* provided by that external developer (this is the "callback").
|
||||
|
||||
## The normal **FastAPI** app
|
||||
|
||||
Let's first see how the normal API app would look like before adding the callback.
|
||||
|
||||
It will have a *path operation* that will receive an `Invoice` body, and a query parameter `callback_url` that will contain the URL for the callback.
|
||||
|
||||
This part is pretty normal, most of the code is probably already familiar to you:
|
||||
|
||||
```Python hl_lines="8 9 10 11 12 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53"
|
||||
{!./src/openapi_callbacks/tutorial001.py!}
|
||||
```
|
||||
|
||||
!!! tip
|
||||
The `callback_url` query parameter uses a Pydantic <a href="https://pydantic-docs.helpmanual.io/usage/types/#urls" target="_blank">URL</a> type.
|
||||
|
||||
The only new thing is the `callbacks=messages_callback_router.routes` as an argument to the *path operation decorator*. We'll see what that is next.
|
||||
|
||||
## Documenting the callback
|
||||
|
||||
The actual callback code will depend heavily on your own API app.
|
||||
|
||||
And it will probably vary a lot from one app to the next.
|
||||
|
||||
It could be just one or two lines of code, like:
|
||||
|
||||
```Python
|
||||
callback_url = "https://example.com/api/v1/invoices/events/"
|
||||
requests.post(callback_url, json={"description": "Invoice paid", "paid": True})
|
||||
```
|
||||
|
||||
But possibly the most important part of the callback is making sure that your API user (the external developer) implements the *external API* correctly, according to the data that *your API* is going to send in the request body of the callback, etc.
|
||||
|
||||
So, what we will do next is add the code to document how that *external API* should look like to receive the callback from *your API*.
|
||||
|
||||
That documentation will show up in the Swagger UI at `/docs` in your API, and it will let external developers know how to build the *external API*.
|
||||
|
||||
This example doesn't implement the callback itself (that could be just a line of code), only the documentation part.
|
||||
|
||||
!!! tip
|
||||
The actual callback is just an HTTP request.
|
||||
|
||||
When implementing the callback yourself, you could use something like <a href="https://www.encode.io/httpx/" target="_blank">HTTPX</a> or <a href="https://requests.readthedocs.io/" target="_blank">Requests</a>.
|
||||
|
||||
## Write the callback documentation code
|
||||
|
||||
This code won't be executed in your app, we only need it to *document* how that *external API* should look like.
|
||||
|
||||
But, you already know how to easily create automatic documentation for an API with **FastAPI**.
|
||||
|
||||
So we are going to use that same knowledge to document how the *external API* should look like... by creating the *path operation(s)* that the external API should implement (the ones your API will call).
|
||||
|
||||
!!! tip
|
||||
When writing the code to document a callback, it might be useful to imagine that you are that *external developer*. And that you are currently implementing the *external API*, not *your API*.
|
||||
|
||||
Temporarily adopting this point of view (of the *external developer*) can help you feel like it's more obvious where to put the parameters, the Pydantic model for the body, for the response, etc. for that *external API*.
|
||||
|
||||
### Create a callback `APIRouter`
|
||||
|
||||
First create a new `APIRouter` that will contain one or more callbacks.
|
||||
|
||||
This router will never be added to an actual `FastAPI` app (i.e. it will never be passed to `app.include_router(...)`).
|
||||
|
||||
Because of that, you need to declare what will be the `default_response_class`, and set it to `JSONResponse`.
|
||||
|
||||
!!! Note "Technical Details"
|
||||
The `response_class` is normally set by the `FastAPI` app during the call to `app.include_router(some_router)`.
|
||||
|
||||
But as we are never calling `app.include_router(some_router)`, we need to set the `default_response_class` during creation of the `APIRouter`.
|
||||
|
||||
```Python hl_lines="3 24"
|
||||
{!./src/openapi_callbacks/tutorial001.py!}
|
||||
```
|
||||
|
||||
### Create the callback *path operation*
|
||||
|
||||
To create the callback *path operation* use the same `APIRouter` you created above.
|
||||
|
||||
It should look just like a normal FastAPI *path operation*:
|
||||
|
||||
* It should probably have a declaration of the body it should receive, e.g. `body: InvoiceEvent`.
|
||||
* And it could also have a declaration of the response it should return, e.g. `response_model=InvoiceEventReceived`.
|
||||
|
||||
```Python hl_lines="15 16 17 20 21 27 28 29 30 31"
|
||||
{!./src/openapi_callbacks/tutorial001.py!}
|
||||
```
|
||||
|
||||
There are 2 main differences from a normal *path operation*:
|
||||
|
||||
* It doesn't need to have any actual code, because your app will never call this code. It's only used to document the *external API*. So, the function could just have `pass`.
|
||||
* The *path* can contain an <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#key-expression" target="_blank">OpenAPI 3 expression</a> (see more below) where it can use variables with parameters and parts of the original request sent to *your API*.
|
||||
|
||||
### The callback path expression
|
||||
|
||||
The callback *path* can have an <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#key-expression" target="_blank">OpenAPI 3 expression</a> that can contain parts of the original request sent to *your API*.
|
||||
|
||||
In this case, it's the `str`:
|
||||
|
||||
```Python
|
||||
"{$callback_url}/invoices/{$request.body.id}"
|
||||
```
|
||||
|
||||
So, if your API user (the external developer) sends a request to *your API* to:
|
||||
|
||||
```
|
||||
https://yourapi.com/invoices/?callback_url=https://www.external.org/events
|
||||
```
|
||||
|
||||
with a JSON body of:
|
||||
|
||||
```JSON
|
||||
{
|
||||
"id": "2expen51ve",
|
||||
"customer": "Mr. Richie Rich",
|
||||
"total": "9999"
|
||||
}
|
||||
```
|
||||
|
||||
Then *your API* will process the invoice, and at some point later, send a callback request to the `callback_url` (the *external API*):
|
||||
|
||||
```
|
||||
https://www.external.org/events/invoices/2expen51ve
|
||||
```
|
||||
|
||||
with a JSON body containing something like:
|
||||
|
||||
```JSON
|
||||
{
|
||||
"description": "Payment celebration",
|
||||
"paid": true
|
||||
}
|
||||
```
|
||||
|
||||
and it would expect a response from that *external API* with a JSON body like:
|
||||
|
||||
```JSON
|
||||
{
|
||||
"ok": true
|
||||
}
|
||||
```
|
||||
|
||||
!!! tip
|
||||
Notice how the callback URL used contains the URL received as a query parameter in `callback_url` (`https://www.external.org/events`) and also the invoice `id` from inside of the JSON body (`2expen51ve`).
|
||||
|
||||
### Add the callback router
|
||||
|
||||
At this point you have the *callback path operation(s)* needed (the one(s) that the *external developer* should implement in the *external API*) in the callback router you created above.
|
||||
|
||||
Now use the parameter `callbacks` in *your API's path operation decorator* to pass the attribute `.routes` (that's actually just a `list` of routes/*path operations*) from that callback router:
|
||||
|
||||
```Python hl_lines="34"
|
||||
{!./src/openapi_callbacks/tutorial001.py!}
|
||||
```
|
||||
|
||||
!!! tip
|
||||
Notice that you are not passing the router itself (`invoices_callback_router`) to `callback=`, but the attribute `.routes`, as in `invoices_callback_router.routes`.
|
||||
|
||||
### Check the docs
|
||||
|
||||
Now you can start your app with Uvicorn and go to <a href="http://127.0.0.1:8000/docs" target="_blank">http://127.0.0.1:8000/docs</a>.
|
||||
|
||||
You will see your docs including a "Callback" section for your *path operation* that shows how the *external API* should look like:
|
||||
|
||||
<img src="/img/tutorial/openapi-callbacks/image01.png">
|
||||
@@ -11,10 +11,40 @@ You would have to make sure that it is unique for each operation.
|
||||
{!./src/path_operation_advanced_configuration/tutorial001.py!}
|
||||
```
|
||||
|
||||
### Using the *path operation function* name as the operationId
|
||||
|
||||
If you want to use your APIs' function names as `operationId`s, you can iterate over all of them and override each *path operation's* `operation_id` using their `APIRoute.name`.
|
||||
|
||||
You should do it after adding all your *path operations*.
|
||||
|
||||
```Python hl_lines="2 12 13 14 15 16 17 18 19 20 21 24"
|
||||
{!./src/path_operation_advanced_configuration/tutorial002.py!}
|
||||
```
|
||||
|
||||
!!! tip
|
||||
If you manually call `app.openapi()`, you should update the `operationId`s before that.
|
||||
|
||||
!!! warning
|
||||
If you do this, you have to make sure each one of your *path operation functions* has a unique name.
|
||||
|
||||
Even if they are in different modules (Python files).
|
||||
|
||||
## Exclude from OpenAPI
|
||||
|
||||
To exclude a path operation from the generated OpenAPI schema (and thus, from the automatic documentation systems), use the parameter `include_in_schema` and set it to `False`;
|
||||
|
||||
```Python hl_lines="6"
|
||||
{!./src/path_operation_advanced_configuration/tutorial002.py!}
|
||||
{!./src/path_operation_advanced_configuration/tutorial003.py!}
|
||||
```
|
||||
|
||||
## Advanced description from docstring
|
||||
|
||||
You can limit the lines used from the docstring of a *path operation function* for OpenAPI.
|
||||
|
||||
Adding an `\f` (an escaped "form feed" character) causes **FastAPI** to truncate the output used for OpenAPI at this point.
|
||||
|
||||
It won't show up in the documentation, but other tools (such as Sphinx) will be able to use the rest.
|
||||
|
||||
```Python hl_lines="19 20 21 22 23 24 25 26 27 28 29"
|
||||
{!./src/path_operation_advanced_configuration/tutorial004.py!}
|
||||
```
|
||||
|
||||
@@ -119,7 +119,9 @@ If you have a *path operation* that receives a *path parameter*, but you want th
|
||||
|
||||
### Create an `Enum` class
|
||||
|
||||
Import `Enum` and create a sub-class that inherits from it.
|
||||
Import `Enum` and create a sub-class that inherits from `str` and from `Enum`.
|
||||
|
||||
By inheriting from `str` the API docs will be able to know that the values must be of type `string` and will be able to render correctly.
|
||||
|
||||
And create class attributes with fixed values, those fixed values will be the available valid values:
|
||||
|
||||
|
||||
@@ -183,6 +183,19 @@ the default of `q` will be: `["foo", "bar"]` and your response will be:
|
||||
}
|
||||
```
|
||||
|
||||
#### Using `list`
|
||||
|
||||
You can also use `list` directly instead of `List[str]`:
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!./src/query_params_str_validations/tutorial013.py!}
|
||||
```
|
||||
|
||||
!!! note
|
||||
Have in mind that in this case, FastAPI won't check the contents of the list.
|
||||
|
||||
For example, `List[int]` would check (and document) that the contents of the list are integers. But `list` alone wouldn't.
|
||||
|
||||
## Declare more metadata
|
||||
|
||||
You can add more information about the parameter.
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
You can define files to be uploaded by the client using `File`.
|
||||
|
||||
!!! info
|
||||
To receive uploaded files, first install [`python-multipart`](https://andrew-d.github.io/python-multipart/).
|
||||
|
||||
E.g. `pip install python-multipart`.
|
||||
|
||||
This is because uploaded files are sent as "form data".
|
||||
|
||||
## Import `File`
|
||||
|
||||
Import `File` and `UploadFile` from `fastapi`:
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
You can define files and form fields at the same time using `File` and `Form`.
|
||||
|
||||
!!! info
|
||||
To receive uploaded files and/or form data, first install [`python-multipart`](https://andrew-d.github.io/python-multipart/).
|
||||
|
||||
E.g. `pip install python-multipart`.
|
||||
|
||||
## Import `File` and `Form`
|
||||
|
||||
```Python hl_lines="1"
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
When you need to receive form fields instead of JSON, you can use `Form`.
|
||||
|
||||
!!! info
|
||||
To use forms, first install [`python-multipart`](https://andrew-d.github.io/python-multipart/).
|
||||
|
||||
E.g. `pip install python-multipart`.
|
||||
|
||||
## Import `Form`
|
||||
|
||||
Import `Form` from `fastapi`:
|
||||
|
||||
31
docs/tutorial/response-change-status-code.md
Normal file
31
docs/tutorial/response-change-status-code.md
Normal file
@@ -0,0 +1,31 @@
|
||||
You probably read before that you can set a <a href="https://fastapi.tiangolo.com/tutorial/response-status-code/" target="_blank">default Response Status Code</a>.
|
||||
|
||||
But in some cases you need to return a different status code than the default.
|
||||
|
||||
## Use case
|
||||
|
||||
For example, imagine that you want to return an HTTP status code of "OK" `200` by default.
|
||||
|
||||
But if the data didn't exist, you want to create it, and return an HTTP status code of "CREATED" `201`.
|
||||
|
||||
But you still want to be able to filter and convert the data you return with a `response_model`.
|
||||
|
||||
For those cases, you can use a `Response` parameter.
|
||||
|
||||
## Use a `Response` parameter
|
||||
|
||||
You can declare a parameter of type `Response` in your *path operation function* (as you can do for cookies and headers).
|
||||
|
||||
And then you can set the `status_code` in that *temporal* response object.
|
||||
|
||||
```Python hl_lines="2 11 14"
|
||||
{!./src/response_change_status_code/tutorial001.py!}
|
||||
```
|
||||
|
||||
And then you can return any object you need, as you normally would (a `dict`, a database model, etc).
|
||||
|
||||
And if you declared a `response_model`, it will still be used to filter and convert the object you returned.
|
||||
|
||||
**FastAPI** will use that *temporal* response to extract the status code (also cookies and headers), and will put them in the final response that contains the value you returned, filtered by any `response_model`.
|
||||
|
||||
You can also declare the `Response` parameter in dependencies, and set the status code in them. But have in mind that the last one to be set will win.
|
||||
@@ -1,4 +1,24 @@
|
||||
You can create (set) Cookies in your response.
|
||||
## Use a `Response` parameter
|
||||
|
||||
You can declare a parameter of type `Response` in your *path operation function*, the same way you can declare a `Request` parameter.
|
||||
|
||||
And then you can set headers in that *temporal* response object.
|
||||
|
||||
```Python hl_lines="2 8 9"
|
||||
{!./src/response_cookies/tutorial002.py!}
|
||||
```
|
||||
|
||||
And then you can return any object you need, as you normally would (a `dict`, a database model, etc).
|
||||
|
||||
And if you declared a `response_model`, it will still be used to filter and convert the object you returned.
|
||||
|
||||
**FastAPI** will use that *temporal* response to extract the cookies (also headers and status code), and will put them in the final response that contains the value you returned, filtered by any `response_model`.
|
||||
|
||||
You can also declare the `Response` parameter in dependencies, and set cookies (and headers) in them.
|
||||
|
||||
## Return a `Response` directly
|
||||
|
||||
You can also create cookies when returning a `Response` directly in your code.
|
||||
|
||||
To do that, you can create a response as described in <a href="https://fastapi.tiangolo.com/tutorial/response-directly/" target="_blank">Return a Response directly</a>.
|
||||
|
||||
@@ -8,6 +28,13 @@ Then set Cookies in it, and then return it:
|
||||
{!./src/response_cookies/tutorial001.py!}
|
||||
```
|
||||
|
||||
## More info
|
||||
!!! tip
|
||||
Have in mind that if you return a response directly instead of using the `Response` parameter, FastAPI will return it directly.
|
||||
|
||||
So, you will have to make sure your data is of the correct type. E.g. it is compatible with JSON, if you are returning a `JSONResponse`.
|
||||
|
||||
And also that you are not sending any data that should have been filtered by a `response_model`.
|
||||
|
||||
### More info
|
||||
|
||||
To see all the available parameters and options, check the <a href="https://www.starlette.io/responses/#set-cookie" target="_blank">documentation in Starlette</a>.
|
||||
|
||||
@@ -56,7 +56,7 @@ You could put your XML content in a string, put it in a Starlette Response, and
|
||||
|
||||
When you return a `Response` directly its data is not validated, converted (serialized), nor documented automatically.
|
||||
|
||||
But you can still <a href="tutorial/additional-responses/" target="_blank">document it</a>.
|
||||
But you can still <a href="https://fastapi.tiangolo.com/tutorial/additional-responses/" target="_blank">document it</a>.
|
||||
|
||||
In the next sections you will see how to use/declare these custom `Response`s while still having automatic data conversion, documentation, etc.
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user