Compare commits
131 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3986f79029 | ||
|
|
7379fde5ee | ||
|
|
7b63bc5551 | ||
|
|
747ae8210f | ||
|
|
c651416e05 | ||
|
|
814f95e2bf | ||
|
|
d8716f94ae | ||
|
|
67f8cb3b4f | ||
|
|
5c2828bd13 | ||
|
|
b087246f26 | ||
|
|
219d299426 | ||
|
|
31da760729 | ||
|
|
fc89eb8f81 | ||
|
|
6fca1041e9 | ||
|
|
9db1f5641b | ||
|
|
c3beb56e63 | ||
|
|
325edd5f00 | ||
|
|
08322ef359 | ||
|
|
01b43e6e25 | ||
|
|
3cf92a156c | ||
|
|
f54d8d57a4 | ||
|
|
56ab106bbb | ||
|
|
e92b43b5c8 | ||
|
|
7c50025c47 | ||
|
|
adfbd27100 | ||
|
|
eada8bf7db | ||
|
|
440b7a8efa | ||
|
|
fcaff64646 | ||
|
|
d240421378 | ||
|
|
ca27317b65 | ||
|
|
ce02d3cb83 | ||
|
|
95475aaa9c | ||
|
|
7a8b054a12 | ||
|
|
7b2993682f | ||
|
|
73fad03b46 | ||
|
|
b0b88f9d5b | ||
|
|
49d33f9f70 | ||
|
|
1f27981045 | ||
|
|
f541d2c200 | ||
|
|
0e99b23ebc | ||
|
|
de341abe66 | ||
|
|
4a1648b04e | ||
|
|
5f13b53ea5 | ||
|
|
5189c93d85 | ||
|
|
e612989313 | ||
|
|
d675991a34 | ||
|
|
d03678dfbb | ||
|
|
6ff89284c5 | ||
|
|
7f673cf4e9 | ||
|
|
bac7230027 | ||
|
|
866af5bca6 | ||
|
|
f4be79be51 | ||
|
|
3797c04946 | ||
|
|
9a9bfd7f93 | ||
|
|
ada1ecdb00 | ||
|
|
3bbd38313b | ||
|
|
c1df0f6b84 | ||
|
|
0cd5485597 | ||
|
|
528ef7e079 | ||
|
|
8e3a7699a3 | ||
|
|
8998ccaffb | ||
|
|
2b7f201a44 | ||
|
|
192ebba2a2 | ||
|
|
8880c4cb03 | ||
|
|
6324be684f | ||
|
|
c705685394 | ||
|
|
945f401d8e | ||
|
|
f216d340ec | ||
|
|
a4558e7053 | ||
|
|
298f8478e2 | ||
|
|
b86d163470 | ||
|
|
9e2d37b89c | ||
|
|
97adadd9e1 | ||
|
|
26e3dffb37 | ||
|
|
aa7b4bd101 | ||
|
|
ffc4c716c0 | ||
|
|
ef7b6e8eaf | ||
|
|
596243f4a5 | ||
|
|
766bf1c5aa | ||
|
|
9e748dbca4 | ||
|
|
cefe6cf92c | ||
|
|
be3953499f | ||
|
|
546d233dec | ||
|
|
61dd36a945 | ||
|
|
27f9d55c3e | ||
|
|
906cc60f65 | ||
|
|
69afaf256f | ||
|
|
4ab349a2a8 | ||
|
|
9c258107b4 | ||
|
|
29a4f90bcd | ||
|
|
361fd00777 | ||
|
|
4c3cf31730 | ||
|
|
aad6b123f7 | ||
|
|
e40e87c662 | ||
|
|
84de980977 | ||
|
|
08484603ee | ||
|
|
ab6dd60997 | ||
|
|
c9ef7bd6dc | ||
|
|
88ece95a30 | ||
|
|
366c5db0bb | ||
|
|
46e3811f8d | ||
|
|
613e211d20 | ||
|
|
500f2b2ad4 | ||
|
|
5cf7718657 | ||
|
|
727b656c8d | ||
|
|
cc7102e9b8 | ||
|
|
5123915fe4 | ||
|
|
1b8bbd51d8 | ||
|
|
1e4f86db6d | ||
|
|
7391056daf | ||
|
|
6a274d18b4 | ||
|
|
62626b0175 | ||
|
|
c8df3ae57c | ||
|
|
6f7f9268f6 | ||
|
|
50653e205f | ||
|
|
50a280b17b | ||
|
|
c1da3b38a3 | ||
|
|
e68a68c97c | ||
|
|
907e613ff2 | ||
|
|
f0fc2fad2c | ||
|
|
ad471307e2 | ||
|
|
2bd775988f | ||
|
|
eda9b28338 | ||
|
|
7514ac6fb0 | ||
|
|
25fb4239cc | ||
|
|
65568065e0 | ||
|
|
95679ca5e6 | ||
|
|
84a300ef84 | ||
|
|
c6d28c8209 | ||
|
|
3984e9b8ac | ||
|
|
aa0bca7bb2 |
@@ -10,7 +10,7 @@ python:
|
||||
|
||||
install:
|
||||
- pip install flit
|
||||
- flit install
|
||||
- flit install --symlink
|
||||
|
||||
script:
|
||||
- bash scripts/test.sh
|
||||
@@ -20,6 +20,7 @@ after_script:
|
||||
|
||||
deploy:
|
||||
provider: script
|
||||
script: bash scripts/trigger-docker.sh
|
||||
script: bash scripts/deploy.sh
|
||||
on:
|
||||
branch: master
|
||||
tags: true
|
||||
python: "3.6"
|
||||
|
||||
1
CONTRIBUTING.md
Normal file
@@ -0,0 +1 @@
|
||||
Please read the [Development - Contributing](https://fastapi.tiangolo.com/contributing/) guidelines in the documentation site.
|
||||
5
Pipfile
@@ -25,9 +25,10 @@ sqlalchemy = "*"
|
||||
uvicorn = "*"
|
||||
|
||||
[packages]
|
||||
starlette = "==0.11.1"
|
||||
pydantic = "==0.21.0"
|
||||
starlette = "==0.12.0"
|
||||
pydantic = "==0.26.0"
|
||||
databases = {extras = ["sqlite"],version = "*"}
|
||||
hypercorn = "*"
|
||||
|
||||
[requires]
|
||||
python_version = "3.6"
|
||||
|
||||
380
Pipfile.lock
generated
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "24b3b7b88d3cbe671ddbe296e64c15f8558f0e5d5df977200119872a363aac13"
|
||||
"sha256": "4a33b47e814fa75533548874ffadbc6163b3058db4d1615ff633512366d72ccb"
|
||||
},
|
||||
"pipfile-spec": 6,
|
||||
"requires": {
|
||||
@@ -18,34 +18,33 @@
|
||||
"default": {
|
||||
"aiocontextvars": {
|
||||
"hashes": [
|
||||
"sha256:1e0ff5837c8b01c36a1107acdd0baf7853ebdf6c9fc43e8e311f4be37ac2038a",
|
||||
"sha256:6ff7aee14f549d52f0446cbb84d0deddcd3fc677bcf8fbc2ce13f5756d2064dc"
|
||||
"sha256:885daf8261818767d8f7cbd79f9d4482d118f024b6586ef6e67980236a27bfa3",
|
||||
"sha256:f027372dc48641f683c559f247bd84962becaacdc9ba711d583c3871fb5652aa"
|
||||
],
|
||||
"markers": "python_version < '3.7'",
|
||||
"version": "==0.2.1"
|
||||
"version": "==0.2.2"
|
||||
},
|
||||
"aiosqlite": {
|
||||
"hashes": [
|
||||
"sha256:af4fed9e778756fa0ffffc7a8b14c4d7b1a57155dc5669f18e45107313f6019e"
|
||||
"sha256:ad84fbd7516ca7065d799504fc41d6845c938e5306d1b7dd960caaeda12e22a9"
|
||||
],
|
||||
"version": "==0.9.0"
|
||||
"version": "==0.10.0"
|
||||
},
|
||||
"contextvars": {
|
||||
"hashes": [
|
||||
"sha256:2341042e1c03a271813e07dba29b6b60fa85c1005ea5ed1638a076cf50b4d625"
|
||||
"sha256:f38c908aaa59c14335eeea12abea5f443646216c4e29380d7bf34d2018e2c39e"
|
||||
],
|
||||
"markers": "python_version < '3.7'",
|
||||
"version": "==2.3"
|
||||
"version": "==2.4"
|
||||
},
|
||||
"databases": {
|
||||
"extras": [
|
||||
"sqlite"
|
||||
],
|
||||
"hashes": [
|
||||
"sha256:4a0f15669c390a04b439972426350c0ae921ddc08c42bd54f125eb2fb86ee728"
|
||||
"sha256:d365cff2035c5177ef5fd8c5abf6671da01189521da64848a01251c870daf48f"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.2.0"
|
||||
"version": "==0.2.2"
|
||||
},
|
||||
"dataclasses": {
|
||||
"hashes": [
|
||||
@@ -55,42 +54,104 @@
|
||||
"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:1e4f4513254ef11e0230a558ee0dcb4551b914993c330005d15338da595d3750",
|
||||
"sha256:228e38dc7a810ba4ff88909908ac47f840e5dc6c4c0da6b25009c626a9ae771c",
|
||||
"sha256:2ae88fbfe1d04f4e5859c924e97313edf70e72b4f19871bf329b96a67ede9ba0",
|
||||
"sha256:2d32b61c222cba1dd11f0faff67c7fb6204ef1982454e1b5b001d4b79966ef17",
|
||||
"sha256:35af186bfac5b62522fdf2cab11120d7b0547f405aa399b6a1e443cf5f5e318c",
|
||||
"sha256:63023fa0cceedc62e0d1535cd4ca7a1f6df3120a6d8e5c34e89037402a6fd809",
|
||||
"sha256:6bf5857f42a96331fd0929c357dc0b36a72f339f3b6acaf870b149c96b141f69",
|
||||
"sha256:7bb1590024a032c7a57f79faf8c8ff5e91340662550d2980e0177f67e66e9c9c",
|
||||
"sha256:7c090687d7e623d4eca22962635b5e1a1ee2d6f9a9aca2f3fb5a184a1ffef1f2",
|
||||
"sha256:bc36a0a8749881eebd753f696b081bd51145e4d77291d671d2e2f622e5b65d2f",
|
||||
"sha256:d9fc6a236018d99af6453ead945a6bb55f98d14b1801a2c229dd993edc753a00"
|
||||
"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.6"
|
||||
"version": "==0.9"
|
||||
},
|
||||
"pydantic": {
|
||||
"hashes": [
|
||||
"sha256:93fa585402e7c8c01623ea8af6ca23363e8b4c6a020b7a2de9e99fa29d642d50",
|
||||
"sha256:eb441dd50779347a450494c437db3ecbb13c1f3854497df879662782af516c5c"
|
||||
"sha256:b72e0df2463cee746cf42639845d4106c19f30136375e779352d710e69617731",
|
||||
"sha256:dab99d3070e040b8b2e987dfbe237350ab92d5d57a22d4e0e268ede2d85c7964"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.21.0"
|
||||
"version": "==0.26.0"
|
||||
},
|
||||
"pytoml": {
|
||||
"hashes": [
|
||||
"sha256:ca2d0cb127c938b8b76a9a0d0f855cf930c1d50cc3a0af6d3595b566519a1013"
|
||||
],
|
||||
"version": "==0.1.20"
|
||||
},
|
||||
"sqlalchemy": {
|
||||
"hashes": [
|
||||
"sha256:781fb7b9d194ed3fc596b8f0dd4623ff160e3e825dd8c15472376a438c19598b"
|
||||
"sha256:91c54ca8345008fceaec987e10924bf07dcab36c442925357e5a467b36a38319"
|
||||
],
|
||||
"version": "==1.3.1"
|
||||
"version": "==1.3.3"
|
||||
},
|
||||
"starlette": {
|
||||
"hashes": [
|
||||
"sha256:9d48b35d1fc7521d59ae53c421297ab3878d3c7cd4b75266d77f6c73cccb78bb"
|
||||
"sha256:d313433ef5cc38e0a276b59688ca2b11b8f031c78808c1afdf9d55cb86f34590"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.11.1"
|
||||
"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": {
|
||||
@@ -117,10 +178,10 @@
|
||||
},
|
||||
"autoflake": {
|
||||
"hashes": [
|
||||
"sha256:c103e63466f11db3617167a2c68ff6a0cda35b940222920631c6eeec6b67e807"
|
||||
"sha256:6b59e5b9b82e30077499578856282debb81186d10b4f899e8c2e1d616cdef973"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.2"
|
||||
"version": "==1.3"
|
||||
},
|
||||
"backcall": {
|
||||
"hashes": [
|
||||
@@ -175,37 +236,35 @@
|
||||
},
|
||||
"coverage": {
|
||||
"hashes": [
|
||||
"sha256:029c69deaeeeae1b15bc6c59f0ffa28aa8473721c614a23f2c2976dec245cd12",
|
||||
"sha256:02abbbebc6e9d5abe13cd28b5e963dedb6ffb51c146c916d17b18f141acd9947",
|
||||
"sha256:1bbfe5b82a3921d285e999c6d256c1e16b31c554c29da62d326f86c173d30337",
|
||||
"sha256:210c02f923df33a8d0e461c86fdcbbb17228ff4f6d92609fc06370a98d283c2d",
|
||||
"sha256:2d0807ba935f540d20b49d5bf1c0237b90ce81e133402feda906e540003f2f7a",
|
||||
"sha256:35d7a013874a7c927ce997350d314144ffc5465faf787bb4e46e6c4f381ef562",
|
||||
"sha256:3636f9d0dcb01aed4180ef2e57a4e34bb4cac3ecd203c2a23db8526d86ab2fb4",
|
||||
"sha256:42f4be770af2455a75e4640f033a82c62f3fb0d7a074123266e143269d7010ef",
|
||||
"sha256:48440b25ba6cda72d4c638f3a9efa827b5b87b489c96ab5f4ff597d976413156",
|
||||
"sha256:4dac8dfd1acf6a3ac657475dfdc66c621f291b1b7422a939cc33c13ac5356473",
|
||||
"sha256:4e8474771c69c2991d5eab65764289a7dd450bbea050bc0ebb42b678d8222b42",
|
||||
"sha256:551f10ddfeff56a1325e5a34eff304c5892aa981fd810babb98bfee77ee2fb17",
|
||||
"sha256:5b104982f1809c1577912519eb249f17d9d7e66304ad026666cb60a5ef73309c",
|
||||
"sha256:5c62aef73dfc87bfcca32cee149a1a7a602bc74bac72223236b0023543511c88",
|
||||
"sha256:633151f8d1ad9467b9f7e90854a7f46ed8f2919e8bc7d98d737833e8938fc081",
|
||||
"sha256:772207b9e2d5bf3f9d283b88915723e4e92d9a62c83f44ec92b9bd0cd685541b",
|
||||
"sha256:7d5e02f647cd727afc2659ec14d4d1cc0508c47e6cfb07aea33d7aa9ca94d288",
|
||||
"sha256:a9798a4111abb0f94584000ba2a2c74841f2cfe5f9254709756367aabbae0541",
|
||||
"sha256:b38ea741ab9e35bfa7015c93c93bbd6a1623428f97a67083fc8ebd366238b91f",
|
||||
"sha256:b6a5478c904236543c0347db8a05fac6fc0bd574c870e7970faa88e1d9890044",
|
||||
"sha256:c6248bfc1de36a3844685a2e10ba17c18119ba6252547f921062a323fb31bff1",
|
||||
"sha256:c705ab445936457359b1424ef25ccc0098b0491b26064677c39f1d14a539f056",
|
||||
"sha256:d95a363d663ceee647291131dbd213af258df24f41350246842481ec3709bd33",
|
||||
"sha256:e27265eb80cdc5dab55a40ef6f890e04ecc618649ad3da5265f128b141f93f78",
|
||||
"sha256:ebc276c9cb5d917bd2ae959f84ffc279acafa9c9b50b0fa436ebb70bbe2166ea",
|
||||
"sha256:f4d229866d030863d0fe3bf297d6d11e6133ca15bbb41ed2534a8b9a3d6bd061",
|
||||
"sha256:f95675bd88b51474d4fe5165f3266f419ce754ffadfb97f10323931fa9ac95e5",
|
||||
"sha256:f95bc54fb6d61b9f9ff09c4ae8ff6a3f5edc937cda3ca36fc937302a7c152bf1",
|
||||
"sha256:fd0f6be53de40683584e5331c341e65a679dbe5ec489a0697cec7c2ef1a48cda"
|
||||
"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.0a4"
|
||||
"version": "==5.0a5"
|
||||
},
|
||||
"decorator": {
|
||||
"hashes": [
|
||||
@@ -216,10 +275,10 @@
|
||||
},
|
||||
"defusedxml": {
|
||||
"hashes": [
|
||||
"sha256:24d7f2f94f7f3cb6061acb215685e5125fbcdc40a857eff9de22518820b0a4f4",
|
||||
"sha256:702a91ade2968a82beb0db1e0766a6a273f33d4616a6ce8cde475d8e09853b20"
|
||||
"sha256:6687150770438374ab581bb7a1b327a847dd9c5749e396102de3fad4e8a3ef93",
|
||||
"sha256:f684034d135af4c6cbb949b8a4d2ed61634515257a67299e5f940fbaa34377f5"
|
||||
],
|
||||
"version": "==0.5.0"
|
||||
"version": "==0.6.0"
|
||||
},
|
||||
"dnspython": {
|
||||
"hashes": [
|
||||
@@ -238,10 +297,10 @@
|
||||
},
|
||||
"email-validator": {
|
||||
"hashes": [
|
||||
"sha256:ddc4b5b59fa699bb10127adcf7ad4de78fde4ec539a072b104b8bb16da666ae5"
|
||||
"sha256:79966e318d6d68fed359c90f8f19d242bcc178b724011f1c07145bd093da6cc7"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.0.3"
|
||||
"version": "==1.0.4"
|
||||
},
|
||||
"entrypoints": {
|
||||
"hashes": [
|
||||
@@ -288,18 +347,18 @@
|
||||
},
|
||||
"ipykernel": {
|
||||
"hashes": [
|
||||
"sha256:0aeb7ec277ac42cc2b59ae3d08b10909b2ec161dc6908096210527162b53675d",
|
||||
"sha256:0fc0bf97920d454102168ec2008620066878848fcfca06c22b669696212e292f"
|
||||
"sha256:346189536b88859937b5f4848a6fd85d1ad0729f01724a411de5cae9b618819c",
|
||||
"sha256:f0e962052718068ad3b1d8bcc703794660858f58803c3798628817f492a8769c"
|
||||
],
|
||||
"version": "==5.1.0"
|
||||
"version": "==5.1.1"
|
||||
},
|
||||
"ipython": {
|
||||
"hashes": [
|
||||
"sha256:b038baa489c38f6d853a3cfc4c635b0cda66f2864d136fe8f40c1a6e334e2a6b",
|
||||
"sha256:f5102c1cd67e399ec8ea66bcebe6e3968ea25a8977e53f012963e5affeb1fe38"
|
||||
"sha256:54c5a8aa1eadd269ac210b96923688ccf01ebb2d0f21c18c3c717909583579a8",
|
||||
"sha256:e840810029224b56cd0d9e7719dc3b39cf84d577f8ac686547c8ba7a06eeab26"
|
||||
],
|
||||
"markers": "python_version >= '3.3'",
|
||||
"version": "==7.4.0"
|
||||
"version": "==7.5.0"
|
||||
},
|
||||
"ipython-genutils": {
|
||||
"hashes": [
|
||||
@@ -317,11 +376,11 @@
|
||||
},
|
||||
"isort": {
|
||||
"hashes": [
|
||||
"sha256:08f8e3f0f0b7249e9fad7e5c41e2113aba44969798a26452ee790c06f155d4ec",
|
||||
"sha256:4e9e9c4bd1acd66cf6c36973f29b031ec752cbfd991c69695e4e259f9a756927"
|
||||
"sha256:c40744b6bc5162bbb39c1257fe298b7a393861d50978b565f3ccd9cb9de0182a",
|
||||
"sha256:f57abacd059dc3bd666258d1efb0377510a89777fda3e3274e3c01f7c03ae22d"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==4.3.16"
|
||||
"version": "==4.3.20"
|
||||
},
|
||||
"jedi": {
|
||||
"hashes": [
|
||||
@@ -332,10 +391,10 @@
|
||||
},
|
||||
"jinja2": {
|
||||
"hashes": [
|
||||
"sha256:74c935a1b8bb9a3947c50a54766a969d4846290e1e788ea44c1392163723c3bd",
|
||||
"sha256:f84be1bb0040caca4cea721fcbbbbd61f9be9464ca236387158b0feea01914a4"
|
||||
"sha256:065c4f02ebe7f7cf559e49ee5a95fb800a9e4528727aec6f24402a5374c65013",
|
||||
"sha256:14dd6caf1527abb21f08f86c784eac40853ba93edb79552aa1e4b8aef1b61c7b"
|
||||
],
|
||||
"version": "==2.10"
|
||||
"version": "==2.10.1"
|
||||
},
|
||||
"jsonschema": {
|
||||
"hashes": [
|
||||
@@ -376,17 +435,17 @@
|
||||
},
|
||||
"livereload": {
|
||||
"hashes": [
|
||||
"sha256:29cadfabcedd12eed792e0131991235b9d4764d4474bed75cf525f57109ec0a2",
|
||||
"sha256:e632a6cd1d349155c1d7f13a65be873b38f43ef02961804a1bba8d817fa649a7"
|
||||
"sha256:78d55f2c268a8823ba499305dcac64e28ddeb9a92571e12d543cd304faf5817b",
|
||||
"sha256:89254f78d7529d7ea0a3417d224c34287ebfe266b05e67e51facaf82c27f0f66"
|
||||
],
|
||||
"version": "==2.6.0"
|
||||
"version": "==2.6.1"
|
||||
},
|
||||
"markdown": {
|
||||
"hashes": [
|
||||
"sha256:c00429bd503a47ec88d5e30a751e147dcb4c6889663cd3e2ba0afe858e009baa",
|
||||
"sha256:d02e0f9b04c500cde6637c11ad7c72671f359b87b9fe924b2383649d8841db7c"
|
||||
"sha256:2e50876bcdd74517e7b71f3e7a76102050edec255b3983403f1a63e7c8a41e7a",
|
||||
"sha256:56a46ac655704b91e5b7e6326ce43d5ef72411376588afa1dd90e881b83c7e8c"
|
||||
],
|
||||
"version": "==3.0.1"
|
||||
"version": "==3.1.1"
|
||||
},
|
||||
"markdown-include": {
|
||||
"hashes": [
|
||||
@@ -452,27 +511,36 @@
|
||||
},
|
||||
"mkdocs-material": {
|
||||
"hashes": [
|
||||
"sha256:0b394aa034b25a09a5874ae2a6ccc426fd81f5764e0991217b169e31cb0c1c0e",
|
||||
"sha256:f5bb80a2c16d045d380edb2c5b05636af1bb709cb859bfaa9d01063a11df803f"
|
||||
"sha256:1c39b6af13a900d9f47ab2b8ac67b3258799f4570b552573e9d6868ad6a438e9",
|
||||
"sha256:22073941cff7176e810b719aced6a90381e64a96d346b8a6803a06b7192b7ad5"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==4.1.0"
|
||||
"version": "==4.3.0"
|
||||
},
|
||||
"more-itertools": {
|
||||
"hashes": [
|
||||
"sha256:0125e8f60e9e031347105eb1682cef932f5e97d7b9a1a28d9bf00c22a5daef40",
|
||||
"sha256:590044e3942351a1bdb1de960b739ff4ce277960f2425ad4509446dbace8d9d1"
|
||||
"sha256:2112d2ca570bb7c3e53ea1a35cd5df42bb0fd10c45f0fb97178679c3c03d64c7",
|
||||
"sha256:c3e4748ba1aad8dba30a4886b0b1a2004f9a863837b8654e7059eebf727afa5a"
|
||||
],
|
||||
"markers": "python_version > '2.7'",
|
||||
"version": "==6.0.0"
|
||||
"version": "==7.0.0"
|
||||
},
|
||||
"mypy": {
|
||||
"hashes": [
|
||||
"sha256:308c274eb8482fbf16006f549137ddc0d69e5a589465e37b99c4564414363ca7",
|
||||
"sha256:e80fd6af34614a0e898a57f14296d0dacb584648f0339c2e000ddbf0f4cc2f8d"
|
||||
"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.670"
|
||||
"version": "==0.701"
|
||||
},
|
||||
"mypy-extensions": {
|
||||
"hashes": [
|
||||
@@ -483,10 +551,10 @@
|
||||
},
|
||||
"nbconvert": {
|
||||
"hashes": [
|
||||
"sha256:302554a2e219bc0fc84f3edd3e79953f3767b46ab67626fdec16e38ba3f7efe4",
|
||||
"sha256:5de8fb2284422272a1d45abc77c07b888127550a6d602ce619592a2b08a474ff"
|
||||
"sha256:138381baa41d83584459b5cfecfc38c800ccf1f37d9ddd0bd440783346a4c39c",
|
||||
"sha256:4a978548d8383f6b2cfca4a3b0543afb77bc7cb5a96e8b424337ab58c12da9bc"
|
||||
],
|
||||
"version": "==5.4.1"
|
||||
"version": "==5.5.0"
|
||||
},
|
||||
"nbformat": {
|
||||
"hashes": [
|
||||
@@ -497,10 +565,10 @@
|
||||
},
|
||||
"notebook": {
|
||||
"hashes": [
|
||||
"sha256:18a98858c0331fb65a60f2ebb6439f8c0c4defd14ca363731b6cabc7f61624b4",
|
||||
"sha256:cc027a62be0f7756e0ef3d2d98458c4d7f4b3566449fb1a05891207f5bd9a1bf"
|
||||
"sha256:573e0ae650c5d76b18b6e564ba6d21bf321d00847de1d215b418acb64f056eb8",
|
||||
"sha256:f64fa6624d2323fbef6210a621817d6505a45d0d4a9367f1843b20a38a4666ee"
|
||||
],
|
||||
"version": "==5.7.6"
|
||||
"version": "==5.7.8"
|
||||
},
|
||||
"pandocfilters": {
|
||||
"hashes": [
|
||||
@@ -510,18 +578,18 @@
|
||||
},
|
||||
"parso": {
|
||||
"hashes": [
|
||||
"sha256:4580328ae3f548b358f4901e38c0578229186835f0fa0846e47369796dd5bcc9",
|
||||
"sha256:68406ebd7eafe17f8e40e15a84b56848eccbf27d7c1feb89e93d8fca395706db"
|
||||
"sha256:17cc2d7a945eb42c3569d4564cdf49bde221bc2b552af3eca9c1aad517dcdd33",
|
||||
"sha256:2e9574cb12e7112a87253e14e2c380ce312060269d04bd018478a3c92ea9a376"
|
||||
],
|
||||
"version": "==0.3.4"
|
||||
"version": "==0.4.0"
|
||||
},
|
||||
"pexpect": {
|
||||
"hashes": [
|
||||
"sha256:2a8e88259839571d1251d278476f3eec5db26deb73a70be5ed5dc5435e418aba",
|
||||
"sha256:3fbd41d4caf27fa4a377bfd16fef87271099463e6fa73e92a52f92dfee5d425b"
|
||||
"sha256:2094eefdfcf37a1fdbfb9aa090862c1a4878e5c7e0e7e7088bdb511c558e5cd1",
|
||||
"sha256:9e2c1fd0e6ee3a49b28f95d4b33bc389c89b20af6a1255906e90ff1262ce62eb"
|
||||
],
|
||||
"markers": "sys_platform != 'win32'",
|
||||
"version": "==4.6.0"
|
||||
"version": "==4.7.0"
|
||||
},
|
||||
"pickleshare": {
|
||||
"hashes": [
|
||||
@@ -532,10 +600,10 @@
|
||||
},
|
||||
"pluggy": {
|
||||
"hashes": [
|
||||
"sha256:19ecf9ce9db2fce065a7a0586e07cfb4ac8614fe96edf628a264b1c70116cf8f",
|
||||
"sha256:84d306a647cc805219916e62aab89caa97a33a1dd8c342e87a37f91073cd4746"
|
||||
"sha256:25a1bc1d148c9a640211872b4ff859878d422bccb59c9965e04eed468a0aa180",
|
||||
"sha256:964cedd2b27c492fbf0b7f58b3284a09cf7f99b0f715941fb24a439b3af1bd1a"
|
||||
],
|
||||
"version": "==0.9.0"
|
||||
"version": "==0.11.0"
|
||||
},
|
||||
"prometheus-client": {
|
||||
"hashes": [
|
||||
@@ -582,10 +650,10 @@
|
||||
},
|
||||
"pygments": {
|
||||
"hashes": [
|
||||
"sha256:5ffada19f6203563680669ee7f53b64dabbeb100eb51b61996085e99c03b284a",
|
||||
"sha256:e8218dd399a61674745138520d0d4cf2621d7e032439341bc3f647bff125818d"
|
||||
"sha256:31cba6ffb739f099a85e243eff8cb717089fdd3c7300767d9fc34cb8e1b065f5",
|
||||
"sha256:5ad302949b3c98dd73f8d9fcdc7e9cb592f120e32a18e23efd7f3dc51194472b"
|
||||
],
|
||||
"version": "==2.3.1"
|
||||
"version": "==2.4.0"
|
||||
},
|
||||
"pymdown-extensions": {
|
||||
"hashes": [
|
||||
@@ -596,25 +664,25 @@
|
||||
},
|
||||
"pyrsistent": {
|
||||
"hashes": [
|
||||
"sha256:3ca82748918eb65e2d89f222b702277099aca77e34843c5eb9d52451173970e2"
|
||||
"sha256:16692ee739d42cf5e39cef8d27649a8c1fdb7aa99887098f1460057c5eb75c3a"
|
||||
],
|
||||
"version": "==0.14.11"
|
||||
"version": "==0.15.2"
|
||||
},
|
||||
"pytest": {
|
||||
"hashes": [
|
||||
"sha256:592eaa2c33fae68c7d75aacf042efc9f77b27c08a6224a4f59beab8d9a420523",
|
||||
"sha256:ad3ad5c450284819ecde191a654c09b0ec72257a2c711b9633d677c71c9850c4"
|
||||
"sha256:1a8aa4fa958f8f451ac5441f3ac130d9fc86ea38780dd2715e6d5c5882700b24",
|
||||
"sha256:b8bf138592384bd4e87338cb0f256bf5f615398a649d4bd83915f0e4047a5ca6"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==4.3.1"
|
||||
"version": "==4.5.0"
|
||||
},
|
||||
"pytest-cov": {
|
||||
"hashes": [
|
||||
"sha256:0ab664b25c6aa9716cbf203b17ddb301932383046082c081b9848a0edf5add33",
|
||||
"sha256:230ef817450ab0699c6cc3c9c8f7a829c34674456f2ed8df1fe1d39780f7c87f"
|
||||
"sha256:2b097cde81a302e1047331b48cadacf23577e431b61e9c6f49a1170bbe3d3da6",
|
||||
"sha256:e00ea4fdde970725482f1f35630d12f074e121a23801aabf2ae154ec6bdd343a"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==2.6.1"
|
||||
"version": "==2.7.1"
|
||||
},
|
||||
"python-dateutil": {
|
||||
"hashes": [
|
||||
@@ -684,18 +752,18 @@
|
||||
},
|
||||
"qtconsole": {
|
||||
"hashes": [
|
||||
"sha256:1ac4a65e81a27b0838330a6d351c2f8435d4013d98a95373e8a41119b2968390",
|
||||
"sha256:bc1ba15f50c29ed50f1268ad823bb6543be263c18dd093b80495e9df63b003ac"
|
||||
"sha256:a667558c7b1e1442a2e5bcef1686c55e096efd0b58d8b2a0a8415f4579991ee3",
|
||||
"sha256:fdfc6002d9d2834c88f9c92e0f6f590284ff3740fa53016f188a62d58bcca6d8"
|
||||
],
|
||||
"version": "==4.4.3"
|
||||
"version": "==4.4.4"
|
||||
},
|
||||
"requests": {
|
||||
"hashes": [
|
||||
"sha256:502a824f31acdacb3a35b6690b5fbf0bc41d63a24a45c4004352b0242707598e",
|
||||
"sha256:7bf2a778576d825600030a110f3c0e3e8edc51dfaafe1c146e39a2027784957b"
|
||||
"sha256:11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4",
|
||||
"sha256:9cf5292fcd0f598c671cfc1e0d7d1a7f13bb8085e9a590f48c010551dc6c4b31"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==2.21.0"
|
||||
"version": "==2.22.0"
|
||||
},
|
||||
"send2trash": {
|
||||
"hashes": [
|
||||
@@ -713,16 +781,16 @@
|
||||
},
|
||||
"sqlalchemy": {
|
||||
"hashes": [
|
||||
"sha256:781fb7b9d194ed3fc596b8f0dd4623ff160e3e825dd8c15472376a438c19598b"
|
||||
"sha256:91c54ca8345008fceaec987e10924bf07dcab36c442925357e5a467b36a38319"
|
||||
],
|
||||
"version": "==1.3.1"
|
||||
"version": "==1.3.3"
|
||||
},
|
||||
"terminado": {
|
||||
"hashes": [
|
||||
"sha256:55abf9ade563b8f9be1f34e4233c7b7bde726059947a593322e8a553cc4c067a",
|
||||
"sha256:65011551baff97f5414c67018e908110693143cfbaeb16831b743fe7cad8b927"
|
||||
"sha256:d9d012de63acb8223ac969c17c3043337c2fcfd28f3aea1ee429b345d01ef460",
|
||||
"sha256:de08e141f83c3a0798b050ecb097ab6259c3f0331b2f7b7750c9075ced2c20c2"
|
||||
],
|
||||
"version": "==0.8.1"
|
||||
"version": "==0.8.2"
|
||||
},
|
||||
"testpath": {
|
||||
"hashes": [
|
||||
@@ -759,27 +827,27 @@
|
||||
},
|
||||
"typed-ast": {
|
||||
"hashes": [
|
||||
"sha256:035a54ede6ce1380599b2ce57844c6554666522e376bd111eb940fbc7c3dad23",
|
||||
"sha256:037c35f2741ce3a9ac0d55abfcd119133cbd821fffa4461397718287092d9d15",
|
||||
"sha256:049feae7e9f180b64efacbdc36b3af64a00393a47be22fa9cb6794e68d4e73d3",
|
||||
"sha256:19228f7940beafc1ba21a6e8e070e0b0bfd1457902a3a81709762b8b9039b88d",
|
||||
"sha256:2ea681e91e3550a30c2265d2916f40a5f5d89b59469a20f3bad7d07adee0f7a6",
|
||||
"sha256:3a6b0a78af298d82323660df5497bcea0f0a4a25a0b003afd0ce5af049bd1f60",
|
||||
"sha256:5385da8f3b801014504df0852bf83524599df890387a3c2b17b7caa3d78b1773",
|
||||
"sha256:606d8afa07eef77280c2bf84335e24390055b478392e1975f96286d99d0cb424",
|
||||
"sha256:69245b5b23bbf7fb242c9f8f08493e9ecd7711f063259aefffaeb90595d62287",
|
||||
"sha256:6f6d839ab09830d59b7fa8fb6917023d8cb5498ee1f1dbd82d37db78eb76bc99",
|
||||
"sha256:730888475f5ac0e37c1de4bd05eeb799fdb742697867f524dc8a4cd74bcecc23",
|
||||
"sha256:9819b5162ffc121b9e334923c685b0d0826154e41dfe70b2ede2ce29034c71d8",
|
||||
"sha256:9e60ef9426efab601dd9aa120e4ff560f4461cf8442e9c0a2b92548d52800699",
|
||||
"sha256:af5fbdde0690c7da68e841d7fc2632345d570768ea7406a9434446d7b33b0ee1",
|
||||
"sha256:b64efdbdf3bbb1377562c179f167f3bf301251411eb5ac77dec6b7d32bcda463",
|
||||
"sha256:bac5f444c118aeb456fac1b0b5d14c6a71ea2a42069b09c176f75e9bd4c186f6",
|
||||
"sha256:bda9068aafb73859491e13b99b682bd299c1b5fd50644d697533775828a28ee0",
|
||||
"sha256:d659517ca116e6750101a1326107d3479028c5191f0ecee3c7203c50f5b915b0",
|
||||
"sha256:eddd3fb1f3e0f82e5915a899285a39ee34ce18fd25d89582bc89fc9fb16cd2c6"
|
||||
"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.1"
|
||||
"version": "==1.3.5"
|
||||
},
|
||||
"ujson": {
|
||||
"hashes": [
|
||||
@@ -790,17 +858,17 @@
|
||||
},
|
||||
"urllib3": {
|
||||
"hashes": [
|
||||
"sha256:61bf29cada3fc2fbefad4fdf059ea4bd1b4a86d2b6d15e1c7c0b582b9752fe39",
|
||||
"sha256:de9529817c93f27c8ccbfead6985011db27bd0ddfcdb2d86f3f663385c6a9c22"
|
||||
"sha256:a53063d8b9210a7bdec15e7b272776b9d42b2fd6816401a0d43006ad2f9902db",
|
||||
"sha256:d363e3607d8de0c220d31950a8f38b18d5ba7c0830facd71a1c6b1036b7ce06c"
|
||||
],
|
||||
"version": "==1.24.1"
|
||||
"version": "==1.25.2"
|
||||
},
|
||||
"uvicorn": {
|
||||
"hashes": [
|
||||
"sha256:d700b65169820fc260f39402b7f966c178691daaa40cb376cad99d7cd737f772"
|
||||
"sha256:c10da7a54a6552279870900c881a2f1726314e2dd6270d4d3f9251683c643783"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.7.0b1"
|
||||
"version": "==0.7.1"
|
||||
},
|
||||
"uvloop": {
|
||||
"hashes": [
|
||||
|
||||
26
README.md
@@ -43,6 +43,28 @@ The key features are:
|
||||
|
||||
<small>* estimation based on tests on an internal development team, building production applications.</small>
|
||||
|
||||
## Opinions
|
||||
|
||||
"*[...] I'm using **FastAPI** a ton these days. [...] I'm actually planning to use it for all of my team's **ML services at Microsoft**. Some of them are getting integrated into the core **Windows** product and some **Office** products.*"
|
||||
|
||||
<div style="text-align: right; margin-right: 10%;">Kabir Khan - <strong>Microsoft</strong> <a href="https://github.com/tiangolo/fastapi/pull/26" target="_blank"><small>(ref)</small></a></div>
|
||||
|
||||
---
|
||||
|
||||
"*I’m over the moon excited about **FastAPI**. It’s so fun!*"
|
||||
|
||||
<div style="text-align: right; margin-right: 10%;">Brian Okken - <strong><a href="https://pythonbytes.fm/episodes/show/123/time-to-right-the-py-wrongs?time_in_sec=855" target="_blank">Python Bytes</a> podcast host</strong> <a href="https://twitter.com/brianokken/status/1112220079972728832" target="_blank"><small>(ref)</small></a></div>
|
||||
|
||||
---
|
||||
|
||||
"*Honestly, what you've built looks super solid and polished. In many ways, it's what I wanted **Hug** to be - it's really inspiring to see someone build that.*"
|
||||
|
||||
<div style="text-align: right; margin-right: 10%;">Timothy Crosley - <strong><a href="http://www.hug.rest/" target="_blank">Hug</a> creator</strong> <a href="https://news.ycombinator.com/item?id=19455465" target="_blank"><small>(ref)</small></a></div>
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
## Requirements
|
||||
|
||||
@@ -60,7 +82,7 @@ FastAPI stands on the shoulders of giants:
|
||||
$ pip install fastapi
|
||||
```
|
||||
|
||||
You will also need an ASGI server, for production such as <a href="http://www.uvicorn.org" target="_blank">uvicorn</a>.
|
||||
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
|
||||
@@ -198,7 +220,7 @@ def read_item(item_id: int, q: str = None):
|
||||
|
||||
|
||||
@app.put("/items/{item_id}")
|
||||
def create_item(item_id: int, item: Item):
|
||||
def update_item(item_id: int, item: Item):
|
||||
return {"item_name": item.name, "item_id": item_id}
|
||||
```
|
||||
|
||||
|
||||
@@ -236,6 +236,19 @@ It was one of the first extremely fast Python frameworks based on `asyncio`. It
|
||||
|
||||
That's why **FastAPI** is based on Starlette, as it is the fastest framework available (tested by third-party benchmarks).
|
||||
|
||||
### <a href="https://falconframework.org/" target="_blank">Falcon</a>
|
||||
|
||||
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 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.
|
||||
|
||||
So, data validation, serialization, and documentation, have to be done in code, not automatically. Or they have to be implemented as a framework on top of Falcon, like Hug. This same distinction happens in other frameworks that are inspired by Falcon's design, of having one request object and one response object as parameters.
|
||||
|
||||
!!! check "Inspired **FastAPI** to"
|
||||
Find ways to get great performance.
|
||||
|
||||
### <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:
|
||||
@@ -257,11 +270,34 @@ Routes are declared in a single place, using functions declared in other places
|
||||
|
||||
This actually inspired updating parts of Pydantic, to support the same validation declaration style (all this functionality is now already available in Pydantic).
|
||||
|
||||
### <a href="http://www.hug.rest/" target="_blank">Hug</a>
|
||||
|
||||
Hug was one of the first frameworks to implement the declaration of API parameter types using Python type hints. This was a great idea that inspired other tools to do the same.
|
||||
|
||||
It used custom types in its declarations instead of standard Python types, but it was still a huge step forward.
|
||||
|
||||
It also was one of the first frameworks to generate a custom schema declaring the whole API in JSON.
|
||||
|
||||
It was not based on a standard like OpenAPI and JSON Schema. So it wouldn't be straightforward to integrate it with other tools, like Swagger UI. But again, it was a very innovative idea.
|
||||
|
||||
It has an interesting, uncommon feature: using the same framework, it's possible to create APIs and also CLIs.
|
||||
|
||||
As it is based on the previous standard for synchronous Python web frameworks (WSGI), it can't handle Websockets and other things, although it still has high performance too.
|
||||
|
||||
!!! info
|
||||
Hug was created by Timothy Crosley, the same creator of <a href="https://github.com/timothycrosley/isort" target="_blank">`isort`</a>, a great tool to automatically sort imports in Python files.
|
||||
|
||||
!!! check "Ideas inspired in **FastAPI**"
|
||||
Hug inspired parts of APIStar, and was one of the tools I found most promising, alongside APIStar.
|
||||
|
||||
Hug helped inspiring **FastAPI** to use Python type hints to declare parameters, and to generate a schema defining the API automatically.
|
||||
|
||||
|
||||
### <a href="https://github.com/encode/apistar" target="_blank">APIStar</a> (<= 0.5)
|
||||
|
||||
Right before deciding to build **FastAPI** I found **APIStar** server. It had almost everything I was looking for and had a great design.
|
||||
|
||||
It was actually the first implementation of a framework using Python type hints to declare parameters and requests that I ever saw (before NestJS and Molten).
|
||||
It was one of the first implementations of a framework using Python type hints to declare parameters and requests that I ever saw (before NestJS and Molten). I found it more or less at the same time as Hug. But APIStar used the OpenAPI standard.
|
||||
|
||||
It had automatic data validation, data serialization and OpenAPI schema generation based on the same type hints in several places.
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Independent TechEmpower benchmarks show **FastAPI** applications running under Uvicorn as <a href="https://www.techempower.com/benchmarks/#section=test&runid=a979de55-980d-4721-a46f-77298b3f3923&hw=ph&test=fortune&l=zijzen-7" target="_blank">one of the fastest Python frameworks available</a>, only below Starlette and Uvicorn themselves (used internally by FastAPI). (*)
|
||||
Independent TechEmpower benchmarks show **FastAPI** applications running under Uvicorn as <a href="https://www.techempower.com/benchmarks/#section=test&runid=7464e520-0dc2-473d-bd34-dbdfd7e85911&hw=ph&test=query&l=zijzen-7" target="_blank">one of the fastest Python frameworks available</a>, only below Starlette and Uvicorn themselves (used internally by FastAPI). (*)
|
||||
|
||||
But when checking benchmarks and comparisons you should have the following in mind.
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
It is recommended to use <a href="https://www.docker.com/" target="_blank">**Docker**</a> for security, replicability, development simplicity, etc.
|
||||
You can use <a href="https://www.docker.com/" target="_blank">**Docker**</a> for deployment. It has several advantages like security, replicability, development simplicity, etc.
|
||||
|
||||
In this section you'll see instructions and links to guides to know how to:
|
||||
|
||||
@@ -237,7 +237,21 @@ The generated project has instructions to deploy it, doing it takes other 2 min.
|
||||
|
||||
You can deploy **FastAPI** directly without Docker too.
|
||||
|
||||
You just need to install <a href="https://www.uvicorn.org/" target="_blank">Uvicorn</a> (or any other ASGI server).
|
||||
You just need to install an ASGI compatible server like:
|
||||
|
||||
* <a href="https://www.uvicorn.org/" target="_blank">Uvicorn</a>, a lightning-fast ASGI server, built on uvloop and httptools.
|
||||
|
||||
```bash
|
||||
pip install uvicorn
|
||||
```
|
||||
|
||||
* <a href="https://gitlab.com/pgjones/hypercorn" target="_blank">Hypercorn</a>, an ASGI server also compatible with HTTP/2.
|
||||
|
||||
```bash
|
||||
pip install hypercorn
|
||||
```
|
||||
|
||||
...or any other ASGI server.
|
||||
|
||||
And run your application the same way you have done in the tutorials, but without the `--reload` option, e.g.:
|
||||
|
||||
@@ -245,9 +259,15 @@ And run your application the same way you have done in the tutorials, but withou
|
||||
uvicorn main:app --host 0.0.0.0 --port 80
|
||||
```
|
||||
|
||||
or with Hypercorn:
|
||||
|
||||
```bash
|
||||
hypercorn main:app --bind 0.0.0.0:80
|
||||
```
|
||||
|
||||
You might want to set up some tooling to make sure it is restarted automatically if it stops.
|
||||
|
||||
You might also want to install <a href="https://gunicorn.org/" target="_blank">Gunicorn</a> and <a href="https://www.uvicorn.org/#running-with-gunicorn" target="_blank">use it as a manager for Uvicorn</a>.
|
||||
You might also want to install <a href="https://gunicorn.org/" target="_blank">Gunicorn</a> and <a href="https://www.uvicorn.org/#running-with-gunicorn" target="_blank">use it as a manager for Uvicorn</a>, or use Hypercorn with multiple workers.
|
||||
|
||||
Making sure to fine-tune the number of workers, etc.
|
||||
|
||||
|
||||
BIN
docs/img/tutorial/additional-responses/image01.png
Normal file
|
After Width: | Height: | Size: 80 KiB |
|
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 76 KiB |
|
Before Width: | Height: | Size: 87 KiB After Width: | Height: | Size: 85 KiB |
|
Before Width: | Height: | Size: 85 KiB After Width: | Height: | Size: 90 KiB |
BIN
docs/img/tutorial/security/image11.png
Normal file
|
After Width: | Height: | Size: 79 KiB |
BIN
docs/img/tutorial/security/image12.png
Normal file
|
After Width: | Height: | Size: 68 KiB |
@@ -43,6 +43,28 @@ The key features are:
|
||||
|
||||
<small>* estimation based on tests on an internal development team, building production applications.</small>
|
||||
|
||||
## Opinions
|
||||
|
||||
"*[...] I'm using **FastAPI** a ton these days. [...] I'm actually planning to use it for all of my team's **ML services at Microsoft**. Some of them are getting integrated into the core **Windows** product and some **Office** products.*"
|
||||
|
||||
<div style="text-align: right; margin-right: 10%;">Kabir Khan - <strong>Microsoft</strong> <a href="https://github.com/tiangolo/fastapi/pull/26" target="_blank"><small>(ref)</small></a></div>
|
||||
|
||||
---
|
||||
|
||||
"*I’m over the moon excited about **FastAPI**. It’s so fun!*"
|
||||
|
||||
<div style="text-align: right; margin-right: 10%;">Brian Okken - <strong><a href="https://pythonbytes.fm/episodes/show/123/time-to-right-the-py-wrongs?time_in_sec=855" target="_blank">Python Bytes</a> podcast host</strong> <a href="https://twitter.com/brianokken/status/1112220079972728832" target="_blank"><small>(ref)</small></a></div>
|
||||
|
||||
---
|
||||
|
||||
"*Honestly, what you've built looks super solid and polished. In many ways, it's what I wanted **Hug** to be - it's really inspiring to see someone build that.*"
|
||||
|
||||
<div style="text-align: right; margin-right: 10%;">Timothy Crosley - <strong><a href="http://www.hug.rest/" target="_blank">Hug</a> creator</strong> <a href="https://news.ycombinator.com/item?id=19455465" target="_blank"><small>(ref)</small></a></div>
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
## Requirements
|
||||
|
||||
@@ -60,7 +82,7 @@ FastAPI stands on the shoulders of giants:
|
||||
$ pip install fastapi
|
||||
```
|
||||
|
||||
You will also need an ASGI server, for production such as <a href="http://www.uvicorn.org" target="_blank">uvicorn</a>.
|
||||
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
|
||||
@@ -198,7 +220,7 @@ def read_item(item_id: int, q: str = None):
|
||||
|
||||
|
||||
@app.put("/items/{item_id}")
|
||||
def create_item(item_id: int, item: Item):
|
||||
def update_item(item_id: int, item: Item):
|
||||
return {"item_name": item.name, "item_id": item_id}
|
||||
```
|
||||
|
||||
|
||||
@@ -1,164 +1,355 @@
|
||||
## Next release
|
||||
|
||||
## 0.25.0
|
||||
|
||||
* Add support for Pydantic's `include`, `exclude`, `by_alias`.
|
||||
* Update documentation: [Response Model](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude).
|
||||
* Add docs for: [Body - updates](https://fastapi.tiangolo.com/tutorial/body-updates/), using Pydantic's `skip_defaults`.
|
||||
* Add method consistency tests.
|
||||
* PR [#264](https://github.com/tiangolo/fastapi/pull/264).
|
||||
|
||||
* Add `CONTRIBUTING.md` file to GitHub, to help new contributors. PR [#255](https://github.com/tiangolo/fastapi/pull/255) by [@wshayes](https://github.com/wshayes).
|
||||
|
||||
* Add support for Pydantic's `skip_defaults`:
|
||||
* There's a new *path operation decorator* parameter `response_model_skip_defaults`.
|
||||
* The name of the parameter will most probably change in a future version to `response_skip_defaults`, `model_skip_defaults` or something similar.
|
||||
* New [documentation section about using `response_model_skip_defaults`](https://fastapi.tiangolo.com/tutorial/response-model/#response-model-encoding-parameters).
|
||||
* PR [#248](https://github.com/tiangolo/fastapi/pull/248) by [@wshayes](https://github.com/wshayes).
|
||||
|
||||
## 0.24.0
|
||||
|
||||
* Add support for WebSockets with dependencies and parameters.
|
||||
* Support included for:
|
||||
* `Depends`
|
||||
* `Security`
|
||||
* `Cookie`
|
||||
* `Header`
|
||||
* `Path`
|
||||
* `Query`
|
||||
* ...as these are compatible with the WebSockets protocol (e.g. `Body` is not).
|
||||
* [Updated documentation for WebSockets](https://fastapi.tiangolo.com/tutorial/websockets/).
|
||||
* PR [#178](https://github.com/tiangolo/fastapi/pull/178) by [@jekirl](https://github.com/jekirl).
|
||||
|
||||
* Upgrade the compatible version of Pydantic to `0.26.0`.
|
||||
* This includes JSON Schema support for IP address and network objects, bug fixes, and other features.
|
||||
* PR [#247](https://github.com/tiangolo/fastapi/pull/247) by [@euri10](https://github.com/euri10).
|
||||
|
||||
## 0.23.0
|
||||
|
||||
* Upgrade the compatible version of Starlette to `0.12.0`.
|
||||
* This includes support for ASGI 3 (the latest version of the standard).
|
||||
* It's now possible to use [Starlette's `StreamingResponse`](https://www.starlette.io/responses/#streamingresponse) with iterators, like [file-like](https://docs.python.org/3/glossary.html#term-file-like-object) objects (as those returned by `open()`).
|
||||
* It's now possible to use the low level utility `iterate_in_threadpool` from `starlette.concurrency` (for advanced scenarios).
|
||||
* PR [#243](https://github.com/tiangolo/fastapi/pull/243).
|
||||
|
||||
* Add OAuth2 redirect page for Swagger UI. This allows having delegated authentication in the Swagger UI docs. For this to work, you need to add `{your_origin}/docs/oauth2-redirect` to the allowed callbacks in your OAuth2 provider (in Auth0, Facebook, Google, etc).
|
||||
* For example, during development, it could be `http://localhost:8000/docs/oauth2-redirect`.
|
||||
* Have in mind that this callback URL is independent of whichever one is used by your frontend. You might also have another callback at `https://yourdomain.com/login/callback`.
|
||||
* This is only to allow delegated authentication in the API docs with Swagger UI.
|
||||
* PR [#198](https://github.com/tiangolo/fastapi/pull/198) by [@steinitzu](https://github.com/steinitzu).
|
||||
|
||||
* Make Swagger UI and ReDoc route handlers (*path operations*) be `async` functions instead of lambdas to improve performance. PR [#241](https://github.com/tiangolo/fastapi/pull/241) by [@Trim21](https://github.com/Trim21).
|
||||
|
||||
* Make Swagger UI and ReDoc URLs parameterizable, allowing to host and serve local versions of them and have offline docs. PR [#112](https://github.com/tiangolo/fastapi/pull/112) by [@euri10](https://github.com/euri10).
|
||||
|
||||
## 0.22.0
|
||||
|
||||
* Add support for `dependencies` parameter:
|
||||
* A parameter in *path operation decorators*, for dependencies that should be executed but the return value is not important or not used in the *path operation function*.
|
||||
* A parameter in the `.include_router()` method of FastAPI applications and routers, to include dependencies that should be executed in each *path operation* in a router.
|
||||
* This is useful, for example, to require authentication or permissions in specific group of *path operations*.
|
||||
* Different `dependencies` can be applied to different routers.
|
||||
* These `dependencies` are run before the normal parameter dependencies. And normal dependencies are run too. They can be combined.
|
||||
* Dependencies declared in a router are executed first, then the ones defined in *path operation decorators*, and then the ones declared in normal parameters. They are all combined and executed.
|
||||
* All this also supports using `Security` with `scopes` in those `dependencies` parameters, for more advanced OAuth 2.0 security scenarios with scopes.
|
||||
* New documentation about [dependencies in *path operation decorators*](https://fastapi.tiangolo.com/tutorial/dependencies/dependencies-in-path-operation-decorators/).
|
||||
* New documentation about [dependencies in the `include_router()` method](https://fastapi.tiangolo.com/tutorial/bigger-applications/#include-an-apirouter-with-a-prefix-tags-responses-and-dependencies).
|
||||
* PR [#235](https://github.com/tiangolo/fastapi/pull/235).
|
||||
|
||||
* Fix OpenAPI documentation of Starlette URL convertors. Specially useful when using `path` convertors, to take a whole path as a parameter, like `/some/url/{p:path}`. PR [#234](https://github.com/tiangolo/fastapi/pull/234) by [@euri10](https://github.com/euri10).
|
||||
|
||||
* Make default parameter utilities exported from `fastapi` be functions instead of classes (the new functions return instances of those classes). To be able to override the return types and fix `mypy` errors in FastAPI's users' code. Applies to `Path`, `Query`, `Header`, `Cookie`, `Body`, `Form`, `File`, `Depends`, and `Security`. PR [#226](https://github.com/tiangolo/fastapi/pull/226) and PR [#231](https://github.com/tiangolo/fastapi/pull/231).
|
||||
|
||||
* Separate development scripts `test.sh`, `lint.sh`, and `format.sh`. PR [#232](https://github.com/tiangolo/fastapi/pull/232).
|
||||
|
||||
* Re-enable `black` formatting checks for Python 3.7. PR [#229](https://github.com/tiangolo/fastapi/pull/229) by [@zamiramir](https://github.com/zamiramir).
|
||||
|
||||
## 0.21.0
|
||||
|
||||
* On body parsing errors, raise `from` previous exception, to allow better introspection in logging code. PR [#192](https://github.com/tiangolo/fastapi/pull/195) by [@ricardomomm](https://github.com/ricardomomm).
|
||||
|
||||
* Use Python logger named "`fastapi`" instead of root logger. PR [#222](https://github.com/tiangolo/fastapi/pull/222) by [@euri10](https://github.com/euri10).
|
||||
|
||||
* Upgrade Pydantic to version 0.25. PR [#225](https://github.com/tiangolo/fastapi/pull/225) by [@euri10](https://github.com/euri10).
|
||||
|
||||
* Fix typo in routing. PR [#221](https://github.com/tiangolo/fastapi/pull/221) by [@djlambert](https://github.com/djlambert).
|
||||
|
||||
## 0.20.1
|
||||
|
||||
* Add typing information to package including file `py.typed`. PR [#209](https://github.com/tiangolo/fastapi/pull/209) by [@meadsteve](https://github.com/meadsteve).
|
||||
|
||||
* Add FastAPI bot for Gitter. To automatically announce new releases. PR [#189](https://github.com/tiangolo/fastapi/pull/189).
|
||||
|
||||
## 0.20.0
|
||||
|
||||
* Upgrade OAuth2:
|
||||
* Upgrade Password flow using Bearer tokens to use the correct HTTP status code 401 `UNAUTHORIZED`, with `WWW-Authenticate` headers.
|
||||
* Update, simplify, and improve all the [security docs](https://fastapi.tiangolo.com/tutorial/security/intro/).
|
||||
* Add new `scope_str` to `SecurityScopes` and update docs: [OAuth2 scopes](https://fastapi.tiangolo.com/tutorial/security/oauth2-scopes/).
|
||||
* Update docs, images, tests.
|
||||
* PR [#188](https://github.com/tiangolo/fastapi/pull/188).
|
||||
|
||||
* Include [Hypercorn](https://gitlab.com/pgjones/hypercorn) as an alternative ASGI server in the docs. PR [#187](https://github.com/tiangolo/fastapi/pull/187).
|
||||
|
||||
* Add docs for [Static Files](https://fastapi.tiangolo.com/tutorial/static-files/) and [Templates](https://fastapi.tiangolo.com/tutorial/templates/). PR [#186](https://github.com/tiangolo/fastapi/pull/186).
|
||||
|
||||
* Add docs for handling [Response Cookies](https://fastapi.tiangolo.com/tutorial/response-cookies/) and [Response Headers](https://fastapi.tiangolo.com/tutorial/response-headers/). PR [#185](https://github.com/tiangolo/fastapi/pull/185).
|
||||
|
||||
* Fix typos in docs. PR [#176](https://github.com/tiangolo/fastapi/pull/176) by [@chdsbd](https://github.com/chdsbd).
|
||||
|
||||
## 0.19.0
|
||||
|
||||
* Rename _path operation decorator_ parameter `content_type` to `response_class`. PR [#183](https://github.com/tiangolo/fastapi/pull/183).
|
||||
|
||||
* Add docs:
|
||||
* How to use the `jsonable_encoder` in [JSON compatible encoder](https://fastapi.tiangolo.com/tutorial/encoder/).
|
||||
* How to [Return a Response directly](https://fastapi.tiangolo.com/tutorial/response-directly/).
|
||||
* Update how to use a [Custom Response Class](https://fastapi.tiangolo.com/tutorial/custom-response/).
|
||||
* PR [#184](https://github.com/tiangolo/fastapi/pull/184).
|
||||
|
||||
## 0.18.0
|
||||
|
||||
* Add docs for [HTTP Basic Auth](https://fastapi.tiangolo.com/tutorial/custom-response/). PR [#177](https://github.com/tiangolo/fastapi/pull/177).
|
||||
|
||||
* Upgrade HTTP Basic Auth handling with automatic headers (automatic browser login prompt). PR [#175](https://github.com/tiangolo/fastapi/pull/175).
|
||||
|
||||
* Update dependencies for security. PR [#174](https://github.com/tiangolo/fastapi/pull/174).
|
||||
|
||||
* Add docs for [Middleware](https://fastapi.tiangolo.com/tutorial/middleware/). PR [#173](https://github.com/tiangolo/fastapi/pull/173).
|
||||
|
||||
## 0.17.0
|
||||
|
||||
* Make Flit publish from CI. PR [#170](https://github.com/tiangolo/fastapi/pull/170).
|
||||
|
||||
* Add documentation about handling [CORS (Cross-Origin Resource Sharing)](https://fastapi.tiangolo.com/tutorial/cors/). PR [#169](https://github.com/tiangolo/fastapi/pull/169).
|
||||
|
||||
* By default, encode by alias. This allows using Pydantic `alias` parameters working by default. PR [#168](https://github.com/tiangolo/fastapi/pull/168).
|
||||
|
||||
## 0.16.0
|
||||
|
||||
* Upgrade _path operation_ `doctsring` parsing to support proper Markdown descriptions. New documentation at [Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/#description-from-docstring). PR [#163](https://github.com/tiangolo/fastapi/pull/163).
|
||||
|
||||
* Refactor internal usage of Pydantic to use correct data types. PR [#164](https://github.com/tiangolo/fastapi/pull/164).
|
||||
|
||||
* Upgrade Pydantic to version `0.23`. PR [#160](https://github.com/tiangolo/fastapi/pull/160) by [@euri10](https://github.com/euri10).
|
||||
|
||||
* Fix typo in Tutorial about Extra Models. PR [#159](https://github.com/tiangolo/fastapi/pull/159) by [@danielmichaels](https://github.com/danielmichaels).
|
||||
|
||||
* Fix [Query Parameters](https://fastapi.tiangolo.com/tutorial/query-params/) URL examples in docs. PR [#157](https://github.com/tiangolo/fastapi/pull/157) by [@hayata-yamamoto](https://github.com/hayata-yamamoto).
|
||||
|
||||
## 0.15.0
|
||||
|
||||
* Add support for multiple file uploads (as a single form field). New docs at: [Multiple file uploads](https://fastapi.tiangolo.com/tutorial/request-files/#multiple-file-uploads). PR [#158](https://github.com/tiangolo/fastapi/pull/158).
|
||||
|
||||
* Add docs for: [Additional Status Codes](https://fastapi.tiangolo.com/tutorial/additional-status-codes/). PR [#156](https://github.com/tiangolo/fastapi/pull/156).
|
||||
|
||||
## 0.14.0
|
||||
|
||||
* Improve automatically generated names of path operations in OpenAPI (in API docs). A function `read_items` instead of having a generated name "Read Items Get" will have "Read Items". PR [#155](https://github.com/tiangolo/fastapi/pull/155).
|
||||
|
||||
* Add docs for: [Testing **FastAPI**](https://fastapi.tiangolo.com/tutorial/testing/). PR [#151](https://github.com/tiangolo/fastapi/pull/151).
|
||||
|
||||
* Update `/docs` Swagger UI to enable deep linking. This allows sharing the URL pointing directly to the path operation documentation in the docs. PR [#148](https://github.com/tiangolo/fastapi/pull/148) by [@wshayes](https://github.com/wshayes).
|
||||
|
||||
* Update development dependencies, `Pipfile.lock`. PR [#150](https://github.com/tiangolo/fastapi/pull/150).
|
||||
|
||||
* Include Falcon and Hug in: [Alternatives, Inspiration and Comparisons](https://fastapi.tiangolo.com/alternatives/).
|
||||
|
||||
## 0.13.0
|
||||
|
||||
* Improve/upgrade OAuth2 scopes support with `SecurityScopes`:
|
||||
* `SecurityScopes` can be declared as a parameter like `Request`, to get the scopes of all super-dependencies/dependants.
|
||||
* Improve `Security` handling, merging scopes when declaring `SecurityScopes`.
|
||||
* Allow using `SecurityBase` (like `OAuth2`) classes with `Depends` and still document them. `Security` now is needed only to declare `scopes`.
|
||||
* Updated docs about: [OAuth2 with Password (and hashing), Bearer with JWT tokens](https://fastapi.tiangolo.com/tutorial/security/oauth2-jwt/).
|
||||
* New docs about: [OAuth2 scopes](https://fastapi.tiangolo.com/tutorial/security/oauth2-scopes/).
|
||||
* PR [#141](https://github.com/tiangolo/fastapi/pull/141).
|
||||
|
||||
## 0.12.1
|
||||
|
||||
* Fix bug: handling additional `responses` in `APIRouter.include_router()`. PR [#140](https://github.com/tiangolo/fastapi/pull/140).
|
||||
|
||||
* Fix typo in SQL tutorial. PR [#138](https://github.com/tiangolo/fastapi/pull/138) by [@mostaphaRoudsari](https://github.com/mostaphaRoudsari).
|
||||
|
||||
* Fix typos in section about nested models and OAuth2 with JWT. PR [#127](https://github.com/tiangolo/fastapi/pull/127) by [@mmcloud](https://github.com/mmcloud).
|
||||
|
||||
## 0.12.0
|
||||
|
||||
* Add additional `responses` parameter to _path operation decorators_ to extend responses in OpenAPI (and API docs).
|
||||
* It also allows extending existing responses generated from `response_model`, declare other media types (like images), etc.
|
||||
* The new documentation is here: [Additional Responses](https://fastapi.tiangolo.com/tutorial/additional-responses/).
|
||||
* `responses` can also be added to `.include_router()`, the updated docs are here: [Bigger Applications](https://fastapi.tiangolo.com/tutorial/bigger-applications/#add-some-custom-tags-and-responses).
|
||||
* PR [#97](https://github.com/tiangolo/fastapi/pull/97) originally initiated by [@barsi](https://github.com/barsi).
|
||||
* Update `scripts/test-cov-html.sh` to allow passing extra parameters like `-vv`, for development.
|
||||
|
||||
## 0.11.0
|
||||
|
||||
* Add `auto_error` parameter to security utility functions. Allowing them to be optional. Also allowing to have multiple alternative security schemes that are then checked in a single dependency instead of each one verifying and returning the error to the client automatically when not satisfied. PR <a href="https://github.com/tiangolo/fastapi/pull/134" target="_blank">#134</a>.
|
||||
* Add `auto_error` parameter to security utility functions. Allowing them to be optional. Also allowing to have multiple alternative security schemes that are then checked in a single dependency instead of each one verifying and returning the error to the client automatically when not satisfied. PR [#134](https://github.com/tiangolo/fastapi/pull/134).
|
||||
|
||||
* Update <a href="https://fastapi.tiangolo.com/tutorial/sql-databases/#create-a-middleware-to-handle-sessions" target="_blank">SQL Tutorial</a> to close database sessions even when there are exceptions. PR <a href="https://github.com/tiangolo/fastapi/pull/89" target="_blank">#89</a> by <a href="https://github.com/alexiri" target="_blank">@alexiri</a>.
|
||||
* Update [SQL Tutorial](https://fastapi.tiangolo.com/tutorial/sql-databases/#create-a-middleware-to-handle-sessions) to close database sessions even when there are exceptions. PR [#89](https://github.com/tiangolo/fastapi/pull/89) by [@alexiri](https://github.com/alexiri).
|
||||
|
||||
* Fix duplicate dependency in `pyproject.toml`. PR <a href="https://github.com/tiangolo/fastapi/pull/128" target="_blank">#128</a> by <a href="https://github.com/zxalif" target="_blank">@zxalif</a>.
|
||||
* Fix duplicate dependency in `pyproject.toml`. PR [#128](https://github.com/tiangolo/fastapi/pull/128) by [@zxalif](https://github.com/zxalif).
|
||||
|
||||
## 0.10.3
|
||||
|
||||
* Add Gitter chat, badge, links, etc. <a href="https://gitter.im/tiangolo/fastapi" target="_blank">https://gitter.im/tiangolo/fastapi
|
||||
</a>. PR <a href="https://github.com/tiangolo/fastapi/pull/117" target="_blank">#117</a>.
|
||||
* Add Gitter chat, badge, links, etc. [https://gitter.im/tiangolo/fastapi](https://gitter.im/tiangolo/fastapi) . PR [#117](https://github.com/tiangolo/fastapi/pull/117).
|
||||
|
||||
* Add docs about <a href="https://fastapi.tiangolo.com/tutorial/extending-openapi/" target="_blank">Extending OpenAPI</a>. PR <a href="https://github.com/tiangolo/fastapi/pull/126" target="_blank">#126</a>.
|
||||
* Add docs about [Extending OpenAPI](https://fastapi.tiangolo.com/tutorial/extending-openapi/). PR [#126](https://github.com/tiangolo/fastapi/pull/126).
|
||||
|
||||
* Make Travis run Ubuntu Xenial (newer version) and Python 3.7 instead of Python 3.7-dev. PR <a href="https://github.com/tiangolo/fastapi/pull/92" target="_blank">#92</a> by <a href="https://github.com/blueyed" target="_blank">@blueyed</a>.
|
||||
* Make Travis run Ubuntu Xenial (newer version) and Python 3.7 instead of Python 3.7-dev. PR [#92](https://github.com/tiangolo/fastapi/pull/92) by [@blueyed](https://github.com/blueyed).
|
||||
|
||||
* Fix duplicated param variable creation. PR <a href="https://github.com/tiangolo/fastapi/pull/123" target="_blank">#123</a> by <a href="https://github.com/yihuang" target="_blank">@yihuang</a>.
|
||||
* Fix duplicated param variable creation. PR [#123](https://github.com/tiangolo/fastapi/pull/123) by [@yihuang](https://github.com/yihuang).
|
||||
|
||||
* Add note in <a href="https://fastapi.tiangolo.com/tutorial/response-model/" target="_blank">Response Model docs</a> about why using a function parameter instead of a function return type annotation. PR <a href="https://github.com/tiangolo/fastapi/pull/109" target="_blank">#109</a> by <a href="https://github.com/JHSaunders" target="_blank">@JHSaunders</a>.
|
||||
* Add note in [Response Model docs](https://fastapi.tiangolo.com/tutorial/response-model/) about why using a function parameter instead of a function return type annotation. PR [#109](https://github.com/tiangolo/fastapi/pull/109) by [@JHSaunders](https://github.com/JHSaunders).
|
||||
|
||||
* Fix event docs (startup/shutdown) function name. PR <a href="https://github.com/tiangolo/fastapi/pull/105" target="_blank">#105</a> by <a href="https://github.com/stratosgear" target="_blank">@stratosgear</a>.
|
||||
* Fix event docs (startup/shutdown) function name. PR [#105](https://github.com/tiangolo/fastapi/pull/105) by [@stratosgear](https://github.com/stratosgear).
|
||||
|
||||
## 0.10.2
|
||||
|
||||
* Fix OpenAPI (JSON Schema) for declarations of Python `Union` (JSON Schema `additionalProperties`). PR <a href="https://github.com/tiangolo/fastapi/pull/121" target="_blank">#121</a>.
|
||||
* Fix OpenAPI (JSON Schema) for declarations of Python `Union` (JSON Schema `additionalProperties`). PR [#121](https://github.com/tiangolo/fastapi/pull/121).
|
||||
|
||||
* Update <a href="https://fastapi.tiangolo.com/tutorial/background-tasks/" target="_blank">Background Tasks</a> with a note on Celery.
|
||||
* Update [Background Tasks](https://fastapi.tiangolo.com/tutorial/background-tasks/) with a note on Celery.
|
||||
|
||||
* Document response models using unions and lists, updated at: <a href="https://fastapi.tiangolo.com/tutorial/extra-models/" target="_blank">Extra Models</a>. PR <a href="https://github.com/tiangolo/fastapi/pull/108" target="_blank">#108</a>.
|
||||
* Document response models using unions and lists, updated at: [Extra Models](https://fastapi.tiangolo.com/tutorial/extra-models/). PR [#108](https://github.com/tiangolo/fastapi/pull/108).
|
||||
|
||||
## 0.10.1
|
||||
|
||||
* Add docs and tests for <a href="https://github.com/encode/databases" target="_blank">encode/databases</a>. New docs at: <a href="https://fastapi.tiangolo.com/tutorial/async-sql-databases/" target="_blank">Async SQL (Relational) Databases</a>. PR <a href="https://github.com/tiangolo/fastapi/pull/107" target="_blank">#107</a>.
|
||||
* Add docs and tests for [encode/databases](https://github.com/encode/databases). New docs at: [Async SQL (Relational) Databases](https://fastapi.tiangolo.com/tutorial/async-sql-databases/). PR [#107](https://github.com/tiangolo/fastapi/pull/107).
|
||||
|
||||
## 0.10.0
|
||||
|
||||
* Add support for Background Tasks in *path operation functions* and dependencies. New documentation about <a href="https://fastapi.tiangolo.com/tutorial/background-tasks/" target="_blank">Background Tasks is here</a>. PR <a href="https://github.com/tiangolo/fastapi/pull/103" target="_blank">#103</a>.
|
||||
* Add support for Background Tasks in _path operation functions_ and dependencies. New documentation about [Background Tasks is here](https://fastapi.tiangolo.com/tutorial/background-tasks/). PR [#103](https://github.com/tiangolo/fastapi/pull/103).
|
||||
|
||||
* Add support for `.websocket_route()` in `APIRouter`. PR <a href="https://github.com/tiangolo/fastapi/pull/100" target="_blank">#100</a> by <a href="https://github.com/euri10" target="_blank">@euri10</a>.
|
||||
* Add support for `.websocket_route()` in `APIRouter`. PR [#100](https://github.com/tiangolo/fastapi/pull/100) by [@euri10](https://github.com/euri10).
|
||||
|
||||
* New docs section about <a href="https://fastapi.tiangolo.com/tutorial/events/" target="_blank">Events: startup - shutdown</a>. PR <a href="https://github.com/tiangolo/fastapi/pull/99" target="_blank">#99</a>.
|
||||
* New docs section about [Events: startup - shutdown](https://fastapi.tiangolo.com/tutorial/events/). PR [#99](https://github.com/tiangolo/fastapi/pull/99).
|
||||
|
||||
## 0.9.1
|
||||
|
||||
* Document receiving <a href="https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#query-parameter-list-multiple-values" target="_blank">Multiple values with the same query parameter</a> and <a href="https://fastapi.tiangolo.com/tutorial/header-params/#duplicate-headers" target="_blank">Duplicate headers</a>. PR <a href="https://github.com/tiangolo/fastapi/pull/95" target="_blank">#95</a>.
|
||||
* Document receiving [Multiple values with the same query parameter](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#query-parameter-list-multiple-values) and [Duplicate headers](https://fastapi.tiangolo.com/tutorial/header-params/#duplicate-headers). PR [#95](https://github.com/tiangolo/fastapi/pull/95).
|
||||
|
||||
## 0.9.0
|
||||
|
||||
* Upgrade compatible Pydantic version to `0.21.0`. PR <a href="https://github.com/tiangolo/fastapi/pull/90" target="_blank">#90</a>.
|
||||
* Upgrade compatible Pydantic version to `0.21.0`. PR [#90](https://github.com/tiangolo/fastapi/pull/90).
|
||||
|
||||
* Add documentation for: <a href="https://fastapi.tiangolo.com/tutorial/application-configuration/" target="_blank">Application Configuration</a>.
|
||||
* Add documentation for: [Application Configuration](https://fastapi.tiangolo.com/tutorial/application-configuration/).
|
||||
|
||||
* Fix typo in docs. PR <a href="https://github.com/tiangolo/fastapi/pull/76" target="_blank">#76</a> by <a href="https://github.com/matthewhegarty" target="_blank">@matthewhegarty</a>.
|
||||
* Fix typo in docs. PR [#76](https://github.com/tiangolo/fastapi/pull/76) by [@matthewhegarty](https://github.com/matthewhegarty).
|
||||
|
||||
* Fix link in "Deployment" to "Bigger Applications".
|
||||
|
||||
## 0.8.0
|
||||
|
||||
* Make development scripts executable. PR <a href="https://github.com/tiangolo/fastapi/pull/76" target="_blank">#76</a> by <a href="https://github.com/euri10" target="_blank">@euri10</a>.
|
||||
* Make development scripts executable. PR [#76](https://github.com/tiangolo/fastapi/pull/76) by [@euri10](https://github.com/euri10).
|
||||
|
||||
* Add support for adding `tags` in `app.include_router()`. PR <a href="https://github.com/tiangolo/fastapi/pull/55" target="_blank">#55</a> by <a href="https://github.com/euri10" target="_blank">@euri10</a>. Documentation updated in the section: <a href="https://fastapi.tiangolo.com/tutorial/bigger-applications/" target="_blank">Bigger Applications</a>.
|
||||
* Add support for adding `tags` in `app.include_router()`. PR [#55](https://github.com/tiangolo/fastapi/pull/55) by [@euri10](https://github.com/euri10). Documentation updated in the section: [Bigger Applications](https://fastapi.tiangolo.com/tutorial/bigger-applications/).
|
||||
|
||||
* Update docs related to Uvicorn to use new `--reload` option from version `0.5.x`. PR <a href="https://github.com/tiangolo/fastapi/pull/74" target="_blank">#74</a>.
|
||||
* Update docs related to Uvicorn to use new `--reload` option from version `0.5.x`. PR [#74](https://github.com/tiangolo/fastapi/pull/74).
|
||||
|
||||
* Update `isort` imports and scripts to be compatible with newer versions. PR <a href="https://github.com/tiangolo/fastapi/pull/75" target="_blank">#75</a>.
|
||||
* Update `isort` imports and scripts to be compatible with newer versions. PR [#75](https://github.com/tiangolo/fastapi/pull/75).
|
||||
|
||||
## 0.7.1
|
||||
|
||||
* Update <a href="https://fastapi.tiangolo.com/async/#path-operation-functions" target="_blank">technical details about `async def` handling</a> with respect to previous frameworks. PR <a href="https://github.com/tiangolo/fastapi/pull/64" target="_blank">#64</a> by <a href="https://github.com/haizaar" target="_blank">@haizaar</a>.
|
||||
* Update [technical details about `async def` handling](https://fastapi.tiangolo.com/async/#path-operation-functions) with respect to previous frameworks. PR [#64](https://github.com/tiangolo/fastapi/pull/64) by [@haizaar](https://github.com/haizaar).
|
||||
|
||||
* Add <a href="https://fastapi.tiangolo.com/deployment/#raspberry-pi-and-other-architectures" target="_blank">deployment documentation for Docker in Raspberry Pi</a> and other architectures.
|
||||
* Add [deployment documentation for Docker in Raspberry Pi](https://fastapi.tiangolo.com/deployment/#raspberry-pi-and-other-architectures) and other architectures.
|
||||
|
||||
* Trigger Docker images build on Travis CI automatically. PR <a href="https://github.com/tiangolo/fastapi/pull/65" target="_blank">#65</a>.
|
||||
* Trigger Docker images build on Travis CI automatically. PR [#65](https://github.com/tiangolo/fastapi/pull/65).
|
||||
|
||||
## 0.7.0
|
||||
|
||||
* Add support for `UploadFile` in `File` parameter annotations.
|
||||
* This includes a file-like interface.
|
||||
* Here's the updated documentation for declaring <a href="https://fastapi.tiangolo.com/tutorial/request-files/#file-parameters-with-uploadfile" target="_blank"> `File` parameters with `UploadFile`</a>.
|
||||
* And here's the updated documentation for using <a href="https://fastapi.tiangolo.com/tutorial/request-forms-and-files/" target="_blank">`Form` parameters mixed with `File` parameters, supporting `bytes` and `UploadFile`</a> at the same time.
|
||||
* PR <a href="https://github.com/tiangolo/fastapi/pull/63" target="_blank">#63</a>.
|
||||
* Here's the updated documentation for declaring [`File` parameters with `UploadFile`](https://fastapi.tiangolo.com/tutorial/request-files/#file-parameters-with-uploadfile).
|
||||
* And here's the updated documentation for using [`Form` parameters mixed with `File` parameters, supporting `bytes` and `UploadFile`](https://fastapi.tiangolo.com/tutorial/request-forms-and-files/) at the same time.
|
||||
* PR [#63](https://github.com/tiangolo/fastapi/pull/63).
|
||||
|
||||
## 0.6.4
|
||||
|
||||
* Add <a href="https://fastapi.tiangolo.com/async/#very-technical-details" target="_blank">technical details about `async def` handling to docs</a>. PR <a href="https://github.com/tiangolo/fastapi/pull/61" target="_blank">#61</a>.
|
||||
* Add [technical details about `async def` handling to docs](https://fastapi.tiangolo.com/async/#very-technical-details). PR [#61](https://github.com/tiangolo/fastapi/pull/61).
|
||||
|
||||
* Add docs for <a href="https://fastapi.tiangolo.com/tutorial/debugging/" target="_blank">Debugging FastAPI applications in editors</a>.
|
||||
* Add docs for [Debugging FastAPI applications in editors](https://fastapi.tiangolo.com/tutorial/debugging/).
|
||||
|
||||
* Clarify <a href="https://fastapi.tiangolo.com/deployment/#bigger-applications" target="_blank">Bigger Applications deployed with Docker</a>.
|
||||
* Clarify [Bigger Applications deployed with Docker](https://fastapi.tiangolo.com/deployment/#bigger-applications).
|
||||
|
||||
* Fix typos in docs.
|
||||
|
||||
* Add section about <a href="https://fastapi.tiangolo.com/history-design-future/" target="_blank">History, Design and Future</a>.
|
||||
* Add section about [History, Design and Future](https://fastapi.tiangolo.com/history-design-future/).
|
||||
|
||||
* Add docs for using <a href="https://fastapi.tiangolo.com/tutorial/websockets/" target="_blank">WebSockets with **FastAPI**</a>. PR <a href="https://github.com/tiangolo/fastapi/pull/62" target="_blank">#62</a>.
|
||||
* Add docs for using [WebSockets with **FastAPI**](https://fastapi.tiangolo.com/tutorial/websockets/). PR [#62](https://github.com/tiangolo/fastapi/pull/62).
|
||||
|
||||
## 0.6.3
|
||||
|
||||
* Add Favicons to docs. PR <a href="https://github.com/tiangolo/fastapi/pull/53" target="_blank">#53</a>.
|
||||
* Add Favicons to docs. PR [#53](https://github.com/tiangolo/fastapi/pull/53).
|
||||
|
||||
## 0.6.2
|
||||
|
||||
* Introduce new project generator based on FastAPI and PostgreSQL: <a href="https://github.com/tiangolo/full-stack-fastapi-postgresql" target="_blank">https://github.com/tiangolo/full-stack-fastapi-postgresql</a>. PR <a href="https://github.com/tiangolo/fastapi/pull/52" target="_blank">#52</a>.
|
||||
* Introduce new project generator based on FastAPI and PostgreSQL: [https://github.com/tiangolo/full-stack-fastapi-postgresql](https://github.com/tiangolo/full-stack-fastapi-postgresql). PR [#52](https://github.com/tiangolo/fastapi/pull/52).
|
||||
|
||||
* Update <a href="https://fastapi.tiangolo.com/tutorial/sql-databases/" target="_blank">SQL tutorial with SQLAlchemy, using `Depends` to improve editor support and reduce code repetition</a>. PR <a href="https://github.com/tiangolo/fastapi/pull/52" target="_blank">#52</a>.
|
||||
* Update [SQL tutorial with SQLAlchemy, using `Depends` to improve editor support and reduce code repetition](https://fastapi.tiangolo.com/tutorial/sql-databases/). PR [#52](https://github.com/tiangolo/fastapi/pull/52).
|
||||
|
||||
* Improve middleware naming in tutorial for SQL with SQLAlchemy <a href="https://fastapi.tiangolo.com/tutorial/sql-databases/" target="_blank">https://fastapi.tiangolo.com/tutorial/sql-databases/</a>.
|
||||
* Improve middleware naming in tutorial for SQL with SQLAlchemy [https://fastapi.tiangolo.com/tutorial/sql-databases/](https://fastapi.tiangolo.com/tutorial/sql-databases/).
|
||||
|
||||
## 0.6.1
|
||||
|
||||
* Add docs for GraphQL: <a href="https://fastapi.tiangolo.com/tutorial/graphql/" target="_blank">https://fastapi.tiangolo.com/tutorial/graphql/</a>. PR <a href="https://github.com/tiangolo/fastapi/pull/48" target="_blank">#48</a>.
|
||||
* Add docs for GraphQL: [https://fastapi.tiangolo.com/tutorial/graphql/](https://fastapi.tiangolo.com/tutorial/graphql/). PR [#48](https://github.com/tiangolo/fastapi/pull/48).
|
||||
|
||||
## 0.6.0
|
||||
|
||||
* Update SQL with SQLAlchemy tutorial at <a href="https://fastapi.tiangolo.com/tutorial/sql-databases/" target="_blank">https://fastapi.tiangolo.com/tutorial/sql-databases/</a> using the new official `request.state`. PR <a href="https://github.com/tiangolo/fastapi/pull/45" target="_blank">#45</a>.
|
||||
* Update SQL with SQLAlchemy tutorial at [https://fastapi.tiangolo.com/tutorial/sql-databases/](https://fastapi.tiangolo.com/tutorial/sql-databases/) using the new official `request.state`. PR [#45](https://github.com/tiangolo/fastapi/pull/45).
|
||||
|
||||
* Upgrade Starlette to version `0.11.1` and add required compatibility changes. PR <a href="https://github.com/tiangolo/fastapi/pull/44" target="_blank">#44</a>.
|
||||
* Upgrade Starlette to version `0.11.1` and add required compatibility changes. PR [#44](https://github.com/tiangolo/fastapi/pull/44).
|
||||
|
||||
## 0.5.1
|
||||
|
||||
* Add section about <a href="https://fastapi.tiangolo.com/help-fastapi/" target="_blank">helping and getting help with **FastAPI**</a>.
|
||||
* Add section about [helping and getting help with **FastAPI**](https://fastapi.tiangolo.com/help-fastapi/).
|
||||
|
||||
* Add note about <a href="https://fastapi.tiangolo.com/tutorial/path-params/#order-matters" target="_blank">path operations order in docs</a>.
|
||||
* Add note about [path operations order in docs](https://fastapi.tiangolo.com/tutorial/path-params/#order-matters).
|
||||
|
||||
* Update <a href="https://fastapi.tiangolo.com/tutorial/handling-errors/" target="_blank">section about error handling</a> with more information and make relation with Starlette error handling utilities more explicit. PR <a href="https://github.com/tiangolo/fastapi/pull/41" target="_blank">#41</a>.
|
||||
* Update [section about error handling](https://fastapi.tiangolo.com/tutorial/handling-errors/) with more information and make relation with Starlette error handling utilities more explicit. PR [#41](https://github.com/tiangolo/fastapi/pull/41).
|
||||
|
||||
* Add <a href="" target="_blank">Development - Contributing section to the docs</a>. PR <a href="https://github.com/tiangolo/fastapi/pull/42" target="_blank">#42</a>.
|
||||
* Add <a href="" target="_blank">Development - Contributing section to the docs</a>. PR [#42](https://github.com/tiangolo/fastapi/pull/42).
|
||||
|
||||
## 0.5.0
|
||||
|
||||
* Add new `HTTPException` with support for custom headers. With new documentation for handling errors at: <a href="https://fastapi.tiangolo.com/tutorial/handling-errors/" target="_blank">https://fastapi.tiangolo.com/tutorial/handling-errors/</a>. PR <a href="https://github.com/tiangolo/fastapi/pull/35" target="_blank">#35</a>.
|
||||
* Add new `HTTPException` with support for custom headers. With new documentation for handling errors at: [https://fastapi.tiangolo.com/tutorial/handling-errors/](https://fastapi.tiangolo.com/tutorial/handling-errors/). PR [#35](https://github.com/tiangolo/fastapi/pull/35).
|
||||
|
||||
* Add <a href="https://fastapi.tiangolo.com/tutorial/using-request-directly/" target="_blank">documentation to use Starlette `Request` object</a> directly. Check <a href="https://github.com/tiangolo/fastapi/pull/25" target="_blank">#25</a> by <a href="https://github.com/euri10" target="_blank">@euri10</a>.
|
||||
* Add [documentation to use Starlette `Request` object](https://fastapi.tiangolo.com/tutorial/using-request-directly/) directly. Check [#25](https://github.com/tiangolo/fastapi/pull/25) by [@euri10](https://github.com/euri10).
|
||||
|
||||
* Add issue templates to simplify reporting bugs, getting help, etc: <a href="https://github.com/tiangolo/fastapi/pull/34" target="_blank">#34</a>.
|
||||
* Add issue templates to simplify reporting bugs, getting help, etc: [#34](https://github.com/tiangolo/fastapi/pull/34).
|
||||
|
||||
* Update example for the SQLAlchemy tutorial at <a href="https://fastapi.tiangolo.com/tutorial/sql-databases/" target="_blank">https://fastapi.tiangolo.com/tutorial/sql-databases/</a> using middleware and database session attached to request.
|
||||
* Update example for the SQLAlchemy tutorial at [https://fastapi.tiangolo.com/tutorial/sql-databases/](https://fastapi.tiangolo.com/tutorial/sql-databases/) using middleware and database session attached to request.
|
||||
|
||||
## 0.4.0
|
||||
|
||||
* Add `openapi_prefix`, support for reverse proxy and mounting sub-applications. See the docs at <a href="https://fastapi.tiangolo.com/tutorial/sub-applications-proxy/" target="_blank">https://fastapi.tiangolo.com/tutorial/sub-applications-proxy/</a>: <a href="https://github.com/tiangolo/fastapi/pull/26" target="_blank">#26</a> by <a href="https://github.com/kabirkhan" target="_blank">@kabirkhan</a>.
|
||||
* Add `openapi_prefix`, support for reverse proxy and mounting sub-applications. See the docs at [https://fastapi.tiangolo.com/tutorial/sub-applications-proxy/](https://fastapi.tiangolo.com/tutorial/sub-applications-proxy/): [#26](https://github.com/tiangolo/fastapi/pull/26) by [@kabirkhan](https://github.com/kabirkhan).
|
||||
|
||||
* Update <a href="https://fastapi.tiangolo.com/tutorial/sql-databases/" target="_blank">docs/tutorial for SQLAlchemy</a> including note about *DB Browser for SQLite*.
|
||||
* Update [docs/tutorial for SQLAlchemy](https://fastapi.tiangolo.com/tutorial/sql-databases/) including note about _DB Browser for SQLite_.
|
||||
|
||||
## 0.3.0
|
||||
|
||||
* Fix/add SQLAlchemy support, including ORM, and update <a href="https://fastapi.tiangolo.com/tutorial/sql-databases/" target="_blank">docs for SQLAlchemy</a>: <a href="https://github.com/tiangolo/fastapi/pull/30" target="_blank">#30</a>.
|
||||
* Fix/add SQLAlchemy support, including ORM, and update [docs for SQLAlchemy](https://fastapi.tiangolo.com/tutorial/sql-databases/): [#30](https://github.com/tiangolo/fastapi/pull/30).
|
||||
|
||||
## 0.2.1
|
||||
|
||||
* Fix `jsonable_encoder` for Pydantic models with `Config` but without `json_encoders`: <a href="https://github.com/tiangolo/fastapi/pull/29" target="_blank">#29</a>.
|
||||
* Fix `jsonable_encoder` for Pydantic models with `Config` but without `json_encoders`: [#29](https://github.com/tiangolo/fastapi/pull/29).
|
||||
|
||||
## 0.2.0
|
||||
|
||||
* Fix typos in Security section: <a href="https://github.com/tiangolo/fastapi/pull/24" target="_blank">#24</a> by <a href="https://github.com/kkinder" target="_blank">@kkinder</a>.
|
||||
* Fix typos in Security section: [#24](https://github.com/tiangolo/fastapi/pull/24) by [@kkinder](https://github.com/kkinder).
|
||||
|
||||
* Add support for Pydantic custom JSON encoders: <a href="https://github.com/tiangolo/fastapi/pull/21" target="_blank">#21</a> by <a href="https://github.com/euri10" target="_blank">@euri10</a>.
|
||||
* Add support for Pydantic custom JSON encoders: [#21](https://github.com/tiangolo/fastapi/pull/21) by [@euri10](https://github.com/euri10).
|
||||
|
||||
## 0.1.19
|
||||
|
||||
* Upgrade Starlette version to the current latest `0.10.1`: <a href="https://github.com/tiangolo/fastapi/pull/17" target="_blank">#17</a> by <a href="https://github.com/euri10" target="_blank">@euri10</a>.
|
||||
* Upgrade Starlette version to the current latest `0.10.1`: [#17](https://github.com/tiangolo/fastapi/pull/17) by [@euri10](https://github.com/euri10).
|
||||
|
||||
23
docs/src/additional_responses/tutorial001.py
Normal file
@@ -0,0 +1,23 @@
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
from starlette.responses import JSONResponse
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
id: str
|
||||
value: str
|
||||
|
||||
|
||||
class Message(BaseModel):
|
||||
message: str
|
||||
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/items/{item_id}", response_model=Item, responses={404: {"model": Message}})
|
||||
async def read_item(item_id: str):
|
||||
if item_id == "foo":
|
||||
return {"id": "foo", "value": "there goes my hero"}
|
||||
else:
|
||||
return JSONResponse(status_code=404, content={"message": "Item not found"})
|
||||
28
docs/src/additional_responses/tutorial002.py
Normal file
@@ -0,0 +1,28 @@
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
from starlette.responses import FileResponse
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
id: str
|
||||
value: str
|
||||
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get(
|
||||
"/items/{item_id}",
|
||||
response_model=Item,
|
||||
responses={
|
||||
200: {
|
||||
"content": {"image/png": {}},
|
||||
"description": "Return the JSON item or an image.",
|
||||
}
|
||||
},
|
||||
)
|
||||
async def read_item(item_id: str, img: bool = None):
|
||||
if img:
|
||||
return FileResponse("image.png", media_type="image/png")
|
||||
else:
|
||||
return {"id": "foo", "value": "there goes my hero"}
|
||||
37
docs/src/additional_responses/tutorial003.py
Normal file
@@ -0,0 +1,37 @@
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
from starlette.responses import JSONResponse
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
id: str
|
||||
value: str
|
||||
|
||||
|
||||
class Message(BaseModel):
|
||||
message: str
|
||||
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get(
|
||||
"/items/{item_id}",
|
||||
response_model=Item,
|
||||
responses={
|
||||
404: {"model": Message, "description": "The item was not found"},
|
||||
200: {
|
||||
"description": "Item requested by ID",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"example": {"id": "bar", "value": "The bar tenders"}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
async def read_item(item_id: str):
|
||||
if item_id == "foo":
|
||||
return {"id": "foo", "value": "there goes my hero"}
|
||||
else:
|
||||
return JSONResponse(status_code=404, content={"message": "Item not found"})
|
||||
30
docs/src/additional_responses/tutorial004.py
Normal file
@@ -0,0 +1,30 @@
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
from starlette.responses import FileResponse
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
id: str
|
||||
value: str
|
||||
|
||||
|
||||
responses = {
|
||||
404: {"description": "Item not found"},
|
||||
302: {"description": "The item was moved"},
|
||||
403: {"description": "Not enough privileges"},
|
||||
}
|
||||
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get(
|
||||
"/items/{item_id}",
|
||||
response_model=Item,
|
||||
responses={**responses, 200: {"content": {"image/png": {}}}},
|
||||
)
|
||||
async def read_item(item_id: str, img: bool = None):
|
||||
if img:
|
||||
return FileResponse("image.png", media_type="image/png")
|
||||
else:
|
||||
return {"id": "foo", "value": "there goes my hero"}
|
||||
20
docs/src/additional_status_codes/tutorial001.py
Normal file
@@ -0,0 +1,20 @@
|
||||
from fastapi import Body, FastAPI
|
||||
from starlette.responses import JSONResponse
|
||||
from starlette.status import HTTP_201_CREATED
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
items = {"foo": {"name": "Fighters", "size": 6}, "bar": {"name": "Tenders", "size": 3}}
|
||||
|
||||
|
||||
@app.put("/items/{item_id}")
|
||||
async def upsert_item(item_id: str, name: str = Body(None), size: int = Body(None)):
|
||||
if item_id in items:
|
||||
item = items[item_id]
|
||||
item["name"] = name
|
||||
item["size"] = size
|
||||
return item
|
||||
else:
|
||||
item = {"name": name, "size": size}
|
||||
items[item_id] = item
|
||||
return JSONResponse(status_code=HTTP_201_CREATED, content=item)
|
||||
0
docs/src/app_testing/__init__.py
Normal file
8
docs/src/app_testing/main.py
Normal file
@@ -0,0 +1,8 @@
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/")
|
||||
async def read_main():
|
||||
return {"msg": "Hello World"}
|
||||
11
docs/src/app_testing/test_main.py
Normal file
@@ -0,0 +1,11 @@
|
||||
from starlette.testclient import TestClient
|
||||
|
||||
from .main import app
|
||||
|
||||
client = TestClient(app)
|
||||
|
||||
|
||||
def test_read_main():
|
||||
response = client.get("/")
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {"msg": "Hello World"}
|
||||
18
docs/src/app_testing/tutorial001.py
Normal file
@@ -0,0 +1,18 @@
|
||||
from fastapi import FastAPI
|
||||
from starlette.testclient import TestClient
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/")
|
||||
async def read_main():
|
||||
return {"msg": "Hello World"}
|
||||
|
||||
|
||||
client = TestClient(app)
|
||||
|
||||
|
||||
def test_read_main():
|
||||
response = client.get("/")
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {"msg": "Hello World"}
|
||||
31
docs/src/app_testing/tutorial002.py
Normal file
@@ -0,0 +1,31 @@
|
||||
from fastapi import FastAPI
|
||||
from starlette.testclient import TestClient
|
||||
from starlette.websockets import WebSocket
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/")
|
||||
async def read_main():
|
||||
return {"msg": "Hello World"}
|
||||
|
||||
|
||||
@app.websocket_route("/ws")
|
||||
async def websocket(websocket: WebSocket):
|
||||
await websocket.accept()
|
||||
await websocket.send_json({"msg": "Hello WebSocket"})
|
||||
await websocket.close()
|
||||
|
||||
|
||||
def test_read_main():
|
||||
client = TestClient(app)
|
||||
response = client.get("/")
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {"msg": "Hello World"}
|
||||
|
||||
|
||||
def test_websocket():
|
||||
client = TestClient(app)
|
||||
with client.websocket_connect("/ws") as websocket:
|
||||
data = websocket.receive_json()
|
||||
assert data == {"msg": "Hello WebSocket"}
|
||||
24
docs/src/app_testing/tutorial003.py
Normal file
@@ -0,0 +1,24 @@
|
||||
from fastapi import FastAPI
|
||||
from starlette.testclient import TestClient
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
items = {}
|
||||
|
||||
|
||||
@app.on_event("startup")
|
||||
async def startup_event():
|
||||
items["foo"] = {"name": "Fighters"}
|
||||
items["bar"] = {"name": "Tenders"}
|
||||
|
||||
|
||||
@app.get("/items/{item_id}")
|
||||
async def read_items(item_id: str):
|
||||
return items[item_id]
|
||||
|
||||
|
||||
def test_read_items():
|
||||
with TestClient(app) as client:
|
||||
response = client.get("/items/foo")
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {"name": "Fighters"}
|
||||
@@ -1,8 +1,20 @@
|
||||
from fastapi import FastAPI
|
||||
from fastapi import Depends, FastAPI, Header, HTTPException
|
||||
|
||||
from .routers import items, users
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
async def get_token_header(x_token: str = Header(...)):
|
||||
if x_token != "fake-super-secret-token":
|
||||
raise HTTPException(status_code=400, detail="X-Token header invalid")
|
||||
|
||||
|
||||
app.include_router(users.router)
|
||||
app.include_router(items.router, prefix="/items", tags=["items"])
|
||||
app.include_router(
|
||||
items.router,
|
||||
prefix="/items",
|
||||
tags=["items"],
|
||||
dependencies=[Depends(get_token_header)],
|
||||
responses={404: {"description": "Not found"}},
|
||||
)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from fastapi import APIRouter
|
||||
from fastapi import APIRouter, HTTPException
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@@ -11,3 +11,14 @@ async def read_items():
|
||||
@router.get("/{item_id}")
|
||||
async def read_item(item_id: str):
|
||||
return {"name": "Fake Specific Item", "item_id": item_id}
|
||||
|
||||
|
||||
@router.put(
|
||||
"/{item_id}",
|
||||
tags=["custom"],
|
||||
responses={403: {"description": "Operation forbidden"}},
|
||||
)
|
||||
async def update_item(item_id: str):
|
||||
if item_id != "foo":
|
||||
raise HTTPException(status_code=403, detail="You can only update the item: foo")
|
||||
return {"item_id": item_id, "name": "The Fighters"}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
from typing import Set
|
||||
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
from pydantic.types import UrlStr
|
||||
from pydantic import BaseModel, UrlStr
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
from typing import List, Set
|
||||
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
from pydantic.types import UrlStr
|
||||
from pydantic import BaseModel, UrlStr
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
@@ -18,7 +17,7 @@ class Item(BaseModel):
|
||||
price: float
|
||||
tax: float = None
|
||||
tags: Set[str] = []
|
||||
image: List[Image] = None
|
||||
images: List[Image] = None
|
||||
|
||||
|
||||
@app.put("/items/{item_id}")
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
from typing import List, Set
|
||||
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
from pydantic.types import UrlStr
|
||||
from pydantic import BaseModel, UrlStr
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
@@ -18,7 +17,7 @@ class Item(BaseModel):
|
||||
price: float
|
||||
tax: float = None
|
||||
tags: Set[str] = []
|
||||
image: List[Image] = None
|
||||
images: List[Image] = None
|
||||
|
||||
|
||||
class Offer(BaseModel):
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
from typing import List
|
||||
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
from pydantic.types import UrlStr
|
||||
from pydantic import BaseModel, UrlStr
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
34
docs/src/body_updates/tutorial001.py
Normal file
@@ -0,0 +1,34 @@
|
||||
from typing import List
|
||||
|
||||
from fastapi import FastAPI
|
||||
from fastapi.encoders import jsonable_encoder
|
||||
from pydantic import BaseModel
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
name: str = None
|
||||
description: str = None
|
||||
price: float = None
|
||||
tax: float = 10.5
|
||||
tags: List[str] = []
|
||||
|
||||
|
||||
items = {
|
||||
"foo": {"name": "Foo", "price": 50.2},
|
||||
"bar": {"name": "Bar", "description": "The bartenders", "price": 62, "tax": 20.2},
|
||||
"baz": {"name": "Baz", "description": None, "price": 50.2, "tax": 10.5, "tags": []},
|
||||
}
|
||||
|
||||
|
||||
@app.get("/items/{item_id}", response_model=Item)
|
||||
async def read_item(item_id: str):
|
||||
return items[item_id]
|
||||
|
||||
|
||||
@app.put("/items/{item_id}", response_model=Item)
|
||||
async def update_item(item_id: str, item: Item):
|
||||
update_item_encoded = jsonable_encoder(item)
|
||||
items[item_id] = update_item_encoded
|
||||
return update_item_encoded
|
||||
37
docs/src/body_updates/tutorial002.py
Normal file
@@ -0,0 +1,37 @@
|
||||
from typing import List
|
||||
|
||||
from fastapi import FastAPI
|
||||
from fastapi.encoders import jsonable_encoder
|
||||
from pydantic import BaseModel
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
name: str = None
|
||||
description: str = None
|
||||
price: float = None
|
||||
tax: float = 10.5
|
||||
tags: List[str] = []
|
||||
|
||||
|
||||
items = {
|
||||
"foo": {"name": "Foo", "price": 50.2},
|
||||
"bar": {"name": "Bar", "description": "The bartenders", "price": 62, "tax": 20.2},
|
||||
"baz": {"name": "Baz", "description": None, "price": 50.2, "tax": 10.5, "tags": []},
|
||||
}
|
||||
|
||||
|
||||
@app.get("/items/{item_id}", response_model=Item)
|
||||
async def read_item(item_id: str):
|
||||
return items[item_id]
|
||||
|
||||
|
||||
@app.patch("/items/{item_id}", response_model=Item)
|
||||
async def update_item(item_id: str, item: Item):
|
||||
stored_item_data = items[item_id]
|
||||
stored_item_model = Item(**stored_item_data)
|
||||
update_data = item.dict(skip_defaults=True)
|
||||
updated_item = stored_item_model.copy(update=update_data)
|
||||
items[item_id] = jsonable_encoder(updated_item)
|
||||
return updated_item
|
||||
19
docs/src/cors/tutorial001.py
Normal file
@@ -0,0 +1,19 @@
|
||||
from fastapi import FastAPI
|
||||
from starlette.middleware.cors import CORSMiddleware
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
origins = [
|
||||
"http://localhost.tiangolo.com",
|
||||
"https://localhost.tiangolo.com",
|
||||
"http:localhost",
|
||||
"http:localhost:8080",
|
||||
]
|
||||
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=origins,
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
@@ -4,6 +4,6 @@ from starlette.responses import UJSONResponse
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/items/", content_type=UJSONResponse)
|
||||
@app.get("/items/", response_class=UJSONResponse)
|
||||
async def read_items():
|
||||
return [{"item_id": "Foo"}]
|
||||
|
||||
@@ -4,7 +4,7 @@ from starlette.responses import HTMLResponse
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/items/", content_type=HTMLResponse)
|
||||
@app.get("/items/", response_class=HTMLResponse)
|
||||
async def read_items():
|
||||
return """
|
||||
<html>
|
||||
|
||||
@@ -18,6 +18,6 @@ def generate_html_response():
|
||||
return HTMLResponse(content=html_content, status_code=200)
|
||||
|
||||
|
||||
@app.get("/items/", content_type=HTMLResponse)
|
||||
@app.get("/items/", response_class=HTMLResponse)
|
||||
async def read_items():
|
||||
return generate_html_response()
|
||||
|
||||
@@ -1,21 +1,19 @@
|
||||
from fastapi import Depends, FastAPI
|
||||
from fastapi import Depends, FastAPI, Header, HTTPException
|
||||
|
||||
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
|
||||
async def verify_token(x_token: str = Header(...)):
|
||||
if x_token != "fake-super-secret-token":
|
||||
raise HTTPException(status_code=400, detail="X-Token header invalid")
|
||||
|
||||
|
||||
checker = FixedContentQueryChecker("bar")
|
||||
async def verify_key(x_key: str = Header(...)):
|
||||
if x_key != "fake-super-secret-key":
|
||||
raise HTTPException(status_code=400, detail="X-Key header invalid")
|
||||
return x_key
|
||||
|
||||
|
||||
@app.get("/query-checker/")
|
||||
async def read_query_check(fixed_content_included: bool = Depends(checker)):
|
||||
return {"fixed_content_in_query": fixed_content_included}
|
||||
@app.get("/items/", dependencies=[Depends(verify_token), Depends(verify_key)])
|
||||
async def read_items():
|
||||
return [{"item": "Foo"}, {"item": "Bar"}]
|
||||
|
||||
21
docs/src/dependencies/tutorial007.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}
|
||||
22
docs/src/encoder/tutorial001.py
Normal file
@@ -0,0 +1,22 @@
|
||||
from datetime import datetime
|
||||
|
||||
from fastapi import FastAPI
|
||||
from fastapi.encoders import jsonable_encoder
|
||||
from pydantic import BaseModel
|
||||
|
||||
fake_db = {}
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
title: str
|
||||
timestamp: datetime
|
||||
description: str = None
|
||||
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.put("/items/{id}")
|
||||
def update_item(id: str, item: Item):
|
||||
json_compatible_item_data = jsonable_encoder(item)
|
||||
fake_db[id] = json_compatible_item_data
|
||||
@@ -12,5 +12,5 @@ async def startup_event():
|
||||
|
||||
|
||||
@app.get("/items/{item_id}")
|
||||
async def read_item(item_id: str):
|
||||
async def read_items(item_id: str):
|
||||
return items[item_id]
|
||||
|
||||
@@ -6,7 +6,7 @@ items = {"foo": "The Foo Wrestlers"}
|
||||
|
||||
|
||||
@app.get("/items/{item_id}")
|
||||
async def create_item(item_id: str):
|
||||
async def read_item(item_id: str):
|
||||
if item_id not in items:
|
||||
raise HTTPException(status_code=404, detail="Item not found")
|
||||
return {"item": items[item_id]}
|
||||
|
||||
@@ -6,7 +6,7 @@ items = {"foo": "The Foo Wrestlers"}
|
||||
|
||||
|
||||
@app.get("/items-header/{item_id}")
|
||||
async def create_item_header(item_id: str):
|
||||
async def read_item_header(item_id: str):
|
||||
if item_id not in items:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
|
||||
15
docs/src/middleware/tutorial001.py
Normal file
@@ -0,0 +1,15 @@
|
||||
import time
|
||||
|
||||
from fastapi import FastAPI
|
||||
from starlette.requests import Request
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.middleware("http")
|
||||
async def add_process_time_header(request: Request, call_next):
|
||||
start_time = time.time()
|
||||
response = await call_next(request)
|
||||
process_time = time.time() - start_time
|
||||
response.headers["X-Process-Time"] = str(process_time)
|
||||
return response
|
||||
@@ -18,11 +18,11 @@ class Item(BaseModel):
|
||||
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
|
||||
|
||||
- **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
|
||||
"""
|
||||
return item
|
||||
|
||||
@@ -23,11 +23,11 @@ class Item(BaseModel):
|
||||
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
|
||||
|
||||
- **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
|
||||
"""
|
||||
return item
|
||||
|
||||
33
docs/src/request_files/tutorial002.py
Normal file
@@ -0,0 +1,33 @@
|
||||
from typing import List
|
||||
|
||||
from fastapi import FastAPI, File, UploadFile
|
||||
from starlette.responses import HTMLResponse
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.post("/files/")
|
||||
async def create_files(files: List[bytes] = File(...)):
|
||||
return {"file_sizes": [len(file) for file in files]}
|
||||
|
||||
|
||||
@app.post("/uploadfiles/")
|
||||
async def create_upload_files(files: List[UploadFile] = File(...)):
|
||||
return {"filenames": [file.filename for file in files]}
|
||||
|
||||
|
||||
@app.get("/")
|
||||
async def main():
|
||||
content = """
|
||||
<body>
|
||||
<form action="/files/" enctype="multipart/form-data" method="post">
|
||||
<input name="files" type="file" multiple>
|
||||
<input type="submit">
|
||||
</form>
|
||||
<form action="/uploadfiles/" enctype="multipart/form-data" method="post">
|
||||
<input name="files" type="file" multiple>
|
||||
<input type="submit">
|
||||
</form>
|
||||
</body>
|
||||
"""
|
||||
return HTMLResponse(content=content)
|
||||
12
docs/src/response_cookies/tutorial001.py
Normal file
@@ -0,0 +1,12 @@
|
||||
from fastapi import FastAPI
|
||||
from starlette.responses import JSONResponse
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.post("/cookie/")
|
||||
def create_cookie():
|
||||
content = {"message": "Come to the dark side, we have cookies"}
|
||||
response = JSONResponse(content=content)
|
||||
response.set_cookie(key="fakesession", value="fake-cookie-session-value")
|
||||
return response
|
||||
21
docs/src/response_directly/tutorial001.py
Normal file
@@ -0,0 +1,21 @@
|
||||
from datetime import datetime
|
||||
|
||||
from fastapi import FastAPI
|
||||
from fastapi.encoders import jsonable_encoder
|
||||
from pydantic import BaseModel
|
||||
from starlette.responses import JSONResponse
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
title: str
|
||||
timestamp: datetime
|
||||
description: str = None
|
||||
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.put("/items/{id}")
|
||||
def update_item(id: str, item: Item):
|
||||
json_compatible_item_data = jsonable_encoder(item)
|
||||
return JSONResponse(content=json_compatible_item_data)
|
||||
20
docs/src/response_directly/tutorial002.py
Normal file
@@ -0,0 +1,20 @@
|
||||
from fastapi import FastAPI
|
||||
from starlette.responses import Response
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/legacy/")
|
||||
def get_legacy_data():
|
||||
data = """
|
||||
<?xml version="1.0"?>
|
||||
<shampoo>
|
||||
<Header>
|
||||
Apply shampoo here.
|
||||
<Header>
|
||||
<Body>
|
||||
You'll have to use soap here.
|
||||
</Body>
|
||||
</shampoo>
|
||||
"""
|
||||
return Response(content=data, media_type="application/xml")
|
||||
11
docs/src/response_headers/tutorial001.py
Normal file
@@ -0,0 +1,11 @@
|
||||
from fastapi import FastAPI
|
||||
from starlette.responses import JSONResponse
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/headers/")
|
||||
def get_headers():
|
||||
content = {"message": "Hello World"}
|
||||
headers = {"X-Cat-Dog": "alone in the world", "Content-Language": "en-US"}
|
||||
return JSONResponse(content=content, headers=headers)
|
||||
@@ -1,4 +1,4 @@
|
||||
from typing import Set
|
||||
from typing import List
|
||||
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
@@ -11,9 +11,9 @@ class Item(BaseModel):
|
||||
description: str = None
|
||||
price: float
|
||||
tax: float = None
|
||||
tags: Set[str] = []
|
||||
tags: List[str] = []
|
||||
|
||||
|
||||
@app.post("/items/", response_model=Item)
|
||||
async def create_item(*, item: Item):
|
||||
async def create_item(item: Item):
|
||||
return item
|
||||
|
||||
26
docs/src/response_model/tutorial004.py
Normal file
@@ -0,0 +1,26 @@
|
||||
from typing import List
|
||||
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
name: str
|
||||
description: str = None
|
||||
price: float
|
||||
tax: float = 10.5
|
||||
tags: List[str] = []
|
||||
|
||||
|
||||
items = {
|
||||
"foo": {"name": "Foo", "price": 50.2},
|
||||
"bar": {"name": "Bar", "description": "The bartenders", "price": 62, "tax": 20.2},
|
||||
"baz": {"name": "Baz", "description": None, "price": 50.2, "tax": 10.5, "tags": []},
|
||||
}
|
||||
|
||||
|
||||
@app.get("/items/{item_id}", response_model=Item, response_model_skip_defaults=True)
|
||||
async def read_item(item_id: str):
|
||||
return items[item_id]
|
||||
37
docs/src/response_model/tutorial005.py
Normal file
@@ -0,0 +1,37 @@
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
name: str
|
||||
description: str = None
|
||||
price: float
|
||||
tax: float = 10.5
|
||||
|
||||
|
||||
items = {
|
||||
"foo": {"name": "Foo", "price": 50.2},
|
||||
"bar": {"name": "Bar", "description": "The Bar fighters", "price": 62, "tax": 20.2},
|
||||
"baz": {
|
||||
"name": "Baz",
|
||||
"description": "There goes my baz",
|
||||
"price": 50.2,
|
||||
"tax": 10.5,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@app.get(
|
||||
"/items/{item_id}/name",
|
||||
response_model=Item,
|
||||
response_model_include={"name", "description"},
|
||||
)
|
||||
async def read_item_name(item_id: str):
|
||||
return items[item_id]
|
||||
|
||||
|
||||
@app.get("/items/{item_id}/public", response_model=Item, response_model_exclude={"tax"})
|
||||
async def read_item_public_data(item_id: str):
|
||||
return items[item_id]
|
||||
37
docs/src/response_model/tutorial006.py
Normal file
@@ -0,0 +1,37 @@
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
name: str
|
||||
description: str = None
|
||||
price: float
|
||||
tax: float = 10.5
|
||||
|
||||
|
||||
items = {
|
||||
"foo": {"name": "Foo", "price": 50.2},
|
||||
"bar": {"name": "Bar", "description": "The Bar fighters", "price": 62, "tax": 20.2},
|
||||
"baz": {
|
||||
"name": "Baz",
|
||||
"description": "There goes my baz",
|
||||
"price": 50.2,
|
||||
"tax": 10.5,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@app.get(
|
||||
"/items/{item_id}/name",
|
||||
response_model=Item,
|
||||
response_model_include=["name", "description"],
|
||||
)
|
||||
async def read_item_name(item_id: str):
|
||||
return items[item_id]
|
||||
|
||||
|
||||
@app.get("/items/{item_id}/public", response_model=Item, response_model_exclude=["tax"])
|
||||
async def read_item_public_data(item_id: str):
|
||||
return items[item_id]
|
||||
@@ -1,4 +1,4 @@
|
||||
from fastapi import FastAPI, Security
|
||||
from fastapi import Depends, FastAPI
|
||||
from fastapi.security import OAuth2PasswordBearer
|
||||
|
||||
app = FastAPI()
|
||||
@@ -7,5 +7,5 @@ oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/token")
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items(token: str = Security(oauth2_scheme)):
|
||||
async def read_items(token: str = Depends(oauth2_scheme)):
|
||||
return {"token": token}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import Depends, FastAPI, Security
|
||||
from fastapi import Depends, FastAPI
|
||||
from fastapi.security import OAuth2PasswordBearer
|
||||
from pydantic import BaseModel
|
||||
|
||||
@@ -22,7 +22,7 @@ def fake_decode_token(token):
|
||||
)
|
||||
|
||||
|
||||
async def get_current_user(token: str = Security(oauth2_scheme)):
|
||||
async def get_current_user(token: str = Depends(oauth2_scheme)):
|
||||
user = fake_decode_token(token)
|
||||
return user
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
from fastapi import Depends, FastAPI, HTTPException, Security
|
||||
from fastapi import Depends, FastAPI, HTTPException
|
||||
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
|
||||
from pydantic import BaseModel
|
||||
from starlette.status import HTTP_401_UNAUTHORIZED
|
||||
|
||||
fake_users_db = {
|
||||
"johndoe": {
|
||||
@@ -53,11 +54,13 @@ def fake_decode_token(token):
|
||||
return user
|
||||
|
||||
|
||||
async def get_current_user(token: str = Security(oauth2_scheme)):
|
||||
async def get_current_user(token: str = Depends(oauth2_scheme)):
|
||||
user = fake_decode_token(token)
|
||||
if not user:
|
||||
raise HTTPException(
|
||||
status_code=400, detail="Invalid authentication credentials"
|
||||
status_code=HTTP_401_UNAUTHORIZED,
|
||||
detail="Invalid authentication credentials",
|
||||
headers={"WWW-Authenticate": "Bearer"},
|
||||
)
|
||||
return user
|
||||
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
import jwt
|
||||
from fastapi import Depends, FastAPI, HTTPException, Security
|
||||
from fastapi import Depends, FastAPI, HTTPException
|
||||
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
|
||||
from jwt import PyJWTError
|
||||
from passlib.context import CryptContext
|
||||
from pydantic import BaseModel
|
||||
from starlette.status import HTTP_403_FORBIDDEN
|
||||
from starlette.status import HTTP_401_UNAUTHORIZED
|
||||
|
||||
# to get a string like this run:
|
||||
# openssl rand -hex 32
|
||||
SECRET_KEY = "09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7"
|
||||
ALGORITHM = "HS256"
|
||||
TOKEN_SUBJECT = "access"
|
||||
ACCESS_TOKEN_EXPIRE_MINUTES = 30
|
||||
|
||||
|
||||
@@ -32,7 +31,7 @@ class Token(BaseModel):
|
||||
token_type: str
|
||||
|
||||
|
||||
class TokenPayload(BaseModel):
|
||||
class TokenData(BaseModel):
|
||||
username: str = None
|
||||
|
||||
|
||||
@@ -83,20 +82,28 @@ def create_access_token(*, data: dict, expires_delta: timedelta = None):
|
||||
expire = datetime.utcnow() + expires_delta
|
||||
else:
|
||||
expire = datetime.utcnow() + timedelta(minutes=15)
|
||||
to_encode.update({"exp": expire, "sub": TOKEN_SUBJECT})
|
||||
to_encode.update({"exp": expire})
|
||||
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
|
||||
return encoded_jwt
|
||||
|
||||
|
||||
async def get_current_user(token: str = Security(oauth2_scheme)):
|
||||
async def get_current_user(token: str = Depends(oauth2_scheme)):
|
||||
credentials_exception = HTTPException(
|
||||
status_code=HTTP_401_UNAUTHORIZED,
|
||||
detail="Could not validate credentials",
|
||||
headers={"WWW-Authenticate": "Bearer"},
|
||||
)
|
||||
try:
|
||||
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
|
||||
token_data = TokenPayload(**payload)
|
||||
username: str = payload.get("sub")
|
||||
if username is None:
|
||||
raise credentials_exception
|
||||
token_data = TokenData(username=username)
|
||||
except PyJWTError:
|
||||
raise HTTPException(
|
||||
status_code=HTTP_403_FORBIDDEN, detail="Could not validate credentials"
|
||||
)
|
||||
raise credentials_exception
|
||||
user = get_user(fake_users_db, username=token_data.username)
|
||||
if user is None:
|
||||
raise credentials_exception
|
||||
return user
|
||||
|
||||
|
||||
@@ -107,18 +114,22 @@ async def get_current_active_user(current_user: User = Depends(get_current_user)
|
||||
|
||||
|
||||
@app.post("/token", response_model=Token)
|
||||
async def route_login_access_token(form_data: OAuth2PasswordRequestForm = Depends()):
|
||||
async def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends()):
|
||||
user = authenticate_user(fake_users_db, form_data.username, form_data.password)
|
||||
if not user:
|
||||
raise HTTPException(status_code=400, detail="Incorrect email or password")
|
||||
raise HTTPException(
|
||||
status_code=HTTP_401_UNAUTHORIZED,
|
||||
detail="Incorrect username or password",
|
||||
headers={"WWW-Authenticate": "Bearer"},
|
||||
)
|
||||
access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
|
||||
access_token = create_access_token(
|
||||
data={"username": form_data.username}, expires_delta=access_token_expires
|
||||
data={"sub": user.username}, expires_delta=access_token_expires
|
||||
)
|
||||
return {"access_token": access_token, "token_type": "bearer"}
|
||||
|
||||
|
||||
@app.get("/users/me", response_model=User)
|
||||
@app.get("/users/me/", response_model=User)
|
||||
async def read_users_me(current_user: User = Depends(get_current_active_user)):
|
||||
return current_user
|
||||
|
||||
|
||||
175
docs/src/security/tutorial005.py
Normal file
@@ -0,0 +1,175 @@
|
||||
from datetime import datetime, timedelta
|
||||
from typing import List
|
||||
|
||||
import jwt
|
||||
from fastapi import Depends, FastAPI, HTTPException, Security
|
||||
from fastapi.security import (
|
||||
OAuth2PasswordBearer,
|
||||
OAuth2PasswordRequestForm,
|
||||
SecurityScopes,
|
||||
)
|
||||
from jwt import PyJWTError
|
||||
from passlib.context import CryptContext
|
||||
from pydantic import BaseModel, ValidationError
|
||||
from starlette.status import HTTP_401_UNAUTHORIZED
|
||||
|
||||
# to get a string like this run:
|
||||
# openssl rand -hex 32
|
||||
SECRET_KEY = "09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7"
|
||||
ALGORITHM = "HS256"
|
||||
ACCESS_TOKEN_EXPIRE_MINUTES = 30
|
||||
|
||||
|
||||
fake_users_db = {
|
||||
"johndoe": {
|
||||
"username": "johndoe",
|
||||
"full_name": "John Doe",
|
||||
"email": "johndoe@example.com",
|
||||
"hashed_password": "$2b$12$EixZaYVK1fsbw1ZfbX3OXePaWxn96p36WQoeG6Lruj3vjPGga31lW",
|
||||
"disabled": False,
|
||||
},
|
||||
"alice": {
|
||||
"username": "alice",
|
||||
"full_name": "Alice Chains",
|
||||
"email": "alicechains@example.com",
|
||||
"hashed_password": "$2b$12$gSvqqUPvlXP2tfVFaWK1Be7DlH.PKZbv5H8KnzzVgXXbVxpva.pFm",
|
||||
"disabled": True,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class Token(BaseModel):
|
||||
access_token: str
|
||||
token_type: str
|
||||
|
||||
|
||||
class TokenData(BaseModel):
|
||||
username: str = None
|
||||
scopes: List[str] = []
|
||||
|
||||
|
||||
class User(BaseModel):
|
||||
username: str
|
||||
email: str = None
|
||||
full_name: str = None
|
||||
disabled: bool = None
|
||||
|
||||
|
||||
class UserInDB(User):
|
||||
hashed_password: str
|
||||
|
||||
|
||||
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
|
||||
|
||||
oauth2_scheme = OAuth2PasswordBearer(
|
||||
tokenUrl="/token",
|
||||
scopes={"me": "Read information about the current user.", "items": "Read items."},
|
||||
)
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
def verify_password(plain_password, hashed_password):
|
||||
return pwd_context.verify(plain_password, hashed_password)
|
||||
|
||||
|
||||
def get_password_hash(password):
|
||||
return pwd_context.hash(password)
|
||||
|
||||
|
||||
def get_user(db, username: str):
|
||||
if username in db:
|
||||
user_dict = db[username]
|
||||
return UserInDB(**user_dict)
|
||||
|
||||
|
||||
def authenticate_user(fake_db, username: str, password: str):
|
||||
user = get_user(fake_db, username)
|
||||
if not user:
|
||||
return False
|
||||
if not verify_password(password, user.hashed_password):
|
||||
return False
|
||||
return user
|
||||
|
||||
|
||||
def create_access_token(*, data: dict, expires_delta: timedelta = None):
|
||||
to_encode = data.copy()
|
||||
if expires_delta:
|
||||
expire = datetime.utcnow() + expires_delta
|
||||
else:
|
||||
expire = datetime.utcnow() + timedelta(minutes=15)
|
||||
to_encode.update({"exp": expire})
|
||||
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
|
||||
return encoded_jwt
|
||||
|
||||
|
||||
async def get_current_user(
|
||||
security_scopes: SecurityScopes, token: str = Depends(oauth2_scheme)
|
||||
):
|
||||
if security_scopes.scopes:
|
||||
authenticate_value = f'Bearer scope="{security_scopes.scope_str}"'
|
||||
else:
|
||||
authenticate_value = f"Bearer"
|
||||
credentials_exception = HTTPException(
|
||||
status_code=HTTP_401_UNAUTHORIZED,
|
||||
detail="Could not validate credentials",
|
||||
headers={"WWW-Authenticate": authenticate_value},
|
||||
)
|
||||
try:
|
||||
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
|
||||
username: str = payload.get("sub")
|
||||
if username is None:
|
||||
raise credentials_exception
|
||||
token_scopes = payload.get("scopes", [])
|
||||
token_data = TokenData(scopes=token_scopes, username=username)
|
||||
except (PyJWTError, ValidationError):
|
||||
raise credentials_exception
|
||||
user = get_user(fake_users_db, username=token_data.username)
|
||||
if user is None:
|
||||
raise credentials_exception
|
||||
for scope in security_scopes.scopes:
|
||||
if scope not in token_data.scopes:
|
||||
raise HTTPException(
|
||||
status_code=HTTP_401_UNAUTHORIZED,
|
||||
detail="Not enough permissions",
|
||||
headers={"WWW-Authenticate": authenticate_value},
|
||||
)
|
||||
return user
|
||||
|
||||
|
||||
async def get_current_active_user(
|
||||
current_user: User = Security(get_current_user, scopes=["me"])
|
||||
):
|
||||
if current_user.disabled:
|
||||
raise HTTPException(status_code=400, detail="Inactive user")
|
||||
return current_user
|
||||
|
||||
|
||||
@app.post("/token", response_model=Token)
|
||||
async def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends()):
|
||||
user = authenticate_user(fake_users_db, form_data.username, form_data.password)
|
||||
if not user:
|
||||
raise HTTPException(status_code=400, detail="Incorrect username or password")
|
||||
access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
|
||||
access_token = create_access_token(
|
||||
data={"sub": user.username, "scopes": form_data.scopes},
|
||||
expires_delta=access_token_expires,
|
||||
)
|
||||
return {"access_token": access_token, "token_type": "bearer"}
|
||||
|
||||
|
||||
@app.get("/users/me/", response_model=User)
|
||||
async def read_users_me(current_user: User = Depends(get_current_active_user)):
|
||||
return current_user
|
||||
|
||||
|
||||
@app.get("/users/me/items/")
|
||||
async def read_own_items(
|
||||
current_user: User = Security(get_current_active_user, scopes=["items"])
|
||||
):
|
||||
return [{"item_id": "Foo", "owner": current_user.username}]
|
||||
|
||||
|
||||
@app.get("/status/")
|
||||
async def read_system_status(current_user: User = Depends(get_current_user)):
|
||||
return {"status": "ok"}
|
||||
11
docs/src/security/tutorial006.py
Normal file
@@ -0,0 +1,11 @@
|
||||
from fastapi import Depends, FastAPI
|
||||
from fastapi.security import HTTPBasic, HTTPBasicCredentials
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
security = HTTPBasic()
|
||||
|
||||
|
||||
@app.get("/users/me")
|
||||
def read_current_user(credentials: HTTPBasicCredentials = Depends(security)):
|
||||
return {"username": credentials.username, "password": credentials.password}
|
||||
22
docs/src/security/tutorial007.py
Normal file
@@ -0,0 +1,22 @@
|
||||
from fastapi import Depends, FastAPI, HTTPException
|
||||
from fastapi.security import HTTPBasic, HTTPBasicCredentials
|
||||
from starlette.status import HTTP_401_UNAUTHORIZED
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
security = HTTPBasic()
|
||||
|
||||
|
||||
def get_current_username(credentials: HTTPBasicCredentials = Depends(security)):
|
||||
if credentials.username != "foo" or credentials.password != "password":
|
||||
raise HTTPException(
|
||||
status_code=HTTP_401_UNAUTHORIZED,
|
||||
detail="Incorrect email or password",
|
||||
headers={"WWW-Authenticate": "Basic"},
|
||||
)
|
||||
return credentials.username
|
||||
|
||||
|
||||
@app.get("/users/me")
|
||||
def read_current_user(username: str = Depends(get_current_username)):
|
||||
return {"username": username}
|
||||
6
docs/src/static_files/tutorial001.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from fastapi import FastAPI
|
||||
from starlette.staticfiles import StaticFiles
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
app.mount("/static", StaticFiles(directory="static"), name="static")
|
||||
3
docs/src/templates/static/styles.css
Normal file
@@ -0,0 +1,3 @@
|
||||
h1 {
|
||||
color: green;
|
||||
}
|
||||
9
docs/src/templates/templates/item.html
Normal file
@@ -0,0 +1,9 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Item Details</title>
|
||||
<link href="{{ url_for('static', path='/styles.css') }}" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<h1>Item ID: {{ id }}</h1>
|
||||
</body>
|
||||
</html>
|
||||
16
docs/src/templates/tutorial001.py
Normal file
@@ -0,0 +1,16 @@
|
||||
from fastapi import FastAPI
|
||||
from starlette.requests import Request
|
||||
from starlette.staticfiles import StaticFiles
|
||||
from starlette.templating import Jinja2Templates
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
app.mount("/static", StaticFiles(directory="static"), name="static")
|
||||
|
||||
|
||||
templates = Jinja2Templates(directory="templates")
|
||||
|
||||
|
||||
@app.get("/items/{id}")
|
||||
async def read_item(request: Request, id: str):
|
||||
return templates.TemplateResponse("item.html", {"request": request, "id": id})
|
||||
0
docs/src/websockets/__init__.py
Normal file
@@ -44,10 +44,9 @@ async def get():
|
||||
return HTMLResponse(html)
|
||||
|
||||
|
||||
@app.websocket_route("/ws")
|
||||
@app.websocket("/ws")
|
||||
async def websocket_endpoint(websocket: WebSocket):
|
||||
await websocket.accept()
|
||||
while True:
|
||||
data = await websocket.receive_text()
|
||||
await websocket.send_text(f"Message text was: {data}")
|
||||
await websocket.close()
|
||||
|
||||
78
docs/src/websockets/tutorial002.py
Normal file
@@ -0,0 +1,78 @@
|
||||
from fastapi import Cookie, Depends, FastAPI, Header
|
||||
from starlette.responses import HTMLResponse
|
||||
from starlette.status import WS_1008_POLICY_VIOLATION
|
||||
from starlette.websockets import WebSocket
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
html = """
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Chat</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>WebSocket Chat</h1>
|
||||
<form action="" onsubmit="sendMessage(event)">
|
||||
<label>Item ID: <input type="text" id="itemId" autocomplete="off" value="foo"/></label>
|
||||
<button onclick="connect(event)">Connect</button>
|
||||
<br>
|
||||
<label>Message: <input type="text" id="messageText" autocomplete="off"/></label>
|
||||
<button>Send</button>
|
||||
</form>
|
||||
<ul id='messages'>
|
||||
</ul>
|
||||
<script>
|
||||
var ws = null;
|
||||
function connect(event) {
|
||||
var input = document.getElementById("itemId")
|
||||
ws = new WebSocket("ws://localhost:8000/items/" + input.value + "/ws");
|
||||
ws.onmessage = function(event) {
|
||||
var messages = document.getElementById('messages')
|
||||
var message = document.createElement('li')
|
||||
var content = document.createTextNode(event.data)
|
||||
message.appendChild(content)
|
||||
messages.appendChild(message)
|
||||
};
|
||||
}
|
||||
function sendMessage(event) {
|
||||
var input = document.getElementById("messageText")
|
||||
ws.send(input.value)
|
||||
input.value = ''
|
||||
event.preventDefault()
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
|
||||
@app.get("/")
|
||||
async def get():
|
||||
return HTMLResponse(html)
|
||||
|
||||
|
||||
async def get_cookie_or_client(
|
||||
websocket: WebSocket, session: str = Cookie(None), x_client: str = Header(None)
|
||||
):
|
||||
if session is None and x_client is None:
|
||||
await websocket.close(code=WS_1008_POLICY_VIOLATION)
|
||||
return session or x_client
|
||||
|
||||
|
||||
@app.websocket("/items/{item_id}/ws")
|
||||
async def websocket_endpoint(
|
||||
websocket: WebSocket,
|
||||
item_id: int,
|
||||
q: str = None,
|
||||
cookie_or_client: str = Depends(get_cookie_or_client),
|
||||
):
|
||||
await websocket.accept()
|
||||
while True:
|
||||
data = await websocket.receive_text()
|
||||
await websocket.send_text(
|
||||
f"Session Cookie or X-Client Header value is: {cookie_or_client}"
|
||||
)
|
||||
if q is not None:
|
||||
await websocket.send_text(f"Query parameter q is: {q}")
|
||||
await websocket.send_text(f"Message text was: {data}, for item ID: {item_id}")
|
||||
235
docs/tutorial/additional-responses.md
Normal file
@@ -0,0 +1,235 @@
|
||||
!!! warning
|
||||
This is a rather advanced topic.
|
||||
|
||||
If you are starting with **FastAPI**, you might not need this.
|
||||
|
||||
You can declare additional responses, with additional status codes, media types, descriptions, etc.
|
||||
|
||||
Those additional responses will be included in the OpenAPI schema, so they will also appear in the API docs.
|
||||
|
||||
But for those additional responses you have to make sure you return a `Response` like `JSONResponse` directly, with your status code and content.
|
||||
|
||||
## Additional Response with `model`
|
||||
|
||||
You can pass to your *path operation decorators* a parameter `responses`.
|
||||
|
||||
It receives a `dict`, the keys are status codes for each response, like `200`, and the values are other `dict`s with the information for each of them.
|
||||
|
||||
Each of those response `dict`s can have a key `model`, containing a Pydantic model, just like `response_model`.
|
||||
|
||||
**FastAPI** will take that model, generate its JSON Schema and include it in the correct place in OpenAPI.
|
||||
|
||||
For example, to declare another response with a status code `404` and a Pydantic model `Message`, you can write:
|
||||
|
||||
|
||||
```Python hl_lines="18 23"
|
||||
{!./src/additional_responses/tutorial001.py!}
|
||||
```
|
||||
|
||||
!!! note
|
||||
Have in mind that you have to return the `JSONResponse` directly.
|
||||
|
||||
!!! info
|
||||
The `model` key is not part of OpenAPI.
|
||||
|
||||
**FastAPI** will take the Pydantic model from there, generate the `JSON Schema`, and put it in the correct place.
|
||||
|
||||
The correct place is:
|
||||
|
||||
* In the key `content`, that has as value another JSON object (`dict`) that contains:
|
||||
* A key with the media type, e.g. `application/json`, that contains as value another JSON object, that contains:
|
||||
* A key `schema`, that has as the value the JSON Schema from the model, here's the correct place.
|
||||
* **FastAPI** adds a reference here to the global JSON Schemas in another place in your OpenAPI instead of including it directly. This way, other applications and clients can use those JSON Schemas directly, provide better code generation tools, etc.
|
||||
|
||||
The generated responses in the OpenAPI for this *path operation* will be:
|
||||
|
||||
```JSON hl_lines="3 4 5 6 7 8 9 10 11 12"
|
||||
{
|
||||
"responses": {
|
||||
"404": {
|
||||
"description": "Additional Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/Message"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/Item"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"422": {
|
||||
"description": "Validation Error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/HTTPValidationError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The schemas are referenced to another place inside the OpenAPI schema:
|
||||
|
||||
```JSON hl_lines="4 5 6 7 8 9 10 11 12 13 14 15 16"
|
||||
{
|
||||
"components": {
|
||||
"schemas": {
|
||||
"Message": {
|
||||
"title": "Message",
|
||||
"required": [
|
||||
"message"
|
||||
],
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"message": {
|
||||
"title": "Message",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Item": {
|
||||
"title": "Item",
|
||||
"required": [
|
||||
"id",
|
||||
"value"
|
||||
],
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"title": "Id",
|
||||
"type": "string"
|
||||
},
|
||||
"value": {
|
||||
"title": "Value",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ValidationError": {
|
||||
"title": "ValidationError",
|
||||
"required": [
|
||||
"loc",
|
||||
"msg",
|
||||
"type"
|
||||
],
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"loc": {
|
||||
"title": "Location",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"msg": {
|
||||
"title": "Message",
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"title": "Error Type",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"HTTPValidationError": {
|
||||
"title": "HTTPValidationError",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"detail": {
|
||||
"title": "Detail",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/ValidationError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Additional media types for the main response
|
||||
|
||||
You can use this same `responses` parameter to add different media types for the same main response.
|
||||
|
||||
For example, you can add an additional media type of `image/png`, declaring that your *path operation* can return a JSON object (with media type `application/json`) or a PNG image:
|
||||
|
||||
```Python hl_lines="17 18 19 20 21 22 23 24 28"
|
||||
{!./src/additional_responses/tutorial002.py!}
|
||||
```
|
||||
|
||||
!!! note
|
||||
Notice that you have to return the image using a `FileResponse` directly.
|
||||
|
||||
## Combining information
|
||||
|
||||
You can also combine response information from multiple places, including the `response_model`, `status_code`, and `responses` parameters.
|
||||
|
||||
You can declare a `response_model`, using the default status code `200` (or a custom one if you need), and then declare additional information for that same response in `responses`, directly in the OpenAPI schema.
|
||||
|
||||
**FastAPI** will keep the additional information from `responses`, and combine it with the JSON Schema from your model.
|
||||
|
||||
For example, you can declare a response with a status code `404` that uses a Pydantic model and has a custom `description`.
|
||||
|
||||
And a response with a status code `200` that uses your `response_model`, but includes a custom `example`:
|
||||
|
||||
```Python hl_lines="20 21 22 23 24 25 26 27 28 29 30 31"
|
||||
{!./src/additional_responses/tutorial003.py!}
|
||||
```
|
||||
|
||||
It will all be combined and included in your OpenAPI, and shown in the API docs:
|
||||
|
||||
<img src="/img/tutorial/additional-responses/image01.png">
|
||||
|
||||
|
||||
## Combine predefined responses and custom ones
|
||||
|
||||
You might want to have some predefined responses that apply to many *path operations*, but you want to combine them with custom responses needed by each *path operation*.
|
||||
|
||||
For those cases, you can use the Python technique of "unpacking" a `dict` with `**dict_to_unpack`:
|
||||
|
||||
```Python
|
||||
old_dict = {
|
||||
"old key": "old value",
|
||||
"second old key": "second old value",
|
||||
}
|
||||
new_dict = {**old_dict, "new key": "new value"}
|
||||
```
|
||||
|
||||
Here, `new_dict` will contain all the key-value pairs from `old_dict` plus the new key-value pair:
|
||||
|
||||
```Python
|
||||
{
|
||||
"old key": "old value",
|
||||
"second old key": "second old value",
|
||||
"new key": "new value",
|
||||
}
|
||||
```
|
||||
|
||||
You can use that technique to re-use some predefined responses in your *path operations* and combine them with additional custom ones.
|
||||
|
||||
For example:
|
||||
|
||||
```Python hl_lines="11 12 13 14 15 24"
|
||||
{!./src/additional_responses/tutorial004.py!}
|
||||
```
|
||||
|
||||
## More information about OpenAPI responses
|
||||
|
||||
To see what exactly you can include in the responses, you can check these sections in the OpenAPI specification:
|
||||
|
||||
* <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#responsesObject" target="_blank">OpenAPI Responses Object</a>, it includes the `Response Object`.
|
||||
* <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#responseObject" target="_blank">OpenAPI Response Object</a>, you can include anything from this directly in each response inside your `responses` parameter. Including `description`, `headers`, `content` (inside of this is that you declare different media types and JSON Schemas), and `links`.
|
||||
30
docs/tutorial/additional-status-codes.md
Normal file
@@ -0,0 +1,30 @@
|
||||
By default, **FastAPI** will return the responses using Starlette's `JSONResponse`, putting the content you return from your *path operation* inside of that `JSONResponse`.
|
||||
|
||||
It will use the default status code or the one you set in your *path operation*.
|
||||
|
||||
## Additional status codes
|
||||
|
||||
If you want to return additional status codes apart from the main one, you can do that by returning a `Response` directly, like a `JSONResponse`, and set the additional status code directly.
|
||||
|
||||
For example, let's say that you want to have a *path operation* that allows to update items, and returns HTTP status codes of 200 "OK" when successful.
|
||||
|
||||
But you also want it to accept new items. And when the items didn't exist before, it creates them, and returns an HTTP status code of 201 "Created".
|
||||
|
||||
To achieve that, import `JSONResponse`, and return your content there directly, setting the `status_code` that you want:
|
||||
|
||||
```Python hl_lines="2 20"
|
||||
{!./src/additional_status_codes/tutorial001.py!}
|
||||
```
|
||||
|
||||
!!! warning
|
||||
When you return a `Response` directly, like in the example above, it will be returned directly.
|
||||
|
||||
It won't be serialized with a model, etc.
|
||||
|
||||
Make sure it has the data you want it to have, and that the values are valid JSON (if you are using `JSONResponse`).
|
||||
|
||||
## OpenAPI and API docs
|
||||
|
||||
If you return additional status codes and responses directly, they won't be included in the OpenAPI schema (the API docs), because FastAPI doesn't have a way to know before hand what you are going to return.
|
||||
|
||||
But you can document that in your code, using: <a href="https://fastapi.tiangolo.com/tutorial/additional-responses/" target="_blank">Additional Responses</a>.
|
||||
@@ -22,16 +22,15 @@ Let's say you have a file structure like this:
|
||||
|
||||
!!! tip
|
||||
There are two `__init__.py` files: one in each directory or subdirectory.
|
||||
|
||||
|
||||
This is what allows importing code from one file into another.
|
||||
|
||||
For example, in `app/main.py` you could have a line like:
|
||||
|
||||
|
||||
```
|
||||
from app.routers import items
|
||||
```
|
||||
|
||||
|
||||
* The `app` directory contains everything.
|
||||
* This `app` directory has an empty file `app/__init__.py`.
|
||||
* So, the `app` directory is a "Python package" (a collection of "Python modules").
|
||||
@@ -103,7 +102,17 @@ But let's say that this time we are more lazy.
|
||||
|
||||
And we don't want to have to explicitly type `/items/` and `tags=["items"]` in every *path operation* (we will be able to do it later):
|
||||
|
||||
```Python hl_lines="6 11 16"
|
||||
```Python hl_lines="6 11"
|
||||
{!./src/bigger_applications/app/routers/items.py!}
|
||||
```
|
||||
|
||||
### Add some custom `tags`, `responses`, and `dependencies`
|
||||
|
||||
We are not adding the prefix `/items/` nor the `tags=["items"]` to add them later.
|
||||
|
||||
But we can add custom `tags` and `responses` that will be applied to a specific *path operation*:
|
||||
|
||||
```Python hl_lines="18 19"
|
||||
{!./src/bigger_applications/app/routers/items.py!}
|
||||
```
|
||||
|
||||
@@ -187,12 +196,11 @@ So, to be able to use both of them in the same file, we import the submodules di
|
||||
{!./src/bigger_applications/app/main.py!}
|
||||
```
|
||||
|
||||
|
||||
### Include an `APIRouter`
|
||||
|
||||
Now, let's include the `router` from the submodule `users`:
|
||||
|
||||
```Python hl_lines="8"
|
||||
```Python hl_lines="13"
|
||||
{!./src/bigger_applications/app/main.py!}
|
||||
```
|
||||
|
||||
@@ -211,15 +219,14 @@ It will include all the routes from that router as part of it.
|
||||
|
||||
!!! check
|
||||
You don't have to worry about performance when including routers.
|
||||
|
||||
|
||||
This will take microseconds and will only happen at startup.
|
||||
|
||||
|
||||
So it won't affect performance.
|
||||
|
||||
### Include an `APIRouter` with a `prefix`, `tags`, `responses`, and `dependencies`
|
||||
|
||||
### Include an `APIRouter` with a prefix
|
||||
|
||||
Now, let's include the router form the `items` submodule.
|
||||
Now, let's include the router from the `items` submodule.
|
||||
|
||||
But, remember that we were lazy and didn't add `/items/` nor `tags` to all the *path operations*?
|
||||
|
||||
@@ -237,9 +244,13 @@ async def read_item(item_id: str):
|
||||
|
||||
So, the prefix in this case would be `/items`.
|
||||
|
||||
And we can also add a list of `tags` that will be applied to all the *path operations* included in this router:
|
||||
We can also add a list of `tags` that will be applied to all the *path operations* included in this router.
|
||||
|
||||
```Python hl_lines="9"
|
||||
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.
|
||||
|
||||
```Python hl_lines="8 9 10 14 15 16 17 18 19 20"
|
||||
{!./src/bigger_applications/app/main.py!}
|
||||
```
|
||||
|
||||
@@ -250,21 +261,28 @@ The end result is that the item paths are now:
|
||||
|
||||
...as we intended.
|
||||
|
||||
And they are marked with a list of tags that contain a single string `"items"`.
|
||||
* They will be marked with a list of tags that contain a single string `"items"`.
|
||||
* The *path operation* that declared a `"custom"` tag will have both tags, `items` and `custom`.
|
||||
* These "tags" are especially useful for the automatic interactive documentation systems (using OpenAPI).
|
||||
* All of them will include the predefined `responses`.
|
||||
* The *path operation* that declared a custom `403` response will have both the predefined responses (`404`) and the `403` declared in it directly.
|
||||
* All these *path operations* will have the list of `dependencies` evaluated/executed before them.
|
||||
* If you also declare dependencies in a specific *path operation*, **they will be executed too**.
|
||||
* The router dependencies are executed first, then the <a href="https://fastapi.tiangolo.com/tutorial/dependencies/dependencies-in-decorator/" target="_blank">`dependencies` in the decorator</a>, and then the normal parameter dependencies.
|
||||
* You can also add <a href="https://fastapi.tiangolo.com/tutorial/security/oauth2-scopes/" target="_blank">`Security` dependencies with `scopes`</a>.
|
||||
|
||||
These "tags" are especially useful for the automatic interactive documentation systems (using OpenAPI).
|
||||
!!! tip
|
||||
Having `dependencies` in a decorator can be used, for example, to require authentication for a whole group of *path operations*. Even if the dependencies are not added individually to each one of them.
|
||||
|
||||
!!! check
|
||||
The `prefix` and `tags` parameters are (as in many other cases) just a feature from **FastAPI** to help you avoid code duplication.
|
||||
|
||||
The `prefix`, `tags`, `responses` and `dependencies` parameters are (as in many other cases) just a feature from **FastAPI** to help you avoid code duplication.
|
||||
|
||||
!!! tip
|
||||
You could also add path operations directly, for example with: `@app.get(...)`.
|
||||
|
||||
Apart from `app.include_router()`, in the same **FastAPI** app.
|
||||
|
||||
It would still work the same.
|
||||
|
||||
Apart from `app.include_router()`, in the same **FastAPI** app.
|
||||
|
||||
It would still work the same.
|
||||
|
||||
!!! info "Very Technical Details"
|
||||
**Note**: this is a very technical detail that you probably can **just skip**.
|
||||
|
||||
@@ -120,7 +120,7 @@ To see all the options you have, checkout the docs for <a href="https://pydantic
|
||||
|
||||
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`:
|
||||
|
||||
```Python hl_lines="5 11"
|
||||
```Python hl_lines="4 10"
|
||||
{!./src/body_nested_models/tutorial005.py!}
|
||||
```
|
||||
|
||||
@@ -130,7 +130,7 @@ The string will be checked to be a valid URL, and documented in JSON Schema / Op
|
||||
|
||||
You can also use Pydantic models as subtypes of `list`, `set`, etc:
|
||||
|
||||
```Python hl_lines="21"
|
||||
```Python hl_lines="20"
|
||||
{!./src/body_nested_models/tutorial006.py!}
|
||||
```
|
||||
|
||||
@@ -167,7 +167,7 @@ This will expect (convert, validate, document, etc) a JSON body like:
|
||||
|
||||
You can define arbitrarily deeply nested models:
|
||||
|
||||
```Python hl_lines="10 15 21 24 28"
|
||||
```Python hl_lines="9 14 20 23 27"
|
||||
{!./src/body_nested_models/tutorial007.py!}
|
||||
```
|
||||
|
||||
@@ -184,7 +184,7 @@ images: List[Image]
|
||||
|
||||
as in:
|
||||
|
||||
```Python hl_lines="16"
|
||||
```Python hl_lines="15"
|
||||
{!./src/body_nested_models/tutorial008.py!}
|
||||
```
|
||||
|
||||
|
||||
@@ -22,12 +22,13 @@ You can then use `Schema` with model attributes:
|
||||
|
||||
`Schema` works the same way as `Query`, `Path` and `Body`, it has all the same parameters, etc.
|
||||
|
||||
|
||||
!!! info
|
||||
!!! 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`.
|
||||
|
||||
|
||||
97
docs/tutorial/body-updates.md
Normal file
@@ -0,0 +1,97 @@
|
||||
## Update replacing with `PUT`
|
||||
|
||||
To update an item you can use the [HTTP `PUT`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PUT) operation.
|
||||
|
||||
You can use the `jsonable_encoder` to convert the input data to data that can be stored as JSON (e.g. with a NoSQL database). For example, converting `datetime` to `str`.
|
||||
|
||||
```Python hl_lines="30 31 32 33 34 35"
|
||||
{!./src/body_updates/tutorial001.py!}
|
||||
```
|
||||
|
||||
`PUT` is used to receive data that should replace the existing data.
|
||||
|
||||
### Warning about replacing
|
||||
|
||||
That means that if you want to update the item `bar` using `PUT` with a body containing:
|
||||
|
||||
```Python
|
||||
{
|
||||
"name": "Barz",
|
||||
"price": 3,
|
||||
"description": None,
|
||||
}
|
||||
```
|
||||
|
||||
because it doesn't include the already stored attribute `"tax": 20.2`, the input model would take the default value of `"tax": 10.5`.
|
||||
|
||||
And the data would be saved with that "new" `tax` of `10.5`.
|
||||
|
||||
## Partial updates with `PATCH`
|
||||
|
||||
You can also use the [HTTP `PATCH`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PATCH) operation to *partially* update data.
|
||||
|
||||
This means that you can send only the data that you want to update, leaving the rest intact.
|
||||
|
||||
!!! Note
|
||||
`PATCH` is less commonly used and known than `PUT`.
|
||||
|
||||
And many teams use only `PUT`, even for partial updates.
|
||||
|
||||
You are **free** to use them however you want, **FastAPI** doesn't impose any restrictions.
|
||||
|
||||
But this guide shows you, more or less, how they are intended to be used.
|
||||
|
||||
### Using Pydantic's `skip_defaults` parameter
|
||||
|
||||
If you want to receive partial updates, it's very useful to use the parameter `skip_defaults` in Pydantic's model's `.dict()`.
|
||||
|
||||
Like `item.dict(skip_defaults=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:
|
||||
|
||||
```Python hl_lines="34"
|
||||
{!./src/body_updates/tutorial002.py!}
|
||||
```
|
||||
|
||||
### Using Pydantic's `update` parameter
|
||||
|
||||
Now, you can create a copy of the existing model using `.copy()`, and pass the `update` parameter with a `dict` containing the data to update.
|
||||
|
||||
Like `stored_item_model.copy(update=update_data)`:
|
||||
|
||||
```Python hl_lines="35"
|
||||
{!./src/body_updates/tutorial002.py!}
|
||||
```
|
||||
|
||||
### Partial updates recap
|
||||
|
||||
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`).
|
||||
* 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`).
|
||||
* This is comparable to using the model's `.dict()` method again, but it makes sure (and converts) the values to data types that can be converted to JSON, for example, `datetime` to `str`.
|
||||
* Save the data to your DB.
|
||||
* Return the updated model.
|
||||
|
||||
```Python hl_lines="30 31 32 33 34 35 36 37"
|
||||
{!./src/body_updates/tutorial002.py!}
|
||||
```
|
||||
|
||||
!!! tip
|
||||
You can actually use this same technique with an HTTP `PUT` operation.
|
||||
|
||||
But the example here uses `PATCH` because it was created for these use cases.
|
||||
|
||||
!!! note
|
||||
Notice that the input model is still validated.
|
||||
|
||||
So, if you want to receive partial updates that can omit all the attributes, you need to have a model with all the attributes marked as optional (with default values or `None`).
|
||||
|
||||
To distinguish from the models with all optional values for **updates** and models with required values for **creation**, you can use the ideas described in <a href="https://fastapi.tiangolo.com/tutorial/extra-models/" target="_blank">Extra Models</a>.
|
||||
@@ -18,9 +18,11 @@ The first value is the default value, you can pass all the extra validation or a
|
||||
{!./src/cookie_params/tutorial001.py!}
|
||||
```
|
||||
|
||||
!!! info
|
||||
!!! note "Technical Details"
|
||||
`Cookie` is a "sister" class of `Path` and `Query`. It also inherits from the same common `Param` class.
|
||||
|
||||
But remember that when you import `Query`, `Path`, `Cookie` 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>.
|
||||
|
||||
!!! info
|
||||
To declare cookies, you need to use `Cookie`, because otherwise the parameters would be interpreted as query parameters.
|
||||
|
||||
|
||||
55
docs/tutorial/cors.md
Normal file
@@ -0,0 +1,55 @@
|
||||
<a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS" target="_blank">CORS or "Cross-Origin Resource Sharing"</a> refers to the situations when a frontend running in a browser has JavaScript code that communicates with a backend, and the backend is in a different "origin" than the frontend.
|
||||
|
||||
## Origin
|
||||
|
||||
An origin is the combination of protocol (`http`, `https`), domain (`myapp.com`, `localhost`, `localhost.tiangolo.com`), and port (`80`, `443`, `8080`).
|
||||
|
||||
So, all these are different origins:
|
||||
|
||||
* `http://localhost`
|
||||
* `https://localhost`
|
||||
* `http://localhost:8080`
|
||||
|
||||
Even if they are all in `localhost`, they use different protocols or ports, so, they are different "origins".
|
||||
|
||||
## Steps
|
||||
|
||||
So, let's say you have a frontend running in your browser at `http://localhost:8080`, and its JavaScript is trying to communicate with a backend running at `http://localhost` (because we don't specify a port, the browser will assume the default port `80`).
|
||||
|
||||
Then, the browser will send an HTTP `OPTIONS` request to the backend, and if the backend sends the appropriate headers authorizing the communication from this different origin (`http://localhost:8080`) then the browser will let the JavaScript in the frontend send its request to the backend.
|
||||
|
||||
To achieve this, the backend must have a list of "allowed origins".
|
||||
|
||||
In this case, it would have to include `http://localhost:8080` for the frontend to work correctly.
|
||||
|
||||
## Wildcards
|
||||
|
||||
It's also possible to declare the list as `"*"` (a "wildcard") to say that all are allowed.
|
||||
|
||||
But that will only allow certain types of communication, excluding everything that involves credentials: Cookies, Authorization headers like those used with Bearer Tokens, etc.
|
||||
|
||||
So, for everything to work correctly, it's better to specify explicitly the allowed origins.
|
||||
|
||||
## Use `CORSMiddleware`
|
||||
|
||||
You can configure it in your **FastAPI** application using Starlette's <a href="https://www.starlette.io/middleware/#corsmiddleware" target="_blank">`CORSMiddleware`</a>.
|
||||
|
||||
* Import it from Starlette.
|
||||
* Create a list of allowed origins (as strings).
|
||||
* Add it as a "middleware" to your **FastAPI** application.
|
||||
|
||||
You can also specify if your backend allows:
|
||||
|
||||
* Credentials (Authorization headers, Cookies, etc).
|
||||
* Specific HTTP methods (`POST`, `PUT`) or all of them with the wildcard `"*"`.
|
||||
* Specific HTTP headers or all of them with the wildcard `"*"`.
|
||||
|
||||
```Python hl_lines="2 6 7 8 9 10 11 13 14 15 16 17 18 19"
|
||||
{!./src/cors/tutorial001.py!}
|
||||
```
|
||||
|
||||
## More info
|
||||
|
||||
For more details of what you can specify in `CORSMiddleware`, check <a href="https://www.starlette.io/middleware/#corsmiddleware" target="_blank">Starlette's `CORSMiddleware` docs</a>.
|
||||
|
||||
For more info about <abbr title="Cross-Origin Resource Sharing">CORS</abbr>, check the <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS" target="_blank">Mozilla CORS documentation</a>.
|
||||
@@ -1,98 +1,88 @@
|
||||
!!! warning
|
||||
This is a rather advanced topic.
|
||||
|
||||
|
||||
If you are starting with **FastAPI**, you might not need this.
|
||||
|
||||
By default, **FastAPI** will return the responses using Starlette's `JSONResponse`.
|
||||
|
||||
But you can override it.
|
||||
You can override it by returning a `Response` directly, <a href="https://fastapi.tiangolo.com/tutorial/response-directly/" target="_blank">as seen in a previous section</a>.
|
||||
|
||||
But if you return a `Response` directly, the data won't be automatically converted, and the documentation won't be automatically generated (for example, including the specific "media type", in the HTTP header `Content-Type`).
|
||||
|
||||
But you can also declare the `Response` that you want to be used, in the *path operation decorator*.
|
||||
|
||||
The contents that you return from your *path operation function* will be put inside of that `Response`.
|
||||
|
||||
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*.
|
||||
|
||||
## Use `UJSONResponse`
|
||||
|
||||
For example, if you are squeezing performance, you can use `ujson` and set the response to be Starlette's `UJSONResponse`.
|
||||
For example, if you are squeezing performance, you can install and use `ujson` and set the response to be Starlette's `UJSONResponse`.
|
||||
|
||||
### Import `UJSONResponse`
|
||||
Import the `Response` class (sub-class) you want to use and declare it in the *path operation decorator*.
|
||||
|
||||
```Python hl_lines="2"
|
||||
```Python hl_lines="2 7"
|
||||
{!./src/custom_response/tutorial001.py!}
|
||||
```
|
||||
|
||||
!!! note
|
||||
Notice that you import it directly from `starlette.responses`, not from `fastapi`.
|
||||
|
||||
### Make your path operation use it
|
||||
|
||||
Make your path operation use `UJSONResponse` as the response class using the parameter `content_type`:
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!./src/custom_response/tutorial001.py!}
|
||||
```
|
||||
|
||||
!!! info
|
||||
The parameter is called `content_type` because it will also be used to define the "media type" of the response.
|
||||
The parameter `response_class` will also be used to define the "media type" of the response.
|
||||
|
||||
And will be documented as such in OpenAPI.
|
||||
In this case, the HTTP header `Content-Type` will be set to `application/json`.
|
||||
|
||||
And it will be documented as such in OpenAPI.
|
||||
|
||||
## HTML Response
|
||||
|
||||
To return a response with HTML directly from **FastAPI**, use `HTMLResponse`.
|
||||
|
||||
### Import `HTMLResponse`
|
||||
* Import `HTMLResponse`.
|
||||
* Pass `HTMLResponse` as the parameter `content_type` of your path operation.
|
||||
|
||||
```Python hl_lines="2"
|
||||
```Python hl_lines="2 7"
|
||||
{!./src/custom_response/tutorial002.py!}
|
||||
```
|
||||
|
||||
!!! note
|
||||
Notice that you import it directly from `starlette.responses`, not from `fastapi`.
|
||||
|
||||
|
||||
### Define your `content_type` class
|
||||
|
||||
Pass `HTMLResponse` as the parameter `content_type` of your path operation:
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!./src/custom_response/tutorial002.py!}
|
||||
```
|
||||
|
||||
!!! info
|
||||
The parameter is called `content_type` because it will also be used to define the "media type" of the response.
|
||||
The parameter `response_class` will also be used to define the "media type" of the response.
|
||||
|
||||
In this case, the HTTP header `Content-Type` will be set to `text/html`.
|
||||
|
||||
And it will be documented as such in OpenAPI.
|
||||
|
||||
|
||||
### Return a Starlette `Response`
|
||||
|
||||
You can also override the response directly in your path operation.
|
||||
|
||||
If you return an object that is an instance of Starlette's `Response`, it will be used as the response directly.
|
||||
As seen in <a href="https://fastapi.tiangolo.com/tutorial/response-directly/" target="_blank">another section</a>, you can also override the response directly in your path operation, by returning it.
|
||||
|
||||
The same example from above, returning an `HTMLResponse`, could look like:
|
||||
|
||||
```Python hl_lines="7"
|
||||
```Python hl_lines="2 7 19"
|
||||
{!./src/custom_response/tutorial003.py!}
|
||||
```
|
||||
|
||||
!!! info
|
||||
Of course, the `Content-Type` header will come from the the `Response` object your returned.
|
||||
|
||||
!!! warning
|
||||
A `Response` returned directly by your path operation function won't be documented in OpenAPI and won't be visible in the automatic interactive docs.
|
||||
A `Response` returned directly by your path operation function won't be documented in OpenAPI (for example, the `Content-Type` won't be documented) and won't be visible in the automatic interactive docs.
|
||||
|
||||
!!! info
|
||||
Of course, the actual `Content-Type` header, status code, etc, will come from the `Response` object your returned.
|
||||
|
||||
### Document in OpenAPI and override `Response`
|
||||
|
||||
If you want to override the response from inside of the function but at the same time document the "media type" in OpenAPI, you can use the `content_type` parameter AND return a `Response` object.
|
||||
If you want to override the response from inside of the function but at the same time document the "media type" in OpenAPI, you can use the `response_class` parameter AND return a `Response` object.
|
||||
|
||||
The `content_type` class will then be used only to document the OpenAPI path operation, but your `Response` will be used as is.
|
||||
The `response_class` will then be used only to document the OpenAPI path operation, but your `Response` will be used as is.
|
||||
|
||||
#### Return an `HTMLResponse` directly
|
||||
|
||||
For example, it could be something like:
|
||||
|
||||
```Python hl_lines="7 23"
|
||||
```Python hl_lines="7 23 21"
|
||||
{!./src/custom_response/tutorial004.py!}
|
||||
```
|
||||
|
||||
@@ -100,16 +90,10 @@ In this example, the function `generate_html_response()` already generates a Sta
|
||||
|
||||
By returning the result of calling `generate_html_response()`, you are already returning a `Response` that will override the default **FastAPI** behavior.
|
||||
|
||||
#### Declare `HTMLResponse` as `content_type`
|
||||
|
||||
But by declaring it also in the path operation decorator:
|
||||
|
||||
```Python hl_lines="21"
|
||||
{!./src/custom_response/tutorial004.py!}
|
||||
```
|
||||
|
||||
#### OpenAPI knows how to document it
|
||||
|
||||
...**FastAPI** will be able to document it in OpenAPI and in the interactive docs as HTML with `text/html`:
|
||||
But as you passed the `HTMLResponse` in the `response_class`, **FastAPI** will know how to document it in OpenAPI and the interactive docs as HTML with `text/html`:
|
||||
|
||||
<img src="/img/tutorial/custom-response/image01.png">
|
||||
|
||||
## Additional documentation
|
||||
|
||||
You can also declare the media type and many other details in OpenAPI using `responses`: <a href="https://fastapi.tiangolo.com/tutorial/additional-responses/" target="_blank">Additional Responses in OpenAPI</a>.
|
||||
|
||||
@@ -69,7 +69,7 @@ will not be executed.
|
||||
|
||||
## Run your code with your debugger
|
||||
|
||||
Because you are running the Uvicorn server directly from your code, you can call your Python program (your FastAPI application) directly form the debugger.
|
||||
Because you are running the Uvicorn server directly from your code, you can call your Python program (your FastAPI application) directly from the debugger.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -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/tutorial006.py!}
|
||||
{!./src/dependencies/tutorial007.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/tutorial006.py!}
|
||||
{!./src/dependencies/tutorial007.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/tutorial006.py!}
|
||||
{!./src/dependencies/tutorial007.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/tutorial006.py!}
|
||||
{!./src/dependencies/tutorial007.py!}
|
||||
```
|
||||
|
||||
!!! tip
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
In some cases you don't really need the return value of a dependency inside your *path operation function*.
|
||||
|
||||
Or the dependency doesn't return a value.
|
||||
|
||||
But you still need it to be executed/solved.
|
||||
|
||||
For those cases, instead of declaring a *path operation function* parameter with `Depends`, you can add a `list` of `dependencies` to the *path operation decorator*.
|
||||
|
||||
## Add `dependencies` to the *path operation decorator*
|
||||
|
||||
The *path operation decorator* receives an optional argument `dependencies`.
|
||||
|
||||
It should be a `list` of `Depends()`:
|
||||
|
||||
```Python hl_lines="17"
|
||||
{!./src/dependencies/tutorial006.py!}
|
||||
```
|
||||
|
||||
These dependencies will be executed/solved the same way normal dependencies. But their value (if they return any) won't be passed to your *path operation function*.
|
||||
|
||||
!!! tip
|
||||
Some editors check for unused function parameters, and show them as errors.
|
||||
|
||||
Using these `dependencies` in the *path operation decorator* you can make sure they are executed while avoiding editor/tooling errors.
|
||||
|
||||
It might also help avoiding confusion for new developers that see an un-used parameter in your code and could think it's unnecessary.
|
||||
|
||||
## Dependencies errors and return values
|
||||
|
||||
You can use the same dependency *functions* you use normally.
|
||||
|
||||
### Dependency requirements
|
||||
|
||||
They can declare request requirements (like headers) or other sub-dependencies:
|
||||
|
||||
```Python hl_lines="6 11"
|
||||
{!./src/dependencies/tutorial006.py!}
|
||||
```
|
||||
|
||||
### Raise exceptions
|
||||
|
||||
These dependencies can `raise` exceptions, the same as normal dependencies:
|
||||
|
||||
```Python hl_lines="8 13"
|
||||
{!./src/dependencies/tutorial006.py!}
|
||||
```
|
||||
|
||||
### Return values
|
||||
|
||||
And they can return values or not, the values won't be used.
|
||||
|
||||
So, you can re-use a normal dependency (that returns a value) you already use somewhere else, and even though the value won't be used, the dependency will be executed:
|
||||
|
||||
```Python hl_lines="9 14"
|
||||
{!./src/dependencies/tutorial006.py!}
|
||||
```
|
||||
|
||||
## Dependencies for a group of *path operations*
|
||||
|
||||
Later, when reading about how to <a href="https://fastapi.tiangolo.com/tutorial/bigger-applications/" target="_blank">structure bigger applications</a>, possibly with multiple files, you will learn how to declare a single `dependencies` parameter for a group of *path operations*.
|
||||
32
docs/tutorial/encoder.md
Normal file
@@ -0,0 +1,32 @@
|
||||
There are some cases where you might need to convert a data type (like a Pydantic model) to something compatible with JSON (like a `dict`, `list`, etc).
|
||||
|
||||
For example, if you need to store it in a database.
|
||||
|
||||
For that, **FastAPI** provides a `jsonable_encoder()` function.
|
||||
|
||||
## Using the `jsonable_encoder`
|
||||
|
||||
Let's imagine that you have a database `fake_db` that only receives JSON compatible data.
|
||||
|
||||
For example, it doesn't receive `datetime` objects, as those are not compatible with JSON.
|
||||
|
||||
So, a `datetime` object would have to be converted to a `str` containing the data in <a href="https://en.wikipedia.org/wiki/ISO_8601" target="_blank">ISO format</a>.
|
||||
|
||||
The same way, this database wouldn't receive a Pydantic model (an object with attributes), only a `dict`.
|
||||
|
||||
You can use `jsonable_encoder` for that.
|
||||
|
||||
It receives an object, like a Pydantic model, and returns a JSON compatible version:
|
||||
|
||||
```Python hl_lines="4 21"
|
||||
{!./src/encoder/tutorial001.py!}
|
||||
```
|
||||
|
||||
In this example, it would convert the Pydantic model to a `dict`, and the `datetime` to a `str`.
|
||||
|
||||
The result of calling it is something that can be encoded with the Python standard <a href="https://docs.python.org/3/library/json.html#json.dumps" target="_blank">`json.dumps()`</a>.
|
||||
|
||||
It doesn't return a large `str` containing the data in JSON format (as a string). It returns a Python standard data structure (e.g. a `dict`) with values and sub-values that are all compatible with JSON.
|
||||
|
||||
!!! note
|
||||
`jsonable_encoder` is actually used by **FastAPI** internally to convert data. But it is useful in many other scenarios.
|
||||
@@ -3,7 +3,7 @@ Continuing with the previous example, it will be common to have more than one re
|
||||
This is especially the case for user models, because:
|
||||
|
||||
* The **input model** needs to be able to have a password.
|
||||
* The **output model** should do not have a password.
|
||||
* The **output model** should not have a password.
|
||||
* The **database model** would probably need to have a hashed password.
|
||||
|
||||
!!! danger
|
||||
|
||||
@@ -18,9 +18,11 @@ The first value is the default value, you can pass all the extra validation or a
|
||||
{!./src/header_params/tutorial001.py!}
|
||||
```
|
||||
|
||||
!!! info
|
||||
!!! note "Technical Details"
|
||||
`Header` is a "sister" class of `Path`, `Query` and `Cookie`. It also inherits from the same common `Param` class.
|
||||
|
||||
But remember that when you import `Query`, `Path`, `Header`, 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>.
|
||||
|
||||
!!! info
|
||||
To declare headers, you need to use `Header`, because otherwise the parameters would be interpreted as query parameters.
|
||||
|
||||
|
||||
60
docs/tutorial/middleware.md
Normal file
@@ -0,0 +1,60 @@
|
||||
You can add middleware to **FastAPI** applications.
|
||||
|
||||
A "middleware" is a function that works with every **request** before it is processed by any specific *path operation*. And also with every **response** before returning it.
|
||||
|
||||
* It takes each **request** that comes to your application.
|
||||
* It can then do something to that **request** or run any needed code.
|
||||
* Then it passes the **request** to be processed by the rest of the application (by some *path operation*).
|
||||
* It then takes the **response** generated by the application (by some *path operation*).
|
||||
* It can do something to that **response** or run any needed code.
|
||||
* Then it returns the **response**.
|
||||
|
||||
## Create a middleware
|
||||
|
||||
To create a middleware you use the decorator `@app.middleware("http")` on top of a function.
|
||||
|
||||
The middleware function receives:
|
||||
|
||||
* The `request`.
|
||||
* A function `call_next` that will receive the `request` as a parameter.
|
||||
* This function will pass the `request` to the corresponding *path operation*.
|
||||
* Then it returns the `response` generated by the corresponding *path operation*.
|
||||
* You can then modify further the `response` before returning it.
|
||||
|
||||
```Python hl_lines="9 10 12 15"
|
||||
{!./src/middleware/tutorial001.py!}
|
||||
```
|
||||
|
||||
!!! tip
|
||||
This technique is used in the tutorial about <a href="https://fastapi.tiangolo.com/tutorial/sql-databases/" target="_blank">SQL (Relational) Databases</a>.
|
||||
|
||||
|
||||
!!! tip
|
||||
Have in mind that custom proprietary headers can be added <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers" target="_blank">using the 'X-' prefix</a>.
|
||||
|
||||
But if you have custom headers that you want a client in a browser to be able to see, you need to add them to your <a href="https://fastapi.tiangolo.com/tutorial/cors/" target="_blank">CORS configurations</a>, using the parameter `expose_headers` documented in <a href="https://www.starlette.io/middleware/#corsmiddleware" target="_blank">Starlette's CORS docs</a>.
|
||||
|
||||
### Before and after the `response`
|
||||
|
||||
You can add code to be run with the `request`, before any *path operation* receives it.
|
||||
|
||||
And also after the `response` is generated, before returning it.
|
||||
|
||||
For example, you could add a custom header `X-Process-Time` containing the time in seconds that it took to process the request and generate a response:
|
||||
|
||||
```Python hl_lines="11 13 14"
|
||||
{!./src/middleware/tutorial001.py!}
|
||||
```
|
||||
|
||||
## Starlette's Middleware
|
||||
|
||||
You can also add any other <a href="https://www.starlette.io/middleware/" target="_blank">Starlette Middleware</a>.
|
||||
|
||||
These are classes instead of plain functions.
|
||||
|
||||
Including:
|
||||
|
||||
* `CORSMiddleware` (described in the next section).
|
||||
* `GZipMiddleware`.
|
||||
* `SentryMiddleware`.
|
||||
* ...and others.
|
||||
@@ -42,6 +42,8 @@ You can add a `summary` and `description`:
|
||||
|
||||
As descriptions tend to be long and cover multiple lines, you can declare the path operation description in the function <abbr title="a multi-line string as the first expression inside a function (not assigned to any variable) used for documentation">docstring</abbr> and **FastAPI** will read it from there.
|
||||
|
||||
You can write <a href="https://en.wikipedia.org/wiki/Markdown" target="_blank">Markdown</a> in the docstring, it will be interpreted and displayed correctly (taking into account docstring indentation).
|
||||
|
||||
```Python hl_lines="19 20 21 22 23 24 25 26 27"
|
||||
{!./src/path_operation_configuration/tutorial004.py!}
|
||||
```
|
||||
@@ -50,9 +52,6 @@ It will be used in the interactive docs:
|
||||
|
||||
<img src="/img/tutorial/path-operation-configuration/image02.png">
|
||||
|
||||
!!! info
|
||||
OpenAPI specifies that descriptions can be written in Markdown syntax, but the interactive documentation systems included still don't support it at the time of writing this, although they have it in their plans.
|
||||
|
||||
## Response description
|
||||
|
||||
You can specify the response description with the parameter `response_description`:
|
||||
|
||||
@@ -103,6 +103,17 @@ And you can also declare numeric validations:
|
||||
* `le`: `l`ess than or `e`qual
|
||||
|
||||
!!! info
|
||||
`Query`, `Path` and others you will see later are subclasses of a common `Param` class (that you don't need to use).
|
||||
|
||||
And all of them share the same all these same parameters of additional validation and metadata you have seen.
|
||||
`Query`, `Path` and others you will see later subclasses of a common `Param` class (that you don't need to use).
|
||||
|
||||
And all of them share the same all these same parameters of additional validation and metadata you have seen.
|
||||
|
||||
!!! note "Technical Details"
|
||||
When you import `Query`, `Path` and others from `fastapi`, they are actually functions.
|
||||
|
||||
That when called, return instances of classes of the same name.
|
||||
|
||||
So, you import `Query`, which is a function. And when you call it, it returns an instance of a class also named `Query`.
|
||||
|
||||
These functions are there (instead of just using the classes directly) so that your editor doesn't mark errors about their types.
|
||||
|
||||
That way you can use your normal editor and coding tools without having to add custom configurations to disregard those errors.
|
||||
|
||||
@@ -81,31 +81,31 @@ You can also declare `bool` types, and they will be converted:
|
||||
In this case, if you go to:
|
||||
|
||||
```
|
||||
http://127.0.0.1:8000/items/?short=1
|
||||
http://127.0.0.1:8000/items/foo?short=1
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```
|
||||
http://127.0.0.1:8000/items/?short=True
|
||||
http://127.0.0.1:8000/items/foo?short=True
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```
|
||||
http://127.0.0.1:8000/items/?short=true
|
||||
http://127.0.0.1:8000/items/foo?short=true
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```
|
||||
http://127.0.0.1:8000/items/?short=on
|
||||
http://127.0.0.1:8000/items/foo?short=on
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```
|
||||
http://127.0.0.1:8000/items/?short=yes
|
||||
http://127.0.0.1:8000/items/foo?short=yes
|
||||
```
|
||||
|
||||
or any other case variation (uppercase, first letter in uppercase, etc), your function will see the parameter `short` with a `bool` value of `True`. Otherwise as `False`.
|
||||
|
||||
@@ -19,6 +19,8 @@ Create file parameters the same way you would for `Body` or `Form`:
|
||||
!!! info
|
||||
`File` is a class that inherits directly from `Form`.
|
||||
|
||||
But remember that when you import `Query`, `Path`, `File` 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>.
|
||||
|
||||
!!! info
|
||||
To declare File bodies, you need to use `File`, because otherwise the parameters would be interpreted as query parameters or body (JSON) parameters.
|
||||
|
||||
@@ -43,7 +45,7 @@ Using `UploadFile` has several advantages over `bytes`:
|
||||
|
||||
* It uses a "spooled" file:
|
||||
* A file stored in memory up to a maximum size limit, and after passing this limit it will be stored in disk.
|
||||
* This means that it will work well for large files like images, videos, large binaries, etc. All without consuming all the memory.
|
||||
* This means that it will work well for large files like images, videos, large binaries, etc. without consuming all the memory.
|
||||
* You can get metadata from the uploaded file.
|
||||
* It has a <a href="https://docs.python.org/3/glossary.html#term-file-like-object" target="_blank">file-like</a> `async` interface.
|
||||
* It exposes an actual Python <a href="https://docs.python.org/3/library/tempfile.html#tempfile.SpooledTemporaryFile" target="_blank">`SpooledTemporaryFile`</a> object that you can pass directly to other libraries that expect a file-like object.
|
||||
@@ -107,6 +109,27 @@ The way HTML forms (`<form></form>`) sends the data to the server normally uses
|
||||
|
||||
This is not a limitation of **FastAPI**, it's part of the HTTP protocol.
|
||||
|
||||
## Multiple file uploads
|
||||
|
||||
It's possible to upload several files at the same time.
|
||||
|
||||
They would be associated to the same "form field" sent using "form data".
|
||||
|
||||
To use that, declare a `List` of `bytes` or `UploadFile`:
|
||||
|
||||
```Python hl_lines="10 15"
|
||||
{!./src/request_files/tutorial002.py!}
|
||||
```
|
||||
|
||||
You will receive, as declared, a `list` of `bytes` or `UploadFile`s.
|
||||
|
||||
!!! note
|
||||
Notice that, as of 2019-04-14, Swagger UI doesn't support multiple file uploads in the same form field. For more information, check <a href="https://github.com/swagger-api/swagger-ui/issues/4276" target="_blank">#4276</a> and <a href="https://github.com/swagger-api/swagger-ui/issues/3641" target="_blank">#3641</a>.
|
||||
|
||||
Nevertheless, **FastAPI** is already compatible with it, using the standard OpenAPI.
|
||||
|
||||
So, whenever Swagger UI supports multi-file uploads, or any other tools that supports OpenAPI, they will be compatible with **FastAPI**.
|
||||
|
||||
## Recap
|
||||
|
||||
Use `File` to declare files to be uploaded as input parameters (as form data).
|
||||
|
||||
13
docs/tutorial/response-cookies.md
Normal file
@@ -0,0 +1,13 @@
|
||||
You can create (set) Cookies in your response.
|
||||
|
||||
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>.
|
||||
|
||||
Then set Cookies in it, and then return it:
|
||||
|
||||
```Python hl_lines="10 11 12"
|
||||
{!./src/response_cookies/tutorial001.py!}
|
||||
```
|
||||
|
||||
## 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>.
|
||||
63
docs/tutorial/response-directly.md
Normal file
@@ -0,0 +1,63 @@
|
||||
When you create a **FastAPI** *path operation* you can normally return any data from it: a `dict`, a `list`, a Pydantic model, a database model, etc.
|
||||
|
||||
By default, **FastAPI** would automatically convert that return value to JSON using the <a href="https://fastapi.tiangolo.com/tutorial/encoder/" target="_blank">`jsonable_encoder`</a>.
|
||||
|
||||
Then, behind the scenes, it would put that JSON-compatible data (e.g. a `dict`) inside of a Starlette `JSONResponse` that would be used to send the response to the client.
|
||||
|
||||
But you can return a `JSONResponse` directly from your *path operations*.
|
||||
|
||||
It might be useful, for example, to return custom headers or cookies.
|
||||
|
||||
## Starlette `Response`
|
||||
|
||||
In fact, you can return any <a href="https://www.starlette.io/responses/" target="_blank">Starlette `Response`</a> or any sub-class of it.
|
||||
|
||||
!!! tip
|
||||
`JSONResponse` itself is a sub-class of `Response`.
|
||||
|
||||
And when you return a Starlette `Response`, **FastAPI** will pass it directly.
|
||||
|
||||
It won't do any data conversion with Pydantic models, it won't convert the contents to any type, etc.
|
||||
|
||||
This gives you a lot of flexibility. You can return any data type, override any data declaration or validation, etc.
|
||||
|
||||
## Using the `jsonable_encoder` in a `Response`
|
||||
|
||||
Because **FastAPI** doesn't do any change to a `Response` you return, you have to make sure it's contents are ready for it.
|
||||
|
||||
For example, you cannot put a Pydantic model in a `JSONResponse` without first converting it to a `dict` with all the data types (like `datetime`, `UUID`, etc) converted to JSON-compatible types.
|
||||
|
||||
For those cases, you can use the `jsonable_encoder` to convert your data before passing it to a response:
|
||||
|
||||
```Python hl_lines="4 6 20 21"
|
||||
{!./src/response_directly/tutorial001.py!}
|
||||
```
|
||||
|
||||
!!! note
|
||||
Notice that you import it directly from `starlette.responses`, not from `fastapi`.
|
||||
|
||||
## Returning a custom `Response`
|
||||
|
||||
The example above shows all the parts you need, but it's not very useful yet, as you could have just returned the `item` directly, and **FastAPI** would put it in a `JSONResponse` for you, converting it to a `dict`, etc. All that by default.
|
||||
|
||||
Now, let's see how you could use that to return a custom response.
|
||||
|
||||
Let's say you want to return a response that is not available in the default <a href="https://www.starlette.io/responses/" target="_blank">Starlette `Response`s</a>.
|
||||
|
||||
Let's say that you want to return <a href="https://en.wikipedia.org/wiki/XML" target="_blank">XML</a>.
|
||||
|
||||
You could put your XML content in a string, put it in a Starlette Response, and return it:
|
||||
|
||||
```Python hl_lines="2 20"
|
||||
{!./src/response_directly/tutorial002.py!}
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
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>.
|
||||
|
||||
In the next sections you will see how to use/declare these custom `Response`s while still having automatic data conversion, documentation, etc.
|
||||
|
||||
You will also see how to use them to set response Headers and Cookies.
|
||||
12
docs/tutorial/response-headers.md
Normal file
@@ -0,0 +1,12 @@
|
||||
You can add headers to your response.
|
||||
|
||||
Create a response as described in <a href="https://fastapi.tiangolo.com/tutorial/response-directly/" target="_blank">Return a Response directly</a> and pass the headers as an additional parameter:
|
||||
|
||||
```Python hl_lines="10 11 12"
|
||||
{!./src/response_headers/tutorial001.py!}
|
||||
```
|
||||
|
||||
!!! tip
|
||||
Have in mind that custom proprietary headers can be added <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers" target="_blank">using the 'X-' prefix</a>.
|
||||
|
||||
But if you have custom headers that you want a client in a browser to be able to see, you need to add them to your <a href="https://fastapi.tiangolo.com/tutorial/cors/" target="_blank">CORS configurations</a>, using the parameter `expose_headers` documented in <a href="https://www.starlette.io/middleware/#corsmiddleware" target="_blank">Starlette's CORS docs</a>.
|
||||
@@ -13,12 +13,14 @@ You can declare the model used for the response with the parameter `response_mod
|
||||
!!! note
|
||||
Notice that `response_model` is a parameter of the "decorator" method (`get`, `post`, etc). Not of your path operation function, like all the parameters and body.
|
||||
|
||||
It receives a standard Pydantic model and will:
|
||||
It receives the same type you would declare for a Pydantic model attribute, so, it can be a Pydantic model, but it can also be, e.g. a `list` of Pydantic models, like `List[Item]`.
|
||||
|
||||
* Convert the output data to the type declarations of the model
|
||||
* Validate the data
|
||||
* Add a JSON Schema for the response, in the OpenAPI path operation
|
||||
* Will be used by the automatic documentation systems
|
||||
FastAPI will use this `response_model` to:
|
||||
|
||||
* Convert the output data to its type declaration.
|
||||
* Validate the data.
|
||||
* Add a JSON Schema for the response, in the OpenAPI path operation.
|
||||
* Will be used by the automatic documentation systems.
|
||||
|
||||
But most importantly:
|
||||
|
||||
@@ -45,7 +47,7 @@ Now, whenever a browser is creating a user with a password, the API will return
|
||||
|
||||
In this case, it might not be a problem, because the user himself is sending the password.
|
||||
|
||||
But if we use the same model for another path operation, we could be sending the passwords of our users to every client.
|
||||
But if we use the same model for another path operation, we could be sending our user's passwords to every client.
|
||||
|
||||
!!! danger
|
||||
Never send the plain password of a user in a response.
|
||||
@@ -82,6 +84,114 @@ And both models will be used for the interactive API documentation:
|
||||
|
||||
<img src="/img/tutorial/response-model/image02.png">
|
||||
|
||||
## Response Model encoding parameters
|
||||
|
||||
Your response model could have default values, like:
|
||||
|
||||
```Python hl_lines="11 13 14"
|
||||
{!./src/response_model/tutorial004.py!}
|
||||
```
|
||||
|
||||
* `description: str = None` has a default of `None`.
|
||||
* `tax: float = None` has a default of `None`.
|
||||
* `tags: List[str] = []` has a default of an empty list: `[]`.
|
||||
|
||||
but you might want to omit them from the result if they were not actually stored.
|
||||
|
||||
For example, if you have models with many optional attributes in a NoSQL database, but you don't want to send very long JSON responses full of default values.
|
||||
|
||||
### Use the `response_model_skip_defaults` parameter
|
||||
|
||||
You can set the *path operation decorator* parameter `response_model_skip_defaults=True`:
|
||||
|
||||
```Python hl_lines="24"
|
||||
{!./src/response_model/tutorial004.py!}
|
||||
```
|
||||
|
||||
and those default values won't be included in the response.
|
||||
|
||||
So, if you send a request to that *path operation* for the item with ID `foo`, the response (not including default values) will be:
|
||||
|
||||
```JSON
|
||||
{
|
||||
"name": "Foo",
|
||||
"price": 50.2
|
||||
}
|
||||
```
|
||||
|
||||
!!! info
|
||||
FastAPI uses Pydantic model's `.dict()` with <a href="https://pydantic-docs.helpmanual.io/#copying" target="_blank">its `skip_defaults` parameter</a> to achieve this.
|
||||
|
||||
#### Data with values for fields with defaults
|
||||
|
||||
But if your data has values for the model's fields with default values, like the item with ID `bar`:
|
||||
|
||||
```Python hl_lines="3 5"
|
||||
{
|
||||
"name": "Bar",
|
||||
"description": "The bartenders",
|
||||
"price": 62,
|
||||
"tax": 20.2
|
||||
}
|
||||
```
|
||||
|
||||
they will be included in the response.
|
||||
|
||||
#### Data with the same values as the defaults
|
||||
|
||||
If the data has the same values as the default ones, like the item with ID `baz`:
|
||||
|
||||
```Python hl_lines="3 5 6"
|
||||
{
|
||||
"name": "Baz",
|
||||
"description": None,
|
||||
"price": 50.2,
|
||||
"tax": 10.5,
|
||||
"tags": []
|
||||
}
|
||||
```
|
||||
|
||||
FastAPI is smart enough (actually, Pydantic is smart enough) to realize that, even though `description`, `tax`, and `tags` have the same values as the defaults, they were set explicitly (instead of taken from the defaults).
|
||||
|
||||
So, they will be included in the JSON response.
|
||||
|
||||
!!! tip
|
||||
Notice that the default values can be anything, not only `None`.
|
||||
|
||||
They can be a list (`[]`), a `float` of `10.5`, etc.
|
||||
|
||||
### `response_model_include` and `response_model_exclude`
|
||||
|
||||
You can also use the *path operation decorator* parameters `response_model_include` and `response_model_exclude`.
|
||||
|
||||
They take a `set` of `str` with the name of the attributes to include (omitting the rest) or to exclude (including the rest).
|
||||
|
||||
This can be used as a quick shortcut if you have only one Pydantic model and want to remove some data from the output.
|
||||
|
||||
!!! tip
|
||||
But it is still recommended to use the ideas above, using multiple classes, instead of these parameters.
|
||||
|
||||
This is because the JSON Schema generated in your app's OpenAPI (and the docs) will still be the one for the complete model, even if you use `response_model_include` or `response_model_exclude` to omit some attributes.
|
||||
|
||||
```Python hl_lines="29 35"
|
||||
{!./src/response_model/tutorial005.py!}
|
||||
```
|
||||
|
||||
!!! tip
|
||||
The syntax `{"name", "description"}` creates a `set` with those two values.
|
||||
|
||||
It is equivalent to `set(["name", "description"])`.
|
||||
|
||||
#### Using `list`s instead of `set`s
|
||||
|
||||
If you forget to use a `set` and use a `list` or `tuple` instead, FastAPI will still convert it to a `set` and it will work correctly:
|
||||
|
||||
```Python hl_lines="29 35"
|
||||
{!./src/response_model/tutorial006.py!}
|
||||
```
|
||||
|
||||
## Recap
|
||||
|
||||
Use the path operation decorator's parameter `response_model` to define response models and especially to ensure private data is filtered out.
|
||||
|
||||
Use `response_model_skip_defaults` to return only the values explicitly set.
|
||||
|
||||
@@ -12,7 +12,7 @@ Let's use the tools provided by **FastAPI** to handle security.
|
||||
|
||||
## How it looks
|
||||
|
||||
But let's first just use the code and see how it works, and then we'll come back to understand what's happening.
|
||||
Let's first just use the code and see how it works, and then we'll come back to understand what's happening.
|
||||
|
||||
## Create `main.py`
|
||||
|
||||
@@ -77,37 +77,14 @@ So, let's review it from that simplified point of view:
|
||||
* The API checks that `username` and `password`, and responds with a "token".
|
||||
* A "token" is just a string with some content that we can use later to verify this user.
|
||||
* Normally, a token is set to expire after some time.
|
||||
* So, the user will have to login again at some point later.
|
||||
* And if the token is stolen, the risk is less. It is not like a permanent key that will work forever.
|
||||
* So, the user will have to login again at some point later.
|
||||
* And if the token is stolen, the risk is less. It is not like a permanent key that will work forever (in most of the cases).
|
||||
* The frontend stores that token temporarily somewhere.
|
||||
* The user clicks in the frontend to go to another section of the frontend web app.
|
||||
* The frontend needs to fetch some more data from the API.
|
||||
* But it needs authentication for that specific endpoint.
|
||||
* So, to authenticate with our API, it sends a header `Authorization` with a value of `Bearer ` plus the token.
|
||||
* If the token contains `foobar`, the content of the `Authorization` header would be: `Bearer foobar`.
|
||||
* Note that although the header is case-insensitive (`Authorization` is the same as `authorization`), the value is not. So, `bearer foobar` would not be valid. It has to be `Bearer foobar`.
|
||||
|
||||
## **FastAPI**'s `Security`
|
||||
|
||||
### Import it
|
||||
|
||||
The same way **FastAPI** provides a `Depends`, there is a `Security` that you can import:
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!./src/security/tutorial001.py!}
|
||||
```
|
||||
|
||||
### Use it
|
||||
|
||||
It is actually a subclass of `Depends`, and it has just one extra parameter that we'll see later.
|
||||
|
||||
But by using `Security` instead of `Depends`, **FastAPI** will know that it can use this dependency to define "security schemes" in OpenAPI.
|
||||
|
||||
```Python hl_lines="10"
|
||||
{!./src/security/tutorial001.py!}
|
||||
```
|
||||
|
||||
In this case, we have a `Security` definition (which at the same time is a dependency definition) that will provide a `str` that is assigned to the parameter `token`.
|
||||
|
||||
## **FastAPI**'s `OAuth2PasswordBearer`
|
||||
|
||||
@@ -146,13 +123,30 @@ It could be called as:
|
||||
oauth2_scheme(some, parameters)
|
||||
```
|
||||
|
||||
So, it can be used with `Security` (as it could be used with `Depends`).
|
||||
So, it can be used with `Depends`.
|
||||
|
||||
### Use it
|
||||
|
||||
Now you can pass that `oauth2_scheme` in a dependency with `Depends`.
|
||||
|
||||
```Python hl_lines="10"
|
||||
{!./src/security/tutorial001.py!}
|
||||
```
|
||||
|
||||
This dependency will provide a `str` that is assigned to the parameter `token` of the *path operation function*.
|
||||
|
||||
**FastAPI** will know that it can use this dependency to define a "security scheme" in the OpenAPI schema (and the automatic API docs).
|
||||
|
||||
!!! info "Technical Details"
|
||||
**FastAPI** will know that it can use the class `OAuth2PasswordBearer` (declared in a dependency) to define the security scheme in OpenAPI because it inherits from `fastapi.security.oauth2.OAuth2`, which in turn inherits from `fastapi.security.base.SecurityBase`.
|
||||
|
||||
All the security utilities that integrate with OpenAPI (and the automatic API docs) inherit from `SecurityBase`, that's how **FastAPI** can know how to integrate them in OpenAPI.
|
||||
|
||||
## What it does
|
||||
|
||||
It will go and look in the request for that `Authorization` header, check if the value is `Bearer ` plus some token, and will return the token as a `str`.
|
||||
|
||||
If it doesn't see an `Authorization` header, or the value doesn't have a `Bearer ` token, it will respond with a 403 status code error (`FORBIDDEN`) directly.
|
||||
If it doesn't see an `Authorization` header, or the value doesn't have a `Bearer ` token, it will respond with a 401 status code error (`UNAUTHORIZED`) directly.
|
||||
|
||||
You don't even have to check if the token exists to return an error. You can be sure that if your function is executed, it will have a `str` in that token.
|
||||
|
||||
|
||||
@@ -24,13 +24,9 @@ Let's create a dependency `get_current_user`.
|
||||
|
||||
Remember that dependencies can have sub-dependencies?
|
||||
|
||||
And remember that `Security` is based on `Depends`?
|
||||
`get_current_user` will have a dependency with the same `oauth2_scheme` we created before.
|
||||
|
||||
So, we can have sub-dependencies using `Security` too.
|
||||
|
||||
`get_current_user` will have a `Security` dependency with the same `oauth2_scheme` we created before.
|
||||
|
||||
The same as we were doing before in the path operation directly, our new dependency will receive a `token` as a `str` from the `Security` dependency:
|
||||
The same as we were doing before in the path operation directly, our new dependency `get_current_user` will receive a `token` as a `str` from the sub-dependency `oauth2_scheme`:
|
||||
|
||||
```Python hl_lines="25"
|
||||
{!./src/security/tutorial002.py!}
|
||||
@@ -52,15 +48,6 @@ So now we can use the same `Depends` with our `get_current_user` in the path ope
|
||||
{!./src/security/tutorial002.py!}
|
||||
```
|
||||
|
||||
!!! info
|
||||
Here you could actually use `Security` instead of depends too.
|
||||
|
||||
But it is not required.
|
||||
|
||||
The key point where you should use `Security` is when passing an instance of `OAuth2PasswordBearer`.
|
||||
|
||||
Because **FastAPI** will use the fact that you are using `Security` and that you are passing an instance of that class `OAuth2PasswordBearer` (that inherits from `SecurityBase`) to create all the security definitions in OpenAPI.
|
||||
|
||||
Notice that we declare the type of `current_user` as the Pydantic model `User`.
|
||||
|
||||
This will help us inside of the function with all the completion and type checks.
|
||||
@@ -68,7 +55,7 @@ This will help us inside of the function with all the completion and type checks
|
||||
!!! tip
|
||||
You might remember that request bodies are also declared with Pydantic models.
|
||||
|
||||
Here **FastAPI** won't get confused because you are using `Depends` or `Security`.
|
||||
Here **FastAPI** won't get confused because you are using `Depends`.
|
||||
|
||||
!!! check
|
||||
The way this dependency system is designed allows us to have different dependencies (different "dependables") that all return a `User` model.
|
||||
@@ -78,7 +65,7 @@ This will help us inside of the function with all the completion and type checks
|
||||
|
||||
## Other models
|
||||
|
||||
You can now get the current user directly in the path operation functions and deal with the security mechanisms at the **Dependency Injection** level, using `Security`.
|
||||
You can now get the current user directly in the path operation functions and deal with the security mechanisms at the **Dependency Injection** level, using `Depends`.
|
||||
|
||||
And you can use any model or data for the security requirements (in this case, a Pydantic model `User`).
|
||||
|
||||
@@ -88,6 +75,10 @@ Do you want to have an `id` and `email` and not have any `username` in your mode
|
||||
|
||||
Do you want to just have a `str`? Or just a `dict`? Or a database class model instance directly? It all works the same way.
|
||||
|
||||
You actually don't have users that log in to your application but robots, bots, or other systems, that have just an access token? Again, it all works the same.
|
||||
|
||||
Just use any kind of model, any kind of class, any kind of database that you need for your application. **FastAPI** has you covered with the dependency injection system.
|
||||
|
||||
|
||||
## Code size
|
||||
|
||||
@@ -97,7 +88,7 @@ But here's the key point.
|
||||
|
||||
The security and dependency injection stuff is written once.
|
||||
|
||||
And you can make it as complex as you want. And still, have it written only once, in a single place.
|
||||
And you can make it as complex as you want. And still, have it written only once, in a single place. With all the flexibility.
|
||||
|
||||
But you can have thousands of endpoints (path operations) using the same security system.
|
||||
|
||||
@@ -115,6 +106,6 @@ You can now get the current user directly in your path operation function.
|
||||
|
||||
We are already halfway there.
|
||||
|
||||
We just need to add a path operation for the user / client to actually send the `username` and `password`.
|
||||
We just need to add a path operation for the user/client to actually send the `username` and `password`.
|
||||
|
||||
That comes next.
|
||||
40
docs/tutorial/security/http-basic-auth.md
Normal file
@@ -0,0 +1,40 @@
|
||||
For the simplest cases, you can use HTTP Basic Auth.
|
||||
|
||||
In HTTP Basic Auth, the application expects a header that contains a username and a password.
|
||||
|
||||
If it doesn't receive it, it returns an HTTP 401 "Unauthorized" error.
|
||||
|
||||
And returns a header `WWW-Authenticate` with a value of `Basic`, and an optional `realm` parameter.
|
||||
|
||||
That tells the browser to show the integrated prompt for a username and password.
|
||||
|
||||
Then, when you type that username and password, the browser sends them in the header automatically.
|
||||
|
||||
## Simple HTTP Basic Auth
|
||||
|
||||
* Import `HTTPBAsic` and `HTTPBasicCredentials`.
|
||||
* Create a "`security` scheme" using `HTTPBAsic`.
|
||||
* Use that `security` with a dependency in your *path operation*.
|
||||
* It returns an object of type `HTTPBasicCredentials`:
|
||||
* It contains the `username` and `password` sent.
|
||||
|
||||
|
||||
```Python hl_lines="2 6 10"
|
||||
{!./src/security/tutorial006.py!}
|
||||
```
|
||||
|
||||
When you try to open the URL for the first time (or click the "Execute" button in the docs) the browser will ask you for your username and password:
|
||||
|
||||
<img src="/img/tutorial/security/image12.png">
|
||||
|
||||
## Check the username
|
||||
|
||||
Here's a more complete example.
|
||||
|
||||
Use a dependency to check if the username and password are correct.
|
||||
|
||||
If the credentials are incorrect, return an `HTTPException` with a status code 401 (the same returned when no credentials are provided) and add the header `WWW-Authenticate` to make the browser show the login prompt again:
|
||||
|
||||
```Python hl_lines="10 11 12 13 14 15 16 17 21"
|
||||
{!./src/security/tutorial007.py!}
|
||||
```
|
||||
@@ -20,7 +20,7 @@ It is quite an extensive specification and covers several complex use cases.
|
||||
|
||||
It includes ways to authenticate using a "third party".
|
||||
|
||||
That's what all the system with "login with Facebook, Google, Twitter, GitHub" use underneath.
|
||||
That's what all the systems with "login with Facebook, Google, Twitter, GitHub" use underneath.
|
||||
|
||||
### OAuth 1
|
||||
|
||||
@@ -40,7 +40,7 @@ OpenID Connect is another specification, based on **OAuth2**.
|
||||
|
||||
It just extends OAuth2 specifying some things that are relatively ambiguous in OAuth2, to try to make it more interoperable.
|
||||
|
||||
For example, Google login used OpenID Connect (which underneath uses OAuth2).
|
||||
For example, Google login uses OpenID Connect (which underneath uses OAuth2).
|
||||
|
||||
But Facebook login doesn't support OpenID Connect. It has its own flavor of OAuth2.
|
||||
|
||||
@@ -75,7 +75,7 @@ OpenAPI defines the following security schemes:
|
||||
* HTTP Basic authentication.
|
||||
* HTTP Digest, etc.
|
||||
* `oauth2`: all the OAuth2 ways to handle security (called "flows").
|
||||
* Several of these flows are appropriate for delegating the authentication to a third party (like Google, Facebook, Twitter, GitHub, etc):
|
||||
* Several of these flows are appropriate for building an OAuth 2.0 authentication provider (like Google, Facebook, Twitter, GitHub, etc):
|
||||
* `implicit`
|
||||
* `clientCredentials`
|
||||
* `authorizationCode`
|
||||
@@ -84,10 +84,16 @@ OpenAPI defines the following security schemes:
|
||||
* `openIdConnect`: has a way to define how to discover OAuth2 authentication data automatically.
|
||||
* This automatic discovery is what is defined in the OpenID Connect specification.
|
||||
|
||||
|
||||
!!! tip
|
||||
Integrating other authentication/authorization providers like Google, Facebook, Twitter, GitHub, etc. is also possible and relatively easy.
|
||||
|
||||
The most complex problem is building an authentication/authorization provider like those, but **FastAPI** gives you the tools to do it easily, while doing the heavy lifting for you.
|
||||
|
||||
## **FastAPI** utilities
|
||||
|
||||
FastAPI provides several tools for each of these security schemes in the `fastapi.security` module, to simplify using these security mechanisms.
|
||||
FastAPI provides several tools for each of these security schemes in the `fastapi.security` module that simplify using these security mechanisms.
|
||||
|
||||
In the next chapters you will see how to add security to your API in a very simple way, using the tools provided by **FastAPI**.
|
||||
In the next chapters you will see how to add security to your API using those tools provided by **FastAPI**.
|
||||
|
||||
And you will also see how it gets automatically integrated into the interactive documentation system.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Now that we have all the security flow, let's make the application actually secure, using JWT tokens and secure password hashing.
|
||||
Now that we have all the security flow, let's make the application actually secure, using <abbr title="JSON Web Tokens">JWT</abbr> tokens and secure password hashing.
|
||||
|
||||
This code is something you can actually use in your application, save the password hashes in your database, etc.
|
||||
|
||||
@@ -8,7 +8,11 @@ We are going to start from where we left in the previous chapter and increment i
|
||||
|
||||
JWT means "JSON Web Tokens".
|
||||
|
||||
It's a standard to codify a JSON object in a long string.
|
||||
It's a standard to codify a JSON object in a long dense string without spaces. It looks like this:
|
||||
|
||||
```
|
||||
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
|
||||
```
|
||||
|
||||
It is not encrypted, so, anyone could recover the information from the contents.
|
||||
|
||||
@@ -16,13 +20,13 @@ But it's signed. So, when you receive a token that you emitted, you can verify t
|
||||
|
||||
That way, you can create a token with an expiration of, let's say, 1 week, and then, after a week, when the user comes back with the token, you know he's still signed into your system.
|
||||
|
||||
And after a week, the token will be expired. And if the user (or a third party) tried to modify the token to change the expiration, you would be able to discover it, because the signature would not match.
|
||||
And after a week, the token will be expired. And if the user (or a third party) tried to modify the token to change the expiration, you would be able to discover it, because the signatures would not match.
|
||||
|
||||
If you want to play with JWT tokens and see how they work, check <a href="https://jwt.io/" target="_blank">https://jwt.io</a>.
|
||||
|
||||
## Install `PyJWT`
|
||||
|
||||
We need to install `PyJWT` to generate and verity the JWT tokens in Python:
|
||||
We need to install `PyJWT` to generate and verify the JWT tokens in Python:
|
||||
|
||||
```bash
|
||||
pip install pyjwt
|
||||
@@ -30,7 +34,7 @@ pip install pyjwt
|
||||
|
||||
## Password hashing
|
||||
|
||||
"Hashing" means converting some content (a password in this case) into a sequence of bytes (just a string) that look like gibberish.
|
||||
"Hashing" means converting some content (a password in this case) into a sequence of bytes (just a string) that looks like gibberish.
|
||||
|
||||
Whenever you pass exactly the same content (exactly the same password) you get exactly the same gibberish.
|
||||
|
||||
@@ -46,7 +50,7 @@ So, the thief won't be able to try to use that password in another system (as ma
|
||||
|
||||
PassLib is a great Python package to handle password hashes.
|
||||
|
||||
It supports many secure hashing algorithms, and utilities to work with them.
|
||||
It supports many secure hashing algorithms and utilities to work with them.
|
||||
|
||||
The recommended algorithm is "Bcrypt".
|
||||
|
||||
@@ -57,10 +61,11 @@ pip install passlib[bcrypt]
|
||||
```
|
||||
|
||||
!!! tip
|
||||
With `passlib`, you could even configure it to be able to read passwords created by **Django** (among many others).
|
||||
With `passlib`, you could even configure it to be able to read passwords created by **Django**, a **Flask** security plug-in or many others.
|
||||
|
||||
So, you would be able to, for example, share the same data from a Django application in a database with a FastAPI application. Or gradually migrate a Django application using the same database.
|
||||
|
||||
And your users would be able to login from your Django app or from your **FastAPI** app, at the same time.
|
||||
|
||||
## Hash and verify the passwords
|
||||
|
||||
@@ -69,7 +74,7 @@ Import the tools we need from `passlib`.
|
||||
Create a PassLib "context". This is what will be used to hash and verify passwords.
|
||||
|
||||
!!! tip
|
||||
The PassLib context also has functionality to use different hashing algorithms, deprecate old ones, but allow verifying them, etc.
|
||||
The PassLib context also has functionality to use different hashing algorithms, including deprecate old ones only to allow verifying them, etc.
|
||||
|
||||
For example, you could use it to read and verify passwords generated by another system (like Django) but hash any new passwords with a different algorithm like Bcrypt.
|
||||
|
||||
@@ -81,7 +86,7 @@ And another utility to verify if a received password matches the hash stored.
|
||||
|
||||
And another one to authenticate and return a user.
|
||||
|
||||
```Python hl_lines="7 50 57 58 61 62 71 72 73 74 75 76 77"
|
||||
```Python hl_lines="7 39 56 57 60 61 70 71 72 73 74 75 76"
|
||||
{!./src/security/tutorial004.py!}
|
||||
```
|
||||
|
||||
@@ -94,7 +99,7 @@ Import the modules installed.
|
||||
|
||||
Create a random secret key that will be used to sign the JWT tokens.
|
||||
|
||||
To generate a secure random secret, key use the command:
|
||||
To generate a secure random secret key use the command:
|
||||
|
||||
```bash
|
||||
openssl rand -hex 32
|
||||
@@ -104,15 +109,13 @@ And copy the output to the variable `SECRET_KEY` (don't use the one in the examp
|
||||
|
||||
Create a variable `ALGORITHM` with the algorithm used to sign the JWT token and set it to `"HS256"`.
|
||||
|
||||
And another one for the `TOKEN_SUBJECT`, and set it to, for example, `"access"`.
|
||||
|
||||
Create a variable for the expiration of the token.
|
||||
|
||||
Define a Pydantic Model that will be used in the token endpoint for the response.
|
||||
|
||||
Create a utility function to generate a new access token.
|
||||
|
||||
```Python hl_lines="3 6 13 14 15 16 30 31 32 80 81 82 83 84 85 86 87 88"
|
||||
```Python hl_lines="3 6 13 14 15 29 30 31 79 80 81 82 83 84 85 86 87"
|
||||
{!./src/security/tutorial004.py!}
|
||||
```
|
||||
|
||||
@@ -124,7 +127,7 @@ Decode the received token, verify it, and return the current user.
|
||||
|
||||
If the token is invalid, return an HTTP error right away.
|
||||
|
||||
```Python hl_lines="91 92 93 94 95 96 97 98 99 100"
|
||||
```Python hl_lines="90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107"
|
||||
{!./src/security/tutorial004.py!}
|
||||
```
|
||||
|
||||
@@ -134,10 +137,33 @@ Create a `timedelta` with the expiration time of the token.
|
||||
|
||||
Create a real JWT access token and return it.
|
||||
|
||||
```Python hl_lines="114 115 116 117 118"
|
||||
```Python hl_lines="116 117 118 119 120 121 122 123 124 125 126 127 128 129"
|
||||
{!./src/security/tutorial004.py!}
|
||||
```
|
||||
|
||||
|
||||
### Technical details about the JWT "subject" `sub`
|
||||
|
||||
The JWT specification says that there's a key `sub`, with the subject of the token.
|
||||
|
||||
It's optional to use it, but that's where you would put the user's identification, so we are using it here.
|
||||
|
||||
JWT might be used for other things apart from identifying a user and allowing him to perform operations directly on your API.
|
||||
|
||||
For example, you could identify a "car" or a "blog post".
|
||||
|
||||
Then you could add permissions about that entity, like "drive" (for the car) or "edit" (for the blog).
|
||||
|
||||
And then, you could give that JWT token to a user (or bot), and he could use it to perform those actions (drive the car, or edit the blog post) without even needing to have an account, just with the JWT token your API generated for that.
|
||||
|
||||
Using these ideas, JWT can be used for way more sophisticate scenarios.
|
||||
|
||||
In those cases, several of those entities could have the same ID, let's say `foo` (a user `foo`, a car `foo`, and a blog post `foo`).
|
||||
|
||||
So, to avoid ID collisions, when creating the JWT token for the user, you could prefix the value of the `sub` key, e.g. with `username:`. So, in this example, the value of `sub` could have been: `username:johndoe`.
|
||||
|
||||
The important thing to have in mind is that the `sub` key should have a unique identifier across the entire application, and it should be a string.
|
||||
|
||||
## Check it
|
||||
|
||||
Run the server and go to the docs: <a href="http://127.0.0.1:8000/docs" target="_blank">http://127.0.0.1:8000/docs</a>.
|
||||
@@ -158,7 +184,7 @@ Password: `secret`
|
||||
|
||||
<img src="/img/tutorial/security/image08.png">
|
||||
|
||||
Call the endpoint `/users/me`, you will get the response as:
|
||||
Call the endpoint `/users/me/`, you will get the response as:
|
||||
|
||||
```JSON
|
||||
{
|
||||
@@ -171,7 +197,7 @@ Call the endpoint `/users/me`, you will get the response as:
|
||||
|
||||
<img src="/img/tutorial/security/image09.png">
|
||||
|
||||
If you open the developer tools, you could see how the data sent and received is just the token, the password is only sent in the first request to authenticate the user:
|
||||
If you open the developer tools, you could see how the data sent and only includes the token, the password is only sent in the first request to authenticate the user and get that access token, but not afterwards:
|
||||
|
||||
<img src="/img/tutorial/security/image10.png">
|
||||
|
||||
@@ -180,15 +206,17 @@ If you open the developer tools, you could see how the data sent and received is
|
||||
|
||||
## Advanced usage with `scopes`
|
||||
|
||||
We didn't use it in this example, but `Security` can receive a parameter `scopes`, as a list of strings.
|
||||
OAuth2 has the notion of "scopes".
|
||||
|
||||
It would describe the scopes required for a specific path operation, as different path operations might require different security scopes, even while using the same `OAuth2PasswordBearer` (or any of the other tools).
|
||||
You can use them to add a specific set of permissions to a JWT token.
|
||||
|
||||
This only applies to OAuth2, and it might be, more or less, an advanced feature, but it is there, if you need to use it.
|
||||
Then you can give this token to a user directly or a third party, to interact with your API with a set of restrictions.
|
||||
|
||||
You can learn how to use them and how they are integrated into **FastAPI** in the next chapter.
|
||||
|
||||
## Recap
|
||||
|
||||
This concludes our tour for the security features of **FastAPI**.
|
||||
With what you have seen up to now, you can set up a secure **FastAPI** application using standards like OAuth2 and JWT.
|
||||
|
||||
In almost any framework handling the security becomes a rather complex subject quite quickly.
|
||||
|
||||
@@ -198,10 +226,14 @@ Many packages that simplify it a lot have to make many compromises with the data
|
||||
|
||||
**FastAPI** doesn't make any compromise with any database, data model or tool.
|
||||
|
||||
It gives you all the flexibility to chose the ones that fit your project the best.
|
||||
It gives you all the flexibility to choose the ones that fit your project the best.
|
||||
|
||||
And you can use directly many well maintained and widely used packages like `passlib` and `pyjwt`, because **FastAPI** doesn't require any complex mechanisms to integrate external packages.
|
||||
|
||||
But it provides you the tools to simplify the process as much as possible without compromising flexibility, robustness or security.
|
||||
|
||||
And you can use secure, standard protocols like OAuth2 in a relatively simple way.
|
||||
And you can use and implement secure, standard protocols, like OAuth2 in a relatively simple way.
|
||||
|
||||
In the next (optional) section you can see how to extend this even further, using OAuth2 "scopes", for a more fine-grained permission system, following these same standards.
|
||||
|
||||
OAuth2 with scopes (explained in the next section) is the mechanism used by many big authentication providers, like Facebook, Google, GitHub, Microsoft, Twitter, etc.
|
||||
|
||||