committed by
GitHub
57 changed files with 3036 additions and 933 deletions
@ -0,0 +1,3 @@ |
|||
# সম্পর্কে |
|||
|
|||
**FastAPI** সম্পর্কে বিস্তারিত — এর ডিজাইন, অনুপ্রেরণা ও আরও অনেক কিছু। 🤓 |
File diff suppressed because it is too large
@ -1,495 +1,495 @@ |
|||
- name: full-stack-fastapi-template |
|||
html_url: https://github.com/fastapi/full-stack-fastapi-template |
|||
stars: 33079 |
|||
stars: 34156 |
|||
owner_login: fastapi |
|||
owner_html_url: https://github.com/fastapi |
|||
- name: Hello-Python |
|||
html_url: https://github.com/mouredev/Hello-Python |
|||
stars: 30350 |
|||
stars: 30835 |
|||
owner_login: mouredev |
|||
owner_html_url: https://github.com/mouredev |
|||
- name: serve |
|||
html_url: https://github.com/jina-ai/serve |
|||
stars: 21593 |
|||
stars: 21631 |
|||
owner_login: jina-ai |
|||
owner_html_url: https://github.com/jina-ai |
|||
- name: HivisionIDPhotos |
|||
html_url: https://github.com/Zeyi-Lin/HivisionIDPhotos |
|||
stars: 17229 |
|||
stars: 18125 |
|||
owner_login: Zeyi-Lin |
|||
owner_html_url: https://github.com/Zeyi-Lin |
|||
- name: sqlmodel |
|||
html_url: https://github.com/fastapi/sqlmodel |
|||
stars: 16068 |
|||
stars: 16249 |
|||
owner_login: fastapi |
|||
owner_html_url: https://github.com/fastapi |
|||
- name: Douyin_TikTok_Download_API |
|||
html_url: https://github.com/Evil0ctal/Douyin_TikTok_Download_API |
|||
stars: 12689 |
|||
stars: 13279 |
|||
owner_login: Evil0ctal |
|||
owner_html_url: https://github.com/Evil0ctal |
|||
- name: fastapi-best-practices |
|||
html_url: https://github.com/zhanymkanov/fastapi-best-practices |
|||
stars: 11965 |
|||
stars: 12334 |
|||
owner_login: zhanymkanov |
|||
owner_html_url: https://github.com/zhanymkanov |
|||
- name: awesome-fastapi |
|||
html_url: https://github.com/mjhea0/awesome-fastapi |
|||
stars: 9773 |
|||
stars: 9934 |
|||
owner_login: mjhea0 |
|||
owner_html_url: https://github.com/mjhea0 |
|||
- name: FastUI |
|||
html_url: https://github.com/pydantic/FastUI |
|||
stars: 8829 |
|||
stars: 8838 |
|||
owner_login: pydantic |
|||
owner_html_url: https://github.com/pydantic |
|||
- name: XHS-Downloader |
|||
html_url: https://github.com/JoeanAmier/XHS-Downloader |
|||
stars: 7962 |
|||
owner_login: JoeanAmier |
|||
owner_html_url: https://github.com/JoeanAmier |
|||
- name: nonebot2 |
|||
html_url: https://github.com/nonebot/nonebot2 |
|||
stars: 6779 |
|||
stars: 6834 |
|||
owner_login: nonebot |
|||
owner_html_url: https://github.com/nonebot |
|||
- name: FileCodeBox |
|||
html_url: https://github.com/vastsa/FileCodeBox |
|||
stars: 6652 |
|||
stars: 6783 |
|||
owner_login: vastsa |
|||
owner_html_url: https://github.com/vastsa |
|||
- name: serge |
|||
html_url: https://github.com/serge-chat/serge |
|||
stars: 5722 |
|||
owner_login: serge-chat |
|||
owner_html_url: https://github.com/serge-chat |
|||
- name: fastapi_mcp |
|||
html_url: https://github.com/tadata-org/fastapi_mcp |
|||
stars: 5846 |
|||
owner_login: tadata-org |
|||
owner_html_url: https://github.com/tadata-org |
|||
- name: hatchet |
|||
html_url: https://github.com/hatchet-dev/hatchet |
|||
stars: 5607 |
|||
stars: 5773 |
|||
owner_login: hatchet-dev |
|||
owner_html_url: https://github.com/hatchet-dev |
|||
- name: serge |
|||
html_url: https://github.com/serge-chat/serge |
|||
stars: 5728 |
|||
owner_login: serge-chat |
|||
owner_html_url: https://github.com/serge-chat |
|||
- name: polar |
|||
html_url: https://github.com/polarsource/polar |
|||
stars: 5327 |
|||
stars: 5709 |
|||
owner_login: polarsource |
|||
owner_html_url: https://github.com/polarsource |
|||
- name: fastapi-users |
|||
html_url: https://github.com/fastapi-users/fastapi-users |
|||
stars: 5235 |
|||
stars: 5336 |
|||
owner_login: fastapi-users |
|||
owner_html_url: https://github.com/fastapi-users |
|||
- name: fastapi_mcp |
|||
html_url: https://github.com/tadata-org/fastapi_mcp |
|||
stars: 5193 |
|||
owner_login: tadata-org |
|||
owner_html_url: https://github.com/tadata-org |
|||
- name: SurfSense |
|||
html_url: https://github.com/MODSetter/SurfSense |
|||
stars: 4833 |
|||
owner_login: MODSetter |
|||
owner_html_url: https://github.com/MODSetter |
|||
- name: chatgpt-web-share |
|||
html_url: https://github.com/chatpire/chatgpt-web-share |
|||
stars: 4307 |
|||
owner_login: chatpire |
|||
owner_html_url: https://github.com/chatpire |
|||
- name: strawberry |
|||
html_url: https://github.com/strawberry-graphql/strawberry |
|||
stars: 4281 |
|||
stars: 4317 |
|||
owner_login: strawberry-graphql |
|||
owner_html_url: https://github.com/strawberry-graphql |
|||
- name: chatgpt-web-share |
|||
html_url: https://github.com/chatpire/chatgpt-web-share |
|||
stars: 4301 |
|||
owner_login: chatpire |
|||
owner_html_url: https://github.com/chatpire |
|||
- name: atrilabs-engine |
|||
html_url: https://github.com/Atri-Labs/atrilabs-engine |
|||
stars: 4110 |
|||
stars: 4106 |
|||
owner_login: Atri-Labs |
|||
owner_html_url: https://github.com/Atri-Labs |
|||
- name: dynaconf |
|||
html_url: https://github.com/dynaconf/dynaconf |
|||
stars: 4008 |
|||
stars: 4045 |
|||
owner_login: dynaconf |
|||
owner_html_url: https://github.com/dynaconf |
|||
- name: poem |
|||
html_url: https://github.com/poem-web/poem |
|||
stars: 3977 |
|||
stars: 4037 |
|||
owner_login: poem-web |
|||
owner_html_url: https://github.com/poem-web |
|||
- name: farfalle |
|||
html_url: https://github.com/rashadphz/farfalle |
|||
stars: 3317 |
|||
stars: 3348 |
|||
owner_login: rashadphz |
|||
owner_html_url: https://github.com/rashadphz |
|||
- name: LitServe |
|||
html_url: https://github.com/Lightning-AI/LitServe |
|||
stars: 3347 |
|||
owner_login: Lightning-AI |
|||
owner_html_url: https://github.com/Lightning-AI |
|||
- name: fastapi-admin |
|||
html_url: https://github.com/fastapi-admin/fastapi-admin |
|||
stars: 3253 |
|||
stars: 3309 |
|||
owner_login: fastapi-admin |
|||
owner_html_url: https://github.com/fastapi-admin |
|||
- name: datamodel-code-generator |
|||
html_url: https://github.com/koxudaxi/datamodel-code-generator |
|||
stars: 3228 |
|||
stars: 3291 |
|||
owner_login: koxudaxi |
|||
owner_html_url: https://github.com/koxudaxi |
|||
- name: LitServe |
|||
html_url: https://github.com/Lightning-AI/LitServe |
|||
stars: 3175 |
|||
owner_login: Lightning-AI |
|||
owner_html_url: https://github.com/Lightning-AI |
|||
- name: logfire |
|||
html_url: https://github.com/pydantic/logfire |
|||
stars: 3172 |
|||
stars: 3288 |
|||
owner_login: pydantic |
|||
owner_html_url: https://github.com/pydantic |
|||
- name: opyrator |
|||
html_url: https://github.com/ml-tooling/opyrator |
|||
stars: 3122 |
|||
owner_login: ml-tooling |
|||
owner_html_url: https://github.com/ml-tooling |
|||
- name: huma |
|||
html_url: https://github.com/danielgtaylor/huma |
|||
stars: 3110 |
|||
stars: 3201 |
|||
owner_login: danielgtaylor |
|||
owner_html_url: https://github.com/danielgtaylor |
|||
- name: opyrator |
|||
html_url: https://github.com/ml-tooling/opyrator |
|||
stars: 3132 |
|||
owner_login: ml-tooling |
|||
owner_html_url: https://github.com/ml-tooling |
|||
- name: Kokoro-FastAPI |
|||
html_url: https://github.com/remsky/Kokoro-FastAPI |
|||
stars: 3099 |
|||
owner_login: remsky |
|||
owner_html_url: https://github.com/remsky |
|||
- name: docarray |
|||
html_url: https://github.com/docarray/docarray |
|||
stars: 3068 |
|||
stars: 3075 |
|||
owner_login: docarray |
|||
owner_html_url: https://github.com/docarray |
|||
- name: fastapi-realworld-example-app |
|||
html_url: https://github.com/nsidnev/fastapi-realworld-example-app |
|||
stars: 2892 |
|||
stars: 2902 |
|||
owner_login: nsidnev |
|||
owner_html_url: https://github.com/nsidnev |
|||
- name: Kokoro-FastAPI |
|||
html_url: https://github.com/remsky/Kokoro-FastAPI |
|||
stars: 2883 |
|||
owner_login: remsky |
|||
owner_html_url: https://github.com/remsky |
|||
- name: uvicorn-gunicorn-fastapi-docker |
|||
html_url: https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker |
|||
stars: 2770 |
|||
owner_login: tiangolo |
|||
owner_html_url: https://github.com/tiangolo |
|||
- name: tracecat |
|||
html_url: https://github.com/TracecatHQ/tracecat |
|||
stars: 2740 |
|||
stars: 2888 |
|||
owner_login: TracecatHQ |
|||
owner_html_url: https://github.com/TracecatHQ |
|||
- name: uvicorn-gunicorn-fastapi-docker |
|||
html_url: https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker |
|||
stars: 2775 |
|||
owner_login: tiangolo |
|||
owner_html_url: https://github.com/tiangolo |
|||
- name: best-of-web-python |
|||
html_url: https://github.com/ml-tooling/best-of-web-python |
|||
stars: 2517 |
|||
stars: 2537 |
|||
owner_login: ml-tooling |
|||
owner_html_url: https://github.com/ml-tooling |
|||
- name: RasaGPT |
|||
html_url: https://github.com/paulpierre/RasaGPT |
|||
stars: 2423 |
|||
stars: 2427 |
|||
owner_login: paulpierre |
|||
owner_html_url: https://github.com/paulpierre |
|||
- name: fastapi-react |
|||
html_url: https://github.com/Buuntu/fastapi-react |
|||
stars: 2376 |
|||
stars: 2397 |
|||
owner_login: Buuntu |
|||
owner_html_url: https://github.com/Buuntu |
|||
- name: FastAPI-template |
|||
html_url: https://github.com/s3rius/FastAPI-template |
|||
stars: 2301 |
|||
stars: 2334 |
|||
owner_login: s3rius |
|||
owner_html_url: https://github.com/s3rius |
|||
- name: nextpy |
|||
html_url: https://github.com/dot-agent/nextpy |
|||
stars: 2289 |
|||
stars: 2295 |
|||
owner_login: dot-agent |
|||
owner_html_url: https://github.com/dot-agent |
|||
- name: sqladmin |
|||
html_url: https://github.com/aminalaee/sqladmin |
|||
stars: 2196 |
|||
stars: 2235 |
|||
owner_login: aminalaee |
|||
owner_html_url: https://github.com/aminalaee |
|||
- name: 30-Days-of-Python |
|||
html_url: https://github.com/codingforentrepreneurs/30-Days-of-Python |
|||
stars: 2179 |
|||
stars: 2181 |
|||
owner_login: codingforentrepreneurs |
|||
owner_html_url: https://github.com/codingforentrepreneurs |
|||
- name: langserve |
|||
html_url: https://github.com/langchain-ai/langserve |
|||
stars: 2098 |
|||
stars: 2119 |
|||
owner_login: langchain-ai |
|||
owner_html_url: https://github.com/langchain-ai |
|||
- name: fastapi-utils |
|||
html_url: https://github.com/fastapiutils/fastapi-utils |
|||
stars: 2077 |
|||
stars: 2100 |
|||
owner_login: fastapiutils |
|||
owner_html_url: https://github.com/fastapiutils |
|||
- name: supabase-py |
|||
html_url: https://github.com/supabase/supabase-py |
|||
stars: 2047 |
|||
stars: 2084 |
|||
owner_login: supabase |
|||
owner_html_url: https://github.com/supabase |
|||
- name: solara |
|||
html_url: https://github.com/widgetti/solara |
|||
stars: 2044 |
|||
stars: 2056 |
|||
owner_login: widgetti |
|||
owner_html_url: https://github.com/widgetti |
|||
- name: mangum |
|||
html_url: https://github.com/Kludex/mangum |
|||
stars: 1905 |
|||
stars: 1923 |
|||
owner_login: Kludex |
|||
owner_html_url: https://github.com/Kludex |
|||
- name: python-week-2022 |
|||
html_url: https://github.com/rochacbruno/python-week-2022 |
|||
stars: 1823 |
|||
stars: 1821 |
|||
owner_login: rochacbruno |
|||
owner_html_url: https://github.com/rochacbruno |
|||
- name: manage-fastapi |
|||
html_url: https://github.com/ycd/manage-fastapi |
|||
stars: 1754 |
|||
owner_login: ycd |
|||
owner_html_url: https://github.com/ycd |
|||
- name: agentkit |
|||
html_url: https://github.com/BCG-X-Official/agentkit |
|||
stars: 1746 |
|||
stars: 1765 |
|||
owner_login: BCG-X-Official |
|||
owner_html_url: https://github.com/BCG-X-Official |
|||
- name: manage-fastapi |
|||
html_url: https://github.com/ycd/manage-fastapi |
|||
stars: 1756 |
|||
owner_login: ycd |
|||
owner_html_url: https://github.com/ycd |
|||
- name: ormar |
|||
html_url: https://github.com/collerek/ormar |
|||
stars: 1742 |
|||
stars: 1755 |
|||
owner_login: collerek |
|||
owner_html_url: https://github.com/collerek |
|||
- name: langchain-serve |
|||
html_url: https://github.com/jina-ai/langchain-serve |
|||
stars: 1630 |
|||
stars: 1631 |
|||
owner_login: jina-ai |
|||
owner_html_url: https://github.com/jina-ai |
|||
- name: termpair |
|||
html_url: https://github.com/cs01/termpair |
|||
stars: 1611 |
|||
owner_login: cs01 |
|||
owner_html_url: https://github.com/cs01 |
|||
- name: piccolo |
|||
html_url: https://github.com/piccolo-orm/piccolo |
|||
stars: 1609 |
|||
stars: 1629 |
|||
owner_login: piccolo-orm |
|||
owner_html_url: https://github.com/piccolo-orm |
|||
- name: coronavirus-tracker-api |
|||
html_url: https://github.com/ExpDev07/coronavirus-tracker-api |
|||
stars: 1587 |
|||
owner_login: ExpDev07 |
|||
owner_html_url: https://github.com/ExpDev07 |
|||
- name: fastapi-cache |
|||
html_url: https://github.com/long2ice/fastapi-cache |
|||
stars: 1575 |
|||
owner_login: long2ice |
|||
owner_html_url: https://github.com/long2ice |
|||
- name: termpair |
|||
html_url: https://github.com/cs01/termpair |
|||
stars: 1616 |
|||
owner_login: cs01 |
|||
owner_html_url: https://github.com/cs01 |
|||
- name: openapi-python-client |
|||
html_url: https://github.com/openapi-generators/openapi-python-client |
|||
stars: 1568 |
|||
stars: 1603 |
|||
owner_login: openapi-generators |
|||
owner_html_url: https://github.com/openapi-generators |
|||
- name: fastapi-crudrouter |
|||
html_url: https://github.com/awtkns/fastapi-crudrouter |
|||
stars: 1508 |
|||
owner_login: awtkns |
|||
owner_html_url: https://github.com/awtkns |
|||
- name: fastapi-cache |
|||
html_url: https://github.com/long2ice/fastapi-cache |
|||
stars: 1589 |
|||
owner_login: long2ice |
|||
owner_html_url: https://github.com/long2ice |
|||
- name: coronavirus-tracker-api |
|||
html_url: https://github.com/ExpDev07/coronavirus-tracker-api |
|||
stars: 1580 |
|||
owner_login: ExpDev07 |
|||
owner_html_url: https://github.com/ExpDev07 |
|||
- name: slowapi |
|||
html_url: https://github.com/laurentS/slowapi |
|||
stars: 1501 |
|||
stars: 1533 |
|||
owner_login: laurentS |
|||
owner_html_url: https://github.com/laurentS |
|||
- name: fastapi-crudrouter |
|||
html_url: https://github.com/awtkns/fastapi-crudrouter |
|||
stars: 1518 |
|||
owner_login: awtkns |
|||
owner_html_url: https://github.com/awtkns |
|||
- name: awesome-fastapi-projects |
|||
html_url: https://github.com/Kludex/awesome-fastapi-projects |
|||
stars: 1453 |
|||
stars: 1461 |
|||
owner_login: Kludex |
|||
owner_html_url: https://github.com/Kludex |
|||
- name: vue-fastapi-admin |
|||
html_url: https://github.com/mizhexiaoxiao/vue-fastapi-admin |
|||
stars: 1409 |
|||
owner_login: mizhexiaoxiao |
|||
owner_html_url: https://github.com/mizhexiaoxiao |
|||
- name: awesome-python-resources |
|||
html_url: https://github.com/DjangoEx/awesome-python-resources |
|||
stars: 1390 |
|||
stars: 1393 |
|||
owner_login: DjangoEx |
|||
owner_html_url: https://github.com/DjangoEx |
|||
- name: fastapi-pagination |
|||
html_url: https://github.com/uriyyo/fastapi-pagination |
|||
stars: 1353 |
|||
stars: 1378 |
|||
owner_login: uriyyo |
|||
owner_html_url: https://github.com/uriyyo |
|||
- name: budgetml |
|||
html_url: https://github.com/ebhy/budgetml |
|||
stars: 1342 |
|||
owner_login: ebhy |
|||
owner_html_url: https://github.com/ebhy |
|||
- name: fastapi-boilerplate |
|||
html_url: https://github.com/teamhide/fastapi-boilerplate |
|||
stars: 1325 |
|||
stars: 1348 |
|||
owner_login: teamhide |
|||
owner_html_url: https://github.com/teamhide |
|||
- name: vue-fastapi-admin |
|||
html_url: https://github.com/mizhexiaoxiao/vue-fastapi-admin |
|||
stars: 1306 |
|||
owner_login: mizhexiaoxiao |
|||
owner_html_url: https://github.com/mizhexiaoxiao |
|||
- name: budgetml |
|||
html_url: https://github.com/ebhy/budgetml |
|||
stars: 1344 |
|||
owner_login: ebhy |
|||
owner_html_url: https://github.com/ebhy |
|||
- name: fastapi-amis-admin |
|||
html_url: https://github.com/amisadmin/fastapi-amis-admin |
|||
stars: 1256 |
|||
stars: 1284 |
|||
owner_login: amisadmin |
|||
owner_html_url: https://github.com/amisadmin |
|||
- name: bracket |
|||
html_url: https://github.com/evroon/bracket |
|||
stars: 1274 |
|||
owner_login: evroon |
|||
owner_html_url: https://github.com/evroon |
|||
- name: fastapi-tutorial |
|||
html_url: https://github.com/liaogx/fastapi-tutorial |
|||
stars: 1245 |
|||
stars: 1265 |
|||
owner_login: liaogx |
|||
owner_html_url: https://github.com/liaogx |
|||
- name: fastapi-code-generator |
|||
html_url: https://github.com/koxudaxi/fastapi-code-generator |
|||
stars: 1201 |
|||
stars: 1216 |
|||
owner_login: koxudaxi |
|||
owner_html_url: https://github.com/koxudaxi |
|||
- name: bracket |
|||
html_url: https://github.com/evroon/bracket |
|||
stars: 1201 |
|||
owner_login: evroon |
|||
owner_html_url: https://github.com/evroon |
|||
- name: bolt-python |
|||
html_url: https://github.com/slackapi/bolt-python |
|||
stars: 1179 |
|||
stars: 1190 |
|||
owner_login: slackapi |
|||
owner_html_url: https://github.com/slackapi |
|||
- name: fastapi_production_template |
|||
html_url: https://github.com/zhanymkanov/fastapi_production_template |
|||
stars: 1147 |
|||
owner_login: zhanymkanov |
|||
owner_html_url: https://github.com/zhanymkanov |
|||
- name: fastcrud |
|||
html_url: https://github.com/benavlabs/fastcrud |
|||
stars: 1169 |
|||
owner_login: benavlabs |
|||
owner_html_url: https://github.com/benavlabs |
|||
- name: prometheus-fastapi-instrumentator |
|||
html_url: https://github.com/trallnag/prometheus-fastapi-instrumentator |
|||
stars: 1145 |
|||
stars: 1167 |
|||
owner_login: trallnag |
|||
owner_html_url: https://github.com/trallnag |
|||
- name: fastapi_production_template |
|||
html_url: https://github.com/zhanymkanov/fastapi_production_template |
|||
stars: 1165 |
|||
owner_login: zhanymkanov |
|||
owner_html_url: https://github.com/zhanymkanov |
|||
- name: bedrock-chat |
|||
html_url: https://github.com/aws-samples/bedrock-chat |
|||
stars: 1143 |
|||
stars: 1163 |
|||
owner_login: aws-samples |
|||
owner_html_url: https://github.com/aws-samples |
|||
- name: langchain-extract |
|||
html_url: https://github.com/langchain-ai/langchain-extract |
|||
stars: 1134 |
|||
stars: 1142 |
|||
owner_login: langchain-ai |
|||
owner_html_url: https://github.com/langchain-ai |
|||
- name: odmantic |
|||
html_url: https://github.com/art049/odmantic |
|||
stars: 1118 |
|||
stars: 1121 |
|||
owner_login: art049 |
|||
owner_html_url: https://github.com/art049 |
|||
- name: fastapi_best_architecture |
|||
html_url: https://github.com/fastapi-practices/fastapi_best_architecture |
|||
stars: 1118 |
|||
owner_login: fastapi-practices |
|||
owner_html_url: https://github.com/fastapi-practices |
|||
- name: fastapi-alembic-sqlmodel-async |
|||
html_url: https://github.com/jonra1993/fastapi-alembic-sqlmodel-async |
|||
stars: 1110 |
|||
stars: 1116 |
|||
owner_login: jonra1993 |
|||
owner_html_url: https://github.com/jonra1993 |
|||
- name: fastcrud |
|||
html_url: https://github.com/benavlabs/fastcrud |
|||
stars: 1080 |
|||
- name: FastAPI-boilerplate |
|||
html_url: https://github.com/benavlabs/FastAPI-boilerplate |
|||
stars: 1070 |
|||
owner_login: benavlabs |
|||
owner_html_url: https://github.com/benavlabs |
|||
- name: restish |
|||
html_url: https://github.com/rest-sh/restish |
|||
stars: 1056 |
|||
stars: 1069 |
|||
owner_login: rest-sh |
|||
owner_html_url: https://github.com/rest-sh |
|||
- name: fastapi_best_architecture |
|||
html_url: https://github.com/fastapi-practices/fastapi_best_architecture |
|||
stars: 1050 |
|||
owner_login: fastapi-practices |
|||
owner_html_url: https://github.com/fastapi-practices |
|||
- name: runhouse |
|||
html_url: https://github.com/run-house/runhouse |
|||
stars: 1034 |
|||
stars: 1037 |
|||
owner_login: run-house |
|||
owner_html_url: https://github.com/run-house |
|||
- name: autollm |
|||
html_url: https://github.com/viddexa/autollm |
|||
stars: 992 |
|||
stars: 994 |
|||
owner_login: viddexa |
|||
owner_html_url: https://github.com/viddexa |
|||
- name: lanarky |
|||
html_url: https://github.com/ajndkr/lanarky |
|||
stars: 990 |
|||
stars: 992 |
|||
owner_login: ajndkr |
|||
owner_html_url: https://github.com/ajndkr |
|||
- name: FastAPI-boilerplate |
|||
html_url: https://github.com/benavlabs/FastAPI-boilerplate |
|||
stars: 985 |
|||
owner_login: benavlabs |
|||
owner_html_url: https://github.com/benavlabs |
|||
- name: authx |
|||
html_url: https://github.com/yezz123/authx |
|||
stars: 938 |
|||
stars: 953 |
|||
owner_login: yezz123 |
|||
owner_html_url: https://github.com/yezz123 |
|||
- name: secure |
|||
html_url: https://github.com/TypeError/secure |
|||
stars: 935 |
|||
stars: 941 |
|||
owner_login: TypeError |
|||
owner_html_url: https://github.com/TypeError |
|||
- name: langcorn |
|||
html_url: https://github.com/msoedov/langcorn |
|||
stars: 925 |
|||
owner_login: msoedov |
|||
owner_html_url: https://github.com/msoedov |
|||
- name: energy-forecasting |
|||
html_url: https://github.com/iusztinpaul/energy-forecasting |
|||
stars: 913 |
|||
stars: 928 |
|||
owner_login: iusztinpaul |
|||
owner_html_url: https://github.com/iusztinpaul |
|||
- name: langcorn |
|||
html_url: https://github.com/msoedov/langcorn |
|||
stars: 927 |
|||
owner_login: msoedov |
|||
owner_html_url: https://github.com/msoedov |
|||
- name: titiler |
|||
html_url: https://github.com/developmentseed/titiler |
|||
stars: 886 |
|||
stars: 901 |
|||
owner_login: developmentseed |
|||
owner_html_url: https://github.com/developmentseed |
|||
- name: flock |
|||
html_url: https://github.com/Onelevenvy/flock |
|||
stars: 866 |
|||
stars: 896 |
|||
owner_login: Onelevenvy |
|||
owner_html_url: https://github.com/Onelevenvy |
|||
- name: httpdbg |
|||
html_url: https://github.com/cle-b/httpdbg |
|||
stars: 863 |
|||
owner_login: cle-b |
|||
owner_html_url: https://github.com/cle-b |
|||
- name: fastapi-langgraph-agent-production-ready-template |
|||
html_url: https://github.com/wassim249/fastapi-langgraph-agent-production-ready-template |
|||
stars: 896 |
|||
owner_login: wassim249 |
|||
owner_html_url: https://github.com/wassim249 |
|||
- name: marker-api |
|||
html_url: https://github.com/adithya-s-k/marker-api |
|||
stars: 859 |
|||
stars: 875 |
|||
owner_login: adithya-s-k |
|||
owner_html_url: https://github.com/adithya-s-k |
|||
- name: ludic |
|||
html_url: https://github.com/getludic/ludic |
|||
stars: 845 |
|||
owner_login: getludic |
|||
owner_html_url: https://github.com/getludic |
|||
- name: httpdbg |
|||
html_url: https://github.com/cle-b/httpdbg |
|||
stars: 870 |
|||
owner_login: cle-b |
|||
owner_html_url: https://github.com/cle-b |
|||
- name: fastapi-do-zero |
|||
html_url: https://github.com/dunossauro/fastapi-do-zero |
|||
stars: 827 |
|||
stars: 855 |
|||
owner_login: dunossauro |
|||
owner_html_url: https://github.com/dunossauro |
|||
- name: ludic |
|||
html_url: https://github.com/getludic/ludic |
|||
stars: 849 |
|||
owner_login: getludic |
|||
owner_html_url: https://github.com/getludic |
|||
- name: fastapi-observability |
|||
html_url: https://github.com/blueswen/fastapi-observability |
|||
stars: 823 |
|||
stars: 837 |
|||
owner_login: blueswen |
|||
owner_html_url: https://github.com/blueswen |
|||
- name: fastapi-langgraph-agent-production-ready-template |
|||
html_url: https://github.com/wassim249/fastapi-langgraph-agent-production-ready-template |
|||
stars: 803 |
|||
owner_login: wassim249 |
|||
owner_html_url: https://github.com/wassim249 |
|||
- name: fastapi-mail |
|||
html_url: https://github.com/sabuhish/fastapi-mail |
|||
stars: 798 |
|||
owner_login: sabuhish |
|||
owner_html_url: https://github.com/sabuhish |
|||
- name: fastapi-scaf |
|||
html_url: https://github.com/atpuxiner/fastapi-scaf |
|||
stars: 821 |
|||
owner_login: atpuxiner |
|||
owner_html_url: https://github.com/atpuxiner |
|||
- name: starlette-admin |
|||
html_url: https://github.com/jowilf/starlette-admin |
|||
stars: 785 |
|||
stars: 808 |
|||
owner_login: jowilf |
|||
owner_html_url: https://github.com/jowilf |
|||
- name: lccn_predictor |
|||
html_url: https://github.com/baoliay2008/lccn_predictor |
|||
stars: 767 |
|||
owner_login: baoliay2008 |
|||
owner_html_url: https://github.com/baoliay2008 |
|||
- name: fastapi-mail |
|||
html_url: https://github.com/sabuhish/fastapi-mail |
|||
stars: 807 |
|||
owner_login: sabuhish |
|||
owner_html_url: https://github.com/sabuhish |
|||
- name: aktools |
|||
html_url: https://github.com/akfamily/aktools |
|||
stars: 759 |
|||
stars: 796 |
|||
owner_login: akfamily |
|||
owner_html_url: https://github.com/akfamily |
|||
- name: KonomiTV |
|||
html_url: https://github.com/tsukumijima/KonomiTV |
|||
stars: 748 |
|||
owner_login: tsukumijima |
|||
owner_html_url: https://github.com/tsukumijima |
|||
- name: RuoYi-Vue3-FastAPI |
|||
html_url: https://github.com/insistence/RuoYi-Vue3-FastAPI |
|||
stars: 782 |
|||
owner_login: insistence |
|||
owner_html_url: https://github.com/insistence |
|||
|
@ -0,0 +1,444 @@ |
|||
# همزمانی و async / await |
|||
|
|||
جزئیات در مورد سینتکس `async def` برای *توابع عملیات مسیر* و یه کم پیشزمینه در مورد کد ناهمزمان، همزمانی و موازیسازی. |
|||
|
|||
## عجله داری؟ |
|||
|
|||
<abbr title="خیلی طولانی بود؛ نخوندم"><strong>TL;DR:</strong></abbr> |
|||
|
|||
اگه از کتابخونههای سومشخصی استفاده میکنی که بهت میگن با `await` صداشون کنی، مثل: |
|||
|
|||
```Python |
|||
results = await some_library() |
|||
``` |
|||
|
|||
اون وقت، *توابع عملیات مسیرت* رو با `async def` تعریف کن، اینجوری: |
|||
|
|||
```Python hl_lines="2" |
|||
@app.get('/') |
|||
async def read_results(): |
|||
results = await some_library() |
|||
return results |
|||
``` |
|||
|
|||
/// note |
|||
|
|||
فقط توی توابعی که با `async def` ساخته شدن میتونی از `await` استفاده کنی. |
|||
|
|||
/// |
|||
|
|||
--- |
|||
|
|||
اگه از یه کتابخونه سومشخص استفاده میکنی که با یه چیزی (مثل دیتابیس، API، سیستم فایل و غیره) ارتباط داره و از `await` پشتیبانی نمیکنه (که الان برای بیشتر کتابخونههای دیتابیس اینجوریه)، اون وقت *توابع عملیات مسیرت* رو عادی، فقط با `def` تعریف کن، اینجوری: |
|||
|
|||
```Python hl_lines="2" |
|||
@app.get('/') |
|||
def results(): |
|||
results = some_library() |
|||
return results |
|||
``` |
|||
|
|||
--- |
|||
|
|||
اگه برنامهات (به هر دلیلی) لازم نیست با چیز دیگهای ارتباط برقرار کنه و منتظر جوابش بمونه، از `async def` استفاده کن. |
|||
|
|||
--- |
|||
|
|||
اگه نمیدونی چیکار کنی، از `def` معمولی استفاده کن. |
|||
|
|||
--- |
|||
|
|||
**توجه**: میتونی توی *توابع عملیات مسیرت* هر چقدر که لازم داری `def` و `async def` رو قاطی کنی و هر کدوم رو با بهترین گزینه برات تعریف کنی. FastAPI خودش کار درست رو باهاشون انجام میده. |
|||
|
|||
به هر حال، توی هر کدوم از موقعیتهای بالا، FastAPI هنوز ناهمزمان کار میکنه و خیلی خیلی سریع هست. |
|||
|
|||
ولی با دنبال کردن مراحل بالا، میتونه یه سری بهینهسازی عملکرد هم بکنه. |
|||
|
|||
## جزئیات فنی |
|||
|
|||
نسخههای مدرن پایتون از **"کد ناهمزمان"** با چیزی که بهش **"کروتین"** میگن پشتیبانی میکنن، با سینتکس **`async` و `await`**. |
|||
|
|||
بیاید این جمله رو تکهتکه توی بخشهای زیر ببینیم: |
|||
|
|||
* **کد ناهمزمان** |
|||
* **`async` و `await`** |
|||
* **کروتینها** |
|||
|
|||
## کد ناهمزمان |
|||
|
|||
کد ناهمزمان یعنی زبون 💬 یه راهی داره که به کامپیوتر / برنامه 🤖 بگه توی یه جای کد، باید منتظر بمونه تا *یه چیز دیگه* یه جای دیگه تموم بشه. فرض کن اون *یه چیز دیگه* اسمش "فایل-آروم" 📝 باشه. |
|||
|
|||
پس، توی اون مدت، کامپیوتر میتونه بره یه کار دیگه بکنه، تا وقتی "فایل-آروم" 📝 تموم بشه. |
|||
|
|||
بعدش کامپیوتر / برنامه 🤖 هر وقت فرصتی داشته باشه برمیگرده، چون دوباره منتظره، یا هر وقت همه کاری که اون لحظه داشته تموم کرده. و میبینه آیا کارایی که منتظرشون بوده تموم شدن یا نه، و هر کاری که باید بکنه رو انجام میده. |
|||
|
|||
بعد، اون 🤖 اولین کاری که تموم شده (مثلاً "فایل-آروم" 📝 ما) رو برمیداره و هر کاری که باید باهاش بکنه رو ادامه میده. |
|||
|
|||
این "منتظر یه چیز دیگه بودن" معمولاً به عملیات <abbr title="ورودی و خروجی">I/O</abbr> اشاره داره که نسبتاً "آروم" هستن (نسبت به سرعت پردازنده و حافظه RAM)، مثل منتظر موندن برای: |
|||
|
|||
* دادههایی که از کلاینت از طریق شبکه فرستاده میشن |
|||
* دادههایی که برنامهات فرستاده تا از طریق شبکه به کلاینت برسه |
|||
* محتوای یه فایل توی دیسک که سیستم بخوندش و به برنامهات بده |
|||
* محتوایی که برنامهات به سیستم داده تا توی دیسک بنویسه |
|||
* یه عملیات API از راه دور |
|||
* یه عملیات دیتابیس که تموم بشه |
|||
* یه کوئری دیتابیس که نتایجش برگرده |
|||
* و غیره. |
|||
|
|||
چون زمان اجرا بیشتر صرف انتظار برای عملیات <abbr title="ورودی و خروجی">I/O</abbr> میشه، بهشون میگن عملیات "I/O bound". |
|||
|
|||
بهش "ناهمزمان" میگن چون کامپیوتر / برنامه لازم نیست با کار آروم "همزمان" باشه، منتظر لحظه دقیق تموم شدن کار بمونه، در حالی که هیچ کاری نمیکنه، تا نتیجه رو بگیره و کارش رو ادامه بده. |
|||
|
|||
به جاش، چون یه سیستم "ناهمزمان" هست، وقتی کار تموم شد، میتونه یه کم توی صف منتظر بمونه (چند میکروثانیه) تا کامپیوتر / برنامه هر کاری که رفته بکنه رو تموم کنه، و بعد برگرده نتیجه رو بگیره و باهاش کار کنه. |
|||
|
|||
برای "همزمان" (برخلاف "ناهمزمان") معمولاً از اصطلاح "ترتیبی" هم استفاده میکنن، چون کامپیوتر / برنامه همه مراحل رو به ترتیب دنبال میکنه قبل از اینکه بره سراغ یه کار دیگه، حتی اگه اون مراحل شامل انتظار باشن. |
|||
|
|||
### همزمانی و برگرها |
|||
|
|||
این ایده **ناهمزمان** که بالا توضیح دادم گاهی بهش **"همزمانی"** هم میگن. با **"موازیسازی"** فرق داره. |
|||
|
|||
**همزمانی** و **موازیسازی** هر دو به "اتفاق افتادن چیزای مختلف کموبیش همزمان" ربط دارن. |
|||
|
|||
ولی جزئیات بین *همزمانی* و *موازیسازی* خیلی متفاوته. |
|||
|
|||
برای دیدن فرقش، این داستان در مورد برگرها رو تصور کن: |
|||
|
|||
### برگرهای همزمان |
|||
|
|||
با عشقت میری فستفود بگیرین، توی صف وایمیستی در حالی که صندوقدار سفارش آدمای جلوی تو رو میگیره. 😍 |
|||
|
|||
<img src="/img/async/concurrent-burgers/concurrent-burgers-01.png" class="illustration"> |
|||
|
|||
بعد نوبت تو میشه، سفارش دو تا برگر خیلی شیک برای خودت و عشقت میدی. 🍔🍔 |
|||
|
|||
<img src="/img/async/concurrent-burgers/concurrent-burgers-02.png" class="illustration"> |
|||
|
|||
صندوقدار یه چیزی به آشپز توی آشپزخونه میگه تا بدونن باید برگرهای تو رو آماده کنن (گرچه الان دارن برگرهای مشتریای قبلی رو درست میکنن). |
|||
|
|||
<img src="/img/async/concurrent-burgers/concurrent-burgers-03.png" class="illustration"> |
|||
|
|||
پول رو میدی. 💸 |
|||
|
|||
صندوقدار شماره نوبتت رو بهت میده. |
|||
|
|||
<img src="/img/async/concurrent-burgers/concurrent-burgers-04.png" class="illustration"> |
|||
|
|||
وقتی منتظری، با عشقت میری یه میز انتخاب میکنی، میشینی و کلی با عشقت حرف میزنی (چون برگرهات خیلی شیکن و آماده کردنشون یه کم طول میکشه). |
|||
|
|||
وقتی پشت میز با عشقت نشستی، در حالی که منتظر برگرهایی، میتونی اون زمان رو صرف تحسین این کنی که عشقت چقدر باحال، ناز و باهوشه ✨😍✨. |
|||
|
|||
<img src="/img/async/concurrent-burgers/concurrent-burgers-05.png" class="illustration"> |
|||
|
|||
وقتی منتظری و با عشقت حرف میزنی، هر از گاهی شمارهای که رو پیشخون نشون داده میشه رو چک میکنی که ببینی نوبتت شده یا نه. |
|||
|
|||
بعد یه جایی بالاخره نوبتت میشه. میری پیشخون، برگرهات رو میگیری و برمیگردی سر میز. |
|||
|
|||
<img src="/img/async/concurrent-burgers/concurrent-burgers-06.png" class="illustration"> |
|||
|
|||
تو و عشقت برگرها رو میخورین و یه وقت خوب باهم دارین. ✨ |
|||
|
|||
<img src="/img/async/concurrent-burgers/concurrent-burgers-07.png" class="illustration"> |
|||
|
|||
/// info |
|||
|
|||
تصاویر قشنگ از <a href="https://www.instagram.com/ketrinadrawsalot" class="external-link" target="_blank">کترینا تامپسون</a>. 🎨 |
|||
|
|||
/// |
|||
|
|||
--- |
|||
|
|||
تصور کن تو توی این داستان کامپیوتر / برنامه 🤖 هستی. |
|||
|
|||
وقتی توی صف هستی، فقط بیکاری 😴، منتظر نوبتت هستی، کار خیلی "مفیدی" نمیکنی. ولی صف سریع پیش میره چون صندوقدار فقط سفارش میگیره (آمادشون نمیکنه)، پس این خوبه. |
|||
|
|||
بعد، وقتی نوبتت میشه، کار "مفید" واقعی میکنی، منو رو پردازش میکنی، تصمیم میگیری چی میخوای، انتخاب عشقت رو میگیری، پول میدی، چک میکنی اسکناس یا کارت درست رو دادی، چک میکنی درست حساب شده، چک میکنی سفارش آیتمای درست رو داره و غیره. |
|||
|
|||
ولی بعد، گرچه هنوز برگرهات رو نداری، کارت با صندوقدار "موقتاً متوقف" ⏸ میشه، چون باید منتظر بمونی 🕙 تا برگرهات آماده بشن. |
|||
|
|||
ولی وقتی از پیشخون دور میشی و با شماره نوبتت سر میز میشینی، میتونی توجهت رو 🔀 به عشقت بدی و "کار" ⏯ 🤓 رو اون بکنی. بعدش دوباره داری یه چیز خیلی "مفید" انجام میدی، مثل لاس زدن با عشقت 😍. |
|||
|
|||
بعد صندوقدار 💁 با گذاشتن شمارهات رو نمایشگر پیشخون میگه "من با درست کردن برگرها تموم کردم"، ولی تو مثل دیوونهها وقتی شمارهات رو نمایشگر میاد فوری نمیپری. میدونی کسی برگرهات رو نمیدزده چون شماره نوبتت رو داری، و اونا هم مال خودشون رو دارن. |
|||
|
|||
پس منتظر میمونی تا عشقت داستانش رو تموم کنه (کار فعلی ⏯ / وظیفهای که داره پردازش میشه 🤓)، آروم لبخند میزنی و میگی که میری برگرها رو بیاری ⏸. |
|||
|
|||
بعد میری پیشخون 🔀، به کار اولیه که حالا تموم شده ⏯، برگرها رو میگیری، تشکر میکنی و میبرشون سر میز. این مرحله / وظیفه تعامل با پیشخون رو تموم میکنه ⏹. این به نوبه خودش یه وظیفه جدید، "خوردن برگرها" 🔀 ⏯، میسازه، ولی اون قبلی که "گرفتن برگرها" بود تموم شده ⏹. |
|||
|
|||
### برگرهای موازی |
|||
|
|||
حالا فرض کن اینا "برگرهای همزمان" نیستن، بلکه "برگرهای موازی" هستن. |
|||
|
|||
با عشقت میری فستفود موازی بگیری. |
|||
|
|||
توی صف وایمیستی در حالی که چند تا (مثلاً 8 تا) صندوقدار که همزمان آشپز هم هستن سفارش آدمای جلوی تو رو میگیرن. |
|||
|
|||
همه قبل تو منتظرن برگرهاشون آماده بشه قبل از اینکه پیشخون رو ترک کنن، چون هر کدوم از 8 تا صندوقدار میره و برگر رو همون موقع درست میکنه قبل از اینکه سفارش بعدی رو بگیره. |
|||
|
|||
<img src="/img/async/parallel-burgers/parallel-burgers-01.png" class="illustration"> |
|||
|
|||
بالاخره نوبت تو میشه، سفارش دو تا برگر خیلی شیک برای خودت و عشقت میدی. |
|||
|
|||
پول رو میدی 💸. |
|||
|
|||
<img src="/img/async/parallel-burgers/parallel-burgers-02.png" class="illustration"> |
|||
|
|||
صندوقدار میره آشپزخونه. |
|||
|
|||
منتظر میمونی، جلوی پیشخون وایستادی 🕙، که کسی قبل از تو برگرهات رو نگیره، چون شماره نوبت نیست. |
|||
|
|||
<img src="/img/async/parallel-burgers/parallel-burgers-03.png" class="illustration"> |
|||
|
|||
چون تو و عشقت مشغول این هستین که نذارین کسی جلوتون بیاد و هر وقت برگرها رسیدن اونا رو بگیره، نمیتونی به عشقت توجه کنی. 😞 |
|||
|
|||
این کار "همزمان" هست، تو با صندوقدار/آشپز 👨🍳 "همزمان" هستی. باید منتظر بمونی 🕙 و درست همون لحظه که صندوقدار/آشپز 👨🍳 برگرها رو تموم میکنه و بهت میده اونجا باشی، وگرنه ممکنه یکی دیگه اونا رو بگیره. |
|||
|
|||
<img src="/img/async/parallel-burgers/parallel-burgers-04.png" class="illustration"> |
|||
|
|||
بعد صندوقدار/آشپزت 👨🍳 بالاخره بعد از یه مدت طولانی انتظار 🕙 جلوی پیشخون با برگرهات برمیگرده. |
|||
|
|||
<img src="/img/async/parallel-burgers/parallel-burgers-05.png" class="illustration"> |
|||
|
|||
برگرهات رو میگیری و با عشقت میری سر میز. |
|||
|
|||
فقط میخورینشون، و تمومه. ⏹ |
|||
|
|||
<img src="/img/async/parallel-burgers/parallel-burgers-06.png" class="illustration"> |
|||
|
|||
حرف زدن یا لاس زدن زیاد نبود چون بیشتر وقت صرف انتظار 🕙 جلوی پیشخون شد. 😞 |
|||
|
|||
/// info |
|||
|
|||
تصاویر قشنگ از <a href="https://www.instagram.com/ketrinadrawsalot" class="external-link" target="_blank">کترینا تامپسون</a>. 🎨 |
|||
|
|||
/// |
|||
|
|||
--- |
|||
|
|||
توی این سناریوی برگرهای موازی، تو یه کامپیوتر / برنامه 🤖 با دو تا پردازنده (تو و عشقت) هستی، هر دو منتظر 🕙 و توجهشون ⏯ رو برای مدت طولانی "انتظار جلوی پیشخون" 🕙 گذاشتن. |
|||
|
|||
فستفود 8 تا پردازنده (صندوقدار/آشپز) داره. در حالی که فستفود برگرهای همزمان شاید فقط 2 تا داشته (یه صندوقدار و یه آشپز). |
|||
|
|||
ولی با این حال، تجربه نهایی بهترین نیست. 😞 |
|||
|
|||
--- |
|||
|
|||
این معادل موازی داستان برگرها بود. 🍔 |
|||
|
|||
برای یه مثال "واقعیتر" از زندگی، یه بانک رو تصور کن. |
|||
|
|||
تا همین چند وقت پیش، بیشتر بانکها چند تا صندوقدار 👨💼👨💼👨💼👨💼 داشتن و یه صف بزرگ 🕙🕙🕙🕙🕙🕙🕙🕙. |
|||
|
|||
همه صندوقدارها کار رو با یه مشتری بعد از اون یکی 👨💼⏯ انجام میدادن. |
|||
|
|||
و باید توی صف 🕙 مدت زیادی منتظر بمونی وگرنه نوبتت رو از دست میدی. |
|||
|
|||
احتمالاً نمیخوای عشقت 😍 رو با خودت ببری بانک 🏦 برای کارای روزمره. |
|||
|
|||
### نتیجهگیری برگرها |
|||
|
|||
توی این سناریوی "برگرهای فستفود با عشقت"، چون کلی انتظار 🕙 هست، خیلی منطقیتره که یه سیستم همزمان ⏸🔀⏯ داشته باشی. |
|||
|
|||
این برای بیشتر برنامههای وب هم صدق میکنه. |
|||
|
|||
خیلی خیلی کاربر، ولی سرورت منتظر 🕙 اتصال نهچندان خوبشون هست تا درخواستهاشون رو بفرستن. |
|||
|
|||
و بعد دوباره منتظر 🕙 که جوابها برگردن. |
|||
|
|||
این "انتظار" 🕙 توی میکروثانیهها اندازهگیری میشه، ولی با این حال، جمعش که بکنی آخرش کلی انتظار میشه. |
|||
|
|||
برای همین استفاده از کد ناهمزمان ⏸🔀⏯ برای APIهای وب خیلی منطقیه. |
|||
|
|||
این نوع ناهمزمانی چیزیه که NodeJS رو محبوب کرد (گرچه NodeJS موازی نیست) و نقطه قوت Go بهعنوان یه زبون برنامهنویسیه. |
|||
|
|||
و همون سطح عملکردی هست که با **FastAPI** میگیری. |
|||
|
|||
و چون میتونی همزمانی و موازیسازی رو همزمان داشته باشی، عملکرد بالاتری از بیشتر فریمورکهای تستشده NodeJS میگیری و همتراز با Go، که یه زبون کامپایلشده نزدیک به C هست <a href="https://www.techempower.com/benchmarks/#section=data-r17&hw=ph&test=query&l=zijmkf-1" class="external-link" target="_blank">(همه اینا به لطف Starlette)</a>. |
|||
|
|||
### آیا همزمانی از موازیسازی بهتره؟ |
|||
|
|||
نه! این نتیجه داستان نیست. |
|||
|
|||
همزمانی با موازیسازی فرق داره. و توی **سناریوهای خاص** که کلی انتظار دارن بهتره. به همین خاطر، معمولاً برای توسعه برنامههای وب خیلی از موازیسازی بهتره. ولی نه برای همهچیز. |
|||
|
|||
برای اینکه یه تعادل بذاریم، این داستان کوتاه رو تصور کن: |
|||
|
|||
> باید یه خونه بزرگ و کثیف رو تمیز کنی. |
|||
|
|||
*آره، کل داستان همینه*. |
|||
|
|||
--- |
|||
|
|||
هیچ انتظاری 🕙 اونجا نیست، فقط کلی کار برای انجام دادن توی جاهای مختلف خونه. |
|||
|
|||
میتونی مثل مثال برگرها نوبت بذاری، اول پذیرایی، بعد آشپزخونه، ولی چون منتظر چیزی نیستی 🕙، فقط داری تمیز میکنی و تمیز میکنی، نوبتها هیچ تأثیری نداره. |
|||
|
|||
با نوبت یا بدون نوبت (همزمانی) همون قدر طول میکشه تا تمومش کنی و همون مقدار کار رو کردی. |
|||
|
|||
ولی توی این موقعیت، اگه بتونی اون 8 تا صندوقدار/آشپز/حالا-تمیزکار رو بیاری، و هر کدومشون (بهعلاوه خودت) یه قسمت از خونه رو تمیز کنن، میتونی همه کار رو **موازی** انجام بدی، با کمک اضافی، و خیلی زودتر تمومش کنی. |
|||
|
|||
توی این سناریو، هر کدوم از تمیزکارها (از جمله خودت) یه پردازندهست که کار خودش رو میکنه. |
|||
|
|||
و چون بیشتر زمان اجرا صرف کار واقعی میشه (به جای انتظار)، و کار توی کامپیوتر با <abbr title="واحد پردازش مرکزی">CPU</abbr> انجام میشه، به این مشکلات میگن "CPU bound". |
|||
|
|||
--- |
|||
|
|||
مثالهای رایج عملیات CPU bound چیزایی هستن که نیاز به پردازش ریاضی پیچیده دارن. |
|||
|
|||
مثلاً: |
|||
|
|||
* پردازش **صدا** یا **تصویر**. |
|||
* **بینایی کامپیوتری**: یه تصویر از میلیونها پیکسل تشکیل شده، هر پیکسل 3 تا مقدار / رنگ داره، پردازشش معمولاً نیاز داره چیزی رو رو اون پیکسلها همزمان حساب کنی. |
|||
* **یادگیری ماشین**: معمولاً کلی ضرب "ماتریس" و "بردار" لازم داره. یه جدول بزرگ پر از عدد رو تصور کن که همهشون رو همزمان ضرب میکنی. |
|||
* **یادگیری عمیق**: این یه زیرشاخه از یادگیری ماشینه، پس همون قضیه صدق میکنه. فقط این که یه جدول عدد برای ضرب کردن نیست، بلکه یه مجموعه بزرگ از اونا هست، و توی خیلی موارد از یه پردازنده خاص برای ساخت و / یا استفاده از این مدلها استفاده میکنی. |
|||
|
|||
### همزمانی + موازیسازی: وب + یادگیری ماشین |
|||
|
|||
با **FastAPI** میتونی از همزمانی که برای توسعه وب خیلی رایجه (همون جذابیت اصلی NodeJS) استفاده کنی. |
|||
|
|||
ولی میتونی از فواید موازیسازی و چندپردازشی (اجرای چند پروسه بهصورت موازی) برای کارای **CPU bound** مثل سیستمهای یادگیری ماشین هم بهره ببری. |
|||
|
|||
این، بهعلاوه این واقعیت ساده که پایتون زبون اصلی برای **علم داده**، یادگیری ماشین و بهخصوص یادگیری عمیقه، باعث میشه FastAPI یه انتخاب خیلی خوب برای APIها و برنامههای وب علم داده / یادگیری ماشین باشه (بین خیلی چیزای دیگه). |
|||
|
|||
برای دیدن اینکه چطور توی محیط واقعی به این موازیسازی برسی، بخش [استقرار](deployment/index.md){.internal-link target=_blank} رو ببین. |
|||
|
|||
## `async` و `await` |
|||
|
|||
نسخههای مدرن پایتون یه راه خیلی ساده و قابلفهم برای تعریف کد ناهمزمان دارن. این باعث میشه مثل کد "ترتیبی" معمولی به نظر بیاد و توی لحظههای درست "انتظار" رو برات انجام بده. |
|||
|
|||
وقتی یه عملیاتی هست که قبل از دادن نتیجهها نیاز به انتظار داره و از این قابلیتهای جدید پایتون پشتیبانی میکنه، میتونی اینجوری کدنویسیش کنی: |
|||
|
|||
```Python |
|||
burgers = await get_burgers(2) |
|||
``` |
|||
|
|||
نکته کلیدی اینجا `await` هست. به پایتون میگه که باید ⏸ منتظر بمونه تا `get_burgers(2)` کارش 🕙 تموم بشه قبل از اینکه نتیجهها رو توی `burgers` ذخیره کنه. با این، پایتون میدونه که میتونه بره یه کار دیگه 🔀 ⏯ توی این مدت بکنه (مثل گرفتن یه درخواست دیگه). |
|||
|
|||
برای اینکه `await` کار کنه، باید توی یه تابع باشه که از این ناهمزمانی پشتیبانی کنه. برای این کار، فقط با `async def` تعریفش میکنی: |
|||
|
|||
```Python hl_lines="1" |
|||
async def get_burgers(number: int): |
|||
# یه سری کار ناهمزمان برای ساختن برگرها انجام بده |
|||
return burgers |
|||
``` |
|||
|
|||
...به جای `def`: |
|||
|
|||
```Python hl_lines="2" |
|||
# این ناهمزمان نیست |
|||
def get_sequential_burgers(number: int): |
|||
# یه سری کار ترتیبی برای ساختن برگرها انجام بده |
|||
return burgers |
|||
``` |
|||
|
|||
با `async def`، پایتون میدونه که توی اون تابع باید حواسش به عبارتهای `await` باشه، و میتونه اجرای اون تابع رو "موقتاً متوقف" ⏸ کنه و بره یه کار دیگه 🔀 قبل از برگشتن بکنه. |
|||
|
|||
وقتی میخوای یه تابع `async def` رو صدا کنی، باید "منتظرش" بمونی. پس این کار نمیکنه: |
|||
|
|||
```Python |
|||
# این کار نمیکنه، چون get_burgers با async def تعریف شده |
|||
burgers = get_burgers(2) |
|||
``` |
|||
|
|||
--- |
|||
|
|||
پس، اگه از یه کتابخونه استفاده میکنی که بهت میگه میتونی با `await` صداش کنی، باید *توابع عملیات مسیرت* که ازش استفاده میکنن رو با `async def` بسازی، مثل: |
|||
|
|||
```Python hl_lines="2-3" |
|||
@app.get('/burgers') |
|||
async def read_burgers(): |
|||
burgers = await get_burgers(2) |
|||
return burgers |
|||
``` |
|||
|
|||
### جزئیات فنیتر |
|||
|
|||
شاید متوجه شده باشی که `await` فقط توی توابعی که با `async def` تعریف شدن میتونه استفاده بشه. |
|||
|
|||
ولی در عین حال، توابعی که با `async def` تعریف شدن باید "منتظر"شون بمونی. پس توابع با `async def` فقط توی توابعی که با `async def` تعریف شدن میتونن صدا زده بشن. |
|||
|
|||
حالا، قضیه مرغ و تخممرغ چیه، چطور اولین تابع `async` رو صدا میکنی؟ |
|||
|
|||
اگه با **FastAPI** کار میکنی، لازم نیست نگران این باشی، چون اون "اولین" تابع، *تابع عملیات مسیرت* هست، و FastAPI میدونه چطور کار درست رو بکنه. |
|||
|
|||
ولی اگه بخوای بدون FastAPI از `async` / `await` استفاده کنی، اینم ممکنه. |
|||
|
|||
### کد ناهمزمان خودت رو بنویس |
|||
|
|||
Starlette (و **FastAPI**) بر پایه <a href="https://anyio.readthedocs.io/en/stable/" class="external-link" target="_blank">AnyIO</a> هستن، که باعث میشه با کتابخونه استاندارد پایتون <a href="https://docs.python.org/3/library/asyncio-task.html" class="external-link" target="_blank">asyncio</a> و <a href="https://trio.readthedocs.io/en/stable/" class="external-link" target="_blank">Trio</a> سازگار باشه. |
|||
|
|||
بهخصوص، میتونی مستقیماً از <a href="https://anyio.readthedocs.io/en/stable/" class="external-link" target="_blank">AnyIO</a> برای موارد استفاده پیشرفته همزمانی که نیاز به الگوهای پیچیدهتر توی کد خودت دارن استفاده کنی. |
|||
|
|||
و حتی اگه از FastAPI استفاده نکنی، میتونی برنامههای ناهمزمان خودت رو با <a href="https://anyio.readthedocs.io/en/stable/" class="external-link" target="_blank">AnyIO</a> بنویسی تا خیلی سازگار باشه و فوایدش رو بگیری (مثل *همزمانی ساختاریافته*). |
|||
|
|||
من یه کتابخونه دیگه روی AnyIO ساختم، یه لایه نازک روش، تا یه کم annotationهای نوع رو بهتر کنم و **تکمیل خودکار** بهتر، **خطاهای درونخطی** و غیره بگیرم. یه مقدمه و آموزش ساده هم داره که بهت کمک میکنه **بفهمی** و **کد ناهمزمان خودت رو بنویسی**: <a href="https://asyncer.tiangolo.com/" class="external-link" target="_blank">Asyncer</a>. اگه بخوای **کد ناهمزمان رو با کد معمولی** (بلاککننده/همزمان) ترکیب کنی خیلی بهدردت میخوره. |
|||
|
|||
### شکلهای دیگه کد ناهمزمان |
|||
|
|||
این سبک استفاده از `async` و `await` توی زبون نسبتاً جدیده. |
|||
|
|||
ولی کار با کد ناهمزمان رو خیلی سادهتر میکنه. |
|||
|
|||
همین سینتکس (یا تقریباً یکسان) اخیراً توی نسخههای مدرن جاوااسکریپت (توی مرورگر و NodeJS) هم اضافه شده. |
|||
|
|||
ولی قبل از اون، مدیریت کد ناهمزمان خیلی پیچیدهتر و سختتر بود. |
|||
|
|||
توی نسخههای قبلی پایتون، میتونستی از نخها یا <a href="https://www.gevent.org/" class="external-link" target="_blank">Gevent</a> استفاده کنی. ولی کد خیلی پیچیدهتر میشه برای فهمیدن، دیباگ کردن و فکر کردن بهش. |
|||
|
|||
توی نسخههای قبلی NodeJS / جاوااسکریپت مرورگر، از "کالبکها" استفاده میکردی. که میرسید به <a href="http://callbackhell.com/" class="external-link" target="_blank">جهان کالبکها</a>. |
|||
|
|||
## کروتینها |
|||
|
|||
**کروتین** فقط یه اصطلاح خیلی شیک برای چیزیه که یه تابع `async def` برمیگردونه. پایتون میدونه که این یه چیزی مثل تابع هست، میتونه شروع بشه و یه جایی تموم بشه، ولی ممکنه داخلش هم موقف ⏸ بشه، هر وقت یه `await` توش باشه. |
|||
|
|||
ولی همه این قابلیت استفاده از کد ناهمزمان با `async` و `await` خیلی وقتا خلاصه میشه به استفاده از "کروتینها". این قابل مقایسه با ویژگی اصلی Go، یعنی "Goroutineها" هست. |
|||
|
|||
## نتیجهگیری |
|||
|
|||
بیاید همون جمله از بالا رو ببینیم: |
|||
|
|||
> نسخههای مدرن پایتون از **"کد ناهمزمان"** با چیزی که بهش **"کروتین"** میگن پشتیبانی میکنن، با سینتکس **`async` و `await`**. |
|||
|
|||
حالا باید بیشتر برات معنی بده. ✨ |
|||
|
|||
همه اینا چیزیه که به FastAPI (از طریق Starlette) قدرت میده و باعث میشه عملکرد چشمگیری داشته باشه. |
|||
|
|||
## جزئیات خیلی فنی |
|||
|
|||
/// warning |
|||
|
|||
احتمالاً میتونی اینو رد کنی. |
|||
|
|||
اینا جزئیات خیلی فنی از نحوه کار **FastAPI** زیر پوستهست. |
|||
|
|||
اگه یه کم دانش فنی (کروتینها، نخها، بلاک کردن و غیره) داری و کنجکاوی که FastAPI چطور `async def` رو در مقابل `def` معمولی مدیریت میکنه، ادامه بده. |
|||
|
|||
/// |
|||
|
|||
### توابع عملیات مسیر |
|||
|
|||
وقتی یه *تابع عملیات مسیر* رو با `def` معمولی به جای `async def` تعریف میکنی، توی یه استخر نخ خارجی اجرا میشه که بعدش منتظرش میمونن، به جای اینکه مستقیم صداش کنن (چون سرور رو بلاک میکنه). |
|||
|
|||
اگه از یه فریمورک ناهمزمان دیگه میای که به روش بالا کار نمیکنه و عادت داری *توابع عملیات مسیر* ساده فقط محاسباتی رو با `def` معمولی برای یه سود کوچیک عملکرد (حدود 100 نانوثانیه) تعریف کنی، توجه کن که توی **FastAPI** اثرش کاملاً برعکسه. توی این موارد، بهتره از `async def` استفاده کنی مگه اینکه *توابع عملیات مسیرت* کدی داشته باشن که عملیات <abbr title="ورودی/خروجی: خوندن یا نوشتن دیسک، ارتباطات شبکه">I/O</abbr> بلاککننده انجام بده. |
|||
|
|||
با این حال، توی هر دو موقعیت، احتمالش زیاده که **FastAPI** هنوز [سریعتر](index.md#performance){.internal-link target=_blank} از فریمورک قبلیات باشه (یا حداقل قابل مقایسه باهاش). |
|||
|
|||
### وابستگیها |
|||
|
|||
همین برای [وابستگیها](tutorial/dependencies/index.md){.internal-link target=_blank} هم صدق میکنه. اگه یه وابستگی یه تابع `def` معمولی به جای `async def` باشه، توی استخر نخ خارجی اجرا میشه. |
|||
|
|||
### زیروابستگیها |
|||
|
|||
میتونی چند تا وابستگی و [زیروابستگی](tutorial/dependencies/sub-dependencies.md){.internal-link target=_blank} داشته باشی که همدیگه رو نیاز دارن (بهعنوان پارامترهای تعریف تابع)، بعضیهاشون ممکنه با `async def` ساخته بشن و بعضیها با `def` معمولی. بازم کار میکنه، و اونایی که با `def` معمولی ساخته شدن توی یه نخ خارجی (از استخر نخ) صدا زده میشن به جای اینکه "منتظرشون" بمونن. |
|||
|
|||
### توابع کاربردی دیگه |
|||
|
|||
هر تابع کاربردی دیگهای که مستقیم خودت صداش میکنی میتونه با `def` معمولی یا `async def` ساخته بشه و FastAPI رو نحوه صدازدنش تأثیر نمیذاره. |
|||
|
|||
این برخلاف توابعی هست که FastAPI برات صداشون میکنه: *توابع عملیات مسیر* و وابستگیها. |
|||
|
|||
اگه تابع کاربردیت یه تابع معمولی با `def` باشه، مستقیم صداش میکنن (همونطور که توی کدت نوشتی)، نه توی استخر نخ، اگه تابع با `async def` ساخته شده باشه، باید وقتی توی کدت صداش میکنی `await`ش کنی. |
|||
|
|||
--- |
|||
|
|||
دوباره، اینا جزئیات خیلی فنی هستن که احتمالاً اگه دنبالشون اومده باشی برات مفید باشن. |
|||
|
|||
وگرنه، با راهنماییهای بخش بالا باید خوب باشی: <a href="#in-a-hurry">عجله داری؟</a>. |
@ -0,0 +1,5 @@ |
|||
# یادگیری |
|||
|
|||
اینجا بخشهای مقدماتی و آموزشهایی هستن که برای یادگیری **FastAPI** بهت کمک میکنن. |
|||
|
|||
میتونی اینو یه **کتاب**، یه **دوره آموزشی**، یا راه **رسمی** و پیشنهادی برای یادگیری FastAPI در نظر بگیری. 😎 |
@ -0,0 +1,578 @@ |
|||
# مقدمهای بر انواع نوع در پایتون |
|||
|
|||
پایتون از "نوعنما"های اختیاری (که بهشون "type hints" یا "type annotations" هم میگن) پشتیبانی میکنه. |
|||
|
|||
این **"نوعنماها"** یا annotationها یه سینتکس خاص هستن که بهت اجازه میدن <abbr title="مثلاً: str, int, float, bool">نوع</abbr> یه متغیر رو مشخص کنی. |
|||
|
|||
با مشخص کردن نوع متغیرها، ویرایشگرها و ابزارها میتونن پشتیبانی بهتری بهت بدن. |
|||
|
|||
این فقط یه **آموزش سریع / یادآوری** در مورد نوعنماهای پایتونه. فقط حداقل چیزایی که برای استفاده ازشون با **FastAPI** لازمه رو پوشش میده... که در واقع خیلی کمه. |
|||
|
|||
**FastAPI** کاملاً بر پایه این نوعنماهاست و این بهش کلی مزیت و فایده میده. |
|||
|
|||
ولی حتی اگه هیچوقت از **FastAPI** استفاده نکنی، بازم یادگیری یه کم در موردشون به نفعته. |
|||
|
|||
/// note |
|||
|
|||
اگه حرفهای پایتونی و همهچیز رو در مورد نوعنماها میدونی، برو سراغ فصل بعدی. |
|||
|
|||
/// |
|||
|
|||
## انگیزه |
|||
|
|||
بیاید با یه مثال ساده شروع کنیم: |
|||
|
|||
{* ../../docs_src/python_types/tutorial001.py *} |
|||
|
|||
وقتی این برنامه رو اجرا کنی، خروجی اینه: |
|||
|
|||
``` |
|||
John Doe |
|||
``` |
|||
|
|||
این تابع این کارا رو میکنه: |
|||
|
|||
* یه `first_name` و `last_name` میگیره. |
|||
* حرف اول هر کدوم رو با `title()` بزرگ میکنه. |
|||
* <abbr title="اونا رو کنار هم میذاره، یکی بعد از اون یکی.">ترکیبشون</abbr> میکنه با یه فاصله وسطشون. |
|||
|
|||
{* ../../docs_src/python_types/tutorial001.py hl[2] *} |
|||
|
|||
### ویرایشش کن |
|||
|
|||
این یه برنامه خیلی سادهست. |
|||
|
|||
ولی حالا تصور کن داری از صفر مینویسیش. |
|||
|
|||
یه جایی شروع کردی به تعریف تابع، پارامترهات آمادهست... |
|||
|
|||
ولی بعد باید "اون متدی که حرف اول رو بزرگ میکنه" رو صدا کنی. |
|||
|
|||
آیا اسمش `upper` بود؟ یا `uppercase`؟ شاید `first_uppercase`؟ یا `capitalize`؟ |
|||
|
|||
بعد، با دوست قدیمی برنامهنویسا، تکمیل خودکار ویرایشگر، امتحان میکنی. |
|||
|
|||
پارامتر اول تابع، `first_name` رو تایپ میکنی، بعد یه نقطه (`.`) میذاری و `Ctrl+Space` رو میزنی تا تکمیل خودکار بیاد. |
|||
|
|||
ولی متأسفانه، چیز مفیدی نمیگیری: |
|||
|
|||
<img src="/img/python-types/image01.png"> |
|||
|
|||
### نوع اضافه کن |
|||
|
|||
بیا فقط یه خط از نسخه قبلی رو تغییر بدیم. |
|||
|
|||
دقیقاً این بخش، پارامترهای تابع رو، از: |
|||
|
|||
```Python |
|||
first_name, last_name |
|||
``` |
|||
|
|||
به: |
|||
|
|||
```Python |
|||
first_name: str, last_name: str |
|||
``` |
|||
|
|||
عوض میکنیم. |
|||
|
|||
همینه. |
|||
|
|||
اینا همون "نوعنماها" هستن: |
|||
|
|||
{* ../../docs_src/python_types/tutorial002.py hl[1] *} |
|||
|
|||
این با تعریف مقدار پیشفرض فرق داره، مثل: |
|||
|
|||
```Python |
|||
first_name="john", last_name="doe" |
|||
``` |
|||
|
|||
یه چیز متفاوته. |
|||
|
|||
ما از دونقطه (`:`) استفاده میکنیم، نه علامت مساوی (`=`). |
|||
|
|||
و اضافه کردن نوعنماها معمولاً چیزی که اتفاق میافته رو از چیزی که بدون اونا میافتاد تغییر نمیده. |
|||
|
|||
ولی حالا، دوباره تصور کن وسط ساختن اون تابع هستی، ولی این بار با نوعنماها. |
|||
|
|||
توی همون نقطه، سعی میکنی تکمیل خودکار رو با `Ctrl+Space` فعال کنی و اینو میبینی: |
|||
|
|||
<img src="/img/python-types/image02.png"> |
|||
|
|||
با این، میتونی اسکرول کنی، گزینهها رو ببینی، تا وقتی که اون چیزی که "به نظرت آشنا میاد" رو پیدا کنی: |
|||
|
|||
<img src="/img/python-types/image03.png"> |
|||
|
|||
## انگیزه بیشتر |
|||
|
|||
این تابع رو چک کن، الان نوعنما داره: |
|||
|
|||
{* ../../docs_src/python_types/tutorial003.py hl[1] *} |
|||
|
|||
چون ویرایشگر نوع متغیرها رو میدونه، فقط تکمیل خودکار نمیگیری، بلکه چک خطاها هم داری: |
|||
|
|||
<img src="/img/python-types/image04.png"> |
|||
|
|||
حالا میدونی که باید درستش کنی، `age` رو با `str(age)` به یه رشته تبدیل کنی: |
|||
|
|||
{* ../../docs_src/python_types/tutorial004.py hl[2] *} |
|||
|
|||
## تعریف نوعها |
|||
|
|||
تازه اصلیترین جا برای تعریف نوعنماها رو دیدی. بهعنوان پارامترهای تابع. |
|||
|
|||
این هم اصلیترین جاییه که با **FastAPI** ازشون استفاده میکنی. |
|||
|
|||
### نوعهای ساده |
|||
|
|||
میتونی همه نوعهای استاندارد پایتون رو تعریف کنی، نه فقط `str`. |
|||
|
|||
مثلاً میتونی از اینا استفاده کنی: |
|||
|
|||
* `int` |
|||
* `float` |
|||
* `bool` |
|||
* `bytes` |
|||
|
|||
{* ../../docs_src/python_types/tutorial005.py hl[1] *} |
|||
|
|||
### نوعهای عمومی با پارامترهای نوع |
|||
|
|||
یه سری ساختار داده هستن که میتونن مقدارهای دیگه رو نگه دارن، مثل `dict`، `list`، `set` و `tuple`. و مقدارهای داخلیشون هم میتونن نوع خودشون رو داشته باشن. |
|||
|
|||
به این نوعها که نوعهای داخلی دارن میگن "**عمومی**" یا "generic". و میشه اونا رو تعریف کرد، حتی با نوعهای داخلیشون. |
|||
|
|||
برای تعریف این نوعها و نوعهای داخلیشون، میتونی از ماژول استاندارد پایتون `typing` استفاده کنی. این ماژول مخصوص پشتیبانی از نوعنماهاست. |
|||
|
|||
#### نسخههای جدیدتر پایتون |
|||
|
|||
سینتکس با استفاده از `typing` با همه نسخهها، از پایتون 3.6 تا جدیدترینها، از جمله پایتون 3.9، 3.10 و غیره **سازگاره**. |
|||
|
|||
با پیشرفت پایتون، **نسخههای جدیدتر** پشتیبانی بهتری برای این نوعنماها دارن و توی خیلی موارد حتی لازم نیست ماژول `typing` رو وارد کنی و ازش برای تعریف نوعنماها استفاده کنی. |
|||
|
|||
اگه بتونی برای پروژهات از یه نسخه جدیدتر پایتون استفاده کنی، میتونی از این سادگی اضافه بهره ببری. |
|||
|
|||
توی همه مستندات، مثالهایی هستن که با هر نسخه پایتون سازگارن (وقتی تفاوتی هست). |
|||
|
|||
مثلاً "**Python 3.6+**" یعنی با پایتون 3.6 یا بالاتر (مثل 3.7، 3.8، 3.9، 3.10 و غیره) سازگاره. و "**Python 3.9+**" یعنی با پایتون 3.9 یا بالاتر (مثل 3.10 و غیره) سازگاره. |
|||
|
|||
اگه بتونی از **جدیدترین نسخههای پایتون** استفاده کنی، از مثالهای نسخه آخر استفاده کن، چون اونا **بهترین و سادهترین سینتکس** رو دارن، مثلاً "**Python 3.10+**". |
|||
|
|||
#### لیست |
|||
|
|||
مثلاً، بیایم یه متغیر تعریف کنیم که یه `list` از `str` باشه. |
|||
|
|||
//// tab | Python 3.9+ |
|||
|
|||
متغیر رو با همون سینتکس دونقطه (`:`) تعریف کن. |
|||
|
|||
بهعنوان نوع، `list` رو بذار. |
|||
|
|||
چون لیست یه نوعه که نوعهای داخلی داره، اونا رو توی کروشهها میذاری: |
|||
|
|||
```Python hl_lines="1" |
|||
{!> ../../docs_src/python_types/tutorial006_py39.py!} |
|||
``` |
|||
|
|||
//// |
|||
|
|||
//// tab | Python 3.8+ |
|||
|
|||
از `typing`، `List` رو (با `L` بزرگ) وارد کن: |
|||
|
|||
```Python hl_lines="1" |
|||
{!> ../../docs_src/python_types/tutorial006.py!} |
|||
``` |
|||
|
|||
متغیر رو با همون سینتکس دونقطه (`:`) تعریف کن. |
|||
|
|||
بهعنوان نوع، `List` رو که از `typing` وارد کردی بذار. |
|||
|
|||
چون لیست یه نوعه که نوعهای داخلی داره، اونا رو توی کروشهها میذاری: |
|||
|
|||
```Python hl_lines="4" |
|||
{!> ../../docs_src/python_types/tutorial006.py!} |
|||
``` |
|||
|
|||
//// |
|||
|
|||
/// info |
|||
|
|||
اون نوعهای داخلی توی کروشهها بهشون "پارامترهای نوع" میگن. |
|||
|
|||
توی این مورد، `str` پارامتر نوعیه که به `List` (یا `list` توی پایتون 3.9 و بالاتر) پاس داده شده. |
|||
|
|||
/// |
|||
|
|||
یعنی: "متغیر `items` یه `list` هست، و هر کدوم از آیتمهای این لیست یه `str` هستن". |
|||
|
|||
/// tip |
|||
|
|||
اگه از پایتون 3.9 یا بالاتر استفاده میکنی، لازم نیست `List` رو از `typing` وارد کنی، میتونی همون نوع معمولی `list` رو به جاش استفاده کنی. |
|||
|
|||
/// |
|||
|
|||
با این کار، ویرایشگرت حتی وقتی داری آیتمهای لیست رو پردازش میکنی بهت کمک میکنه: |
|||
|
|||
<img src="/img/python-types/image05.png"> |
|||
|
|||
بدون نوعها، رسیدن به این تقریباً غیرممکنه. |
|||
|
|||
توجه کن که متغیر `item` یکی از عناصر توی لیست `items` هست. |
|||
|
|||
و با این حال، ویرایشگر میدونه که یه `str` هست و براش پشتیبانی میده. |
|||
|
|||
#### تاپل و ست |
|||
|
|||
برای تعریف `tuple`ها و `set`ها هم همین کار رو میکنی: |
|||
|
|||
//// tab | Python 3.9+ |
|||
|
|||
```Python hl_lines="1" |
|||
{!> ../../docs_src/python_types/tutorial007_py39.py!} |
|||
``` |
|||
|
|||
//// |
|||
|
|||
//// tab | Python 3.8+ |
|||
|
|||
```Python hl_lines="1 4" |
|||
{!> ../../docs_src/python_types/tutorial007.py!} |
|||
``` |
|||
|
|||
//// |
|||
|
|||
یعنی: |
|||
|
|||
* متغیر `items_t` یه `tuple` با 3 تا آیتمه، یه `int`، یه `int` دیگه، و یه `str`. |
|||
* متغیر `items_s` یه `set` هست، و هر کدوم از آیتمهاش از نوع `bytes` هستن. |
|||
|
|||
#### دیکشنری |
|||
|
|||
برای تعریف یه `dict`، 2 تا پارامتر نوع میدی، که با کاما از هم جدا شدن. |
|||
|
|||
پارامتر نوع اول برای کلیدهای `dict` هست. |
|||
|
|||
پارامتر نوع دوم برای مقدارهای `dict` هست: |
|||
|
|||
//// tab | Python 3.9+ |
|||
|
|||
```Python hl_lines="1" |
|||
{!> ../../docs_src/python_types/tutorial008_py39.py!} |
|||
``` |
|||
|
|||
//// |
|||
|
|||
//// tab | Python 3.8+ |
|||
|
|||
```Python hl_lines="1 4" |
|||
{!> ../../docs_src/python_types/tutorial008.py!} |
|||
``` |
|||
|
|||
//// |
|||
|
|||
یعنی: |
|||
|
|||
* متغیر `prices` یه `dict` هست: |
|||
* کلیدهای این `dict` از نوع `str` هستن (مثلاً اسم هر آیتم). |
|||
* مقدارهای این `dict` از نوع `float` هستن (مثلاً قیمت هر آیتم). |
|||
|
|||
#### اتحادیه |
|||
|
|||
میتونی تعریف کنی که یه متغیر میتونه هر کدوم از **چند تا نوع** باشه، مثلاً یه `int` یا یه `str`. |
|||
|
|||
توی پایتون 3.6 و بالاتر (از جمله پایتون 3.10) میتونی از نوع `Union` توی `typing` استفاده کنی و نوعهای ممکن رو توی کروشهها بذاری. |
|||
|
|||
توی پایتون 3.10 یه **سینتکس جدید** هم هست که میتونی نوعهای ممکن رو با یه <abbr title="بهش 'عملگر بیتی یا' هم میگن، ولی اینجا معنیش مهم نیست">خط عمودی (`|`)</abbr> جدا کنی. |
|||
|
|||
//// tab | Python 3.10+ |
|||
|
|||
```Python hl_lines="1" |
|||
{!> ../../docs_src/python_types/tutorial008b_py310.py!} |
|||
``` |
|||
|
|||
//// |
|||
|
|||
//// tab | Python 3.8+ |
|||
|
|||
```Python hl_lines="1 4" |
|||
{!> ../../docs_src/python_types/tutorial008b.py!} |
|||
``` |
|||
|
|||
//// |
|||
|
|||
توی هر دو حالت یعنی `item` میتونه یه `int` یا یه `str` باشه. |
|||
|
|||
#### شاید `None` |
|||
|
|||
میتونی تعریف کنی که یه مقدار میتونه یه نوع باشه، مثلاً `str`، ولی میتونه `None` هم باشه. |
|||
|
|||
توی پایتون 3.6 و بالاتر (از جمله پایتون 3.10) میتونی با وارد کردن و استفاده از `Optional` از ماژول `typing` اینو تعریف کنی. |
|||
|
|||
```Python hl_lines="1 4" |
|||
{!../../docs_src/python_types/tutorial009.py!} |
|||
``` |
|||
|
|||
استفاده از `Optional[str]` به جای فقط `str` به ویرایشگر کمک میکنه خطاهایی که ممکنه فکر کنی یه مقدار همیشه `str` هست رو پیدا کنه، در حالی که میتونه `None` هم باشه. |
|||
|
|||
`Optional[Something]` در واقع میانبر برای `Union[Something, None]` هست، این دو تا معادلن. |
|||
|
|||
یعنی توی پایتون 3.10، میتونی از `Something | None` استفاده کنی: |
|||
|
|||
//// tab | Python 3.10+ |
|||
|
|||
```Python hl_lines="1" |
|||
{!> ../../docs_src/python_types/tutorial009_py310.py!} |
|||
``` |
|||
|
|||
//// |
|||
|
|||
//// tab | Python 3.8+ |
|||
|
|||
```Python hl_lines="1 4" |
|||
{!> ../../docs_src/python_types/tutorial009.py!} |
|||
``` |
|||
|
|||
//// |
|||
|
|||
//// tab | Python 3.8+ جایگزین |
|||
|
|||
```Python hl_lines="1 4" |
|||
{!> ../../docs_src/python_types/tutorial009b.py!} |
|||
``` |
|||
|
|||
//// |
|||
|
|||
#### استفاده از `Union` یا `Optional` |
|||
|
|||
اگه از نسخه پایتون زیر 3.10 استفاده میکنی، یه نکته از دید خیلی **شخصی** خودم: |
|||
|
|||
* 🚨 از `Optional[SomeType]` استفاده نکن |
|||
* به جاش ✨ **از `Union[SomeType, None]` استفاده کن** ✨. |
|||
|
|||
هر دو معادلن و زیر پوسته یکیان، ولی من `Union` رو به `Optional` ترجیح میدم چون کلمه "**اختیاری**" انگار暗示 میکنه که مقدار اختیاریه، در حالی که در واقع یعنی "میتونه `None` باشه"، حتی اگه اختیاری نباشه و هنوز لازم باشه. |
|||
|
|||
فکر میکنم `Union[SomeType, None]` واضحتر نشون میده چی معنی میده. |
|||
|
|||
فقط بحث کلمات و اسمهاست. ولی این کلمات میتونن رو طرز فکر تو و تیمت نسبت به کد تأثیر بذارن. |
|||
|
|||
بهعنوان مثال، این تابع رو ببین: |
|||
|
|||
{* ../../docs_src/python_types/tutorial009c.py hl[1,4] *} |
|||
|
|||
پارامتر `name` بهعنوان `Optional[str]` تعریف شده، ولی **اختیاری نیست**، نمیتونی تابع رو بدون پارامتر صدا کنی: |
|||
|
|||
```Python |
|||
say_hi() # اوه نه، این خطا میده! 😱 |
|||
``` |
|||
|
|||
پارامتر `name` **هنوز لازمه** (نه *اختیاری*) چون مقدار پیشفرض نداره. با این حال، `name` مقدار `None` رو قبول میکنه: |
|||
|
|||
```Python |
|||
say_hi(name=None) # این کار میکنه، None معتبره 🎉 |
|||
``` |
|||
|
|||
خبر خوب اینه که وقتی رو پایتون 3.10 باشی، لازم نیست نگران این باشی، چون میتونی بهسادگی از `|` برای تعریف اتحادیه نوعها استفاده کنی: |
|||
|
|||
{* ../../docs_src/python_types/tutorial009c_py310.py hl[1,4] *} |
|||
|
|||
اون موقع دیگه لازم نیست نگران اسمهایی مثل `Optional` و `Union` باشی. 😎 |
|||
|
|||
#### نوعهای عمومی |
|||
|
|||
این نوعهایی که پارامترهای نوع رو توی کروشهها میگیرن بهشون **نوعهای عمومی** یا **Generics** میگن، مثلاً: |
|||
|
|||
//// tab | Python 3.10+ |
|||
|
|||
میتونی از همون نوعهای داخلی بهعنوان نوعهای عمومی استفاده کنی (با کروشهها و نوعها داخلشون): |
|||
|
|||
* `list` |
|||
* `tuple` |
|||
* `set` |
|||
* `dict` |
|||
|
|||
و همونطور که توی پایتون 3.8 بود، از ماژول `typing`: |
|||
|
|||
* `Union` |
|||
* `Optional` (همونطور که توی پایتون 3.8 بود) |
|||
* ...و بقیه. |
|||
|
|||
توی پایتون 3.10، بهعنوان جایگزین برای استفاده از نوعهای عمومی `Union` و `Optional`، میتونی از <abbr title="بهش 'عملگر بیتی یا' هم میگن، ولی اینجا معنیش مهم نیست">خط عمودی (`|`)</abbr> برای تعریف اتحادیه نوعها استفاده کنی، که خیلی بهتر و سادهتره. |
|||
|
|||
//// |
|||
|
|||
//// tab | Python 3.9+ |
|||
|
|||
میتونی از همون نوعهای داخلی بهعنوان نوعهای عمومی استفاده کنی (با کروشهها و نوعها داخلشون): |
|||
|
|||
* `list` |
|||
* `tuple` |
|||
* `set` |
|||
* `dict` |
|||
|
|||
و همونطور که توی پایتون 3.8 بود، از ماژول `typing`: |
|||
|
|||
* `Union` |
|||
* `Optional` |
|||
* ...و بقیه. |
|||
|
|||
//// |
|||
|
|||
//// tab | Python 3.8+ |
|||
|
|||
* `List` |
|||
* `Tuple` |
|||
* `Set` |
|||
* `Dict` |
|||
* `Union` |
|||
* `Optional` |
|||
* ...و بقیه. |
|||
|
|||
//// |
|||
|
|||
### کلاسها بهعنوان نوع |
|||
|
|||
میتونی یه کلاس رو هم بهعنوان نوع یه متغیر تعریف کنی. |
|||
|
|||
فرض کن یه کلاس `Person` داری، با یه نام: |
|||
|
|||
{* ../../docs_src/python_types/tutorial010.py hl[1:3] *} |
|||
|
|||
بعد میتونی یه متغیر رو از نوع `Person` تعریف کنی: |
|||
|
|||
{* ../../docs_src/python_types/tutorial010.py hl[6] *} |
|||
|
|||
و بعد، دوباره، همه پشتیبانی ویرایشگر رو داری: |
|||
|
|||
<img src="/img/python-types/image06.png"> |
|||
|
|||
توجه کن که این یعنی "`one_person` یه **نمونه** از کلاس `Person` هست". |
|||
|
|||
یعنی "`one_person` خود **کلاس** به اسم `Person` نیست". |
|||
|
|||
## مدلهای Pydantic |
|||
|
|||
<a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic</a> یه کتابخونه پایتونه برای اعتبارسنجی دادهها. |
|||
|
|||
"شکل" دادهها رو بهعنوان کلاسهایی با ویژگیها تعریف میکنی. |
|||
|
|||
و هر ویژگی یه نوع داره. |
|||
|
|||
بعد یه نمونه از اون کلاس رو با یه سری مقدار میسازی و اون مقدارها رو اعتبارسنجی میکنه، به نوع مناسب تبدیلشون میکنه (اگه لازم باشه) و یه شیء با همه دادهها بهت میده. |
|||
|
|||
و با اون شیء نهایی همه پشتیبانی ویرایشگر رو میگیری. |
|||
|
|||
یه مثال از مستندات رسمی Pydantic: |
|||
|
|||
//// tab | Python 3.10+ |
|||
|
|||
```Python |
|||
{!> ../../docs_src/python_types/tutorial011_py310.py!} |
|||
``` |
|||
|
|||
//// |
|||
|
|||
//// tab | Python 3.9+ |
|||
|
|||
```Python |
|||
{!> ../../docs_src/python_types/tutorial011_py39.py!} |
|||
``` |
|||
|
|||
//// |
|||
|
|||
//// tab | Python 3.8+ |
|||
|
|||
```Python |
|||
{!> ../../docs_src/python_types/tutorial011.py!} |
|||
``` |
|||
|
|||
//// |
|||
|
|||
/// info |
|||
|
|||
برای اطلاعات بیشتر در مورد <a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic، مستنداتش رو چک کن</a>. |
|||
|
|||
/// |
|||
|
|||
**FastAPI** کاملاً بر پایه Pydantic هست. |
|||
|
|||
توی [آموزش - راهنمای کاربر](tutorial/index.md){.internal-link target=_blank} خیلی بیشتر از اینا رو توی عمل میبینی. |
|||
|
|||
/// tip |
|||
|
|||
Pydantic یه رفتار خاص داره وقتی از `Optional` یا `Union[Something, None]` بدون مقدار پیشفرض استفاده میکنی، میتونی توی مستندات Pydantic در مورد <a href="https://docs.pydantic.dev/2.3/usage/models/#required-fields" class="external-link" target="_blank">فیلدهای اختیاری لازم</a> بیشتر بخونی. |
|||
|
|||
/// |
|||
|
|||
## نوعنماها با Annotationهای متادیتا |
|||
|
|||
پایتون یه قابلیت هم داره که بهت اجازه میده **<abbr title="داده در مورد داده، اینجا یعنی اطلاعات در مورد نوع، مثلاً یه توضیح">متادیتا</abbr> اضافی** رو توی این نوعنماها بذاری با استفاده از `Annotated`. |
|||
|
|||
//// tab | Python 3.9+ |
|||
|
|||
توی پایتون 3.9، `Annotated` بخشی از کتابخونه استاندارده، پس میتونی از `typing` واردش کنی. |
|||
|
|||
```Python hl_lines="1 4" |
|||
{!> ../../docs_src/python_types/tutorial013_py39.py!} |
|||
``` |
|||
|
|||
//// |
|||
|
|||
//// tab | Python 3.8+ |
|||
|
|||
توی نسخههای زیر پایتون 3.9، `Annotated` رو از `typing_extensions` وارد میکنی. |
|||
|
|||
با **FastAPI** از قبل نصب شده. |
|||
|
|||
```Python hl_lines="1 4" |
|||
{!> ../../docs_src/python_types/tutorial013.py!} |
|||
``` |
|||
|
|||
//// |
|||
|
|||
خود پایتون با این `Annotated` کاری نمیکنه. و برای ویرایشگرها و ابزارهای دیگه، نوع هنوز `str` هست. |
|||
|
|||
ولی میتونی از این فضا توی `Annotated` استفاده کنی تا به **FastAPI** متادیتای اضافی در مورد اینکه چطور میخوای برنامهات رفتار کنه بدی. |
|||
|
|||
نکته مهم اینه که **اولین *پارامتر نوع*** که به `Annotated` میدی، **نوع واقعی** هست. بقیش فقط متادیتا برای ابزارهای دیگهست. |
|||
|
|||
الان فقط باید بدونی که `Annotated` وجود داره، و اینکه پایتون استاندارده. 😎 |
|||
|
|||
بعداً میبینی که چقدر **قوی** میتونه باشه. |
|||
|
|||
/// tip |
|||
|
|||
اینکه این **پایتون استاندارده** یعنی هنوز **بهترین تجربه توسعهدهنده** رو توی ویرایشگرت، با ابزارهایی که برای تحلیل و بازسازی کدت استفاده میکنی و غیره میگیری. ✨ |
|||
|
|||
و همینطور کدت با خیلی از ابزارها و کتابخونههای دیگه پایتون خیلی سازگار میمونه. 🚀 |
|||
|
|||
/// |
|||
|
|||
## نوعنماها توی **FastAPI** |
|||
|
|||
**FastAPI** از این نوعنماها استفاده میکنه تا چند تا کار بکنه. |
|||
|
|||
با **FastAPI** پارامترها رو با نوعنماها تعریف میکنی و اینا رو میگیری: |
|||
|
|||
* **پشتیبانی ویرایشگر**. |
|||
* **چک نوعها**. |
|||
|
|||
...و **FastAPI** از همون تعریفها برای اینا استفاده میکنه: |
|||
|
|||
* **تعریف نیازها**: از پارامترهای مسیر درخواست، پارامترهای کوئری، هدرها، بدنهها، وابستگیها و غیره. |
|||
* **تبدیل داده**: از درخواست به نوع مورد نیاز. |
|||
* **اعتبارسنجی داده**: که از هر درخواست میاد: |
|||
* تولید **خطاهای خودکار** که به کلاینت برمیگرده وقتی داده نامعتبره. |
|||
* **مستندسازی** API با استفاده از OpenAPI: |
|||
* که بعدش توسط رابطهای کاربری مستندات تعاملی خودکار استفاده میشه. |
|||
|
|||
اینا شاید همهش انتزاعی به نظر بیاد. نگران نباش. همه اینا رو توی عمل توی [آموزش - راهنمای کاربر](tutorial/index.md){.internal-link target=_blank} میبینی. |
|||
|
|||
نکته مهم اینه که با استفاده از نوعهای استاندارد پایتون، توی یه جا (به جای اضافه کردن کلاسهای بیشتر، دکوراتورها و غیره)، **FastAPI** کلی از کار رو برات انجام میده. |
|||
|
|||
/// info |
|||
|
|||
اگه همه آموزش رو گذروندی و برگشتی که بیشتر در مورد نوعها ببینی، یه منبع خوب <a href="https://mypy.readthedocs.io/en/latest/cheat_sheet_py3.html" class="external-link" target="_blank">"تقلبنامه" از `mypy`</a> هست. |
|||
|
|||
/// |
@ -1,84 +1,28 @@ |
|||
# Geração de Projetos - Modelo |
|||
|
|||
Você pode usar um gerador de projetos para começar, por já incluir configurações iniciais, segurança, banco de dados e os primeiros _endpoints_ API já feitos para você. |
|||
|
|||
Um gerador de projetos sempre terá uma pré-configuração que você pode atualizar e adaptar para suas próprias necessidades, mas pode ser um bom ponto de partida para seu projeto. |
|||
|
|||
## Full Stack FastAPI PostgreSQL |
|||
|
|||
GitHub: <a href="https://github.com/tiangolo/full-stack-fastapi-postgresql" class="external-link" target="_blank">https://github.com/tiangolo/full-stack-fastapi-postgresql</a> |
|||
|
|||
### Full Stack FastAPI PostgreSQL - Recursos |
|||
|
|||
* Integração completa **Docker**. |
|||
* Modo de implantação Docker Swarm. |
|||
* Integração e otimização **Docker Compose** para desenvolvimento local. |
|||
* **Pronto para Produção** com servidor _web_ usando Uvicorn e Gunicorn. |
|||
* _Backend_ <a href="https://github.com/fastapi/fastapi" class="external-link" target="_blank">**FastAPI**</a> Python: |
|||
* **Rápido**: Alta performance, no nível de **NodeJS** e **Go** (graças ao Starlette e Pydantic). |
|||
* **Intuitivo**: Ótimo suporte de editor. <abbr title="também conhecido como auto-complete, auto completação, IntelliSense">_Auto-Complete_</abbr> em todo lugar. Menos tempo _debugando_. |
|||
* **Fácil**: Projetado para ser fácil de usar e aprender. Menos tempo lendo documentações. |
|||
* **Curto**: Minimize duplicação de código. Múltiplos recursos para cada declaração de parâmetro. |
|||
* **Robusto**: Tenha código pronto para produção. Com documentação interativa automática. |
|||
* **Baseado em Padrões**: Baseado em (e completamente compatível com) padrões abertos para APIs: <a href="https://github.com/OAI/OpenAPI-Specification" class="external-link" target="_blank">OpenAPI</a> e <a href="http://json-schema.org/" class="external-link" target="_blank">JSON Schema</a>. |
|||
* <a href="https://fastapi.tiangolo.com/features/" class="external-link" target="_blank">**Muitos outros recursos**</a> incluindo validação automática, serialização, documentação interativa, autenticação com _tokens_ OAuth2 JWT etc. |
|||
* **Senha segura** _hashing_ por padrão. |
|||
* Autenticação **Token JWT**. |
|||
* Modelos **SQLAlchemy** (independente de extensões Flask, para que eles possam ser usados com _workers_ Celery diretamente). |
|||
* Modelos básicos para usuários (modifique e remova conforme suas necessidades). |
|||
* Migrações **Alembic**. |
|||
* **CORS** (_Cross Origin Resource Sharing_ - Compartilhamento de Recursos Entre Origens). |
|||
* _Worker_ **Celery** que pode importar e usar modelos e códigos do resto do _backend_ seletivamente. |
|||
* Testes _backend_ _REST_ baseados no **Pytest**, integrados com Docker, então você pode testar a interação completa da API, independente do banco de dados. Como roda no Docker, ele pode construir um novo repositório de dados do zero toda vez (assim você pode usar ElasticSearch, MongoDB, CouchDB, ou o que quiser, e apenas testar que a API esteja funcionando). |
|||
* Fácil integração com Python através dos **Kernels Jupyter** para desenvolvimento remoto ou no Docker com extensões como Atom Hydrogen ou Visual Studio Code Jupyter. |
|||
* _Frontend_ **Vue**: |
|||
* Gerado com Vue CLI. |
|||
* Controle de **Autenticação JWT**. |
|||
* Visualização de _login_. |
|||
* Após o _login_, visualização do painel de controle principal. |
|||
* Painel de controle principal com criação e edição de usuário. |
|||
* Edição do próprio usuário. |
|||
* **Vuex**. |
|||
* **Vue-router**. |
|||
* **Vuetify** para belos componentes _material design_. |
|||
* **TypeScript**. |
|||
* Servidor Docker baseado em **Nginx** (configurado para rodar "lindamente" com Vue-router). |
|||
* Construção multi-estágio Docker, então você não precisa salvar ou _commitar_ código compilado. |
|||
* Testes _frontend_ rodados na hora da construção (pode ser desabilitado também). |
|||
* Feito tão modular quanto possível, então ele funciona fora da caixa, mas você pode gerar novamente com Vue CLI ou criar conforme você queira, e reutilizar o que quiser. |
|||
* **PGAdmin** para banco de dados PostgreSQL, você pode modificar para usar PHPMyAdmin e MySQL facilmente. |
|||
* **Flower** para monitoração de tarefas Celery. |
|||
* Balanceamento de carga entre _frontend_ e _backend_ com **Traefik**, então você pode ter ambos sob o mesmo domínio, separados por rota, mas servidos por diferentes containers. |
|||
* Integração Traefik, incluindo geração automática de certificados **HTTPS** Let's Encrypt. |
|||
* GitLab **CI** (integração contínua), incluindo testes _frontend_ e _backend_. |
|||
|
|||
## Full Stack FastAPI Couchbase |
|||
|
|||
GitHub: <a href="https://github.com/tiangolo/full-stack-fastapi-couchbase" class="external-link" target="_blank">https://github.com/tiangolo/full-stack-fastapi-couchbase</a> |
|||
|
|||
⚠️ **WARNING** ⚠️ |
|||
|
|||
Se você está iniciando um novo projeto do zero, verifique as alternativas aqui. |
|||
|
|||
Por exemplo, o gerador de projetos <a href="https://github.com/tiangolo/full-stack-fastapi-postgresql" class="external-link" target="_blank">Full Stack FastAPI PostgreSQL</a> pode ser uma alternativa melhor, como ele é ativamente mantido e utilizado. E ele inclui todos os novos recursos e melhorias. |
|||
|
|||
Você ainda é livre para utilizar o gerador baseado em Couchbase se quiser, ele provavelmente ainda funciona bem, e você já tem um projeto gerado com ele que roda bem também (e você provavelmente já atualizou ele para encaixar nas suas necessidades). |
|||
|
|||
Você pode ler mais sobre nas documentaçãoes do repositório. |
|||
|
|||
## Full Stack FastAPI MongoDB |
|||
|
|||
...pode demorar, dependendo do meu tempo disponível e outros fatores. 😅 🎉 |
|||
|
|||
## Modelos de Aprendizado de Máquina com spaCy e FastAPI |
|||
|
|||
GitHub: <a href="https://github.com/microsoft/cookiecutter-spacy-fastapi" class="external-link" target="_blank">https://github.com/microsoft/cookiecutter-spacy-fastapi</a> |
|||
|
|||
### Modelos de Aprendizado de Máquina com spaCy e FastAPI - Recursos |
|||
|
|||
* Integração com modelo NER **spaCy**. |
|||
* Formato de requisição **Busca Cognitiva Azure** acoplado. |
|||
* Servidor Python _web_ **Pronto para Produção** usando Uvicorn e Gunicorn. |
|||
* Implantação **Azure DevOps** Kubernetes (AKS) CI/CD acoplada. |
|||
* **Multilingual** facilmente escolhido como uma das linguagens spaCy acopladas durante a configuração do projeto. |
|||
* **Facilmente extensível** para outros modelos de _frameworks_ (Pytorch, Tensorflow), não apenas spaCy. |
|||
# Full Stack FastAPI Template |
|||
|
|||
_Templates_, embora tipicamente venham com alguma configuração específica, são desenhados para serem flexíveis e customizáveis. Isso permite que você os modifique e adapte para as especificações do seu projeto, fazendo-os um excelente ponto de partida. 🏁 |
|||
|
|||
Você pode usar esse _template_ para começar, já que ele inclui várias configurações iniciais, segurança, banco de dados, e alguns _endpoints_ de API já feitos para você. |
|||
|
|||
Repositório GitHub: <a href="https://github.com/tiangolo/full-stack-fastapi-template" class="external-link" target="_blank">Full Stack FastAPI Template</a> |
|||
|
|||
## Full Stack FastAPI Template - Pilha de Tecnologias e Recursos |
|||
|
|||
- ⚡ [**FastAPI**](https://fastapi.tiangolo.com) para a API do backend em Python. |
|||
- 🧰 [SQLModel](https://sqlmodel.tiangolo.com) para as interações do Python com bancos de dados SQL (ORM). |
|||
- 🔍 [Pydantic](https://docs.pydantic.dev), usado pelo FastAPI, para validação de dados e gerenciamento de configurações. |
|||
- 💾 [PostgreSQL](https://www.postgresql.org) como banco de dados SQL. |
|||
- 🚀 [React](https://react.dev) para o frontend. |
|||
- 💃 Usando TypeScript, hooks, [Vite](https://vitejs.dev), e outras partes de uma _stack_ frontend moderna. |
|||
- 🎨 [Chakra UI](https://chakra-ui.com) para os componentes de frontend. |
|||
- 🤖 Um cliente frontend automaticamente gerado. |
|||
- 🧪 [Playwright](https://playwright.dev) para testes Ponta-a-Ponta. |
|||
- 🦇 Suporte para modo escuro. |
|||
- 🐋 [Docker Compose](https://www.docker.com) para desenvolvimento e produção. |
|||
- 🔒 _Hash_ seguro de senhas por padrão. |
|||
- 🔑 Autenticação por token JWT. |
|||
- 📫 Recuperação de senhas baseada em email. |
|||
- ✅ Testes com [Pytest](https://pytest.org). |
|||
- 📞 [Traefik](https://traefik.io) como proxy reverso / balanceador de carga. |
|||
- 🚢 Instruções de _deployment_ usando Docker Compose, incluindo como configurar um proxy frontend com Traefik para gerenciar automaticamente certificados HTTPS. |
|||
- 🏭 CI (Integração Contínua) e CD (_Deploy_ Contínuo) baseado em GitHub Actions. |
|||
|
@ -0,0 +1,41 @@ |
|||
# Дополнительные статус коды |
|||
|
|||
По умолчанию **FastAPI** возвращает ответы, используя `JSONResponse`, помещая содержимое, которое вы возвращаете из вашей *операции пути*, внутрь этого `JSONResponse`. |
|||
|
|||
Он будет использовать код статуса по умолчанию или тот, который вы укажете в вашей *операции пути*. |
|||
|
|||
## Дополнительные статус коды |
|||
|
|||
Если вы хотите возвращать дополнительный статус код помимо основного, вы можете сделать это, возвращая объект `Response` напрямую, как `JSONResponse`, и устанавливая нужный статус код напрямую. |
|||
|
|||
Например, скажем, вы хотите создать *операцию пути*, которая позволяет обновлять элементы и возвращает HTTP-код 200 "OK" при успешном выполнении. |
|||
|
|||
Но вы также хотите, чтобы она принимала новые элементы. И если элемент ранее не существовал, он создаётся, и возвращался HTTP-код 201 "Created". |
|||
|
|||
Чтобы реализовать это, импортируйте `JSONResponse` и возвращайте ваш контент напрямую, устанавливая нужный `status_code`: |
|||
|
|||
{* ../../docs_src/additional_status_codes/tutorial001_an_py310.py hl[4,25] *} |
|||
|
|||
/// warning | Внимание |
|||
|
|||
Когда вы возвращаете объект `Response` напрямую, как в примере выше, он будет возвращён как есть. |
|||
|
|||
Он не будет сериализован при помощи модели и т.д. |
|||
|
|||
Убедитесь, что в нём содержатся именно те данные, которые вы хотите, и что значения являются валидным JSON (если вы используете `JSONResponse`). |
|||
|
|||
/// |
|||
|
|||
/// note | Технические детали |
|||
|
|||
Вы также можете использовать `from starlette.responses import JSONResponse`. |
|||
|
|||
**FastAPI** предоставляет тот же `starlette.responses` через `fastapi.responses` просто для вашего удобства, как разработчика. Но большинство доступных Response-классов поступают напрямую из Starlette. То же самое касается и `status`. |
|||
|
|||
/// |
|||
|
|||
## OpenAPI и документация API |
|||
|
|||
Если вы возвращаете дополнительные коды статусов и ответы напрямую, они не будут включены в схему OpenAPI (документацию API), потому что FastAPI не может заранее знать, что вы собираетесь вернуть. |
|||
|
|||
Но вы можете задокументировать это в вашем коде, используя: [Дополнительные ответы в OpenAPI](additional-responses.md){.internal-link target=_blank}. |
@ -0,0 +1,21 @@ |
|||
# Расширенное руководство пользователя |
|||
|
|||
## Дополнительные возможности |
|||
|
|||
Основное [Учебник - Руководство пользователя](../tutorial/index.md){.internal-link target=_blank} должно быть достаточно, чтобы познакомить вас со всеми основными функциями **FastAPI**. |
|||
|
|||
В следующих разделах вы увидите другие варианты, конфигурации и дополнительные возможности. |
|||
|
|||
/// tip |
|||
|
|||
Следующие разделы **не обязательно являются "продвинутыми"**. |
|||
|
|||
И вполне возможно, что для вашего случая использования решение находится в одном из них. |
|||
|
|||
/// |
|||
|
|||
## Сначала прочитайте Учебник - Руководство пользователя |
|||
|
|||
Вы все еще можете использовать большинство функций **FastAPI** со знаниями из [Учебник - Руководство пользователя](../tutorial/index.md){.internal-link target=_blank}. |
|||
|
|||
И следующие разделы предполагают, что вы уже прочитали его, и предполагают, что вы знаете эти основные идеи. |
@ -0,0 +1,31 @@ |
|||
# Response - Изменение cтатус кода |
|||
|
|||
Вы, вероятно, уже читали о том, что можно установить [Состояние ответа по умолчанию](../tutorial/response-status-code.md){.internal-link target=_blank}. |
|||
|
|||
Но в некоторых случаях вам нужно вернуть код состояния, отличный от установленного по умолчанию. |
|||
|
|||
## Пример использования |
|||
|
|||
Например, представьте, что вы хотите возвращать HTTP код состояния "OK" `200` по умолчанию. |
|||
|
|||
Но если данные не существовали, вы хотите создать их и вернуть HTTP код состояния "CREATED" `201`. |
|||
|
|||
При этом вы всё ещё хотите иметь возможность фильтровать и преобразовывать возвращаемые данные с помощью `response_model`. |
|||
|
|||
Для таких случаев вы можете использовать параметр `Response`. |
|||
|
|||
## Использование параметра `Response` |
|||
|
|||
Вы можете объявить параметр типа `Response` в вашей *функции обработки пути* (так же как для cookies и headers). |
|||
|
|||
И затем вы можете установить `status_code` в этом *временном* объекте ответа. |
|||
|
|||
{* ../../docs_src/response_change_status_code/tutorial001.py hl[1,9,12] *} |
|||
|
|||
После этого вы можете вернуть любой объект, который вам нужен, как обычно (`dict`, модель базы данных и т.д.). |
|||
|
|||
И если вы объявили `response_model`, он всё равно будет использоваться для фильтрации и преобразования возвращаемого объекта. |
|||
|
|||
**FastAPI** будет использовать этот *временный* ответ для извлечения кода состояния (а также cookies и headers) и поместит их в финальный ответ, который содержит возвращаемое вами значение, отфильтрованное любым `response_model`. |
|||
|
|||
Вы также можете объявить параметр `Response` в зависимостях и установить код состояния в них. Но помните, что последнее установленное значение будет иметь приоритет. |
@ -0,0 +1,65 @@ |
|||
# Возврат ответа напрямую |
|||
|
|||
Когда вы создаёте **FastAPI** *операцию пути*, вы можете возвращать из неё любые данные: `dict`, `list`, Pydantic-модель, модель базы данных и т.д. |
|||
|
|||
По умолчанию **FastAPI** автоматически преобразует возвращаемое значение в JSON с помощью `jsonable_encoder`, как описано в [JSON кодировщик](../tutorial/encoder.md){.internal-link target=_blank}. |
|||
|
|||
Затем "под капотом" эти данные, совместимые с JSON (например `dict`), помещаются в `JSONResponse`, который используется для отправки ответа клиенту. |
|||
|
|||
Но вы можете возвращать `JSONResponse` напрямую из ваших *операций пути*. |
|||
|
|||
Это может быть полезно, например, если нужно вернуть пользовательские заголовки или куки. |
|||
|
|||
## Возврат `Response` |
|||
|
|||
На самом деле, вы можете возвращать любой объект `Response` или его подкласс. |
|||
|
|||
/// tip | Подсказка |
|||
|
|||
`JSONResponse` сам по себе является подклассом `Response`. |
|||
|
|||
/// |
|||
|
|||
И когда вы возвращаете `Response`, **FastAPI** передаст его напрямую. |
|||
|
|||
Это не приведет к преобразованию данных с помощью Pydantic-моделей, содержимое не будет преобразовано в какой-либо тип и т.д. |
|||
|
|||
Это даёт вам большую гибкость. Вы можете возвращать любые типы данных, переопределять любые объявления или валидацию данных и т.д. |
|||
|
|||
## Использование `jsonable_encoder` в `Response` |
|||
|
|||
Поскольку **FastAPI** не изменяет объект `Response`, который вы возвращаете, вы должны убедиться, что его содержимое готово к отправке. |
|||
|
|||
Например, вы не можете поместить Pydantic-модель в `JSONResponse`, не преобразовав её сначала в `dict` с помощью преобразования всех типов данных (таких как `datetime`, `UUID` и т.д.) в совместимые с JSON типы. |
|||
|
|||
В таких случаях вы можете использовать `jsonable_encoder` для преобразования данных перед передачей их в ответ: |
|||
|
|||
{* ../../docs_src/response_directly/tutorial001.py hl[6:7,21:22] *} |
|||
|
|||
/// note | Технические детали |
|||
|
|||
Вы также можете использовать `from starlette.responses import JSONResponse`. |
|||
|
|||
**FastAPI** предоставляет `starlette.responses` через `fastapi.responses` просто для вашего удобства, как разработчика. Но большинство доступных Response-классов поступают напрямую из Starlette. |
|||
|
|||
/// |
|||
|
|||
## Возврат пользовательского `Response` |
|||
|
|||
Пример выше показывает все необходимые части, но он пока не очень полезен, так как вы могли бы просто вернуть `item` напрямую, и **FastAPI** поместил бы его в `JSONResponse`, преобразовав в `dict` и т.д. Всё это происходит по умолчанию. |
|||
|
|||
Теперь давайте посмотрим, как можно использовать это для возврата пользовательского ответа. |
|||
|
|||
Допустим, вы хотите вернуть ответ в формате <a href="https://en.wikipedia.org/wiki/XML" class="external-link" target="_blank">XML</a>. |
|||
|
|||
Вы можете поместить ваш XML-контент в строку, поместить её в `Response` и вернуть: |
|||
|
|||
{* ../../docs_src/response_directly/tutorial002.py hl[1,18] *} |
|||
|
|||
## Примечания |
|||
|
|||
Когда вы возвращаете объект `Response` напрямую, его данные не валидируются, не преобразуются (не сериализуются) и не документируются автоматически. |
|||
|
|||
Но вы всё равно можете задокументировать это, как описано в [Дополнительные ответы в OpenAPI](additional-responses.md){.internal-link target=_blank}. |
|||
|
|||
В следующих разделах вы увидите, как использовать/объявлять такие кастомные `Response`, при этом сохраняя автоматическое преобразование данных, документацию и т.д. |
@ -0,0 +1,116 @@ |
|||
# Тіло – Оновлення |
|||
|
|||
## Оновлення з використанням `PUT` |
|||
|
|||
Щоб оновити елемент, Ви можете використати <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PUT" class="external-link" target="_blank">HTTP `PUT`</a> операцію. |
|||
|
|||
Ви можете використати `jsonable_encoder`, щоб перетворити вхідні дані на такі, які можна зберігати як JSON (наприклад, у NoSQL базі даних). Наприклад, перетворюючи `datetime` у `str`. |
|||
|
|||
{* ../../docs_src/body_updates/tutorial001_py310.py hl[28:33] *} |
|||
|
|||
`PUT` використовується для отримання даних, які мають замінити чинні дані. |
|||
|
|||
### Попередження про заміну |
|||
|
|||
Це означає, що якщо Ви хочете оновити елемент `bar`, використовуючи `PUT` з тілом: |
|||
|
|||
```Python |
|||
{ |
|||
"name": "Barz", |
|||
"price": 3, |
|||
"description": None, |
|||
} |
|||
``` |
|||
|
|||
оскільки він не містить вже збереженого атрибута `"tax": 20.2`, модель введення прийме значення за замовчуванням `"tax": 10.5`. |
|||
|
|||
І дані будуть збережені з цим "новим" значенням `tax` = `10.5`. |
|||
|
|||
## Часткові оновлення з `PATCH` |
|||
|
|||
Ви також можете використовувати операцію <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PATCH" class="external-link" target="_blank">HTTP `PATCH`</a> для *часткового* оновлення даних. |
|||
|
|||
Це означає, що Ви можете надіслати лише ті дані, які хочете оновити, залишаючи інші без змін. |
|||
|
|||
/// note | Примітка |
|||
|
|||
`PATCH` менш відомий і рідше використовується, ніж `PUT`. |
|||
|
|||
І багато команд використовують лише `PUT`, навіть для часткових оновлень. |
|||
|
|||
Ви **вільні** використовувати їх так, як хочете, **FastAPI** не накладає обмежень. |
|||
|
|||
Але цей посібник показує Вам більш-менш як їх задумано використовувати. |
|||
|
|||
/// |
|||
|
|||
### Використання параметра `exclude_unset` у Pydantic |
|||
|
|||
Якщо Ви хочете отримати часткові оновлення, дуже зручно використовувати параметр `exclude_unset` у методі `.model_dump()` моделі Pydantic. |
|||
|
|||
Наприклад: `item.model_dump(exclude_unset=True)`. |
|||
|
|||
/// info | Інформація |
|||
|
|||
У Pydantic v1 цей метод називався `.dict()`, він був застарілий (але все ще підтримується) у Pydantic v2, і був перейменований у `.model_dump()`. |
|||
|
|||
Приклади тут використовують `.dict()` для сумісності з Pydantic v1, але Вам слід використовувати `.model_dump()`, якщо можете використовувати Pydantic v2. |
|||
|
|||
/// |
|||
|
|||
Це створить `dict` лише з тими даними, які були явно встановлені під час створення моделі `item`, виключаючи значення за замовчуванням. |
|||
|
|||
Тоді Ви можете використовувати це, щоб створити `dict` лише з даними, які були встановлені (надіслані у запиті), пропускаючи значення за замовчуванням: |
|||
|
|||
{* ../../docs_src/body_updates/tutorial002_py310.py hl[32] *} |
|||
|
|||
### Використання параметра `update` у Pydantic |
|||
|
|||
Тепер Ви можете створити копію наявної моделі за допомогою `.model_copy()`, і передати параметр `update` з `dict` , який містить дані для оновлення. |
|||
|
|||
/// info | Інформація |
|||
|
|||
У Pydantic v1 метод називався `.copy()`, він був застарілий (але все ще підтримується) у Pydantic v2, і був перейменований у `.model_copy()`. |
|||
|
|||
Приклади тут використовують `.copy()` для сумісності з Pydantic v1, але якщо Ви можете використовувати Pydantic v2 — Вам слід використовувати `.model_copy()` замість цього. |
|||
|
|||
/// |
|||
|
|||
Наприклад: `stored_item_model.model_copy(update=update_data)`: |
|||
|
|||
{* ../../docs_src/body_updates/tutorial002_py310.py hl[33] *} |
|||
|
|||
### Підсумок часткових оновлень |
|||
|
|||
У підсумку, щоб застосувати часткові оновлення, Ви: |
|||
|
|||
* (Опціонально) використовуєте `PATCH` замість `PUT`. |
|||
* Отримуєте збережені дані. |
|||
* Поміщаєте ці дані в модель Pydantic. |
|||
* Генеруєте `dict` без значень за замовчуванням з моделі введення (використовуючи `exclude_unset`). |
|||
* Таким чином Ви оновите лише ті значення, які були явно задані користувачем, замість того, щоб перезаписувати вже збережені значення значеннями за замовчуванням з вашої моделі. |
|||
* Створюєте копію збереженої моделі, оновлюючи її атрибути отриманими частковими оновленнями (використовуючи параметр `update`). |
|||
* Перетворюєте скопійовану модель на щось, що можна зберегти у вашу БД (наприклад, використовуючи `jsonable_encoder`). |
|||
* Це можна порівняти з повторним використанням методу `.model_dump()` моделі, але це гарантує (і перетворює) значення у типи даних, які можна перетворити на JSON, наприклад, `datetime` на `str`. |
|||
* Зберігаєте дані у вашу БД. |
|||
* Повертаєте оновлену модель. |
|||
|
|||
{* ../../docs_src/body_updates/tutorial002_py310.py hl[28:35] *} |
|||
|
|||
/// tip | Порада |
|||
|
|||
Насправді Ви можете використовувати цю саму техніку і з операцією HTTP `PUT`. |
|||
|
|||
Але приклад тут використовує `PATCH`, тому що він був створений саме для таких випадків. |
|||
|
|||
/// |
|||
|
|||
/// note | Примітка |
|||
|
|||
Зверніть увагу, що модель запиту все ще проходить валідацію. |
|||
|
|||
Тож, якщо Ви хочете отримувати часткові оновлення, які можуть не містити жодного атрибута, Вам потрібно мати модель, де всі атрибути позначені як необов’язкові (зі значеннями за замовчуванням або `None`). |
|||
|
|||
Щоб розрізняти моделі з усіма необов’язковими значеннями для **оновлення** і моделі з обов’язковими значеннями для **створення**, Ви можете скористатись ідеями, описаними у [Додаткові моделі](extra-models.md){.internal-link target=_blank}. |
|||
|
|||
/// |
@ -0,0 +1,358 @@ |
|||
# Модель відповіді — Тип, що повертається |
|||
|
|||
Ви можете оголосити тип, який використовуватиметься у відповіді, за допомогою *анотації типу, що повертається* *функцією операцією шляху* (path operation) |
|||
|
|||
**Анотацію типу** можна вказати так само як і для вхідних **параметрів** функції: це можуть бути моделі Pydantic, списки (lists), словники (dictionaries), скалярні значення, як-от цілі числа (integers), булеві значення (booleans) тощо. |
|||
|
|||
{* ../../docs_src/response_model/tutorial001_01_py310.py hl[16,21] *} |
|||
|
|||
FastAPI використовуватиме цей тип, щоб: |
|||
|
|||
* **Перевірити правильність** повернених даних. |
|||
* Якщо дані не валідні (наприклад, відсутнє поле), це означає, що Ваш код додатку працює некоректно і не повертає те, що повинен. У такому випадку FastAPI поверне помилку сервера, замість того щоб віддати недопустимі дані. Так Ви та Ваші клієнти будете впевнені, що отримуєте очікувані дані у правильному форматі. |
|||
|
|||
* Додати **JSON Schema** відповіді до специфікації OpenAPI в *операціях шляху*. |
|||
* Це буде використано в **автоматичній документації**. |
|||
* А також інструментами, які автоматично генерують клієнтський код. |
|||
|
|||
Але найголовніше: |
|||
|
|||
* FastAPI **обмежить та відфільтрує** вихідні дані відповідно до типу, вказаного у відповіді. |
|||
* Це особливо важливо для **безпеки**. Деталі нижче. |
|||
|
|||
## Параметр `response_model` |
|||
|
|||
Іноді Вам потрібно або зручно повертати інші типи даних, ніж ті, що зазначені як тип відповіді. |
|||
|
|||
Наприклад, Ви можете **повертати словник** або об’єкт бази даних, але **оголосити модель Pydantic** як модель відповіді. Тоді модель Pydantic автоматично оброблятиме валідацію, документацію тощо. |
|||
|
|||
Якщо Ви додасте анотацію типу для повернення, редактор коду або mypy можуть поскаржитися, що функція повертає інший тип (наприклад, dict замість Item). |
|||
|
|||
У таких випадках можна скористатися параметром `response_model` в декораторі маршруту (наприклад, @app.get()). |
|||
|
|||
Параметр `response_model` працює з будь-яким *оператором шляху*: |
|||
|
|||
* `@app.get()` |
|||
* `@app.post()` |
|||
* `@app.put()` |
|||
* `@app.delete()` |
|||
* тощо. |
|||
|
|||
{* ../../docs_src/response_model/tutorial001_py310.py hl[17,22,24:27] *} |
|||
|
|||
/// note | Примітка |
|||
|
|||
Зверніть увагу, що `response_model` є параметром методу-декоратора (`get`, `post`, тощо), а не *функцією операцією шляху* (path operation function), як це робиться з параметрами або тілом запиту. |
|||
|
|||
/// |
|||
|
|||
`response_model` приймає такий самий тип, який Ви б вказали для поля моделі Pydantic. Тобто це може бути як Pydantic-модель, так і, наприклад, `list` із моделей Pydantic — `List[Item]`. |
|||
|
|||
FastAPI використовуватиме `response_model` для створення документації, валідації даних та — найважливіше — **перетворення та фільтрації вихідних даних** згідно з оголошеним типом. |
|||
|
|||
/// tip | Порада |
|||
|
|||
Якщо у Вас увімкнено сувору перевірку типів у редакторі, mypy тощо, Ви можете оголосити тип повернення функції як `Any`. |
|||
|
|||
Таким чином, Ви повідомляєте редактору, що свідомо повертаєте будь-що. Але FastAPI усе одно виконуватиме створення документації, валідацію, фільтрацію тощо за допомогою параметра `response_model`. |
|||
|
|||
/// |
|||
|
|||
### Пріоритет `response_model` |
|||
|
|||
Якщо Ви вказуєте і тип повернення, і `response_model`, то FastAPI використовуватиме `response_model` з пріоритетом. |
|||
|
|||
Таким чином, Ви можете додати правильні анотації типів до ваших функцій, навіть якщо вони повертають тип, відмінний від `response_model`. Це буде корисно для редакторів коду та інструментів, таких як mypy. І при цьому FastAPI продовжить виконувати валідацію даних, генерувати документацію тощо на основі `response_model`. |
|||
|
|||
Ви також можете використати `response_model=None`, щоб вимкнути створення моделі відповіді для цієї *операції шляху*. Це може знадобитися, якщо Ви додаєте анотації типів до об'єктів, які не є допустимими полями Pydantic — приклад цього Ви побачите в одному з наступних розділів. |
|||
|
|||
## Повернути ті самі вхідні дані |
|||
|
|||
Тут ми оголошуємо модель `UserIn`, яка містить звичайний текстовий пароль: |
|||
|
|||
{* ../../docs_src/response_model/tutorial002_py310.py hl[7,9] *} |
|||
|
|||
/// info | Інформація |
|||
|
|||
Щоб використовувати `EmailStr`, спочатку встановіть <a href="https://github.com/JoshData/python-email-validator" class="external-link" target="_blank">`email-validator`</a>. |
|||
|
|||
Переконайтесь, що Ви створили [віртуальне середовище](../virtual-environments.md){.internal-link target=_blank}, активували його, а потім встановили пакет, наприклад: |
|||
|
|||
```console |
|||
$ pip install email-validator |
|||
``` |
|||
|
|||
or with: |
|||
|
|||
```console |
|||
$ pip install "pydantic[email]" |
|||
``` |
|||
|
|||
/// |
|||
|
|||
І ми використовуємо цю модель, щоб оголосити і вхідні, і вихідні дані: |
|||
|
|||
{* ../../docs_src/response_model/tutorial002_py310.py hl[16] *} |
|||
|
|||
Тепер, коли браузер створює користувача з паролем, API поверне той самий пароль у відповіді. |
|||
|
|||
У цьому випадку це може не бути проблемою, адже саме користувач надіслав пароль. |
|||
|
|||
Але якщо ми використаємо цю ж модель для іншої операції шляху, ми можемо випадково надіслати паролі наших користувачів кожному клієнту. |
|||
|
|||
/// danger | Обережно |
|||
|
|||
Ніколи не зберігайте пароль користувача у відкритому вигляді та не надсилайте його у відповіді, якщо тільки Ви не знаєте всі ризики і точно розумієте, що робите. |
|||
|
|||
/// |
|||
|
|||
## Додайте окрему вихідну модель |
|||
|
|||
Замість цього ми можемо створити вхідну модель з відкритим паролем і вихідну модель без нього: |
|||
|
|||
{* ../../docs_src/response_model/tutorial003_py310.py hl[9,11,16] *} |
|||
|
|||
Тут, навіть якщо *функція операції шляху* повертає об'єкт користувача, який містить пароль: |
|||
|
|||
{* ../../docs_src/response_model/tutorial003_py310.py hl[24] *} |
|||
|
|||
...ми оголосили `response_model` як нашу модель `UserOut`, яка не містить пароля: |
|||
|
|||
{* ../../docs_src/response_model/tutorial003_py310.py hl[22] *} |
|||
|
|||
Таким чином, **FastAPI** автоматично відфільтрує всі дані, які не вказані у вихідній моделі (за допомогою Pydantic). |
|||
|
|||
### `response_model` або тип повернення |
|||
|
|||
У цьому випадку, оскільки дві моделі різні, якщо ми анотуємо тип повернення функції як `UserOut`, редактор і такі інструменти, як mypy, видадуть помилку, бо фактично ми повертаємо інший тип. |
|||
|
|||
Тому в цьому прикладі ми використовуємо параметр `response_model`, а не анотацію типу повернення. |
|||
|
|||
...але читайте далі, щоб дізнатися, як обійти це обмеження. |
|||
|
|||
## Тип повернення і фільтрація даних |
|||
|
|||
Продовжимо з попереднього прикладу. Ми хотіли **анотувати функцію одним типом**, але при цьому повертати з неї більше даних. |
|||
|
|||
Ми хочемо, щоб FastAPI продовжував **фільтрувати** ці дані за допомогою response_model. Тобто навіть якщо функція повертає більше інформації, у відповіді будуть лише ті поля, які вказані у response_model. |
|||
|
|||
У попередньому прикладі, оскільки класи були різні, нам довелося використовувати параметр `response_model`. Але це означає, що ми не отримуємо підтримки з боку редактора коду та інструментів перевірки типів щодо типу, який повертає функція. |
|||
|
|||
Проте в більшості випадків, коли нам потрібно зробити щось подібне, ми просто хочемо, щоб модель **відфільтрувала або прибрала** частину даних, як у цьому прикладі. |
|||
|
|||
У таких випадках ми можемо використати класи та спадкування, щоб скористатися **анотаціями типів** функцій — це дає кращу підтримку з боку редактора та інструментів типу mypy, і при цьому FastAPI продовжує виконувати **фільтрацію даних** у відповіді. |
|||
|
|||
{* ../../docs_src/response_model/tutorial003_01_py310.py hl[7:10,13:14,18] *} |
|||
|
|||
Завдяки цьому ми отримуємо підтримку інструментів — від редакторів і mypy, оскільки цей код є коректним з точки зору типів, — але ми також отримуємо фільтрацію даних від FastAPI. |
|||
|
|||
Як це працює? Давайте розберемося. 🤓 |
|||
|
|||
### Типи та підтримка інструментів |
|||
|
|||
Спершу подивимось, як це бачать редактори, mypy та інші інструменти. |
|||
|
|||
`BaseUser` має базові поля. Потім `UserIn` успадковує `BaseUser` і додає поле `password`, отже, він матиме всі поля з обох моделей. |
|||
|
|||
Ми зазначаємо тип повернення функції як `BaseUser`, але фактично повертаємо екземпляр `UserIn`. |
|||
|
|||
Редактор, mypy та інші інструменти не скаржитимуться на це, тому що з точки зору типізації `UserIn` є підкласом `BaseUser`, а це означає, що він є `валідним` типом, коли очікується будь-що, що є `BaseUser`. |
|||
|
|||
### Фільтрація даних у FastAPI |
|||
|
|||
Тепер для FastAPI він бачить тип повернення і переконується, що те, що Ви повертаєте, містить **тільки** поля, які оголошені у цьому типі. |
|||
|
|||
FastAPI виконує кілька внутрішніх операцій з Pydantic, щоб гарантувати, що правила наслідування класів не застосовуються для фільтрації повернених даних, інакше Ви могли б повернути значно більше даних, ніж очікували. |
|||
|
|||
Таким чином, Ви отримуєте найкраще з двох світів: анотації типів **з підтримкою інструментів** і **фільтрацію даних**. |
|||
|
|||
## Подивитись у документації |
|||
|
|||
Коли Ви дивитесь автоматичну документацію, Ви можете побачити, що вхідна модель і вихідна модель мають власну JSON-схему: |
|||
|
|||
<img src="/img/tutorial/response-model/image01.png"> |
|||
|
|||
І обидві моделі використовуються для інтерактивної API-документації: |
|||
|
|||
<img src="/img/tutorial/response-model/image02.png"> |
|||
|
|||
## Інші анотації типів повернення |
|||
|
|||
Існують випадки, коли Ви повертаєте щось, що не є допустимим полем Pydantic, але анотуєте це у функції лише для того, щоб отримати підтримку від інструментів (редактора, mypy тощо). |
|||
|
|||
### Повернення Response напряму |
|||
|
|||
Найпоширенішим випадком буде [повернення Response напряму, як пояснюється пізніше у розширеній документації](../advanced/response-directly.md){.internal-link target=_blank}. |
|||
|
|||
{* ../../docs_src/response_model/tutorial003_02.py hl[8,10:11] *} |
|||
|
|||
Цей простий випадок автоматично обробляється FastAPI, тому що анотація типу повернення — це клас (або підклас) `Response`. |
|||
|
|||
І інструменти також будуть задоволені, бо і `RedirectResponse`, і `JSONResponse` є підкласами `Response`, отже анотація типу коректна. |
|||
|
|||
### Анотація підкласу Response |
|||
|
|||
Також можна використовувати підклас `Response` у анотації типу: |
|||
|
|||
{* ../../docs_src/response_model/tutorial003_03.py hl[8:9] *} |
|||
|
|||
Це теж працюватиме, бо `RedirectResponse` — підклас `Response`, і FastAPI автоматично обробить цей простий випадок. |
|||
|
|||
### Некоректні анотації типу повернення |
|||
|
|||
Але коли Ви повертаєте якийсь інший довільний об’єкт, що не є валідним типом Pydantic (наприклад, об’єкт бази даних), і анотуєте його так у функції, FastAPI спробує створити Pydantic модель відповіді на основі цієї анотації типу, і це завершиться помилкою. |
|||
|
|||
Те саме станеться, якщо Ви використовуєте <abbr title="Об'єднання (union) кількох типів означає: «будь-який з цих типів».">union</abbr> між різними типами, де один або більше не є валідними типами Pydantic, наприклад, це спричинить помилку 💥: |
|||
|
|||
{* ../../docs_src/response_model/tutorial003_04_py310.py hl[8] *} |
|||
|
|||
...це не працює, тому що тип анотації не є типом Pydantic і не є просто класом `Response` або його підкласом, а є об’єднанням (union) — або `Response`, або `dict`. |
|||
|
|||
### Відключення Моделі Відповіді |
|||
|
|||
Продовжуючи приклад вище, можливо, Ви не хочете використовувати стандартну валідацію даних, автоматичну документацію, фільтрацію тощо, які FastAPI виконує за замовчуванням. |
|||
|
|||
Але ви все одно можете залишити анотацію типу у функції, щоб зберегти підтримку з боку інструментів, таких як редактори коду або статичні перевірки типів (наприклад, mypy). |
|||
|
|||
У такому випадку ви можете вимкнути генерацію моделі відповіді, встановивши `response_model=None`: |
|||
|
|||
{* ../../docs_src/response_model/tutorial003_05_py310.py hl[7] *} |
|||
|
|||
Це змусить FastAPI пропустити генерацію моделі відповіді, і таким чином Ви зможете використовувати будь-які анотації типів повернення без впливу на вашу FastAPI аплікацію. 🤓 |
|||
|
|||
## Параметри кодування моделі відповіді |
|||
|
|||
Ваша модель відповіді може мати значення за замовчуванням, наприклад: |
|||
|
|||
{* ../../docs_src/response_model/tutorial004_py310.py hl[9,11:12] *} |
|||
|
|||
* `description: Union[str, None] = None` (або `str | None = None` у Python 3.10) має значення за замовчуванням `None`. |
|||
* `tax: float = 10.5` має значення за замовчуванням `10.5`. |
|||
* `tags: List[str] = []` має значення за замовчуванням порожній список: `[]`. |
|||
|
|||
Але Ви можете захотіти не включати їх у результат, якщо вони фактично не були збережені. |
|||
|
|||
Наприклад, якщо у Вас є моделі з багатьма необов’язковими атрибутами у NoSQL базі даних, але Ви не хочете відправляти дуже довгі JSON-відповіді, повні значень за замовчуванням. |
|||
|
|||
### Використовуйте параметр `response_model_exclude_unset` |
|||
|
|||
Ви можете встановити параметр декоратора шляху `response_model_exclude_unset=True`: |
|||
|
|||
{* ../../docs_src/response_model/tutorial004_py310.py hl[22] *} |
|||
|
|||
і ці значення за замовчуванням не будуть включені у відповідь, тільки фактично встановлені значення. |
|||
|
|||
Отже, якщо Ви надішлете запит до цього оператора шляху для елемента з item_id `foo`, відповідь (без включення значень за замовчуванням) буде: |
|||
|
|||
```JSON |
|||
{ |
|||
"name": "Foo", |
|||
"price": 50.2 |
|||
} |
|||
``` |
|||
|
|||
/// info | Інформація |
|||
|
|||
У Pydantic версії 1 метод називався `.dict()`, він був застарілий (але ще підтримується) у Pydantic версії 2 і перейменований у `.model_dump()`. |
|||
|
|||
Приклади тут використовують `.dict()` для сумісності з Pydantic v1, але Вам слід використовувати `.model_dump()`, якщо Ви можете використовувати Pydantic v2. |
|||
|
|||
/// |
|||
|
|||
/// info | Інформація |
|||
|
|||
FastAPI використовує `.dict()` моделі Pydantic з <a href="https://docs.pydantic.dev/1.10/usage/exporting_models/#modeldict" class="external-link" target="_blank">параметром `exclude_unset`</a>, щоб досягти цього. |
|||
|
|||
/// |
|||
|
|||
/// info | Інформація |
|||
|
|||
Ви також можете використовувати: |
|||
|
|||
* `response_model_exclude_defaults=True` |
|||
* `response_model_exclude_none=True` |
|||
|
|||
як описано в <a href="https://docs.pydantic.dev/1.10/usage/exporting_models/#modeldict" class="external-link" target="_blank">документації Pydantic</a> for `exclude_defaults` та `exclude_none`. |
|||
|
|||
/// |
|||
|
|||
#### Дані зі значеннями для полів із типовими значеннями |
|||
|
|||
Але якщо Ваші дані мають значення для полів моделі з типовими значеннями, як у елемента з item_id `bar`: |
|||
|
|||
```Python hl_lines="3 5" |
|||
{ |
|||
"name": "Bar", |
|||
"description": "The bartenders", |
|||
"price": 62, |
|||
"tax": 20.2 |
|||
} |
|||
``` |
|||
вони будуть включені у відповідь. |
|||
|
|||
#### Дані з тими самими значеннями, що й типові |
|||
|
|||
Якщо дані мають ті самі значення, що й типові, як у елемента з item_id `baz`: |
|||
|
|||
```Python hl_lines="3 5-6" |
|||
{ |
|||
"name": "Baz", |
|||
"description": None, |
|||
"price": 50.2, |
|||
"tax": 10.5, |
|||
"tags": [] |
|||
} |
|||
``` |
|||
|
|||
FastAPI достатньо розумний (насправді, Pydantic достатньо розумний), щоб зрозуміти, що, хоча `description`, `tax` і `tags` мають ті самі значення, що й типові, вони були встановлені явно (а не взяті як значення за замовчуванням). |
|||
|
|||
Отже, вони будуть включені у JSON-відповідь. |
|||
|
|||
/// tip | Порада |
|||
|
|||
Зверніть увагу, що типові значення можуть бути будь-якими, не лише `None`. |
|||
|
|||
Це може бути list (`[]`), `float` 10.5 тощо. |
|||
|
|||
/// |
|||
|
|||
### `response_model_include` та `response_model_exclude` |
|||
|
|||
Ви також можете використовувати параметри *декоратора операції шляху* `response_model_include` та `response_model_exclude`. |
|||
|
|||
Вони приймають `set` (множину) рядків (`str`) з іменами атрибутів, які потрібно включити (пропускаючи інші) або виключити (включаючи інші). |
|||
|
|||
Це можна використовувати як швидкий спосіб, якщо у Вас є лише одна модель Pydantic і Ви хочете видалити деякі дані з виводу. |
|||
|
|||
/// tip | Порада |
|||
|
|||
Але все ж рекомендується використовувати описані вище підходи, із застосуванням кількох класів, замість цих параметрів. |
|||
|
|||
|
|||
Це тому, що JSON Schema, який генерується у вашому OpenAPI додатку (і в документації), все одно буде відповідати повній моделі, навіть якщо Ви використовуєте `response_model_include` або `response_model_exclude` для виключення деяких атрибутів. |
|||
|
|||
Це також стосується `response_model_by_alias`, який працює подібним чином. |
|||
|
|||
/// |
|||
|
|||
{* ../../docs_src/response_model/tutorial005_py310.py hl[29,35] *} |
|||
|
|||
/// tip | Порада |
|||
|
|||
Синтаксис `{"name", "description"}` створює `set` з цими двома значеннями. |
|||
|
|||
Він еквівалентний `set(["name", "description"])`. |
|||
|
|||
/// |
|||
|
|||
#### Використання `list` замість `set` |
|||
|
|||
Якщо Ви забудете використати `set` і натомість застосуєте `list` або `tuple`, FastAPI все одно перетворить це на `set`, і все працюватиме правильно: |
|||
|
|||
{* ../../docs_src/response_model/tutorial006_py310.py hl[29,35] *} |
|||
|
|||
## Підсумок |
|||
|
|||
Використовуйте параметр `response_model` *декоратора операції шляху*, щоб визначати моделі відповіді, особливо щоб гарантувати фільтрацію приватних даних. |
|||
|
|||
Використовуйте `response_model_exclude_unset`, щоб повертати лише явно встановлені значення. |
@ -0,0 +1,104 @@ |
|||
# Безпека |
|||
|
|||
Існує багато способів реалізувати безпеку, автентифікацію та авторизацію. |
|||
|
|||
Це зазвичай складна і "непроста" тема. |
|||
|
|||
У багатьох фреймворках і системах забезпечення безпеки та автентифікації займає величезну частину зусиль і коду (іноді — понад 50% всього написаного коду). |
|||
|
|||
**FastAPI** надає кілька інструментів, які допоможуть Вам впоратися з **безпекою** легко, швидко, стандартним способом, без необхідності вивчати всі специфікації безпеки. |
|||
|
|||
Але спочатку — кілька коротких понять. |
|||
|
|||
## Поспішаєте? |
|||
|
|||
Якщо Вам не цікаві всі ці терміни й просто потрібно *швидко* додати автентифікацію за логіном і паролем — переходьте до наступних розділів. |
|||
|
|||
## OAuth2 |
|||
|
|||
OAuth2 — це специфікація, що описує кілька способів обробки автентифікації та авторизації. |
|||
|
|||
Це досить об'ємна специфікація, яка охоплює складні випадки використання. |
|||
|
|||
Вона включає способи автентифікації через "третю сторону". |
|||
|
|||
Саме це лежить в основі "входу через Google, Facebook, X (Twitter), GitHub" тощо. |
|||
|
|||
### OAuth 1 |
|||
|
|||
Раніше існував OAuth 1, який значно відрізняється від OAuth2 і є складнішим, оскільки містив специфікації для шифрування комунікацій. |
|||
|
|||
Зараз майже не використовується. |
|||
|
|||
OAuth2 не вказує, як саме шифрувати з'єднання — воно очікує, що ваш застосунок працює через HTTPS. |
|||
|
|||
/// tip | Порада |
|||
|
|||
У розділі про **деплой** Ви побачите, як налаштувати HTTPS безкоштовно з Traefik та Let's Encrypt. |
|||
|
|||
/// |
|||
|
|||
## OpenID Connect |
|||
|
|||
OpenID Connect — ще одна специфікація, побудована на основі **OAuth2**. |
|||
|
|||
Вона розширює OAuth2, уточнюючи деякі неоднозначності для досягнення кращої сумісності. |
|||
|
|||
Наприклад, вхід через Google використовує OpenID Connect (який базується на OAuth2). |
|||
|
|||
Але вхід через Facebook — ні. Він має власну реалізацію на базі OAuth2. |
|||
|
|||
### OpenID (не "OpenID Connect") |
|||
|
|||
Існувала також специфікація "OpenID", яка намагалася розвʼязати ті самі задачі, що й **OpenID Connect**, але не базувалась на OAuth2. |
|||
|
|||
Це була зовсім інша система, і сьогодні вона майже не використовується. |
|||
|
|||
## OpenAPI |
|||
|
|||
OpenAPI (раніше Swagger) — це специфікація для побудови API (тепер під егідою Linux Foundation). |
|||
|
|||
**FastAPI** базується на **OpenAPI**. |
|||
|
|||
Завдяки цьому Ви отримуєте автоматичну інтерактивну документацію, генерацію коду та багато іншого. |
|||
|
|||
OpenAPI дозволяє описувати різні "схеми" безпеки. |
|||
|
|||
Використовуючи їх, Ви можете скористатися всіма цими інструментами, що базуються на стандартах, зокрема інтерактивними системами документації. |
|||
|
|||
OpenAPI визначає такі схеми безпеки: |
|||
|
|||
* `apiKey`: специфічний для застосунку ключ, який може передаватися через: |
|||
* Параметр запиту. |
|||
* Заголовок. |
|||
* Cookie. |
|||
* `http`: стандартні методи HTTP-автентифікації, включаючи: |
|||
* `bearer`: заголовок `Authorization` зі значенням `Bearer` та токеном. Це успадковано з OAuth2. |
|||
* HTTP Basic автентифікація |
|||
* HTTP Digest, тощо. |
|||
* `oauth2`: усі способи обробки безпеки за допомогою OAuth2 (так звані «потоки»). |
|||
* Деякі з цих потоків підходять для створення власного провайдера автентифікації OAuth 2.0 (наприклад, Google, Facebook, X (Twitter), GitHub тощо): |
|||
* `implicit`— неявний |
|||
* `clientCredentials`— облікові дані клієнта |
|||
* `authorizationCode` — код авторизації |
|||
* Але є один окремий «потік», який ідеально підходить для реалізації автентифікації всередині одного додатку: |
|||
* `password`: у наступних розділах буде приклад використання цього потоку. |
|||
* `openIdConnect`: дозволяє автоматично виявляти параметри автентифікації OAuth2. |
|||
* Це автоматичне виявлення визначається у специфікації OpenID Connect. |
|||
|
|||
|
|||
/// tip | Порада |
|||
|
|||
Інтеграція інших провайдерів автентифікації/авторизації, таких як Google, Facebook, X (Twitter), GitHub тощо — також можлива і відносно проста. |
|||
|
|||
Найскладніше — це створити власного провайдера автентифікації/авторизації, як Google чи Facebook. Але **FastAPI** надає Вам інструменти, щоб зробити це легко, беручи на себе важку частину роботи. |
|||
|
|||
/// |
|||
|
|||
## Інструменти **FastAPI** |
|||
|
|||
FastAPI надає кілька інструментів для кожної з описаних схем безпеки в модулі `fastapi.security`, які спрощують використання цих механізмів захисту. |
|||
|
|||
У наступних розділах Ви побачите, як додати безпеку до свого API за допомогою цих інструментів **FastAPI**. |
|||
|
|||
А також побачите, як вона автоматично інтегрується в інтерактивну документацію вашого API. |
@ -0,0 +1,31 @@ |
|||
from fastapi import FastAPI |
|||
from fastapi.testclient import TestClient |
|||
from pydantic import BaseModel |
|||
|
|||
app = FastAPI() |
|||
|
|||
|
|||
class MyModel(BaseModel): |
|||
""" |
|||
A model with a form feed character in the title. |
|||
\f |
|||
Text after form feed character. |
|||
""" |
|||
|
|||
|
|||
@app.get("/foo") |
|||
def foo(v: MyModel): # pragma: no cover |
|||
pass |
|||
|
|||
|
|||
client = TestClient(app) |
|||
|
|||
|
|||
def test_openapi(): |
|||
response = client.get("/openapi.json") |
|||
assert response.status_code == 200, response.text |
|||
openapi_schema = response.json() |
|||
|
|||
assert openapi_schema["components"]["schemas"]["MyModel"]["description"] == ( |
|||
"A model with a form feed character in the title.\n" |
|||
) |
@ -1,19 +0,0 @@ |
|||
from fastapi.testclient import TestClient |
|||
from pytest import MonkeyPatch |
|||
|
|||
from ...utils import needs_pydanticv1 |
|||
|
|||
|
|||
@needs_pydanticv1 |
|||
def test_settings(monkeypatch: MonkeyPatch): |
|||
monkeypatch.setenv("ADMIN_EMAIL", "admin@example.com") |
|||
from docs_src.settings.tutorial001_pv1 import app |
|||
|
|||
client = TestClient(app) |
|||
response = client.get("/info") |
|||
assert response.status_code == 200, response.text |
|||
assert response.json() == { |
|||
"app_name": "Awesome API", |
|||
"admin_email": "admin@example.com", |
|||
"items_per_user": 50, |
|||
} |
@ -0,0 +1,156 @@ |
|||
from typing import Union |
|||
|
|||
from fastapi import FastAPI, Form |
|||
from fastapi.testclient import TestClient |
|||
from pydantic import BaseModel |
|||
from typing_extensions import Annotated |
|||
|
|||
app = FastAPI() |
|||
|
|||
|
|||
class UserForm(BaseModel): |
|||
name: str |
|||
email: str |
|||
|
|||
|
|||
class CompanyForm(BaseModel): |
|||
company_name: str |
|||
industry: str |
|||
|
|||
|
|||
@app.post("/form-union/") |
|||
def post_union_form(data: Annotated[Union[UserForm, CompanyForm], Form()]): |
|||
return {"received": data} |
|||
|
|||
|
|||
client = TestClient(app) |
|||
|
|||
|
|||
def test_post_user_form(): |
|||
response = client.post( |
|||
"/form-union/", data={"name": "John Doe", "email": "john@example.com"} |
|||
) |
|||
assert response.status_code == 200, response.text |
|||
assert response.json() == { |
|||
"received": {"name": "John Doe", "email": "john@example.com"} |
|||
} |
|||
|
|||
|
|||
def test_post_company_form(): |
|||
response = client.post( |
|||
"/form-union/", data={"company_name": "Tech Corp", "industry": "Technology"} |
|||
) |
|||
assert response.status_code == 200, response.text |
|||
assert response.json() == { |
|||
"received": {"company_name": "Tech Corp", "industry": "Technology"} |
|||
} |
|||
|
|||
|
|||
def test_invalid_form_data(): |
|||
response = client.post( |
|||
"/form-union/", |
|||
data={"name": "John", "company_name": "Tech Corp"}, |
|||
) |
|||
assert response.status_code == 422, response.text |
|||
|
|||
|
|||
def test_empty_form(): |
|||
response = client.post("/form-union/") |
|||
assert response.status_code == 422, response.text |
|||
|
|||
|
|||
def test_openapi_schema(): |
|||
response = client.get("/openapi.json") |
|||
assert response.status_code == 200, response.text |
|||
|
|||
assert response.json() == { |
|||
"openapi": "3.1.0", |
|||
"info": {"title": "FastAPI", "version": "0.1.0"}, |
|||
"paths": { |
|||
"/form-union/": { |
|||
"post": { |
|||
"summary": "Post Union Form", |
|||
"operationId": "post_union_form_form_union__post", |
|||
"requestBody": { |
|||
"content": { |
|||
"application/x-www-form-urlencoded": { |
|||
"schema": { |
|||
"anyOf": [ |
|||
{"$ref": "#/components/schemas/UserForm"}, |
|||
{"$ref": "#/components/schemas/CompanyForm"}, |
|||
], |
|||
"title": "Data", |
|||
} |
|||
} |
|||
}, |
|||
"required": True, |
|||
}, |
|||
"responses": { |
|||
"200": { |
|||
"description": "Successful Response", |
|||
"content": {"application/json": {"schema": {}}}, |
|||
}, |
|||
"422": { |
|||
"description": "Validation Error", |
|||
"content": { |
|||
"application/json": { |
|||
"schema": { |
|||
"$ref": "#/components/schemas/HTTPValidationError" |
|||
} |
|||
} |
|||
}, |
|||
}, |
|||
}, |
|||
} |
|||
} |
|||
}, |
|||
"components": { |
|||
"schemas": { |
|||
"CompanyForm": { |
|||
"properties": { |
|||
"company_name": {"type": "string", "title": "Company Name"}, |
|||
"industry": {"type": "string", "title": "Industry"}, |
|||
}, |
|||
"type": "object", |
|||
"required": ["company_name", "industry"], |
|||
"title": "CompanyForm", |
|||
}, |
|||
"HTTPValidationError": { |
|||
"properties": { |
|||
"detail": { |
|||
"items": {"$ref": "#/components/schemas/ValidationError"}, |
|||
"type": "array", |
|||
"title": "Detail", |
|||
} |
|||
}, |
|||
"type": "object", |
|||
"title": "HTTPValidationError", |
|||
}, |
|||
"UserForm": { |
|||
"properties": { |
|||
"name": {"type": "string", "title": "Name"}, |
|||
"email": {"type": "string", "title": "Email"}, |
|||
}, |
|||
"type": "object", |
|||
"required": ["name", "email"], |
|||
"title": "UserForm", |
|||
}, |
|||
"ValidationError": { |
|||
"properties": { |
|||
"loc": { |
|||
"items": { |
|||
"anyOf": [{"type": "string"}, {"type": "integer"}] |
|||
}, |
|||
"type": "array", |
|||
"title": "Location", |
|||
}, |
|||
"msg": {"type": "string", "title": "Message"}, |
|||
"type": {"type": "string", "title": "Error Type"}, |
|||
}, |
|||
"type": "object", |
|||
"required": ["loc", "msg", "type"], |
|||
"title": "ValidationError", |
|||
}, |
|||
} |
|||
}, |
|||
} |
@ -1,51 +0,0 @@ |
|||
from typing import List |
|||
|
|||
from fastapi import FastAPI |
|||
from pydantic import BaseModel |
|||
|
|||
app = FastAPI() |
|||
|
|||
|
|||
class RecursiveItem(BaseModel): |
|||
sub_items: List["RecursiveItem"] = [] |
|||
name: str |
|||
|
|||
|
|||
RecursiveItem.model_rebuild() |
|||
|
|||
|
|||
class RecursiveSubitemInSubmodel(BaseModel): |
|||
sub_items2: List["RecursiveItemViaSubmodel"] = [] |
|||
name: str |
|||
|
|||
|
|||
class RecursiveItemViaSubmodel(BaseModel): |
|||
sub_items1: List[RecursiveSubitemInSubmodel] = [] |
|||
name: str |
|||
|
|||
|
|||
RecursiveSubitemInSubmodel.model_rebuild() |
|||
RecursiveItemViaSubmodel.model_rebuild() |
|||
|
|||
|
|||
@app.get("/items/recursive", response_model=RecursiveItem) |
|||
def get_recursive(): |
|||
return {"name": "item", "sub_items": [{"name": "subitem", "sub_items": []}]} |
|||
|
|||
|
|||
@app.get("/items/recursive-submodel", response_model=RecursiveItemViaSubmodel) |
|||
def get_recursive_submodel(): |
|||
return { |
|||
"name": "item", |
|||
"sub_items1": [ |
|||
{ |
|||
"name": "subitem", |
|||
"sub_items2": [ |
|||
{ |
|||
"name": "subsubitem", |
|||
"sub_items1": [{"name": "subsubsubitem", "sub_items2": []}], |
|||
} |
|||
], |
|||
} |
|||
], |
|||
} |
@ -1,12 +1,9 @@ |
|||
from fastapi.testclient import TestClient |
|||
|
|||
from ..utils import needs_pydanticv2 |
|||
from .app import app |
|||
|
|||
|
|||
@needs_pydanticv2 |
|||
def test_recursive(): |
|||
from .app_pv2 import app |
|||
|
|||
client = TestClient(app) |
|||
response = client.get("/items/recursive") |
|||
assert response.status_code == 200, response.text |
@ -1,33 +0,0 @@ |
|||
from fastapi.testclient import TestClient |
|||
|
|||
from ..utils import needs_pydanticv1 |
|||
|
|||
|
|||
@needs_pydanticv1 |
|||
def test_recursive(): |
|||
from .app_pv1 import app |
|||
|
|||
client = TestClient(app) |
|||
response = client.get("/items/recursive") |
|||
assert response.status_code == 200, response.text |
|||
assert response.json() == { |
|||
"sub_items": [{"name": "subitem", "sub_items": []}], |
|||
"name": "item", |
|||
} |
|||
|
|||
response = client.get("/items/recursive-submodel") |
|||
assert response.status_code == 200, response.text |
|||
assert response.json() == { |
|||
"name": "item", |
|||
"sub_items1": [ |
|||
{ |
|||
"name": "subitem", |
|||
"sub_items2": [ |
|||
{ |
|||
"name": "subsubitem", |
|||
"sub_items1": [{"name": "subsubsubitem", "sub_items2": []}], |
|||
} |
|||
], |
|||
} |
|||
], |
|||
} |
Loading…
Reference in new issue