Compare commits

...

70 Commits

Author SHA1 Message Date
Sebastián Ramírez
d666ccb622 🔖 Release version 0.95.0 2023-03-18 20:37:42 +01:00
Sebastián Ramírez
38f0cad517 📝 Tweak release notes 2023-03-18 20:37:20 +01:00
Sebastián Ramírez
bd90bed02a 📝 Update release notes 2023-03-18 20:24:12 +01:00
github-actions
546392db98 📝 Update release notes 2023-03-18 19:08:30 +00:00
Sebastián Ramírez
0bc87ec77c 📝 Tweak tip recommending Annotated in docs (#9270)
📝 Tweak tip recommending Annotated
2023-03-18 19:07:53 +00:00
Sebastián Ramírez
fbfd53542e 📝 Update release notes 2023-03-18 19:46:47 +01:00
github-actions
994ea1ad33 📝 Update release notes 2023-03-18 16:16:37 +00:00
Sebastián Ramírez
69673548bc 📝 Update order of examples, latest Python version first, and simplify version tab names (#9269)
* 📝 Simplify names in Python versions in tabs in docs

* 📝 Update docs for Types Intro, explain Python 3.6+, Python 3.9+, Python 3.10+

* 📝 Re-order all Python examples, show latest Python versions first
2023-03-18 17:16:02 +01:00
github-actions
166d348ea6 📝 Update release notes 2023-03-18 12:30:38 +00:00
Sebastián Ramírez
9eaed2eb37 📝 Update all docs to use Annotated as the main recommendation, with new examples and tests (#9268)
* 🍱 Add new source examples with Annotated for Query Params and String Validations

* 📝 Add new docs with Annotated for Query Params and String Validations

* 🚚 Rename incorrectly named tests for Query Params and str validations

*  Add new tests with Annotated for Query Params and Sring Validations examples

* 🍱 Add new examples with Annotated for Intro to Python Types

* 📝 Update Python Types Intro, include Annotated

* 🎨 Fix formatting in Query params and string validation, and highlight

* 🍱 Add new Annotated source examples for Path Params and Numeric Validations

* 📝 Update docs for Path Params and Numeric Validations with Annotated

* 🍱 Add new source examples with Annotated for Body - Multiple Params

* 📝 Update docs with Annotated for Body - Multiple Parameters

*  Add test for new Annotated examples in Body - Multiple Parameters

* 🍱 Add new Annotated source examples for Body Fields

* 📝 Update docs for Body Fields with new Annotated examples

*  Add new tests for new Annotated examples for Body Fields

* 🍱 Add new Annotated source examples for Schema Extra (Example Data)

* 📝 Update docs for Schema Extra with Annotated

*  Add tests for new Annotated examples for Schema Extra

* 🍱 Add new Annnotated source examples for Extra Data Types

* 📝 Update docs with Annotated for Extra Data Types

*  Add tests for new Annotated examples for Extra Data Types

* 🍱 Add new Annotated source examples for Cookie Parameters

* 📝 Update docs for Cookie Parameters with Annotated examples

*  Add tests for new Annotated source examples in Cookie Parameters

* 🍱 Add new Annotated examples for Header Params

* 📝 Update docs with Annotated examples for Header Parameters

*  Add tests for new Annotated examples for Header Params

* 🍱 Add new Annotated examples for Form Data

* 📝 Update Annotated docs for Form Data

*  Add tests for new Annotated examples in Form Data

* 🍱 Add new Annotated source examples for Request Files

* 📝 Update Annotated docs for Request Files

*  Test new Annotated examples for Request Files

* 🍱 Add new Annotated source examples for Request Forms and Files

*  Add tests for new Anotated examples for Request Forms and Files

* 🍱 Add new Annotated source examples for Dependencies and Advanced Dependencies

*  Add tests for new Annotated dependencies

* 📝 Add new docs for using Annotated with dependencies including type aliases

* 📝 Update docs for Classes as Dependencies with Annotated

* 📝 Update docs for Sub-dependencies with Annotated

* 📝 Update docs for Dependencies in path operation decorators with Annotated

* 📝 Update docs for Global Dependencies with Annotated

* 📝 Update docs for Dependencies with yield with Annotated

* 🎨 Update format in example for dependencies with Annotated

* 🍱 Add source examples with Annotated for Security

*  Add tests for new Annotated examples for security

* 📝 Update docs for Security - First Steps with Annotated

* 📝 Update docs for Security: Get Current User with Annotated

* 📝 Update docs for Simple OAuth2 with Password and Bearer with Annotated

* 📝 Update docs for OAuth2 with Password (and hashing), Bearer with JWT tokens with Annotated

* 📝 Update docs for Request Forms and Files with Annotated

* 🍱 Add new source examples for Bigger Applications with Annotated

*  Add new tests for Bigger Applications with Annotated

* 📝 Update docs for Bigger Applications - Multiple Files with Annotated

* 🍱 Add source examples for background tasks with Annotated

* 📝 Update docs for Background Tasks with Annotated

*  Add test for Background Tasks with Anotated

* 🍱 Add new source examples for docs for Testing with Annotated

* 📝 Update docs for Testing with Annotated

*  Add tests for Annotated examples for Testing

* 🍱 Add new source examples for Additional Status Codes with Annotated

*  Add tests for new Annotated examples for Additional Status Codes

* 📝 Update docs for Additional Status Codes with Annotated

* 📝 Update docs for Advanced Dependencies with Annotated

* 📝 Update docs for OAuth2 scopes with Annotated

* 📝 Update docs for HTTP Basic Auth with Annotated

* 🍱 Add source examples with Annotated for WebSockets

*  Add tests for new Annotated examples for WebSockets

* 📝 Update docs for WebSockets with new Annotated examples

* 🍱 Add source examples with Annotated for Settings and Environment Variables

* 📝 Update docs for Settings and Environment Variables with Annotated

* 🍱 Add new source examples for testing dependencies with Annotated

*  Add tests for new examples for testing dependencies

* 📝 Update docs for testing dependencies with new Annotated examples

*  Update and fix marker for Python 3.9 test

* 🔧 Update Ruff ignores for source examples in docs

*  Fix some tests in the grid for Python 3.9 (incorrectly testing 3.10)

* 🔥 Remove source examples and tests for (non existent) docs section about Annotated, as it's covered in all the rest of the docs
2023-03-18 13:29:59 +01:00
github-actions
f63b3ad53e 📝 Update release notes 2023-03-17 20:36:26 +00:00
Nadav Zingerman
375513f114 Add support for PEP-593 Annotated for specifying dependencies and parameters (#4871)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2023-03-17 21:35:45 +01:00
Sebastián Ramírez
ef176c6631 🔖 Release version 0.94.1 2023-03-14 03:27:11 +01:00
github-actions
7b7e86a307 📝 Update release notes 2023-03-14 02:19:50 +00:00
Sebastián Ramírez
25aabe05ce 🎨 Fix types for lifespan, upgrade Starlette to 0.26.1 (#9245) 2023-03-14 03:19:04 +01:00
Sebastián Ramírez
392ffaae43 🔖 Release version 0.94.0 2023-03-10 20:00:49 +01:00
Sebastián Ramírez
202ee0497a 📝 Update release notes 2023-03-10 20:00:09 +01:00
github-actions
321e873c95 📝 Update release notes 2023-03-10 18:57:58 +00:00
github-actions
4860631468 📝 Update release notes 2023-03-10 18:57:42 +00:00
dependabot[bot]
8e4c96c703 ⬆ Bump black from 22.10.0 to 23.1.0 (#5953)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2023-03-10 19:57:21 +01:00
dependabot[bot]
39813aa9b0 ⬆ Bump types-ujson from 5.6.0.0 to 5.7.0.1 (#6027)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2023-03-10 19:57:07 +01:00
github-actions
3e3278ed3f 📝 Update release notes 2023-03-10 18:51:19 +00:00
dependabot[bot]
c8a07078cf ⬆ Bump dawidd6/action-download-artifact from 2.24.3 to 2.26.0 (#6034)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2023-03-10 19:50:08 +01:00
github-actions
59f91db1d2 📝 Update release notes 2023-03-10 18:49:54 +00:00
pre-commit-ci[bot]
f04b61bd16 ⬆ [pre-commit.ci] pre-commit autoupdate (#5709)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2023-03-10 19:49:18 +01:00
github-actions
253d58bc5c 📝 Update release notes 2023-03-10 18:48:20 +00:00
Yasser Tahiri
78b8a9b6ec Add pydantic to PyPI classifiers (#5914) 2023-03-10 19:47:38 +01:00
github-actions
c26db94a90 📝 Update release notes 2023-03-10 18:43:10 +00:00
Vladislav Kramorenko
d1f3753e5e 🌐 Add Russian translation for docs/ru/docs/history-design-future.md (#5986) 2023-03-10 19:42:25 +01:00
github-actions
d5b0cc9f58 📝 Update release notes 2023-03-10 18:32:12 +00:00
Ben Beasley
42daed222e ⬆ Upgrade python-multipart to support 0.0.6 (#9212) 2023-03-10 19:31:36 +01:00
github-actions
fd3bfe9f50 📝 Update release notes 2023-03-10 18:27:50 +00:00
Steven Eubank
1fea9c5626 📝 Update Sentry link in docs (#9218) 2023-03-10 19:27:10 +01:00
github-actions
d783463ebd 📝 Update release notes 2023-03-10 18:24:42 +00:00
Sebastián Ramírez
8a4cfa52af ⬆️ Upgrade Starlette version, support new lifespan with state (#9239) 2023-03-10 19:24:04 +01:00
Sebastián Ramírez
25382d2d19 🔖 Release version 0.93.0 2023-03-07 17:06:47 +01:00
Sebastián Ramírez
b9bb441b23 📝 Update release notes 2023-03-07 17:04:40 +01:00
github-actions
e33f30a607 📝 Update release notes 2023-03-07 15:46:37 +00:00
Jordan Speicher
cc9a73c3f8 Add support for lifespan async context managers (superseding startup and shutdown events) (#2944)
Co-authored-by: Mike Shantz <mshantz@coldstorage.com>
Co-authored-by: Jonathan Plasse <13716151+JonathanPlasse@users.noreply.github.com>
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2023-03-07 16:46:00 +01:00
github-actions
66e03c816b 📝 Update release notes 2023-03-06 19:32:19 +00:00
gusty1g
639cf3440a 🌐 Tamil translations - initial setup (#5564)
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2023-03-06 20:31:37 +01:00
github-actions
2f1b856fe6 📝 Update release notes 2023-03-06 16:29:17 +00:00
Axel
31e148ba8e 🌐 Add French translation for docs/fr/docs/advanced/path-operation-advanced-configuration.md (#9221)
Co-authored-by: Julian Maurin <julian.maurin.perso@pm.me>
Co-authored-by: Cedric Fraboulet <62244267+frabc@users.noreply.github.com>
2023-03-06 17:28:40 +01:00
github-actions
40c2c44ff9 📝 Update release notes 2023-03-06 16:27:29 +00:00
Cedric Fraboulet
c5f72f02cd 🌐 Add French translation for docs/tutorial/debugging.md (#9175) 2023-03-06 17:26:49 +01:00
github-actions
9b83a00c40 📝 Update release notes 2023-03-04 12:52:10 +00:00
har8
83012a9cf6 🌐 Initiate Armenian translation setup (#5844)
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2023-03-04 12:51:37 +00:00
github-actions
8625189351 📝 Update release notes 2023-03-04 12:43:32 +00:00
eykamp
e570371003 ✏ Fix formatting in docs/en/docs/tutorial/metadata.md for ReDoc (#6005) 2023-03-04 13:42:55 +01:00
Aayush Chhabra
4d099250f6 ✏ Fix typo in docs/en/docs/tutorial/bigger-applications.md, "codes" to "code" (#5990)
Typo in docs: it should be code instead of codes
2023-03-04 13:17:21 +01:00
github-actions
9ef46a2299 📝 Update release notes 2023-03-04 12:02:42 +00:00
Ruidy
30a9d68232 🌐 Add French translation for deployment/manually.md (#3693)
Co-authored-by: Sam Courtemanche <smlep.pro@gmail.com>
Co-authored-by: Ruidy <r.nemausat@empfohlen.de>
Co-authored-by: Ruidy <rnemausat@newstore.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2023-03-04 12:02:09 +00:00
github-actions
03bb7c166c 📝 Update release notes 2023-03-04 11:45:13 +00:00
Sebastián Ramírez
c5f343a4fd 👷 Update translation bot messages (#9206) 2023-03-04 11:44:30 +00:00
github-actions
83050bead6 📝 Update release notes 2023-03-04 10:40:08 +00:00
Sebastián Ramírez
bd219c2bbf 👷 Update translations bot to use Discussions, and notify when a PR is done (#9183) 2023-03-04 11:39:28 +01:00
github-actions
4b95025d44 📝 Update release notes 2023-03-04 07:35:11 +00:00
Sebastián Ramírez
ff64772dd1 🔧 Update sponsors-badges (#9182) 2023-03-04 07:34:39 +00:00
github-actions
e8fd74e737 📝 Update release notes 2023-03-04 07:30:07 +00:00
github-actions[bot]
87842ac0db 👥 Update FastAPI People (#9181)
Co-authored-by: github-actions <github-actions@github.com>
2023-03-04 08:29:30 +01:00
github-actions
7674da89d3 📝 Update release notes 2023-03-02 15:11:54 +00:00
Sebastián Ramírez
e9326de161 🔊 Log GraphQL errors in FastAPI People, because it returns 200, with a payload with an error (#9171) 2023-03-02 15:11:17 +00:00
github-actions
97effae92d 📝 Update release notes 2023-03-02 13:07:40 +00:00
Sebastián Ramírez
a8bde44029 💚 Fix/workaround GitHub Actions in Docker with git for FastAPI People (#9169) 2023-03-02 13:06:57 +00:00
github-actions
be54d44b0c 📝 Update release notes 2023-03-01 13:22:41 +00:00
Sebastián Ramírez
35d93d59d8 ♻️ Refactor FastAPI Experts to use only discussions now that questions are migrated (#9165) 2023-03-01 13:22:00 +00:00
github-actions
7b3727e03e 📝 Update release notes 2023-02-21 10:24:13 +00:00
Sebastián Ramírez
a270ab0c3f ⬆️ Upgrade analytics (#6025) 2023-02-21 11:23:37 +01:00
github-actions
f6f39d8714 📝 Update release notes 2023-02-16 18:51:02 +00:00
Sebastián Ramírez
4f4035262c ⬆️ Upgrade and re-enable installing Typer-CLI (#6008) 2023-02-16 18:50:21 +00:00
433 changed files with 26343 additions and 1862 deletions

View File

@@ -1,10 +1,11 @@
import logging
import random
import sys
import time
from pathlib import Path
from typing import Dict, Union
from typing import Any, Dict, List, Union, cast
import yaml
import httpx
from github import Github
from pydantic import BaseModel, BaseSettings, SecretStr
@@ -13,12 +14,172 @@ lang_all_label = "lang-all"
approved_label = "approved-2"
translations_path = Path(__file__).parent / "translations.yml"
github_graphql_url = "https://api.github.com/graphql"
questions_translations_category_id = "DIC_kwDOCZduT84CT5P9"
all_discussions_query = """
query Q($category_id: ID) {
repository(name: "fastapi", owner: "tiangolo") {
discussions(categoryId: $category_id, first: 100) {
nodes {
title
id
number
labels(first: 10) {
edges {
node {
id
name
}
}
}
}
}
}
}
"""
translation_discussion_query = """
query Q($after: String, $discussion_number: Int!) {
repository(name: "fastapi", owner: "tiangolo") {
discussion(number: $discussion_number) {
comments(first: 100, after: $after) {
edges {
cursor
node {
id
url
body
}
}
}
}
}
}
"""
add_comment_mutation = """
mutation Q($discussion_id: ID!, $body: String!) {
addDiscussionComment(input: {discussionId: $discussion_id, body: $body}) {
comment {
id
url
body
}
}
}
"""
update_comment_mutation = """
mutation Q($comment_id: ID!, $body: String!) {
updateDiscussionComment(input: {commentId: $comment_id, body: $body}) {
comment {
id
url
body
}
}
}
"""
class Comment(BaseModel):
id: str
url: str
body: str
class UpdateDiscussionComment(BaseModel):
comment: Comment
class UpdateCommentData(BaseModel):
updateDiscussionComment: UpdateDiscussionComment
class UpdateCommentResponse(BaseModel):
data: UpdateCommentData
class AddDiscussionComment(BaseModel):
comment: Comment
class AddCommentData(BaseModel):
addDiscussionComment: AddDiscussionComment
class AddCommentResponse(BaseModel):
data: AddCommentData
class CommentsEdge(BaseModel):
node: Comment
cursor: str
class Comments(BaseModel):
edges: List[CommentsEdge]
class CommentsDiscussion(BaseModel):
comments: Comments
class CommentsRepository(BaseModel):
discussion: CommentsDiscussion
class CommentsData(BaseModel):
repository: CommentsRepository
class CommentsResponse(BaseModel):
data: CommentsData
class AllDiscussionsLabelNode(BaseModel):
id: str
name: str
class AllDiscussionsLabelsEdge(BaseModel):
node: AllDiscussionsLabelNode
class AllDiscussionsDiscussionLabels(BaseModel):
edges: List[AllDiscussionsLabelsEdge]
class AllDiscussionsDiscussionNode(BaseModel):
title: str
id: str
number: int
labels: AllDiscussionsDiscussionLabels
class AllDiscussionsDiscussions(BaseModel):
nodes: List[AllDiscussionsDiscussionNode]
class AllDiscussionsRepository(BaseModel):
discussions: AllDiscussionsDiscussions
class AllDiscussionsData(BaseModel):
repository: AllDiscussionsRepository
class AllDiscussionsResponse(BaseModel):
data: AllDiscussionsData
class Settings(BaseSettings):
github_repository: str
input_token: SecretStr
github_event_path: Path
github_event_name: Union[str, None] = None
httpx_timeout: int = 30
input_debug: Union[bool, None] = False
@@ -30,6 +191,113 @@ class PartialGitHubEvent(BaseModel):
pull_request: PartialGitHubEventIssue
def get_graphql_response(
*,
settings: Settings,
query: str,
after: Union[str, None] = None,
category_id: Union[str, None] = None,
discussion_number: Union[int, None] = None,
discussion_id: Union[str, None] = None,
comment_id: Union[str, None] = None,
body: Union[str, None] = None,
) -> Dict[str, Any]:
headers = {"Authorization": f"token {settings.input_token.get_secret_value()}"}
# some fields are only used by one query, but GraphQL allows unused variables, so
# keep them here for simplicity
variables = {
"after": after,
"category_id": category_id,
"discussion_number": discussion_number,
"discussion_id": discussion_id,
"comment_id": comment_id,
"body": body,
}
response = httpx.post(
github_graphql_url,
headers=headers,
timeout=settings.httpx_timeout,
json={"query": query, "variables": variables, "operationName": "Q"},
)
if response.status_code != 200:
logging.error(
f"Response was not 200, after: {after}, category_id: {category_id}"
)
logging.error(response.text)
raise RuntimeError(response.text)
data = response.json()
if "errors" in data:
logging.error(f"Errors in response, after: {after}, category_id: {category_id}")
logging.error(response.text)
raise RuntimeError(response.text)
return cast(Dict[str, Any], data)
def get_graphql_translation_discussions(*, settings: Settings):
data = get_graphql_response(
settings=settings,
query=all_discussions_query,
category_id=questions_translations_category_id,
)
graphql_response = AllDiscussionsResponse.parse_obj(data)
return graphql_response.data.repository.discussions.nodes
def get_graphql_translation_discussion_comments_edges(
*, settings: Settings, discussion_number: int, after: Union[str, None] = None
):
data = get_graphql_response(
settings=settings,
query=translation_discussion_query,
discussion_number=discussion_number,
after=after,
)
graphql_response = CommentsResponse.parse_obj(data)
return graphql_response.data.repository.discussion.comments.edges
def get_graphql_translation_discussion_comments(
*, settings: Settings, discussion_number: int
):
comment_nodes: List[Comment] = []
discussion_edges = get_graphql_translation_discussion_comments_edges(
settings=settings, discussion_number=discussion_number
)
while discussion_edges:
for discussion_edge in discussion_edges:
comment_nodes.append(discussion_edge.node)
last_edge = discussion_edges[-1]
discussion_edges = get_graphql_translation_discussion_comments_edges(
settings=settings,
discussion_number=discussion_number,
after=last_edge.cursor,
)
return comment_nodes
def create_comment(*, settings: Settings, discussion_id: str, body: str):
data = get_graphql_response(
settings=settings,
query=add_comment_mutation,
discussion_id=discussion_id,
body=body,
)
response = AddCommentResponse.parse_obj(data)
return response.data.addDiscussionComment.comment
def update_comment(*, settings: Settings, comment_id: str, body: str):
data = get_graphql_response(
settings=settings,
query=update_comment_mutation,
comment_id=comment_id,
body=body,
)
response = UpdateCommentResponse.parse_obj(data)
return response.data.updateDiscussionComment.comment
if __name__ == "__main__":
settings = Settings()
if settings.input_debug:
@@ -45,60 +313,105 @@ if __name__ == "__main__":
)
contents = settings.github_event_path.read_text()
github_event = PartialGitHubEvent.parse_raw(contents)
translations_map: Dict[str, int] = yaml.safe_load(translations_path.read_text())
logging.debug(f"Using translations map: {translations_map}")
# Avoid race conditions with multiple labels
sleep_time = random.random() * 10 # random number between 0 and 10 seconds
pr = repo.get_pull(github_event.pull_request.number)
logging.debug(
f"Processing PR: {pr.number}, with anti-race condition sleep time: {sleep_time}"
logging.info(
f"Sleeping for {sleep_time} seconds to avoid "
"race conditions and multiple comments"
)
if pr.state == "open":
logging.debug(f"PR is open: {pr.number}")
label_strs = {label.name for label in pr.get_labels()}
if lang_all_label in label_strs and awaiting_label in label_strs:
logging.info(
f"This PR seems to be a language translation and awaiting reviews: {pr.number}"
)
if approved_label in label_strs:
message = (
f"It seems this PR already has the approved label: {pr.number}"
)
logging.error(message)
raise RuntimeError(message)
langs = []
for label in label_strs:
if label.startswith("lang-") and not label == lang_all_label:
langs.append(label[5:])
for lang in langs:
if lang in translations_map:
num = translations_map[lang]
logging.info(
f"Found a translation issue for language: {lang} in issue: {num}"
)
issue = repo.get_issue(num)
message = f"Good news everyone! 😉 There's a new translation PR to be reviewed: #{pr.number} 🎉"
already_notified = False
time.sleep(sleep_time)
logging.info(
f"Sleeping for {sleep_time} seconds to avoid race conditions and multiple comments"
)
logging.info(
f"Checking current comments in issue: {num} to see if already notified about this PR: {pr.number}"
)
for comment in issue.get_comments():
if message in comment.body:
already_notified = True
if not already_notified:
logging.info(
f"Writing comment in issue: {num} about PR: {pr.number}"
)
issue.create_comment(message)
else:
logging.info(
f"Issue: {num} was already notified of PR: {pr.number}"
)
else:
time.sleep(sleep_time)
# Get PR
logging.debug(f"Processing PR: #{github_event.pull_request.number}")
pr = repo.get_pull(github_event.pull_request.number)
label_strs = {label.name for label in pr.get_labels()}
langs = []
for label in label_strs:
if label.startswith("lang-") and not label == lang_all_label:
langs.append(label[5:])
logging.info(f"PR #{pr.number} has labels: {label_strs}")
if not langs or lang_all_label not in label_strs:
logging.info(f"PR #{pr.number} doesn't seem to be a translation PR, skipping")
sys.exit(0)
# Generate translation map, lang ID to discussion
discussions = get_graphql_translation_discussions(settings=settings)
lang_to_discussion_map: Dict[str, AllDiscussionsDiscussionNode] = {}
for discussion in discussions:
for edge in discussion.labels.edges:
label = edge.node.name
if label.startswith("lang-") and not label == lang_all_label:
lang = label[5:]
lang_to_discussion_map[lang] = discussion
logging.debug(f"Using translations map: {lang_to_discussion_map}")
# Messages to create or check
new_translation_message = f"Good news everyone! 😉 There's a new translation PR to be reviewed: #{pr.number} by @{pr.user.login}. 🎉 This requires 2 approvals from native speakers to be merged. 🤓"
done_translation_message = f"~There's a new translation PR to be reviewed: #{pr.number} by @{pr.user.login}~ Good job! This is done. 🍰☕"
# Normally only one language, but still
for lang in langs:
if lang not in lang_to_discussion_map:
log_message = f"Could not find discussion for language: {lang}"
logging.error(log_message)
raise RuntimeError(log_message)
discussion = lang_to_discussion_map[lang]
logging.info(
f"Changing labels in a closed PR doesn't trigger comments, PR: {pr.number}"
f"Found a translation discussion for language: {lang} in discussion: #{discussion.number}"
)
already_notified_comment: Union[Comment, None] = None
already_done_comment: Union[Comment, None] = None
logging.info(
f"Checking current comments in discussion: #{discussion.number} to see if already notified about this PR: #{pr.number}"
)
comments = get_graphql_translation_discussion_comments(
settings=settings, discussion_number=discussion.number
)
for comment in comments:
if new_translation_message in comment.body:
already_notified_comment = comment
elif done_translation_message in comment.body:
already_done_comment = comment
logging.info(
f"Already notified comment: {already_notified_comment}, already done comment: {already_done_comment}"
)
if pr.state == "open" and awaiting_label in label_strs:
logging.info(
f"This PR seems to be a language translation and awaiting reviews: #{pr.number}"
)
if already_notified_comment:
logging.info(
f"This PR #{pr.number} was already notified in comment: {already_notified_comment.url}"
)
else:
logging.info(
f"Writing notification comment about PR #{pr.number} in Discussion: #{discussion.number}"
)
comment = create_comment(
settings=settings,
discussion_id=discussion.id,
body=new_translation_message,
)
logging.info(f"Notified in comment: {comment.url}")
elif pr.state == "closed" or approved_label in label_strs:
logging.info(f"Already approved or closed PR #{pr.number}")
if already_done_comment:
logging.info(
f"This PR #{pr.number} was already marked as done in comment: {already_done_comment.url}"
)
elif already_notified_comment:
updated_comment = update_comment(
settings=settings,
comment_id=already_notified_comment.id,
body=done_translation_message,
)
logging.info(f"Marked as done in comment: {updated_comment.url}")
else:
logging.info(
f"There doesn't seem to be anything to be done about PR #{pr.number}"
)
logging.info("Finished")

View File

@@ -1,21 +0,0 @@
pt: 1211
es: 1218
zh: 1228
ru: 1362
it: 1556
ja: 1572
uk: 1748
tr: 1892
fr: 1972
ko: 2017
fa: 2041
pl: 3169
de: 3716
id: 3717
az: 3994
nl: 4701
uz: 4883
sv: 5146
he: 5157
ta: 5434
ar: 3349

View File

@@ -381,6 +381,10 @@ def get_graphql_response(
logging.error(response.text)
raise RuntimeError(response.text)
data = response.json()
if "errors" in data:
logging.error(f"Errors in response, after: {after}, category_id: {category_id}")
logging.error(response.text)
raise RuntimeError(response.text)
return data
@@ -496,21 +500,25 @@ def get_discussions_experts(settings: Settings):
def get_experts(settings: Settings):
(
issues_commentors,
issues_last_month_commentors,
issues_authors,
) = get_issues_experts(settings=settings)
# Migrated to only use GitHub Discussions
# (
# issues_commentors,
# issues_last_month_commentors,
# issues_authors,
# ) = get_issues_experts(settings=settings)
(
discussions_commentors,
discussions_last_month_commentors,
discussions_authors,
) = get_discussions_experts(settings=settings)
commentors = issues_commentors + discussions_commentors
last_month_commentors = (
issues_last_month_commentors + discussions_last_month_commentors
)
authors = {**issues_authors, **discussions_authors}
# commentors = issues_commentors + discussions_commentors
commentors = discussions_commentors
# last_month_commentors = (
# issues_last_month_commentors + discussions_last_month_commentors
# )
last_month_commentors = discussions_last_month_commentors
# authors = {**issues_authors, **discussions_authors}
authors = {**discussions_authors}
return commentors, last_month_commentors, authors

View File

@@ -4,6 +4,7 @@ on:
pull_request_target:
types:
- labeled
- closed
jobs:
notify-translations:

View File

@@ -15,6 +15,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
# Ref: https://github.com/actions/runner/issues/2033
- name: Fix git safe.directory in container
run: mkdir -p /home/runner/work/_temp/_github_home && printf "[safe]\n\tdirectory = /github/workspace" > /home/runner/work/_temp/_github_home/.gitconfig
# Allow debugging with tmate
- name: Setup tmate session
uses: mxschmitt/action-tmate@v3

View File

@@ -16,7 +16,7 @@ jobs:
rm -rf ./site
mkdir ./site
- name: Download Artifact Docs
uses: dawidd6/action-download-artifact@v2.24.3
uses: dawidd6/action-download-artifact@v2.26.0
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
workflow: build-docs.yml

View File

@@ -20,7 +20,7 @@ jobs:
- run: pip install smokeshow
- uses: dawidd6/action-download-artifact@v2.24.3
- uses: dawidd6/action-download-artifact@v2.26.0
with:
workflow: test.yml
commit: ${{ github.event.workflow_run.head_sha }}

View File

@@ -4,7 +4,7 @@ default_language_version:
python: python3.10
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.3.0
rev: v4.4.0
hooks:
- id: check-added-large-files
- id: check-toml
@@ -14,14 +14,14 @@ repos:
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/asottile/pyupgrade
rev: v3.2.2
rev: v3.3.1
hooks:
- id: pyupgrade
args:
- --py3-plus
- --keep-runtime-typing
- repo: https://github.com/charliermarsh/ruff-pre-commit
rev: v0.0.138
rev: v0.0.254
hooks:
- id: ruff
args:
@@ -38,7 +38,7 @@ repos:
name: isort (pyi)
types: [pyi]
- repo: https://github.com/psf/black
rev: 22.10.0
rev: 23.1.0
hooks:
- id: black
ci:

View File

@@ -45,6 +45,7 @@ nav:
- fa: /fa/
- fr: /fr/
- he: /he/
- hy: /hy/
- id: /id/
- it: /it/
- ja: /ja/
@@ -55,6 +56,7 @@ nav:
- ru: /ru/
- sq: /sq/
- sv: /sv/
- ta: /ta/
- tr: /tr/
- uk: /uk/
- zh: /zh/
@@ -80,7 +82,7 @@ markdown_extensions:
extra:
analytics:
provider: google
property: UA-133183413-1
property: G-YNEVN69SC3
social:
- icon: fontawesome/brands/github-alt
link: https://github.com/tiangolo/fastapi
@@ -111,6 +113,8 @@ extra:
name: fr - français
- link: /he/
name: he
- link: /hy/
name: hy
- link: /id/
name: id
- link: /it/
@@ -131,6 +135,8 @@ extra:
name: sq - shqip
- link: /sv/
name: sv - svenska
- link: /ta/
name: ta - தமிழ்
- link: /tr/
name: tr - Türkçe
- link: /uk/

View File

@@ -45,6 +45,7 @@ nav:
- fa: /fa/
- fr: /fr/
- he: /he/
- hy: /hy/
- id: /id/
- it: /it/
- ja: /ja/
@@ -55,6 +56,7 @@ nav:
- ru: /ru/
- sq: /sq/
- sv: /sv/
- ta: /ta/
- tr: /tr/
- uk: /uk/
- zh: /zh/
@@ -81,7 +83,7 @@ markdown_extensions:
extra:
analytics:
provider: google
property: UA-133183413-1
property: G-YNEVN69SC3
social:
- icon: fontawesome/brands/github-alt
link: https://github.com/tiangolo/fastapi
@@ -112,6 +114,8 @@ extra:
name: fr - français
- link: /he/
name: he
- link: /hy/
name: hy
- link: /id/
name: id
- link: /it/
@@ -132,6 +136,8 @@ extra:
name: sq - shqip
- link: /sv/
name: sv - svenska
- link: /ta/
name: ta - தமிழ்
- link: /tr/
name: tr - Türkçe
- link: /uk/

View File

@@ -2,6 +2,9 @@ sponsors:
- - login: jina-ai
avatarUrl: https://avatars.githubusercontent.com/u/60539444?v=4
url: https://github.com/jina-ai
- - login: armand-sauzay
avatarUrl: https://avatars.githubusercontent.com/u/35524799?u=56e3e944bfe62770d1709c09552d2efc6d285ca6&v=4
url: https://github.com/armand-sauzay
- - login: cryptapi
avatarUrl: https://avatars.githubusercontent.com/u/44925437?u=61369138589bc7fee6c417f3fbd50fbd38286cc4&v=4
url: https://github.com/cryptapi
@@ -35,21 +38,18 @@ sponsors:
- - login: getsentry
avatarUrl: https://avatars.githubusercontent.com/u/1396951?v=4
url: https://github.com/getsentry
- - login: InesIvanova
avatarUrl: https://avatars.githubusercontent.com/u/22920417?u=409882ec1df6dbd77455788bb383a8de223dbf6f&v=4
url: https://github.com/InesIvanova
- - login: vyos
avatarUrl: https://avatars.githubusercontent.com/u/5647000?v=4
url: https://github.com/vyos
- login: takashi-yoneya
avatarUrl: https://avatars.githubusercontent.com/u/33813153?u=2d0522bceba0b8b69adf1f2db866503bd96f944e&v=4
url: https://github.com/takashi-yoneya
- login: mercedes-benz
avatarUrl: https://avatars.githubusercontent.com/u/34240465?v=4
url: https://github.com/mercedes-benz
- login: xoflare
avatarUrl: https://avatars.githubusercontent.com/u/74335107?v=4
url: https://github.com/xoflare
- login: Striveworks
avatarUrl: https://avatars.githubusercontent.com/u/45523576?v=4
url: https://github.com/Striveworks
- login: BoostryJP
avatarUrl: https://avatars.githubusercontent.com/u/57932412?v=4
url: https://github.com/BoostryJP
@@ -59,6 +59,9 @@ sponsors:
- login: HiredScore
avatarUrl: https://avatars.githubusercontent.com/u/3908850?v=4
url: https://github.com/HiredScore
- login: ianshan0915
avatarUrl: https://avatars.githubusercontent.com/u/5893101?u=a178d247d882578b1d1ef214b2494e52eb28634c&v=4
url: https://github.com/ianshan0915
- login: Trivie
avatarUrl: https://avatars.githubusercontent.com/u/8161763?v=4
url: https://github.com/Trivie
@@ -119,9 +122,6 @@ sponsors:
- login: pamelafox
avatarUrl: https://avatars.githubusercontent.com/u/297042?v=4
url: https://github.com/pamelafox
- login: deserat
avatarUrl: https://avatars.githubusercontent.com/u/299332?v=4
url: https://github.com/deserat
- login: ericof
avatarUrl: https://avatars.githubusercontent.com/u/306014?u=cf7c8733620397e6584a451505581c01c5d842d7&v=4
url: https://github.com/ericof
@@ -137,18 +137,15 @@ sponsors:
- login: jqueguiner
avatarUrl: https://avatars.githubusercontent.com/u/690878?u=bd65cc1f228ce6455e56dfaca3ef47c33bc7c3b0&v=4
url: https://github.com/jqueguiner
- login: iobruno
avatarUrl: https://avatars.githubusercontent.com/u/901651?u=460bc34ac298dca9870aafe3a1560a2ae789bc4a&v=4
url: https://github.com/iobruno
- login: tcsmith
avatarUrl: https://avatars.githubusercontent.com/u/989034?u=7d8d741552b3279e8f4d3878679823a705a46f8f&v=4
url: https://github.com/tcsmith
- login: ltieman
avatarUrl: https://avatars.githubusercontent.com/u/1084689?u=e69b17de17cb3ca141a17daa7ccbe173ceb1eb17&v=4
url: https://github.com/ltieman
- login: mrkmcknz
avatarUrl: https://avatars.githubusercontent.com/u/1089376?u=2b9b8a8c25c33a4f6c220095638bd821cdfd13a3&v=4
url: https://github.com/mrkmcknz
- login: theonlynexus
avatarUrl: https://avatars.githubusercontent.com/u/1515004?v=4
url: https://github.com/theonlynexus
- login: coffeewasmyidea
avatarUrl: https://avatars.githubusercontent.com/u/1636488?u=8e32a4f200eff54dd79cd79d55d254bfce5e946d&v=4
url: https://github.com/coffeewasmyidea
@@ -156,11 +153,8 @@ sponsors:
avatarUrl: https://avatars.githubusercontent.com/u/1906344?u=5ca0c9a1a89b6a2ba31abe35c66bdc07af60a632&v=4
url: https://github.com/jonakoudijs
- login: corleyma
avatarUrl: https://avatars.githubusercontent.com/u/2080732?u=aed2ff652294a87d666b1c3f6dbe98104db76d26&v=4
avatarUrl: https://avatars.githubusercontent.com/u/2080732?u=c61f9a4bbc45a45f5d855f93e5f6e0fc8b32c468&v=4
url: https://github.com/corleyma
- login: madisonmay
avatarUrl: https://avatars.githubusercontent.com/u/2645393?u=f22b93c6ea345a4d26a90a3834dfc7f0789fcb63&v=4
url: https://github.com/madisonmay
- login: andre1sk
avatarUrl: https://avatars.githubusercontent.com/u/3148093?v=4
url: https://github.com/andre1sk
@@ -182,15 +176,9 @@ sponsors:
- login: anomaly
avatarUrl: https://avatars.githubusercontent.com/u/3654837?v=4
url: https://github.com/anomaly
- login: peterHoburg
avatarUrl: https://avatars.githubusercontent.com/u/3860655?u=f55f47eb2d6a9b495e806ac5a044e3ae01ccc1fa&v=4
url: https://github.com/peterHoburg
- login: jgreys
avatarUrl: https://avatars.githubusercontent.com/u/4136890?u=c66ae617d614f6c886f1f1c1799d22100b3c848d&v=4
url: https://github.com/jgreys
- login: gorhack
avatarUrl: https://avatars.githubusercontent.com/u/4141690?u=ec119ebc4bdf00a7bc84657a71aa17834f4f27f3&v=4
url: https://github.com/gorhack
- login: jaredtrog
avatarUrl: https://avatars.githubusercontent.com/u/4381365?v=4
url: https://github.com/jaredtrog
@@ -203,6 +191,9 @@ sponsors:
- login: ternaus
avatarUrl: https://avatars.githubusercontent.com/u/5481618?u=fabc8d75c921b3380126adb5a931c5da6e7db04f&v=4
url: https://github.com/ternaus
- login: eseglem
avatarUrl: https://avatars.githubusercontent.com/u/5920492?u=208d419cf667b8ac594c82a8db01932c7e50d057&v=4
url: https://github.com/eseglem
- login: Yaleesa
avatarUrl: https://avatars.githubusercontent.com/u/6135475?v=4
url: https://github.com/Yaleesa
@@ -243,8 +234,14 @@ sponsors:
avatarUrl: https://avatars.githubusercontent.com/u/13686061?u=59f25ef42ecf04c22657aac4238ce0e2d3d30304&v=4
url: https://github.com/khadrawy
- login: pablonnaoji
avatarUrl: https://avatars.githubusercontent.com/u/15187159?u=afc15bd5a4ba9c5c7206bbb1bcaeef606a0932e0&v=4
avatarUrl: https://avatars.githubusercontent.com/u/15187159?u=7480e0eaf959e9c5dfe3a05286f2ea4588c0a3c6&v=4
url: https://github.com/pablonnaoji
- login: mjohnsey
avatarUrl: https://avatars.githubusercontent.com/u/16784016?u=38fad2e6b411244560b3af99c5f5a4751bc81865&v=4
url: https://github.com/mjohnsey
- login: abdalla19977
avatarUrl: https://avatars.githubusercontent.com/u/17257234?v=4
url: https://github.com/abdalla19977
- login: wedwardbeck
avatarUrl: https://avatars.githubusercontent.com/u/19333237?u=1de4ae2bf8d59eb4c013f21d863cbe0f2010575f&v=4
url: https://github.com/wedwardbeck
@@ -254,6 +251,9 @@ sponsors:
- login: shuheng-liu
avatarUrl: https://avatars.githubusercontent.com/u/22414322?u=813c45f30786c6b511b21a661def025d8f7b609e&v=4
url: https://github.com/shuheng-liu
- login: Pablongo24
avatarUrl: https://avatars.githubusercontent.com/u/24843427?u=78a6798469889d7a0690449fc667c39e13d5c6a9&v=4
url: https://github.com/Pablongo24
- login: Joeriksson
avatarUrl: https://avatars.githubusercontent.com/u/25037079?v=4
url: https://github.com/Joeriksson
@@ -284,9 +284,12 @@ sponsors:
- login: ProteinQure
avatarUrl: https://avatars.githubusercontent.com/u/33707203?v=4
url: https://github.com/ProteinQure
- login: AbdulwahabDev
avatarUrl: https://avatars.githubusercontent.com/u/34792253?u=9d27cbb6e196c95d747abf002df7fe0539764265&v=4
url: https://github.com/AbdulwahabDev
- login: askurihin
avatarUrl: https://avatars.githubusercontent.com/u/37978981?v=4
url: https://github.com/askurihin
- login: arleybri18
avatarUrl: https://avatars.githubusercontent.com/u/39681546?u=5c028f81324b0e8c73b3c15bc4e7b0218d2ba0c3&v=4
url: https://github.com/arleybri18
- login: ybressler
avatarUrl: https://avatars.githubusercontent.com/u/40807730?u=41e2c00f1eebe3c402635f0325e41b4e6511462c&v=4
url: https://github.com/ybressler
@@ -300,11 +303,14 @@ sponsors:
avatarUrl: https://avatars.githubusercontent.com/u/51059348?u=f8f0d6d6e90fac39fa786228158ba7f013c74271&v=4
url: https://github.com/rafsaf
- login: dudikbender
avatarUrl: https://avatars.githubusercontent.com/u/53487583?u=494f85229115076121b3639a3806bbac1c6ae7f6&v=4
avatarUrl: https://avatars.githubusercontent.com/u/53487583?u=3a57542938ebfd57579a0111db2b297e606d9681&v=4
url: https://github.com/dudikbender
- login: thisistheplace
avatarUrl: https://avatars.githubusercontent.com/u/57633545?u=a3f3a7f8ace8511c6c067753f6eb6aee0db11ac6&v=4
url: https://github.com/thisistheplace
- login: kyjoconn
avatarUrl: https://avatars.githubusercontent.com/u/58443406?u=a3e9c2acfb7ba62edda9334aba61cf027f41f789&v=4
url: https://github.com/kyjoconn
- login: A-Edge
avatarUrl: https://avatars.githubusercontent.com/u/59514131?v=4
url: https://github.com/A-Edge
@@ -317,9 +323,6 @@ sponsors:
- login: predictionmachine
avatarUrl: https://avatars.githubusercontent.com/u/63719559?v=4
url: https://github.com/predictionmachine
- login: minsau
avatarUrl: https://avatars.githubusercontent.com/u/64386242?u=7e45f24b2958caf946fa3546ea33bacf5cd886f8&v=4
url: https://github.com/minsau
- login: daverin
avatarUrl: https://avatars.githubusercontent.com/u/70378377?u=6d1814195c0de7162820eaad95a25b423a3869c0&v=4
url: https://github.com/daverin
@@ -329,16 +332,19 @@ sponsors:
- login: DelfinaCare
avatarUrl: https://avatars.githubusercontent.com/u/83734439?v=4
url: https://github.com/DelfinaCare
- login: programvx
avatarUrl: https://avatars.githubusercontent.com/u/96057906?v=4
url: https://github.com/programvx
- login: osawa-koki
avatarUrl: https://avatars.githubusercontent.com/u/94336223?u=59c6fe6945bcbbaff87b2a794238671b060620d2&v=4
url: https://github.com/osawa-koki
- login: pyt3h
avatarUrl: https://avatars.githubusercontent.com/u/99658549?v=4
url: https://github.com/pyt3h
- login: Dagmaara
avatarUrl: https://avatars.githubusercontent.com/u/115501964?v=4
url: https://github.com/Dagmaara
- - login: linux-china
- - login: pawamoy
avatarUrl: https://avatars.githubusercontent.com/u/3999221?u=b030e4c89df2f3a36bc4710b925bdeb6745c9856&v=4
url: https://github.com/pawamoy
- login: linux-china
avatarUrl: https://avatars.githubusercontent.com/u/46711?u=cd77c65338b158750eb84dc7ff1acf3209ccfc4f&v=4
url: https://github.com/linux-china
- login: ddanier
@@ -353,12 +359,6 @@ sponsors:
- login: bryanculbertson
avatarUrl: https://avatars.githubusercontent.com/u/144028?u=defda4f90e93429221cc667500944abde60ebe4a&v=4
url: https://github.com/bryanculbertson
- login: hhatto
avatarUrl: https://avatars.githubusercontent.com/u/150309?u=3e8f63c27bf996bfc68464b0ce3f7a3e40e6ea7f&v=4
url: https://github.com/hhatto
- login: slafs
avatarUrl: https://avatars.githubusercontent.com/u/210173?v=4
url: https://github.com/slafs
- login: adamghill
avatarUrl: https://avatars.githubusercontent.com/u/317045?u=f1349d5ffe84a19f324e204777859fbf69ddf633&v=4
url: https://github.com/adamghill
@@ -380,12 +380,6 @@ sponsors:
- login: janfilips
avatarUrl: https://avatars.githubusercontent.com/u/870699?u=50de77b93d3a0b06887e672d4e8c7b9d643085aa&v=4
url: https://github.com/janfilips
- login: woodrad
avatarUrl: https://avatars.githubusercontent.com/u/1410765?u=86707076bb03d143b3b11afc1743d2aa496bd8bf&v=4
url: https://github.com/woodrad
- login: Pytlicek
avatarUrl: https://avatars.githubusercontent.com/u/1430522?u=01b1f2f7671ce3131e0877d08e2e3f8bdbb0a38a&v=4
url: https://github.com/Pytlicek
- login: allen0125
avatarUrl: https://avatars.githubusercontent.com/u/1448456?u=dc2ad819497eef494b88688a1796e0adb87e7cae&v=4
url: https://github.com/allen0125
@@ -398,9 +392,6 @@ sponsors:
- login: cbonoz
avatarUrl: https://avatars.githubusercontent.com/u/2351087?u=fd3e8030b2cc9fbfbb54a65e9890c548a016f58b&v=4
url: https://github.com/cbonoz
- login: Debakel
avatarUrl: https://avatars.githubusercontent.com/u/2857237?u=07df6d11c8feef9306d071cb1c1005a2dd596585&v=4
url: https://github.com/Debakel
- login: paul121
avatarUrl: https://avatars.githubusercontent.com/u/3116995?u=6e2d8691cc345e63ee02e4eb4d7cef82b1fcbedc&v=4
url: https://github.com/paul121
@@ -410,12 +401,6 @@ sponsors:
- login: anthonycorletti
avatarUrl: https://avatars.githubusercontent.com/u/3477132?v=4
url: https://github.com/anthonycorletti
- login: jonathanhle
avatarUrl: https://avatars.githubusercontent.com/u/3851599?u=76b9c5d2fecd6c3a16e7645231878c4507380d4d&v=4
url: https://github.com/jonathanhle
- login: pawamoy
avatarUrl: https://avatars.githubusercontent.com/u/3999221?u=b030e4c89df2f3a36bc4710b925bdeb6745c9856&v=4
url: https://github.com/pawamoy
- login: nikeee
avatarUrl: https://avatars.githubusercontent.com/u/4068864?u=63f8eee593f25138e0f1032ef442e9ad24907d4c&v=4
url: https://github.com/nikeee
@@ -431,11 +416,14 @@ sponsors:
- login: Baghdady92
avatarUrl: https://avatars.githubusercontent.com/u/5708590?v=4
url: https://github.com/Baghdady92
- login: KentShikama
avatarUrl: https://avatars.githubusercontent.com/u/6329898?u=8b236810db9b96333230430837e1f021f9246da1&v=4
url: https://github.com/KentShikama
- login: holec
avatarUrl: https://avatars.githubusercontent.com/u/6438041?u=f5af71ec85b3a9d7b8139cb5af0512b02fa9ab1e&v=4
url: https://github.com/holec
- login: mattwelke
avatarUrl: https://avatars.githubusercontent.com/u/7719209?u=5d963ead289969257190b133250653bd99df06ba&v=4
avatarUrl: https://avatars.githubusercontent.com/u/7719209?v=4
url: https://github.com/mattwelke
- login: hcristea
avatarUrl: https://avatars.githubusercontent.com/u/7814406?u=61d7a4fcf846983a4606788eac25e1c6c1209ba8&v=4
@@ -443,9 +431,6 @@ sponsors:
- login: moonape1226
avatarUrl: https://avatars.githubusercontent.com/u/8532038?u=d9f8b855a429fff9397c3833c2ff83849ebf989d&v=4
url: https://github.com/moonape1226
- login: yenchenLiu
avatarUrl: https://avatars.githubusercontent.com/u/9199638?u=8cdf5ae507448430d90f6f3518d1665a23afe99b&v=4
url: https://github.com/yenchenLiu
- login: xncbf
avatarUrl: https://avatars.githubusercontent.com/u/9462045?u=866a1311e4bd3ec5ae84185c4fcc99f397c883d7&v=4
url: https://github.com/xncbf
@@ -470,12 +455,9 @@ sponsors:
- login: giuliano-oliveira
avatarUrl: https://avatars.githubusercontent.com/u/13181797?u=0ef2dfbf7fc9a9726d45c21d32b5d1038a174870&v=4
url: https://github.com/giuliano-oliveira
- login: logan-connolly
avatarUrl: https://avatars.githubusercontent.com/u/16244943?u=8ae66dfbba936463cc8aa0dd7a6d2b4c0cc757eb&v=4
url: https://github.com/logan-connolly
- login: harripj
avatarUrl: https://avatars.githubusercontent.com/u/16853829?u=14db1ad132af9ec340f3f1da564620a73b6e4981&v=4
url: https://github.com/harripj
- login: TheR1D
avatarUrl: https://avatars.githubusercontent.com/u/16740832?u=b2923ac17fe6e2a7c9ea14800351ddb92f79b100&v=4
url: https://github.com/TheR1D
- login: cdsre
avatarUrl: https://avatars.githubusercontent.com/u/16945936?v=4
url: https://github.com/cdsre
@@ -485,15 +467,9 @@ sponsors:
- login: paulowiz
avatarUrl: https://avatars.githubusercontent.com/u/18649504?u=d8a6ac40321f2bded0eba78b637751c7f86c6823&v=4
url: https://github.com/paulowiz
- login: yannicschroeer
avatarUrl: https://avatars.githubusercontent.com/u/22749683?u=91515328b5418a4e7289a83f0dcec3573f1a6500&v=4
url: https://github.com/yannicschroeer
- login: ghandic
avatarUrl: https://avatars.githubusercontent.com/u/23500353?u=e2e1d736f924d9be81e8bfc565b6d8836ba99773&v=4
url: https://github.com/ghandic
- login: fstau
avatarUrl: https://avatars.githubusercontent.com/u/24669867?u=60e7c8c09f8dafabee8fc3edcd6f9e19abbff918&v=4
url: https://github.com/fstau
- login: pers0n4
avatarUrl: https://avatars.githubusercontent.com/u/24864600?u=f211a13a7b572cbbd7779b9c8d8cb428cc7ba07e&v=4
url: https://github.com/pers0n4
@@ -534,26 +510,14 @@ sponsors:
avatarUrl: https://avatars.githubusercontent.com/u/38921751?u=ae14bc1e40f2dd5a9c5741fc0b0dffbd416a5fa9&v=4
url: https://github.com/ww-daniel-mora
- login: rwxd
avatarUrl: https://avatars.githubusercontent.com/u/40308458?u=34cba2eaca6a52072498e06bccebe141694fe1d7&v=4
avatarUrl: https://avatars.githubusercontent.com/u/40308458?u=cd04a39e3655923be4f25c2ba8a5a07b3da3230a&v=4
url: https://github.com/rwxd
- login: ilias-ant
avatarUrl: https://avatars.githubusercontent.com/u/42189572?u=a84d169eb6f6bbcb85434c2bed0b4a6d4d13c10e&v=4
url: https://github.com/ilias-ant
- login: arrrrrmin
avatarUrl: https://avatars.githubusercontent.com/u/43553423?u=2a812c1a2ec58227ed01778837f255143de9df97&v=4
avatarUrl: https://avatars.githubusercontent.com/u/43553423?u=5265858add14a6822bd145f7547323cf078563e6&v=4
url: https://github.com/arrrrrmin
- login: MauriceKuenicke
avatarUrl: https://avatars.githubusercontent.com/u/47433175?u=37455bc95c7851db296ac42626f0cacb77ca2443&v=4
url: https://github.com/MauriceKuenicke
- login: hgalytoby
avatarUrl: https://avatars.githubusercontent.com/u/50397689?u=f4888c2c54929bd86eed0d3971d09fcb306e5088&v=4
url: https://github.com/hgalytoby
- login: akanz1
avatarUrl: https://avatars.githubusercontent.com/u/51492342?u=2280f57134118714645e16b535c1a37adf6b369b&v=4
url: https://github.com/akanz1
- login: shidenko97
avatarUrl: https://avatars.githubusercontent.com/u/54946990?u=3fdc0caea36af9217dacf1cc7760c7ed9d67dcfe&v=4
url: https://github.com/shidenko97
- login: data-djinn
avatarUrl: https://avatars.githubusercontent.com/u/56449985?u=42146e140806908d49bd59ccc96f222abf587886&v=4
url: https://github.com/data-djinn
@@ -572,27 +536,12 @@ sponsors:
- login: realabja
avatarUrl: https://avatars.githubusercontent.com/u/66185192?u=001e2dd9297784f4218997981b4e6fa8357bb70b&v=4
url: https://github.com/realabja
- login: pondDevThai
avatarUrl: https://avatars.githubusercontent.com/u/71592181?u=08af9a59bccfd8f6b101de1005aa9822007d0a44&v=4
url: https://github.com/pondDevThai
- login: lukzmu
avatarUrl: https://avatars.githubusercontent.com/u/80778518?u=f636ad03cab8e8de15183fa81e768bfad3f515d0&v=4
url: https://github.com/lukzmu
- - login: chrislemke
avatarUrl: https://avatars.githubusercontent.com/u/11752694?u=70ceb6ee7c51d9a52302ab9220ffbf09eaa9c2a4&v=4
url: https://github.com/chrislemke
- login: gabrielmbmb
avatarUrl: https://avatars.githubusercontent.com/u/29572918?u=6d1e00b5d558e96718312ff910a2318f47cc3145&v=4
url: https://github.com/gabrielmbmb
- login: garydsong
avatarUrl: https://avatars.githubusercontent.com/u/105745865?u=03cc1aa9c978be0020e5a1ce1ecca323dd6c8d65&v=4
url: https://github.com/garydsong
- - login: Leon0824
avatarUrl: https://avatars.githubusercontent.com/u/1922026?v=4
url: https://github.com/Leon0824
- login: danburonline
avatarUrl: https://avatars.githubusercontent.com/u/34251194?u=2cad4388c1544e539ecb732d656e42fb07b4ff2d&v=4
url: https://github.com/danburonline
- login: buabaj
avatarUrl: https://avatars.githubusercontent.com/u/49881677?u=a85952891036eb448f86eb847902f25badd5f9f7&v=4
url: https://github.com/buabaj
- login: SoulPancake
avatarUrl: https://avatars.githubusercontent.com/u/70265851?u=9cdd82f2835da7d6a56a2e29e1369d5bf251e8f2&v=4
url: https://github.com/SoulPancake
- login: junah201
avatarUrl: https://avatars.githubusercontent.com/u/75025529?u=2451c256e888fa2a06bcfc0646d09b87ddb6a945&v=4
url: https://github.com/junah201

View File

@@ -1,162 +1,158 @@
maintainers:
- login: tiangolo
answers: 1956
prs: 372
answers: 1827
prs: 384
avatarUrl: https://avatars.githubusercontent.com/u/1326112?u=740f11212a731f56798f558ceddb0bd07642afa7&v=4
url: https://github.com/tiangolo
experts:
- login: Kludex
count: 400
count: 376
avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=62adc405ef418f4b6c8caa93d3eb8ab107bc4927&v=4
url: https://github.com/Kludex
- login: dmontagu
count: 262
count: 237
avatarUrl: https://avatars.githubusercontent.com/u/35119617?u=58ed2a45798a4339700e2f62b2e12e6e54bf0396&v=4
url: https://github.com/dmontagu
- login: ycd
count: 224
avatarUrl: https://avatars.githubusercontent.com/u/62724709?u=bba5af018423a2858d49309bed2a899bb5c34ac5&v=4
url: https://github.com/ycd
- login: Mause
count: 223
count: 220
avatarUrl: https://avatars.githubusercontent.com/u/1405026?v=4
url: https://github.com/Mause
- login: ycd
count: 217
avatarUrl: https://avatars.githubusercontent.com/u/62724709?u=bba5af018423a2858d49309bed2a899bb5c34ac5&v=4
url: https://github.com/ycd
- login: JarroVGIT
count: 196
count: 192
avatarUrl: https://avatars.githubusercontent.com/u/13659033?u=e8bea32d07a5ef72f7dde3b2079ceb714923ca05&v=4
url: https://github.com/JarroVGIT
- login: euri10
count: 166
count: 151
avatarUrl: https://avatars.githubusercontent.com/u/1104190?u=321a2e953e6645a7d09b732786c7a8061e0f8a8b&v=4
url: https://github.com/euri10
- login: phy25
count: 130
count: 126
avatarUrl: https://avatars.githubusercontent.com/u/331403?v=4
url: https://github.com/phy25
- login: iudeen
count: 118
count: 116
avatarUrl: https://avatars.githubusercontent.com/u/10519440?u=2843b3303282bff8b212dcd4d9d6689452e4470c&v=4
url: https://github.com/iudeen
- login: jgould22
count: 95
count: 101
avatarUrl: https://avatars.githubusercontent.com/u/4335847?u=ed77f67e0bb069084639b24d812dbb2a2b1dc554&v=4
url: https://github.com/jgould22
- login: raphaelauv
count: 79
count: 83
avatarUrl: https://avatars.githubusercontent.com/u/10202690?u=e6f86f5c0c3026a15d6b51792fa3e532b12f1371&v=4
url: https://github.com/raphaelauv
- login: ArcLightSlavik
count: 74
count: 71
avatarUrl: https://avatars.githubusercontent.com/u/31127044?u=b0f2c37142f4b762e41ad65dc49581813422bd71&v=4
url: https://github.com/ArcLightSlavik
- login: ghandic
count: 72
count: 71
avatarUrl: https://avatars.githubusercontent.com/u/23500353?u=e2e1d736f924d9be81e8bfc565b6d8836ba99773&v=4
url: https://github.com/ghandic
- login: falkben
count: 59
count: 57
avatarUrl: https://avatars.githubusercontent.com/u/653031?u=0c8d8f33d87f1aa1a6488d3f02105e9abc838105&v=4
url: https://github.com/falkben
- login: sm-Fifteen
count: 52
count: 49
avatarUrl: https://avatars.githubusercontent.com/u/516999?u=437c0c5038558c67e887ccd863c1ba0f846c03da&v=4
url: https://github.com/sm-Fifteen
- login: insomnes
count: 46
avatarUrl: https://avatars.githubusercontent.com/u/16958893?u=f8be7088d5076d963984a21f95f44e559192d912&v=4
url: https://github.com/insomnes
- login: Dustyposa
count: 45
avatarUrl: https://avatars.githubusercontent.com/u/27180793?u=5cf2877f50b3eb2bc55086089a78a36f07042889&v=4
url: https://github.com/Dustyposa
- login: acidjunk
count: 44
avatarUrl: https://avatars.githubusercontent.com/u/685002?u=b5094ab4527fc84b006c0ac9ff54367bdebb2267&v=4
url: https://github.com/acidjunk
- login: adriangb
count: 44
avatarUrl: https://avatars.githubusercontent.com/u/1755071?u=1e2c2c9b39f5c9b780fb933d8995cf08ec235a47&v=4
url: https://github.com/adriangb
- login: insomnes
count: 45
avatarUrl: https://avatars.githubusercontent.com/u/16958893?u=f8be7088d5076d963984a21f95f44e559192d912&v=4
url: https://github.com/insomnes
- login: frankie567
count: 41
count: 43
avatarUrl: https://avatars.githubusercontent.com/u/1144727?u=85c025e3fcc7bd79a5665c63ee87cdf8aae13374&v=4
url: https://github.com/frankie567
- login: acidjunk
count: 43
avatarUrl: https://avatars.githubusercontent.com/u/685002?u=b5094ab4527fc84b006c0ac9ff54367bdebb2267&v=4
url: https://github.com/acidjunk
- login: odiseo0
count: 42
avatarUrl: https://avatars.githubusercontent.com/u/87550035?u=16f9255804161c6ff3c8b7ef69848f0126bcd405&v=4
url: https://github.com/odiseo0
- login: adriangb
count: 40
avatarUrl: https://avatars.githubusercontent.com/u/1755071?u=1e2c2c9b39f5c9b780fb933d8995cf08ec235a47&v=4
url: https://github.com/adriangb
- login: includeamin
count: 40
avatarUrl: https://avatars.githubusercontent.com/u/11836741?u=8bd5ef7e62fe6a82055e33c4c0e0a7879ff8cfb6&v=4
url: https://github.com/includeamin
- login: odiseo0
count: 40
avatarUrl: https://avatars.githubusercontent.com/u/87550035?u=16f9255804161c6ff3c8b7ef69848f0126bcd405&v=4
url: https://github.com/odiseo0
- login: STeveShary
count: 37
avatarUrl: https://avatars.githubusercontent.com/u/5167622?u=de8f597c81d6336fcebc37b32dfd61a3f877160c&v=4
url: https://github.com/STeveShary
- login: chbndrhnns
count: 36
avatarUrl: https://avatars.githubusercontent.com/u/7534547?v=4
url: https://github.com/chbndrhnns
- login: krishnardt
count: 35
avatarUrl: https://avatars.githubusercontent.com/u/31960541?u=47f4829c77f4962ab437ffb7995951e41eeebe9b&v=4
url: https://github.com/krishnardt
- login: prostomarkeloff
count: 33
avatarUrl: https://avatars.githubusercontent.com/u/28061158?u=72309cc1f2e04e40fa38b29969cb4e9d3f722e7b&v=4
url: https://github.com/prostomarkeloff
- login: yinziyan1206
count: 33
count: 34
avatarUrl: https://avatars.githubusercontent.com/u/37829370?u=da44ca53aefd5c23f346fab8e9fd2e108294c179&v=4
url: https://github.com/yinziyan1206
- login: chbndrhnns
count: 34
avatarUrl: https://avatars.githubusercontent.com/u/7534547?v=4
url: https://github.com/chbndrhnns
- login: panla
count: 32
avatarUrl: https://avatars.githubusercontent.com/u/41326348?u=ba2fda6b30110411ecbf406d187907e2b420ac19&v=4
url: https://github.com/panla
- login: wshayes
count: 29
avatarUrl: https://avatars.githubusercontent.com/u/365303?u=07ca03c5ee811eb0920e633cc3c3db73dbec1aa5&v=4
url: https://github.com/wshayes
- login: prostomarkeloff
count: 28
avatarUrl: https://avatars.githubusercontent.com/u/28061158?u=72309cc1f2e04e40fa38b29969cb4e9d3f722e7b&v=4
url: https://github.com/prostomarkeloff
- login: dbanty
count: 26
avatarUrl: https://avatars.githubusercontent.com/u/43723790?u=9bcce836bbce55835291c5b2ac93a4e311f4b3c3&v=4
url: https://github.com/dbanty
- login: wshayes
count: 25
avatarUrl: https://avatars.githubusercontent.com/u/365303?u=07ca03c5ee811eb0920e633cc3c3db73dbec1aa5&v=4
url: https://github.com/wshayes
- login: SirTelemak
count: 24
count: 23
avatarUrl: https://avatars.githubusercontent.com/u/9435877?u=719327b7d2c4c62212456d771bfa7c6b8dbb9eac&v=4
url: https://github.com/SirTelemak
- login: acnebs
count: 22
avatarUrl: https://avatars.githubusercontent.com/u/9054108?u=c27e50269f1ef8ea950cc6f0268c8ec5cebbe9c9&v=4
url: https://github.com/acnebs
- login: nsidnev
count: 22
avatarUrl: https://avatars.githubusercontent.com/u/22559461?u=a9cc3238217e21dc8796a1a500f01b722adb082c&v=4
url: https://github.com/nsidnev
- login: chris-allnutt
- login: caeser1996
count: 21
avatarUrl: https://avatars.githubusercontent.com/u/565544?v=4
url: https://github.com/chris-allnutt
avatarUrl: https://avatars.githubusercontent.com/u/16540232?u=05d2beb8e034d584d0a374b99d8826327bd7f614&v=4
url: https://github.com/caeser1996
- login: rafsaf
count: 21
avatarUrl: https://avatars.githubusercontent.com/u/51059348?u=f8f0d6d6e90fac39fa786228158ba7f013c74271&v=4
url: https://github.com/rafsaf
- login: Hultner
count: 19
avatarUrl: https://avatars.githubusercontent.com/u/2669034?u=115e53df959309898ad8dc9443fbb35fee71df07&v=4
url: https://github.com/Hultner
- login: nsidnev
count: 20
avatarUrl: https://avatars.githubusercontent.com/u/22559461?u=a9cc3238217e21dc8796a1a500f01b722adb082c&v=4
url: https://github.com/nsidnev
- login: acnebs
count: 20
avatarUrl: https://avatars.githubusercontent.com/u/9054108?u=c27e50269f1ef8ea950cc6f0268c8ec5cebbe9c9&v=4
url: https://github.com/acnebs
- login: chris-allnutt
count: 20
avatarUrl: https://avatars.githubusercontent.com/u/565544?v=4
url: https://github.com/chris-allnutt
- login: retnikt
count: 19
count: 18
avatarUrl: https://avatars.githubusercontent.com/u/24581770?v=4
url: https://github.com/retnikt
- login: zoliknemet
count: 18
avatarUrl: https://avatars.githubusercontent.com/u/22326718?u=31ba446ac290e23e56eea8e4f0c558aaf0b40779&v=4
url: https://github.com/zoliknemet
- login: jorgerpo
count: 17
avatarUrl: https://avatars.githubusercontent.com/u/12537771?u=7444d20019198e34911082780cc7ad73f2b97cb3&v=4
url: https://github.com/jorgerpo
- login: nkhitrov
count: 17
avatarUrl: https://avatars.githubusercontent.com/u/28262306?u=66ee21316275ef356081c2efc4ed7a4572e690dc&v=4
@@ -165,75 +161,79 @@ experts:
count: 17
avatarUrl: https://avatars.githubusercontent.com/u/1765494?u=5b1ab7c582db4b4016fa31affe977d10af108ad4&v=4
url: https://github.com/harunyasar
- login: waynerv
count: 16
avatarUrl: https://avatars.githubusercontent.com/u/39515546?u=ec35139777597cdbbbddda29bf8b9d4396b429a9&v=4
url: https://github.com/waynerv
- login: dstlny
count: 16
avatarUrl: https://avatars.githubusercontent.com/u/41964673?u=9f2174f9d61c15c6e3a4c9e3aeee66f711ce311f&v=4
url: https://github.com/dstlny
- login: Hultner
count: 17
avatarUrl: https://avatars.githubusercontent.com/u/2669034?u=115e53df959309898ad8dc9443fbb35fee71df07&v=4
url: https://github.com/Hultner
- login: jonatasoli
count: 16
avatarUrl: https://avatars.githubusercontent.com/u/26334101?u=071c062d2861d3dd127f6b4a5258cd8ef55d4c50&v=4
url: https://github.com/jonatasoli
- login: hellocoldworld
- login: dstlny
count: 16
avatarUrl: https://avatars.githubusercontent.com/u/41964673?u=9f2174f9d61c15c6e3a4c9e3aeee66f711ce311f&v=4
url: https://github.com/dstlny
- login: jorgerpo
count: 15
avatarUrl: https://avatars.githubusercontent.com/u/47581948?u=3d2186796434c507a6cb6de35189ab0ad27c356f&v=4
url: https://github.com/hellocoldworld
- login: mbroton
avatarUrl: https://avatars.githubusercontent.com/u/12537771?u=7444d20019198e34911082780cc7ad73f2b97cb3&v=4
url: https://github.com/jorgerpo
- login: ghost
count: 15
avatarUrl: https://avatars.githubusercontent.com/u/50829834?u=a48610bf1bffaa9c75d03228926e2eb08a2e24ee&v=4
url: https://github.com/mbroton
avatarUrl: https://avatars.githubusercontent.com/u/10137?u=b1951d34a583cf12ec0d3b0781ba19be97726318&v=4
url: https://github.com/ghost
- login: simondale00
count: 15
avatarUrl: https://avatars.githubusercontent.com/u/33907262?v=4
url: https://github.com/simondale00
- login: haizaar
- login: hellocoldworld
count: 14
avatarUrl: https://avatars.githubusercontent.com/u/47581948?u=3d2186796434c507a6cb6de35189ab0ad27c356f&v=4
url: https://github.com/hellocoldworld
- login: waynerv
count: 14
avatarUrl: https://avatars.githubusercontent.com/u/39515546?u=ec35139777597cdbbbddda29bf8b9d4396b429a9&v=4
url: https://github.com/waynerv
- login: mbroton
count: 13
avatarUrl: https://avatars.githubusercontent.com/u/58201?u=dd40d99a3e1935d0b768f122bfe2258d6ea53b2b&v=4
url: https://github.com/haizaar
- login: n8sty
count: 13
avatarUrl: https://avatars.githubusercontent.com/u/2964996?v=4
url: https://github.com/n8sty
avatarUrl: https://avatars.githubusercontent.com/u/50829834?u=a48610bf1bffaa9c75d03228926e2eb08a2e24ee&v=4
url: https://github.com/mbroton
last_month_active:
- login: jgould22
- login: mr-st0rm
count: 7
avatarUrl: https://avatars.githubusercontent.com/u/4335847?u=ed77f67e0bb069084639b24d812dbb2a2b1dc554&v=4
url: https://github.com/jgould22
- login: yinziyan1206
count: 6
avatarUrl: https://avatars.githubusercontent.com/u/37829370?u=da44ca53aefd5c23f346fab8e9fd2e108294c179&v=4
url: https://github.com/yinziyan1206
- login: Kludex
count: 6
avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=62adc405ef418f4b6c8caa93d3eb8ab107bc4927&v=4
url: https://github.com/Kludex
- login: moadennagi
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/16942283?v=4
url: https://github.com/moadennagi
- login: iudeen
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/10519440?u=2843b3303282bff8b212dcd4d9d6689452e4470c&v=4
url: https://github.com/iudeen
- login: anthonycorletti
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/3477132?v=4
url: https://github.com/anthonycorletti
- login: ThirVondukr
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/50728601?u=56010d6430583b2096a96f0946501156cdb79c75&v=4
url: https://github.com/ThirVondukr
avatarUrl: https://avatars.githubusercontent.com/u/48455163?u=6b83550e4e70bea57cd2fdb41e717aeab7f64a91&v=4
url: https://github.com/mr-st0rm
- login: caeser1996
count: 7
avatarUrl: https://avatars.githubusercontent.com/u/16540232?u=05d2beb8e034d584d0a374b99d8826327bd7f614&v=4
url: https://github.com/caeser1996
- login: ebottos94
count: 4
count: 6
avatarUrl: https://avatars.githubusercontent.com/u/100039558?u=e2c672da5a7977fd24d87ce6ab35f8bf5b1ed9fa&v=4
url: https://github.com/ebottos94
- login: odiseo0
- login: jgould22
count: 6
avatarUrl: https://avatars.githubusercontent.com/u/4335847?u=ed77f67e0bb069084639b24d812dbb2a2b1dc554&v=4
url: https://github.com/jgould22
- login: Kludex
count: 5
avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=62adc405ef418f4b6c8caa93d3eb8ab107bc4927&v=4
url: https://github.com/Kludex
- login: clemens-tolboom
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/371014?v=4
url: https://github.com/clemens-tolboom
- login: williamjamir
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/5083518?u=b76ca8e08b906a86fa195fb817dd94e8d9d3d8f6&v=4
url: https://github.com/williamjamir
- login: nymous
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/87550035?u=16f9255804161c6ff3c8b7ef69848f0126bcd405&v=4
url: https://github.com/odiseo0
avatarUrl: https://avatars.githubusercontent.com/u/4216559?u=360a36fb602cded27273cbfc0afc296eece90662&v=4
url: https://github.com/nymous
- login: frankie567
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/1144727?u=85c025e3fcc7bd79a5665c63ee87cdf8aae13374&v=4
url: https://github.com/frankie567
top_contributors:
- login: waynerv
count: 25
@@ -243,6 +243,10 @@ top_contributors:
count: 22
avatarUrl: https://avatars.githubusercontent.com/u/41147016?u=55010621aece725aa702270b54fed829b6a1fe60&v=4
url: https://github.com/tokusumi
- login: Kludex
count: 17
avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=62adc405ef418f4b6c8caa93d3eb8ab107bc4927&v=4
url: https://github.com/Kludex
- login: jaystone776
count: 17
avatarUrl: https://avatars.githubusercontent.com/u/11191137?u=299205a95e9b6817a43144a48b643346a5aac5cc&v=4
@@ -251,10 +255,6 @@ top_contributors:
count: 16
avatarUrl: https://avatars.githubusercontent.com/u/35119617?u=58ed2a45798a4339700e2f62b2e12e6e54bf0396&v=4
url: https://github.com/dmontagu
- login: Kludex
count: 16
avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=62adc405ef418f4b6c8caa93d3eb8ab107bc4927&v=4
url: https://github.com/Kludex
- login: euri10
count: 13
avatarUrl: https://avatars.githubusercontent.com/u/1104190?u=321a2e953e6645a7d09b732786c7a8061e0f8a8b&v=4
@@ -283,6 +283,10 @@ top_contributors:
count: 7
avatarUrl: https://avatars.githubusercontent.com/u/56785022?u=d5c3a02567c8649e146fcfc51b6060ccaf8adef8&v=4
url: https://github.com/rjNemo
- login: batlopes
count: 6
avatarUrl: https://avatars.githubusercontent.com/u/33462923?u=0fb3d7acb316764616f11e4947faf080e49ad8d9&v=4
url: https://github.com/batlopes
- login: wshayes
count: 5
avatarUrl: https://avatars.githubusercontent.com/u/365303?u=07ca03c5ee811eb0920e633cc3c3db73dbec1aa5&v=4
@@ -301,12 +305,12 @@ top_contributors:
url: https://github.com/ComicShrimp
- login: NinaHwang
count: 5
avatarUrl: https://avatars.githubusercontent.com/u/79563565?u=1741703bd6c8f491503354b363a86e879b4c1cab&v=4
avatarUrl: https://avatars.githubusercontent.com/u/79563565?u=eee6bfe9224c71193025ab7477f4f96ceaa05c62&v=4
url: https://github.com/NinaHwang
- login: batlopes
- login: Xewus
count: 5
avatarUrl: https://avatars.githubusercontent.com/u/33462923?u=0fb3d7acb316764616f11e4947faf080e49ad8d9&v=4
url: https://github.com/batlopes
avatarUrl: https://avatars.githubusercontent.com/u/85196001?u=f8e2dc7e5104f109cef944af79050ea8d1b8f914&v=4
url: https://github.com/Xewus
- login: jekirl
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/2546697?u=a027452387d85bd4a14834e19d716c99255fb3b7&v=4
@@ -335,21 +339,17 @@ top_contributors:
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/61513630?u=320e43fe4dc7bc6efc64e9b8f325f8075634fd20&v=4
url: https://github.com/lsglucas
- login: Xewus
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/85196001?u=4bdd4a0300530a504987db27488ba79c37f2fb18&v=4
url: https://github.com/Xewus
top_reviewers:
- login: Kludex
count: 110
count: 111
avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=62adc405ef418f4b6c8caa93d3eb8ab107bc4927&v=4
url: https://github.com/Kludex
- login: BilalAlpaslan
count: 72
count: 75
avatarUrl: https://avatars.githubusercontent.com/u/47563997?u=63ed66e304fe8d765762c70587d61d9196e5c82d&v=4
url: https://github.com/BilalAlpaslan
- login: yezz123
count: 70
count: 71
avatarUrl: https://avatars.githubusercontent.com/u/52716203?u=636b4f79645176df4527dd45c12d5dbb5a4193cf&v=4
url: https://github.com/yezz123
- login: tokusumi
@@ -404,24 +404,24 @@ top_reviewers:
count: 23
avatarUrl: https://avatars.githubusercontent.com/u/35119617?u=58ed2a45798a4339700e2f62b2e12e6e54bf0396&v=4
url: https://github.com/dmontagu
- login: LorhanSohaky
count: 22
avatarUrl: https://avatars.githubusercontent.com/u/16273730?u=095b66f243a2cd6a0aadba9a095009f8aaf18393&v=4
url: https://github.com/LorhanSohaky
- login: rjNemo
count: 20
avatarUrl: https://avatars.githubusercontent.com/u/56785022?u=d5c3a02567c8649e146fcfc51b6060ccaf8adef8&v=4
url: https://github.com/rjNemo
- login: hard-coders
count: 20
avatarUrl: https://avatars.githubusercontent.com/u/9651103?u=95db33927bbff1ed1c07efddeb97ac2ff33068ed&v=4
url: https://github.com/hard-coders
- login: LorhanSohaky
count: 19
avatarUrl: https://avatars.githubusercontent.com/u/16273730?u=095b66f243a2cd6a0aadba9a095009f8aaf18393&v=4
url: https://github.com/LorhanSohaky
- login: 0417taehyun
count: 19
avatarUrl: https://avatars.githubusercontent.com/u/63915557?u=47debaa860fd52c9b98c97ef357ddcec3b3fb399&v=4
url: https://github.com/0417taehyun
- login: rjNemo
count: 18
avatarUrl: https://avatars.githubusercontent.com/u/56785022?u=d5c3a02567c8649e146fcfc51b6060ccaf8adef8&v=4
url: https://github.com/rjNemo
- login: odiseo0
count: 18
count: 19
avatarUrl: https://avatars.githubusercontent.com/u/87550035?u=16f9255804161c6ff3c8b7ef69848f0126bcd405&v=4
url: https://github.com/odiseo0
- login: Smlep
@@ -452,26 +452,30 @@ top_reviewers:
count: 15
avatarUrl: https://avatars.githubusercontent.com/u/63476957?u=6c86e59b48e0394d4db230f37fc9ad4d7e2c27c7&v=4
url: https://github.com/delhi09
- login: Ryandaydev
count: 15
avatarUrl: https://avatars.githubusercontent.com/u/4292423?u=809f3d1074d04bbc28012a7f17f06ea56f5bd71a&v=4
url: https://github.com/Ryandaydev
- login: Xewus
count: 14
avatarUrl: https://avatars.githubusercontent.com/u/85196001?u=f8e2dc7e5104f109cef944af79050ea8d1b8f914&v=4
url: https://github.com/Xewus
- login: sh0nk
count: 13
avatarUrl: https://avatars.githubusercontent.com/u/6478810?u=af15d724875cec682ed8088a86d36b2798f981c0&v=4
url: https://github.com/sh0nk
- login: peidrao
count: 13
avatarUrl: https://avatars.githubusercontent.com/u/32584628?u=5401640e0b961cc199dee39ec79e162c7833cd6b&v=4
url: https://github.com/peidrao
- login: RunningIkkyu
count: 12
avatarUrl: https://avatars.githubusercontent.com/u/31848542?u=494ecc298e3f26197495bb357ad0f57cfd5f7a32&v=4
url: https://github.com/RunningIkkyu
- login: Ryandaydev
count: 12
avatarUrl: https://avatars.githubusercontent.com/u/4292423?u=809f3d1074d04bbc28012a7f17f06ea56f5bd71a&v=4
url: https://github.com/Ryandaydev
- login: solomein-sv
count: 11
avatarUrl: https://avatars.githubusercontent.com/u/46193920?u=46acfb4aeefb1d7b9fdc5a8cbd9eb8744683c47a&v=4
url: https://github.com/solomein-sv
- login: Xewus
count: 11
avatarUrl: https://avatars.githubusercontent.com/u/85196001?u=4bdd4a0300530a504987db27488ba79c37f2fb18&v=4
url: https://github.com/Xewus
- login: mariacamilagl
count: 10
avatarUrl: https://avatars.githubusercontent.com/u/11489395?u=4adb6986bf3debfc2b8216ae701f2bd47d73da7d&v=4
@@ -488,10 +492,10 @@ top_reviewers:
count: 10
avatarUrl: https://avatars.githubusercontent.com/u/43503750?u=f440bc9062afb3c43b9b9c6cdfdcfe31d58699ef&v=4
url: https://github.com/ComicShrimp
- login: peidrao
- login: r0b2g1t
count: 10
avatarUrl: https://avatars.githubusercontent.com/u/32584628?u=5401640e0b961cc199dee39ec79e162c7833cd6b&v=4
url: https://github.com/peidrao
avatarUrl: https://avatars.githubusercontent.com/u/5357541?u=6428442d875d5d71aaa1bb38bb11c4be1a526bc2&v=4
url: https://github.com/r0b2g1t
- login: izaguerreiro
count: 9
avatarUrl: https://avatars.githubusercontent.com/u/2241504?v=4
@@ -532,7 +536,3 @@ top_reviewers:
count: 8
avatarUrl: https://avatars.githubusercontent.com/u/5690226?v=4
url: https://github.com/rogerbrinkmann
- login: NinaHwang
count: 8
avatarUrl: https://avatars.githubusercontent.com/u/79563565?u=1741703bd6c8f491503354b363a86e879b4c1cab&v=4
url: https://github.com/NinaHwang

View File

@@ -15,3 +15,4 @@ logins:
- Doist
- nihpo
- svix
- armand-sauzay

View File

@@ -14,9 +14,41 @@ But you also want it to accept new items. And when the items didn't exist before
To achieve that, import `JSONResponse`, and return your content there directly, setting the `status_code` that you want:
```Python hl_lines="4 25"
{!../../../docs_src/additional_status_codes/tutorial001.py!}
```
=== "Python 3.10+"
```Python hl_lines="4 25"
{!> ../../../docs_src/additional_status_codes/tutorial001_an_py310.py!}
```
=== "Python 3.9+"
```Python hl_lines="4 25"
{!> ../../../docs_src/additional_status_codes/tutorial001_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="4 26"
{!> ../../../docs_src/additional_status_codes/tutorial001_an.py!}
```
=== "Python 3.10+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="2 23"
{!> ../../../docs_src/additional_status_codes/tutorial001_py310.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="4 25"
{!> ../../../docs_src/additional_status_codes/tutorial001.py!}
```
!!! warning
When you return a `Response` directly, like in the example above, it will be returned directly.

View File

@@ -18,9 +18,26 @@ Not the class itself (which is already a callable), but an instance of that clas
To do that, we declare a method `__call__`:
```Python hl_lines="10"
{!../../../docs_src/dependencies/tutorial011.py!}
```
=== "Python 3.9+"
```Python hl_lines="12"
{!> ../../../docs_src/dependencies/tutorial011_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="11"
{!> ../../../docs_src/dependencies/tutorial011_an.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="10"
{!> ../../../docs_src/dependencies/tutorial011.py!}
```
In this case, this `__call__` is what **FastAPI** will use to check for additional parameters and sub-dependencies, and this is what will be called to pass a value to the parameter in your *path operation function* later.
@@ -28,9 +45,26 @@ In this case, this `__call__` is what **FastAPI** will use to check for addition
And now, we can use `__init__` to declare the parameters of the instance that we can use to "parameterize" the dependency:
```Python hl_lines="7"
{!../../../docs_src/dependencies/tutorial011.py!}
```
=== "Python 3.9+"
```Python hl_lines="9"
{!> ../../../docs_src/dependencies/tutorial011_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="8"
{!> ../../../docs_src/dependencies/tutorial011_an.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="7"
{!> ../../../docs_src/dependencies/tutorial011.py!}
```
In this case, **FastAPI** won't ever touch or care about `__init__`, we will use it directly in our code.
@@ -38,9 +72,26 @@ In this case, **FastAPI** won't ever touch or care about `__init__`, we will use
We could create an instance of this class with:
```Python hl_lines="16"
{!../../../docs_src/dependencies/tutorial011.py!}
```
=== "Python 3.9+"
```Python hl_lines="18"
{!> ../../../docs_src/dependencies/tutorial011_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="17"
{!> ../../../docs_src/dependencies/tutorial011_an.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="16"
{!> ../../../docs_src/dependencies/tutorial011.py!}
```
And that way we are able to "parameterize" our dependency, that now has `"bar"` inside of it, as the attribute `checker.fixed_content`.
@@ -56,9 +107,26 @@ checker(q="somequery")
...and pass whatever that returns as the value of the dependency in our *path operation function* as the parameter `fixed_content_included`:
```Python hl_lines="20"
{!../../../docs_src/dependencies/tutorial011.py!}
```
=== "Python 3.9+"
```Python hl_lines="22"
{!> ../../../docs_src/dependencies/tutorial011_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="21"
{!> ../../../docs_src/dependencies/tutorial011_an.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="20"
{!> ../../../docs_src/dependencies/tutorial011.py!}
```
!!! tip
All this might seem contrived. And it might not be very clear how is it useful yet.

View File

@@ -1,13 +1,108 @@
# Events: startup - shutdown
# Lifespan Events
You can define logic (code) that should be executed before the application **starts up**. This means that this code will be executed **once**, **before** the application **starts receiving requests**.
The same way, you can define logic (code) that should be executed when the application is **shutting down**. In this case, this code will be executed **once**, **after** having handled possibly **many requests**.
Because this code is executed before the application **starts** taking requests, and right after it **finishes** handling requests, it covers the whole application **lifespan** (the word "lifespan" will be important in a second 😉).
This can be very useful for setting up **resources** that you need to use for the whole app, and that are **shared** among requests, and/or that you need to **clean up** afterwards. For example, a database connection pool, or loading a shared machine learning model.
## Use Case
Let's start with an example **use case** and then see how to solve it with this.
Let's imagine that you have some **machine learning models** that you want to use to handle requests. 🤖
The same models are shared among requests, so, it's not one model per request, or one per user or something similar.
Let's imagine that loading the model can **take quite some time**, because it has to read a lot of **data from disk**. So you don't want to do it for every request.
You could load it at the top level of the module/file, but that would also mean that it would **load the model** even if you are just running a simple automated test, then that test would be **slow** because it would have to wait for the model to load before being able to run an independent part of the code.
That's what we'll solve, let's load the model before the requests are handled, but only right before the application starts receiving requests, not while the code is being loaded.
## Lifespan
You can define this *startup* and *shutdown* logic using the `lifespan` parameter of the `FastAPI` app, and a "context manager" (I'll show you what that is in a second).
Let's start with an example and then see it in detail.
We create an async function `lifespan()` with `yield` like this:
```Python hl_lines="16 19"
{!../../../docs_src/events/tutorial003.py!}
```
Here we are simulating the expensive *startup* operation of loading the model by putting the (fake) model function in the dictionary with machine learning models before the `yield`. This code will be executed **before** the application **starts taking requests**, during the *startup*.
And then, right after the `yield`, we unload the model. This code will be executed **after** the application **finishes handling requests**, right before the *shutdown*. This could, for example, release resources like memory or a GPU.
!!! tip
The `shutdown` would happen when you are **stopping** the application.
Maybe you need to start a new version, or you just got tired of running it. 🤷
### Lifespan function
The first thing to notice, is that we are defining an async function with `yield`. This is very similar to Dependencies with `yield`.
```Python hl_lines="14-19"
{!../../../docs_src/events/tutorial003.py!}
```
The first part of the function, before the `yield`, will be executed **before** the application starts.
And the part after the `yield` will be executed **after** the application has finished.
### Async Context Manager
If you check, the function is decorated with an `@asynccontextmanager`.
That converts the function into something called an "**async context manager**".
```Python hl_lines="1 13"
{!../../../docs_src/events/tutorial003.py!}
```
A **context manager** in Python is something that you can use in a `with` statement, for example, `open()` can be used as a context manager:
```Python
with open("file.txt") as file:
file.read()
```
In recent versions of Python, there's also an **async context manager**. You would use it with `async with`:
```Python
async with lifespan(app):
await do_stuff()
```
When you create a context manager or an async context manager like above, what it does is that, before entering the `with` block, it will execute the code before the `yield`, and after exiting the `with` block, it will execute the code after the `yield`.
In our code example above, we don't use it directly, but we pass it to FastAPI for it to use it.
The `lifespan` parameter of the `FastAPI` app takes an **async context manager**, so we can pass our new `lifespan` async context manager to it.
```Python hl_lines="22"
{!../../../docs_src/events/tutorial003.py!}
```
## Alternative Events (deprecated)
!!! warning
The recommended way to handle the *startup* and *shutdown* is using the `lifespan` parameter of the `FastAPI` app as described above.
You can probably skip this part.
There's an alternative way to define this logic to be executed during *startup* and during *shutdown*.
You can define event handlers (functions) that need to be executed before the application starts up, or when the application is shutting down.
These functions can be declared with `async def` or normal `def`.
!!! warning
Only event handlers for the main application will be executed, not for [Sub Applications - Mounts](./sub-applications.md){.internal-link target=_blank}.
## `startup` event
### `startup` event
To add a function that should be run before the application starts, declare it with the event `"startup"`:
@@ -21,7 +116,7 @@ You can add more than one event handler function.
And your application won't start receiving requests until all the `startup` event handlers have completed.
## `shutdown` event
### `shutdown` event
To add a function that should be run when the application is shutting down, declare it with the event `"shutdown"`:
@@ -43,5 +138,25 @@ Here, the `shutdown` event handler function will write a text line `"Application
So, we declare the event handler function with standard `def` instead of `async def`.
### `startup` and `shutdown` together
There's a high chance that the logic for your *startup* and *shutdown* is connected, you might want to start something and then finish it, acquire a resource and then release it, etc.
Doing that in separated functions that don't share logic or variables together is more difficult as you would need to store values in global variables or similar tricks.
Because of that, it's now recommended to instead use the `lifespan` as explained above.
## Technical Details
Just a technical detail for the curious nerds. 🤓
Underneath, in the ASGI technical specification, this is part of the <a href="https://asgi.readthedocs.io/en/latest/specs/lifespan.html" class="external-link" target="_blank">Lifespan Protocol</a>, and it defines events called `startup` and `shutdown`.
!!! info
You can read more about these event handlers in <a href="https://www.starlette.io/events/" class="external-link" target="_blank">Starlette's Events' docs</a>.
You can read more about the Starlette `lifespan` handlers in <a href="https://www.starlette.io/lifespan/" class="external-link" target="_blank">Starlette's Lifespan' docs</a>.
Including how to handle lifespan state that can be used in other areas of your code.
## Sub Applications
🚨 Have in mind that these lifespan events (startup and shutdown) will only be executed for the main application, not for [Sub Applications - Mounts](./sub-applications.md){.internal-link target=_blank}.

View File

@@ -16,18 +16,18 @@ If you are building a **frontend**, a very interesting alternative is <a href="h
Let's start with a simple FastAPI application:
=== "Python 3.6 and above"
```Python hl_lines="9-11 14-15 18 19 23"
{!> ../../../docs_src/generate_clients/tutorial001.py!}
```
=== "Python 3.9 and above"
=== "Python 3.9+"
```Python hl_lines="7-9 12-13 16-17 21"
{!> ../../../docs_src/generate_clients/tutorial001_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="9-11 14-15 18 19 23"
{!> ../../../docs_src/generate_clients/tutorial001.py!}
```
Notice that the *path operations* define the models they use for request payload and response payload, using the models `Item` and `ResponseMessage`.
### API Docs
@@ -128,19 +128,18 @@ In many cases your FastAPI app will be bigger, and you will probably use tags to
For example, you could have a section for **items** and another section for **users**, and they could be separated by tags:
=== "Python 3.6 and above"
```Python hl_lines="23 28 36"
{!> ../../../docs_src/generate_clients/tutorial002.py!}
```
=== "Python 3.9 and above"
=== "Python 3.9+"
```Python hl_lines="21 26 34"
{!> ../../../docs_src/generate_clients/tutorial002_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="23 28 36"
{!> ../../../docs_src/generate_clients/tutorial002.py!}
```
### Generate a TypeScript Client with Tags
If you generate a client for a FastAPI app using tags, it will normally also separate the client code based on the tags.
@@ -186,18 +185,18 @@ For example, here it is using the first tag (you will probably have only one tag
You can then pass that custom function to **FastAPI** as the `generate_unique_id_function` parameter:
=== "Python 3.6 and above"
```Python hl_lines="8-9 12"
{!> ../../../docs_src/generate_clients/tutorial003.py!}
```
=== "Python 3.9 and above"
=== "Python 3.9+"
```Python hl_lines="6-7 10"
{!> ../../../docs_src/generate_clients/tutorial003_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="8-9 12"
{!> ../../../docs_src/generate_clients/tutorial003.py!}
```
### Generate a TypeScript Client with Custom Operation IDs
Now if you generate the client again, you will see that it has the improved method names:

View File

@@ -92,7 +92,7 @@ There are many other ASGI middlewares.
For example:
* <a href="https://docs.sentry.io/platforms/python/asgi/" class="external-link" target="_blank">Sentry</a>
* <a href="https://docs.sentry.io/platforms/python/guides/fastapi/" class="external-link" target="_blank">Sentry</a>
* <a href="https://github.com/encode/uvicorn/blob/master/uvicorn/middleware/proxy_headers.py" class="external-link" target="_blank">Uvicorn's `ProxyHeadersMiddleware`</a>
* <a href="https://github.com/florimondmanca/msgpack-asgi" class="external-link" target="_blank">MessagePack</a>

View File

@@ -20,9 +20,26 @@ Then, when you type that username and password, the browser sends them in the he
* It returns an object of type `HTTPBasicCredentials`:
* It contains the `username` and `password` sent.
```Python hl_lines="2 6 10"
{!../../../docs_src/security/tutorial006.py!}
```
=== "Python 3.9+"
```Python hl_lines="4 8 12"
{!> ../../../docs_src/security/tutorial006_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="2 7 11"
{!> ../../../docs_src/security/tutorial006_an.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="2 6 10"
{!> ../../../docs_src/security/tutorial006.py!}
```
When you try to open the URL for the first time (or click the "Execute" button in the docs) the browser will ask you for your username and password:
@@ -42,9 +59,26 @@ To handle that, we first convert the `username` and `password` to `bytes` encodi
Then we can use `secrets.compare_digest()` to ensure that `credentials.username` is `"stanleyjobson"`, and that `credentials.password` is `"swordfish"`.
```Python hl_lines="1 11-21"
{!../../../docs_src/security/tutorial007.py!}
```
=== "Python 3.9+"
```Python hl_lines="1 12-24"
{!> ../../../docs_src/security/tutorial007_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="1 12-24"
{!> ../../../docs_src/security/tutorial007_an.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="1 11-21"
{!> ../../../docs_src/security/tutorial007.py!}
```
This would be similar to:
@@ -108,6 +142,23 @@ That way, using `secrets.compare_digest()` in your application code, it will be
After detecting that the credentials are incorrect, return an `HTTPException` with a status code 401 (the same returned when no credentials are provided) and add the header `WWW-Authenticate` to make the browser show the login prompt again:
```Python hl_lines="23-27"
{!../../../docs_src/security/tutorial007.py!}
```
=== "Python 3.9+"
```Python hl_lines="26-30"
{!> ../../../docs_src/security/tutorial007_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="26-30"
{!> ../../../docs_src/security/tutorial007_an.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="23-27"
{!> ../../../docs_src/security/tutorial007.py!}
```

View File

@@ -56,9 +56,50 @@ They are normally used to declare specific security permissions, for example:
First, let's quickly see the parts that change from the examples in the main **Tutorial - User Guide** for [OAuth2 with Password (and hashing), Bearer with JWT tokens](../../tutorial/security/oauth2-jwt.md){.internal-link target=_blank}. Now using OAuth2 scopes:
```Python hl_lines="2 4 8 12 46 64 105 107-115 121-124 128-134 139 153"
{!../../../docs_src/security/tutorial005.py!}
```
=== "Python 3.10+"
```Python hl_lines="4 8 12 46 64 105 107-115 121-124 128-134 139 155"
{!> ../../../docs_src/security/tutorial005_an_py310.py!}
```
=== "Python 3.9+"
```Python hl_lines="2 4 8 12 46 64 105 107-115 121-124 128-134 139 155"
{!> ../../../docs_src/security/tutorial005_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="2 4 8 12 47 65 106 108-116 122-125 129-135 140 156"
{!> ../../../docs_src/security/tutorial005_an.py!}
```
=== "Python 3.10+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="3 7 11 45 63 104 106-114 120-123 127-133 138 152"
{!> ../../../docs_src/security/tutorial005_py310.py!}
```
=== "Python 3.9+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="2 4 8 12 46 64 105 107-115 121-124 128-134 139 153"
{!> ../../../docs_src/security/tutorial005_py39.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="2 4 8 12 46 64 105 107-115 121-124 128-134 139 153"
{!> ../../../docs_src/security/tutorial005.py!}
```
Now let's review those changes step by step.
@@ -68,9 +109,51 @@ The first change is that now we are declaring the OAuth2 security scheme with tw
The `scopes` parameter receives a `dict` with each scope as a key and the description as the value:
```Python hl_lines="62-65"
{!../../../docs_src/security/tutorial005.py!}
```
=== "Python 3.10+"
```Python hl_lines="62-65"
{!> ../../../docs_src/security/tutorial005_an_py310.py!}
```
=== "Python 3.9+"
```Python hl_lines="62-65"
{!> ../../../docs_src/security/tutorial005_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="63-66"
{!> ../../../docs_src/security/tutorial005_an.py!}
```
=== "Python 3.10+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="61-64"
{!> ../../../docs_src/security/tutorial005_py310.py!}
```
=== "Python 3.9+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="62-65"
{!> ../../../docs_src/security/tutorial005_py39.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="62-65"
{!> ../../../docs_src/security/tutorial005.py!}
```
Because we are now declaring those scopes, they will show up in the API docs when you log-in/authorize.
@@ -93,9 +176,50 @@ And we return the scopes as part of the JWT token.
But in your application, for security, you should make sure you only add the scopes that the user is actually able to have, or the ones you have predefined.
```Python hl_lines="153"
{!../../../docs_src/security/tutorial005.py!}
```
=== "Python 3.10+"
```Python hl_lines="155"
{!> ../../../docs_src/security/tutorial005_an_py310.py!}
```
=== "Python 3.9+"
```Python hl_lines="155"
{!> ../../../docs_src/security/tutorial005_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="156"
{!> ../../../docs_src/security/tutorial005_an.py!}
```
=== "Python 3.10+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="152"
{!> ../../../docs_src/security/tutorial005_py310.py!}
```
=== "Python 3.9+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="153"
{!> ../../../docs_src/security/tutorial005_py39.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="153"
{!> ../../../docs_src/security/tutorial005.py!}
```
## Declare scopes in *path operations* and dependencies
@@ -118,9 +242,50 @@ In this case, it requires the scope `me` (it could require more than one scope).
We are doing it here to demonstrate how **FastAPI** handles scopes declared at different levels.
```Python hl_lines="4 139 166"
{!../../../docs_src/security/tutorial005.py!}
```
=== "Python 3.10+"
```Python hl_lines="4 139 170"
{!> ../../../docs_src/security/tutorial005_an_py310.py!}
```
=== "Python 3.9+"
```Python hl_lines="4 139 170"
{!> ../../../docs_src/security/tutorial005_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="4 140 171"
{!> ../../../docs_src/security/tutorial005_an.py!}
```
=== "Python 3.10+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="3 138 165"
{!> ../../../docs_src/security/tutorial005_py310.py!}
```
=== "Python 3.9+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="4 139 166"
{!> ../../../docs_src/security/tutorial005_py39.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="4 139 166"
{!> ../../../docs_src/security/tutorial005.py!}
```
!!! info "Technical Details"
`Security` is actually a subclass of `Depends`, and it has just one extra parameter that we'll see later.
@@ -143,9 +308,50 @@ We also declare a special parameter of type `SecurityScopes`, imported from `fas
This `SecurityScopes` class is similar to `Request` (`Request` was used to get the request object directly).
```Python hl_lines="8 105"
{!../../../docs_src/security/tutorial005.py!}
```
=== "Python 3.10+"
```Python hl_lines="8 105"
{!> ../../../docs_src/security/tutorial005_an_py310.py!}
```
=== "Python 3.9+"
```Python hl_lines="8 105"
{!> ../../../docs_src/security/tutorial005_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="8 106"
{!> ../../../docs_src/security/tutorial005_an.py!}
```
=== "Python 3.10+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="7 104"
{!> ../../../docs_src/security/tutorial005_py310.py!}
```
=== "Python 3.9+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="8 105"
{!> ../../../docs_src/security/tutorial005_py39.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="8 105"
{!> ../../../docs_src/security/tutorial005.py!}
```
## Use the `scopes`
@@ -159,9 +365,50 @@ We create an `HTTPException` that we can re-use (`raise`) later at several point
In this exception, we include the scopes required (if any) as a string separated by spaces (using `scope_str`). We put that string containing the scopes in the `WWW-Authenticate` header (this is part of the spec).
```Python hl_lines="105 107-115"
{!../../../docs_src/security/tutorial005.py!}
```
=== "Python 3.10+"
```Python hl_lines="105 107-115"
{!> ../../../docs_src/security/tutorial005_an_py310.py!}
```
=== "Python 3.9+"
```Python hl_lines="105 107-115"
{!> ../../../docs_src/security/tutorial005_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="106 108-116"
{!> ../../../docs_src/security/tutorial005_an.py!}
```
=== "Python 3.10+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="104 106-114"
{!> ../../../docs_src/security/tutorial005_py310.py!}
```
=== "Python 3.9+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="105 107-115"
{!> ../../../docs_src/security/tutorial005_py39.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="105 107-115"
{!> ../../../docs_src/security/tutorial005.py!}
```
## Verify the `username` and data shape
@@ -177,9 +424,50 @@ Instead of, for example, a `dict`, or something else, as it could break the appl
We also verify that we have a user with that username, and if not, we raise that same exception we created before.
```Python hl_lines="46 116-127"
{!../../../docs_src/security/tutorial005.py!}
```
=== "Python 3.10+"
```Python hl_lines="46 116-127"
{!> ../../../docs_src/security/tutorial005_an_py310.py!}
```
=== "Python 3.9+"
```Python hl_lines="46 116-127"
{!> ../../../docs_src/security/tutorial005_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="47 117-128"
{!> ../../../docs_src/security/tutorial005_an.py!}
```
=== "Python 3.10+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="45 115-126"
{!> ../../../docs_src/security/tutorial005_py310.py!}
```
=== "Python 3.9+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="46 116-127"
{!> ../../../docs_src/security/tutorial005_py39.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="46 116-127"
{!> ../../../docs_src/security/tutorial005.py!}
```
## Verify the `scopes`
@@ -187,9 +475,50 @@ We now verify that all the scopes required, by this dependency and all the depen
For this, we use `security_scopes.scopes`, that contains a `list` with all these scopes as `str`.
```Python hl_lines="128-134"
{!../../../docs_src/security/tutorial005.py!}
```
=== "Python 3.10+"
```Python hl_lines="128-134"
{!> ../../../docs_src/security/tutorial005_an_py310.py!}
```
=== "Python 3.9+"
```Python hl_lines="128-134"
{!> ../../../docs_src/security/tutorial005_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="129-135"
{!> ../../../docs_src/security/tutorial005_an.py!}
```
=== "Python 3.10+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="127-133"
{!> ../../../docs_src/security/tutorial005_py310.py!}
```
=== "Python 3.9+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="128-134"
{!> ../../../docs_src/security/tutorial005_py39.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="128-134"
{!> ../../../docs_src/security/tutorial005.py!}
```
## Dependency tree and scopes

View File

@@ -216,9 +216,26 @@ Notice that now we don't create a default instance `settings = Settings()`.
Now we create a dependency that returns a new `config.Settings()`.
```Python hl_lines="5 11-12"
{!../../../docs_src/settings/app02/main.py!}
```
=== "Python 3.9+"
```Python hl_lines="6 12-13"
{!> ../../../docs_src/settings/app02_an_py39/main.py!}
```
=== "Python 3.6+"
```Python hl_lines="6 12-13"
{!> ../../../docs_src/settings/app02_an/main.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="5 11-12"
{!> ../../../docs_src/settings/app02/main.py!}
```
!!! tip
We'll discuss the `@lru_cache()` in a bit.
@@ -227,9 +244,26 @@ Now we create a dependency that returns a new `config.Settings()`.
And then we can require it from the *path operation function* as a dependency and use it anywhere we need it.
```Python hl_lines="16 18-20"
{!../../../docs_src/settings/app02/main.py!}
```
=== "Python 3.9+"
```Python hl_lines="17 19-21"
{!> ../../../docs_src/settings/app02_an_py39/main.py!}
```
=== "Python 3.6+"
```Python hl_lines="17 19-21"
{!> ../../../docs_src/settings/app02_an/main.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="16 18-20"
{!> ../../../docs_src/settings/app02/main.py!}
```
### Settings and testing
@@ -304,9 +338,26 @@ we would create that object for each request, and we would be reading the `.env`
But as we are using the `@lru_cache()` decorator on top, the `Settings` object will be created only once, the first time it's called. ✔️
```Python hl_lines="1 10"
{!../../../docs_src/settings/app03/main.py!}
```
=== "Python 3.9+"
```Python hl_lines="1 11"
{!> ../../../docs_src/settings/app03_an_py39/main.py!}
```
=== "Python 3.6+"
```Python hl_lines="1 11"
{!> ../../../docs_src/settings/app03_an/main.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="1 10"
{!> ../../../docs_src/settings/app03/main.py!}
```
Then for any subsequent calls of `get_settings()` in the dependencies for the next requests, instead of executing the internal code of `get_settings()` and creating a new `Settings` object, it will return the same object that was returned on the first call, again and again.

View File

@@ -28,9 +28,41 @@ To override a dependency for testing, you put as a key the original dependency (
And then **FastAPI** will call that override instead of the original dependency.
```Python hl_lines="28-29 32"
{!../../../docs_src/dependency_testing/tutorial001.py!}
```
=== "Python 3.10+"
```Python hl_lines="26-27 30"
{!> ../../../docs_src/dependency_testing/tutorial001_an_py310.py!}
```
=== "Python 3.9+"
```Python hl_lines="28-29 32"
{!> ../../../docs_src/dependency_testing/tutorial001_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="29-30 33"
{!> ../../../docs_src/dependency_testing/tutorial001_an.py!}
```
=== "Python 3.10+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="24-25 28"
{!> ../../../docs_src/dependency_testing/tutorial001_py310.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="28-29 32"
{!> ../../../docs_src/dependency_testing/tutorial001.py!}
```
!!! tip
You can set a dependency override for a dependency used anywhere in your **FastAPI** application.

View File

@@ -112,9 +112,41 @@ In WebSocket endpoints you can import from `fastapi` and use:
They work the same way as for other FastAPI endpoints/*path operations*:
```Python hl_lines="66-77 76-91"
{!../../../docs_src/websockets/tutorial002.py!}
```
=== "Python 3.10+"
```Python hl_lines="68-69 82"
{!> ../../../docs_src/websockets/tutorial002_an_py310.py!}
```
=== "Python 3.9+"
```Python hl_lines="68-69 82"
{!> ../../../docs_src/websockets/tutorial002_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="69-70 83"
{!> ../../../docs_src/websockets/tutorial002_an.py!}
```
=== "Python 3.10+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="66-67 79"
{!> ../../../docs_src/websockets/tutorial002_py310.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="68-69 81"
{!> ../../../docs_src/websockets/tutorial002.py!}
```
!!! info
As this is a WebSocket it doesn't really make sense to raise an `HTTPException`, instead we raise a `WebSocketException`.
@@ -153,9 +185,17 @@ With that you can connect the WebSocket and then send and receive messages:
When a WebSocket connection is closed, the `await websocket.receive_text()` will raise a `WebSocketDisconnect` exception, which you can then catch and handle like in this example.
```Python hl_lines="81-83"
{!../../../docs_src/websockets/tutorial003.py!}
```
=== "Python 3.9+"
```Python hl_lines="79-81"
{!> ../../../docs_src/websockets/tutorial003_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="81-83"
{!> ../../../docs_src/websockets/tutorial003.py!}
```
To try it out:

View File

@@ -1,8 +1,8 @@
# Python Types Intro
Python has support for optional "type hints".
Python has support for optional "type hints" (also called "type annotations").
These **"type hints"** are a special syntax that allow declaring the <abbr title="for example: str, int, float, bool">type</abbr> of a variable.
These **"type hints"** or annotations are a special syntax that allow declaring the <abbr title="for example: str, int, float, bool">type</abbr> of a variable.
By declaring types for your variables, editors and tools can give you better support.
@@ -158,13 +158,31 @@ The syntax using `typing` is **compatible** with all versions, from Python 3.6 t
As Python advances, **newer versions** come with improved support for these type annotations and in many cases you won't even need to import and use the `typing` module to declare the type annotations.
If you can choose a more recent version of Python for your project, you will be able to take advantage of that extra simplicity. See some examples below.
If you can choose a more recent version of Python for your project, you will be able to take advantage of that extra simplicity.
In all the docs there are examples compatible with each version of Python (when there's a difference).
For example "**Python 3.6+**" means it's compatible with Python 3.6 or above (including 3.7, 3.8, 3.9, 3.10, etc). And "**Python 3.9+**" means it's compatible with Python 3.9 or above (including 3.10, etc).
If you can use the **latest versions of Python**, use the examples for the latest version, those will have the **best and simplest syntax**, for example, "**Python 3.10+**".
#### List
For example, let's define a variable to be a `list` of `str`.
=== "Python 3.6 and above"
=== "Python 3.9+"
Declare the variable, with the same colon (`:`) syntax.
As the type, put `list`.
As the list is a type that contains some internal types, you put them in square brackets:
```Python hl_lines="1"
{!> ../../../docs_src/python_types/tutorial006_py39.py!}
```
=== "Python 3.6+"
From `typing`, import `List` (with a capital `L`):
@@ -182,18 +200,6 @@ For example, let's define a variable to be a `list` of `str`.
{!> ../../../docs_src/python_types/tutorial006.py!}
```
=== "Python 3.9 and above"
Declare the variable, with the same colon (`:`) syntax.
As the type, put `list`.
As the list is a type that contains some internal types, you put them in square brackets:
```Python hl_lines="1"
{!> ../../../docs_src/python_types/tutorial006_py39.py!}
```
!!! info
Those internal types in the square brackets are called "type parameters".
@@ -218,18 +224,18 @@ And still, the editor knows it is a `str`, and provides support for that.
You would do the same to declare `tuple`s and `set`s:
=== "Python 3.6 and above"
```Python hl_lines="1 4"
{!> ../../../docs_src/python_types/tutorial007.py!}
```
=== "Python 3.9 and above"
=== "Python 3.9+"
```Python hl_lines="1"
{!> ../../../docs_src/python_types/tutorial007_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="1 4"
{!> ../../../docs_src/python_types/tutorial007.py!}
```
This means:
* The variable `items_t` is a `tuple` with 3 items, an `int`, another `int`, and a `str`.
@@ -243,18 +249,18 @@ The first type parameter is for the keys of the `dict`.
The second type parameter is for the values of the `dict`:
=== "Python 3.6 and above"
```Python hl_lines="1 4"
{!> ../../../docs_src/python_types/tutorial008.py!}
```
=== "Python 3.9 and above"
=== "Python 3.9+"
```Python hl_lines="1"
{!> ../../../docs_src/python_types/tutorial008_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="1 4"
{!> ../../../docs_src/python_types/tutorial008.py!}
```
This means:
* The variable `prices` is a `dict`:
@@ -267,20 +273,20 @@ You can declare that a variable can be any of **several types**, for example, an
In Python 3.6 and above (including Python 3.10) you can use the `Union` type from `typing` and put inside the square brackets the possible types to accept.
In Python 3.10 there's also an **alternative syntax** where you can put the possible types separated by a <abbr title='also called "bitwise or operator", but that meaning is not relevant here'>vertical bar (`|`)</abbr>.
In Python 3.10 there's also a **new syntax** where you can put the possible types separated by a <abbr title='also called "bitwise or operator", but that meaning is not relevant here'>vertical bar (`|`)</abbr>.
=== "Python 3.6 and above"
```Python hl_lines="1 4"
{!> ../../../docs_src/python_types/tutorial008b.py!}
```
=== "Python 3.10 and above"
=== "Python 3.10+"
```Python hl_lines="1"
{!> ../../../docs_src/python_types/tutorial008b_py310.py!}
```
=== "Python 3.6+"
```Python hl_lines="1 4"
{!> ../../../docs_src/python_types/tutorial008b.py!}
```
In both cases this means that `item` could be an `int` or a `str`.
#### Possibly `None`
@@ -299,24 +305,24 @@ Using `Optional[str]` instead of just `str` will let the editor help you detecti
This also means that in Python 3.10, you can use `Something | None`:
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="1"
{!> ../../../docs_src/python_types/tutorial009_py310.py!}
```
=== "Python 3.6+"
```Python hl_lines="1 4"
{!> ../../../docs_src/python_types/tutorial009.py!}
```
=== "Python 3.6 and above - alternative"
=== "Python 3.6+ alternative"
```Python hl_lines="1 4"
{!> ../../../docs_src/python_types/tutorial009b.py!}
```
=== "Python 3.10 and above"
```Python hl_lines="1"
{!> ../../../docs_src/python_types/tutorial009_py310.py!}
```
#### Using `Union` or `Optional`
If you are using a Python version below 3.10, here's a tip from my very **subjective** point of view:
@@ -360,32 +366,7 @@ And then you won't have to worry about names like `Optional` and `Union`. 😎
These types that take type parameters in square brackets are called **Generic types** or **Generics**, for example:
=== "Python 3.6 and above"
* `List`
* `Tuple`
* `Set`
* `Dict`
* `Union`
* `Optional`
* ...and others.
=== "Python 3.9 and above"
You can use the same builtin types as generics (with square brackets and types inside):
* `list`
* `tuple`
* `set`
* `dict`
And the same as with Python 3.6, from the `typing` module:
* `Union`
* `Optional`
* ...and others.
=== "Python 3.10 and above"
=== "Python 3.10+"
You can use the same builtin types as generics (with square brackets and types inside):
@@ -400,7 +381,32 @@ These types that take type parameters in square brackets are called **Generic ty
* `Optional` (the same as with Python 3.6)
* ...and others.
In Python 3.10, as an alternative to using the generics `Union` and `Optional`, you can use the <abbr title='also called "bitwise or operator", but that meaning is not relevant here'>vertical bar (`|`)</abbr> to declare unions of types.
In Python 3.10, as an alternative to using the generics `Union` and `Optional`, you can use the <abbr title='also called "bitwise or operator", but that meaning is not relevant here'>vertical bar (`|`)</abbr> to declare unions of types, that's a lot better and simpler.
=== "Python 3.9+"
You can use the same builtin types as generics (with square brackets and types inside):
* `list`
* `tuple`
* `set`
* `dict`
And the same as with Python 3.6, from the `typing` module:
* `Union`
* `Optional`
* ...and others.
=== "Python 3.6+"
* `List`
* `Tuple`
* `Set`
* `Dict`
* `Union`
* `Optional`
* ...and others.
### Classes as types
@@ -422,6 +428,10 @@ And then, again, you get all the editor support:
<img src="/img/python-types/image06.png">
Notice that this means "`one_person` is an **instance** of the class `Person`".
It doesn't mean "`one_person` is the **class** called `Person`".
## Pydantic models
<a href="https://pydantic-docs.helpmanual.io/" class="external-link" target="_blank">Pydantic</a> is a Python library to perform data validation.
@@ -436,22 +446,22 @@ And you get all the editor support with that resulting object.
An example from the official Pydantic docs:
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python
{!> ../../../docs_src/python_types/tutorial011.py!}
{!> ../../../docs_src/python_types/tutorial011_py310.py!}
```
=== "Python 3.9 and above"
=== "Python 3.9+"
```Python
{!> ../../../docs_src/python_types/tutorial011_py39.py!}
```
=== "Python 3.10 and above"
=== "Python 3.6+"
```Python
{!> ../../../docs_src/python_types/tutorial011_py310.py!}
{!> ../../../docs_src/python_types/tutorial011.py!}
```
!!! info
@@ -464,6 +474,43 @@ You will see a lot more of all this in practice in the [Tutorial - User Guide](t
!!! tip
Pydantic has a special behavior when you use `Optional` or `Union[Something, None]` without a default value, you can read more about it in the Pydantic docs about <a href="https://pydantic-docs.helpmanual.io/usage/models/#required-optional-fields" class="external-link" target="_blank">Required Optional fields</a>.
## Type Hints with Metadata Annotations
Python also has a feature that allows putting **additional metadata** in these type hints using `Annotated`.
=== "Python 3.9+"
In Python 3.9, `Annotated` is part of the standard library, so you can import it from `typing`.
```Python hl_lines="1 4"
{!> ../../../docs_src/python_types/tutorial013_py39.py!}
```
=== "Python 3.6+"
In versions below Python 3.9, you import `Annotated` from `typing_extensions`.
It will already be installed with **FastAPI**.
```Python hl_lines="1 4"
{!> ../../../docs_src/python_types/tutorial013.py!}
```
Python itself doesn't do anything with this `Annotated`. And for editors and other tools, the type is still `str`.
But you can use this space in `Annotated` to provide **FastAPI** with additional metadata about how you want your application to behave.
The important thing to remember is that **the first *type parameter*** you pass to `Annotated` is the **actual type**. The rest, is just metadata for other tools.
For now, you just need to know that `Annotated` exists, and that it's standard Python. 😎
Later you will see how **powerful** it can be.
!!! tip
The fact that this is **standard Python** means that you will still get the **best possible developer experience** in your editor, with the tools you use to analyze and refactor your code, etc. ✨
And also that your code will be very compatible with many other Python tools and libraries. 🚀
## Type hints in **FastAPI**
**FastAPI** takes advantage of these type hints to do several things.

View File

@@ -3,6 +3,213 @@
## Latest Changes
## 0.95.0
### Highlights
This release adds support for dependencies and parameters using `Annotated` and recommends its usage. ✨
This has **several benefits**, one of the main ones is that now the parameters of your functions with `Annotated` would **not be affected** at all.
If you call those functions in **other places in your code**, the actual **default values** will be kept, your editor will help you notice missing **required arguments**, Python will require you to pass required arguments at **runtime**, you will be able to **use the same functions** for different things and with different libraries (e.g. **Typer** will soon support `Annotated` too, then you could use the same function for an API and a CLI), etc.
Because `Annotated` is **standard Python**, you still get all the **benefits** from editors and tools, like **autocompletion**, **inline errors**, etc.
One of the **biggest benefits** is that now you can create `Annotated` dependencies that are then shared by multiple *path operation functions*, this will allow you to **reduce** a lot of **code duplication** in your codebase, while keeping all the support from editors and tools.
For example, you could have code like this:
```Python
def get_current_user(token: str):
# authenticate user
return User()
@app.get("/items/")
def read_items(user: User = Depends(get_current_user)):
...
@app.post("/items/")
def create_item(*, user: User = Depends(get_current_user), item: Item):
...
@app.get("/items/{item_id}")
def read_item(*, user: User = Depends(get_current_user), item_id: int):
...
@app.delete("/items/{item_id}")
def delete_item(*, user: User = Depends(get_current_user), item_id: int):
...
```
There's a bit of code duplication for the dependency:
```Python
user: User = Depends(get_current_user)
```
...the bigger the codebase, the more noticeable it is.
Now you can create an annotated dependency once, like this:
```Python
CurrentUser = Annotated[User, Depends(get_current_user)]
```
And then you can reuse this `Annotated` dependency:
```Python
CurrentUser = Annotated[User, Depends(get_current_user)]
@app.get("/items/")
def read_items(user: CurrentUser):
...
@app.post("/items/")
def create_item(user: CurrentUser, item: Item):
...
@app.get("/items/{item_id}")
def read_item(user: CurrentUser, item_id: int):
...
@app.delete("/items/{item_id}")
def delete_item(user: CurrentUser, item_id: int):
...
```
...and `CurrentUser` has all the typing information as `User`, so your editor will work as expected (autocompletion and everything), and **FastAPI** will be able to understand the dependency defined in `Annotated`. 😎
Roughly **all the docs** have been rewritten to use `Annotated` as the main way to declare **parameters** and **dependencies**. All the **examples** in the docs now include a version with `Annotated` and a version without it, for each of the specific Python versions (when there are small differences/improvements in more recent versions). There were around 23K new lines added between docs, examples, and tests. 🚀
The key updated docs are:
* Python Types Intro:
* [Type Hints with Metadata Annotations](https://fastapi.tiangolo.com/python-types/#type-hints-with-metadata-annotations).
* Tutorial:
* [Query Parameters and String Validations - Additional validation](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#additional-validation)
* [Advantages of `Annotated`](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#advantages-of-annotated)
* [Path Parameters and Numeric Validations - Order the parameters as you need, tricks](https://fastapi.tiangolo.com/tutorial/path-params-numeric-validations/#order-the-parameters-as-you-need-tricks)
* [Better with `Annotated`](https://fastapi.tiangolo.com/tutorial/path-params-numeric-validations/#better-with-annotated)
* [Dependencies - First Steps - Share `Annotated` dependencies](https://fastapi.tiangolo.com/tutorial/dependencies/#share-annotated-dependencies)
Special thanks to [@nzig](https://github.com/nzig) for the core implementation and to [@adriangb](https://github.com/adriangb) for the inspiration and idea with [Xpresso](https://github.com/adriangb/xpresso)! 🚀
### Features
* ✨Add support for PEP-593 `Annotated` for specifying dependencies and parameters. PR [#4871](https://github.com/tiangolo/fastapi/pull/4871) by [@nzig](https://github.com/nzig).
### Docs
* 📝 Tweak tip recommending `Annotated` in docs. PR [#9270](https://github.com/tiangolo/fastapi/pull/9270) by [@tiangolo](https://github.com/tiangolo).
* 📝 Update order of examples, latest Python version first, and simplify version tab names. PR [#9269](https://github.com/tiangolo/fastapi/pull/9269) by [@tiangolo](https://github.com/tiangolo).
* 📝 Update all docs to use `Annotated` as the main recommendation, with new examples and tests. PR [#9268](https://github.com/tiangolo/fastapi/pull/9268) by [@tiangolo](https://github.com/tiangolo).
## 0.94.1
### Fixes
* 🎨 Fix types for lifespan, upgrade Starlette to 0.26.1. PR [#9245](https://github.com/tiangolo/fastapi/pull/9245) by [@tiangolo](https://github.com/tiangolo).
## 0.94.0
### Upgrades
* ⬆ Upgrade python-multipart to support 0.0.6. PR [#9212](https://github.com/tiangolo/fastapi/pull/9212) by [@musicinmybrain](https://github.com/musicinmybrain).
* ⬆️ Upgrade Starlette version, support new `lifespan` with state. PR [#9239](https://github.com/tiangolo/fastapi/pull/9239) by [@tiangolo](https://github.com/tiangolo).
### Docs
* 📝 Update Sentry link in docs. PR [#9218](https://github.com/tiangolo/fastapi/pull/9218) by [@smeubank](https://github.com/smeubank).
### Translations
* 🌐 Add Russian translation for `docs/ru/docs/history-design-future.md`. PR [#5986](https://github.com/tiangolo/fastapi/pull/5986) by [@Xewus](https://github.com/Xewus).
### Internal
* Add `pydantic` to PyPI classifiers. PR [#5914](https://github.com/tiangolo/fastapi/pull/5914) by [@yezz123](https://github.com/yezz123).
* ⬆ Bump black from 22.10.0 to 23.1.0. PR [#5953](https://github.com/tiangolo/fastapi/pull/5953) by [@dependabot[bot]](https://github.com/apps/dependabot).
* ⬆ Bump types-ujson from 5.6.0.0 to 5.7.0.1. PR [#6027](https://github.com/tiangolo/fastapi/pull/6027) by [@dependabot[bot]](https://github.com/apps/dependabot).
* ⬆ Bump dawidd6/action-download-artifact from 2.24.3 to 2.26.0. PR [#6034](https://github.com/tiangolo/fastapi/pull/6034) by [@dependabot[bot]](https://github.com/apps/dependabot).
* ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#5709](https://github.com/tiangolo/fastapi/pull/5709) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci).
## 0.93.0
### Features
* ✨ Add support for `lifespan` async context managers (superseding `startup` and `shutdown` events). Initial PR [#2944](https://github.com/tiangolo/fastapi/pull/2944) by [@uSpike](https://github.com/uSpike).
Now, instead of using independent `startup` and `shutdown` events, you can define that logic in a single function with `yield` decorated with `@asynccontextmanager` (an async context manager).
For example:
```Python
from contextlib import asynccontextmanager
from fastapi import FastAPI
def fake_answer_to_everything_ml_model(x: float):
return x * 42
ml_models = {}
@asynccontextmanager
async def lifespan(app: FastAPI):
# Load the ML model
ml_models["answer_to_everything"] = fake_answer_to_everything_ml_model
yield
# Clean up the ML models and release the resources
ml_models.clear()
app = FastAPI(lifespan=lifespan)
@app.get("/predict")
async def predict(x: float):
result = ml_models["answer_to_everything"](x)
return {"result": result}
```
**Note**: This is the recommended way going forward, instead of using `startup` and `shutdown` events.
Read more about it in the new docs: [Advanced User Guide: Lifespan Events](https://fastapi.tiangolo.com/advanced/events/).
### Docs
* ✏ Fix formatting in `docs/en/docs/tutorial/metadata.md` for `ReDoc`. PR [#6005](https://github.com/tiangolo/fastapi/pull/6005) by [@eykamp](https://github.com/eykamp).
### Translations
* 🌐 Tamil translations - initial setup. PR [#5564](https://github.com/tiangolo/fastapi/pull/5564) by [@gusty1g](https://github.com/gusty1g).
* 🌐 Add French translation for `docs/fr/docs/advanced/path-operation-advanced-configuration.md`. PR [#9221](https://github.com/tiangolo/fastapi/pull/9221) by [@axel584](https://github.com/axel584).
* 🌐 Add French translation for `docs/tutorial/debugging.md`. PR [#9175](https://github.com/tiangolo/fastapi/pull/9175) by [@frabc](https://github.com/frabc).
* 🌐 Initiate Armenian translation setup. PR [#5844](https://github.com/tiangolo/fastapi/pull/5844) by [@har8](https://github.com/har8).
* 🌐 Add French translation for `deployment/manually.md`. PR [#3693](https://github.com/tiangolo/fastapi/pull/3693) by [@rjNemo](https://github.com/rjNemo).
### Internal
* 👷 Update translation bot messages. PR [#9206](https://github.com/tiangolo/fastapi/pull/9206) by [@tiangolo](https://github.com/tiangolo).
* 👷 Update translations bot to use Discussions, and notify when a PR is done. PR [#9183](https://github.com/tiangolo/fastapi/pull/9183) by [@tiangolo](https://github.com/tiangolo).
* 🔧 Update sponsors-badges. PR [#9182](https://github.com/tiangolo/fastapi/pull/9182) by [@tiangolo](https://github.com/tiangolo).
* 👥 Update FastAPI People. PR [#9181](https://github.com/tiangolo/fastapi/pull/9181) by [@github-actions[bot]](https://github.com/apps/github-actions).
* 🔊 Log GraphQL errors in FastAPI People, because it returns 200, with a payload with an error. PR [#9171](https://github.com/tiangolo/fastapi/pull/9171) by [@tiangolo](https://github.com/tiangolo).
* 💚 Fix/workaround GitHub Actions in Docker with git for FastAPI People. PR [#9169](https://github.com/tiangolo/fastapi/pull/9169) by [@tiangolo](https://github.com/tiangolo).
* ♻️ Refactor FastAPI Experts to use only discussions now that questions are migrated. PR [#9165](https://github.com/tiangolo/fastapi/pull/9165) by [@tiangolo](https://github.com/tiangolo).
* ⬆️ Upgrade analytics. PR [#6025](https://github.com/tiangolo/fastapi/pull/6025) by [@tiangolo](https://github.com/tiangolo).
* ⬆️ Upgrade and re-enable installing Typer-CLI. PR [#6008](https://github.com/tiangolo/fastapi/pull/6008) by [@tiangolo](https://github.com/tiangolo).
## 0.92.0
🚨 This is a security fix. Please upgrade as soon as possible.

View File

@@ -57,18 +57,42 @@ Using `BackgroundTasks` also works with the dependency injection system, you can
**FastAPI** knows what to do in each case and how to re-use the same object, so that all the background tasks are merged together and are run in the background afterwards:
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="13 15 22 25"
{!> ../../../docs_src/background_tasks/tutorial002.py!}
{!> ../../../docs_src/background_tasks/tutorial002_an_py310.py!}
```
=== "Python 3.10 and above"
=== "Python 3.9+"
```Python hl_lines="13 15 22 25"
{!> ../../../docs_src/background_tasks/tutorial002_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="14 16 23 26"
{!> ../../../docs_src/background_tasks/tutorial002_an.py!}
```
=== "Python 3.10+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="11 13 20 23"
{!> ../../../docs_src/background_tasks/tutorial002_py310.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="13 15 22 25"
{!> ../../../docs_src/background_tasks/tutorial002.py!}
```
In this example, the messages will be written to the `log.txt` file *after* the response is sent.
If there was a query in the request, it will be written to the log in a background task.

View File

@@ -112,9 +112,26 @@ So we put them in their own `dependencies` module (`app/dependencies.py`).
We will now use a simple dependency to read a custom `X-Token` header:
```Python hl_lines="1 4-6"
{!../../../docs_src/bigger_applications/app/dependencies.py!}
```
=== "Python 3.9+"
```Python hl_lines="3 6-8"
{!> ../../../docs_src/bigger_applications/app_an_py39/dependencies.py!}
```
=== "Python 3.6+"
```Python hl_lines="1 5-7"
{!> ../../../docs_src/bigger_applications/app_an/dependencies.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="1 4-6"
{!> ../../../docs_src/bigger_applications/app/dependencies.py!}
```
!!! tip
We are using an invented header to simplify this example.
@@ -189,7 +206,7 @@ The end result is that the item paths are now:
### Import the dependencies
This codes lives in the module `app.routers.items`, the file `app/routers/items.py`.
This code lives in the module `app.routers.items`, the file `app/routers/items.py`.
And we need to get the dependency function from the module `app.dependencies`, the file `app/dependencies.py`.

View File

@@ -6,18 +6,42 @@ The same way you can declare additional validation and metadata in *path operati
First, you have to import it:
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="4"
{!> ../../../docs_src/body_fields/tutorial001.py!}
{!> ../../../docs_src/body_fields/tutorial001_an_py310.py!}
```
=== "Python 3.10 and above"
=== "Python 3.9+"
```Python hl_lines="4"
{!> ../../../docs_src/body_fields/tutorial001_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="4"
{!> ../../../docs_src/body_fields/tutorial001_an.py!}
```
=== "Python 3.10+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="2"
{!> ../../../docs_src/body_fields/tutorial001_py310.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="4"
{!> ../../../docs_src/body_fields/tutorial001.py!}
```
!!! warning
Notice that `Field` is imported directly from `pydantic`, not from `fastapi` as are all the rest (`Query`, `Path`, `Body`, etc).
@@ -25,18 +49,42 @@ First, you have to import it:
You can then use `Field` with model attributes:
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="11-14"
{!> ../../../docs_src/body_fields/tutorial001.py!}
{!> ../../../docs_src/body_fields/tutorial001_an_py310.py!}
```
=== "Python 3.10 and above"
=== "Python 3.9+"
```Python hl_lines="11-14"
{!> ../../../docs_src/body_fields/tutorial001_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="12-15"
{!> ../../../docs_src/body_fields/tutorial001_an.py!}
```
=== "Python 3.10+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="9-12"
{!> ../../../docs_src/body_fields/tutorial001_py310.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="11-14"
{!> ../../../docs_src/body_fields/tutorial001.py!}
```
`Field` works the same way as `Query`, `Path` and `Body`, it has all the same parameters, etc.
!!! note "Technical Details"

View File

@@ -8,18 +8,42 @@ First, of course, you can mix `Path`, `Query` and request body parameter declara
And you can also declare body parameters as optional, by setting the default to `None`:
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="19-21"
{!> ../../../docs_src/body_multiple_params/tutorial001.py!}
```Python hl_lines="18-20"
{!> ../../../docs_src/body_multiple_params/tutorial001_an_py310.py!}
```
=== "Python 3.10 and above"
=== "Python 3.9+"
```Python hl_lines="18-20"
{!> ../../../docs_src/body_multiple_params/tutorial001_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="19-21"
{!> ../../../docs_src/body_multiple_params/tutorial001_an.py!}
```
=== "Python 3.10+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="17-19"
{!> ../../../docs_src/body_multiple_params/tutorial001_py310.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="19-21"
{!> ../../../docs_src/body_multiple_params/tutorial001.py!}
```
!!! note
Notice that, in this case, the `item` that would be taken from the body is optional. As it has a `None` default value.
@@ -38,18 +62,18 @@ In the previous example, the *path operations* would expect a JSON body with the
But you can also declare multiple body parameters, e.g. `item` and `user`:
=== "Python 3.6 and above"
```Python hl_lines="22"
{!> ../../../docs_src/body_multiple_params/tutorial002.py!}
```
=== "Python 3.10 and above"
=== "Python 3.10+"
```Python hl_lines="20"
{!> ../../../docs_src/body_multiple_params/tutorial002_py310.py!}
```
=== "Python 3.6+"
```Python hl_lines="22"
{!> ../../../docs_src/body_multiple_params/tutorial002.py!}
```
In this case, **FastAPI** will notice that there are more than one body parameters in the function (two parameters that are Pydantic models).
So, it will then use the parameter names as keys (field names) in the body, and expect a body like:
@@ -87,18 +111,42 @@ If you declare it as is, because it is a singular value, **FastAPI** will assume
But you can instruct **FastAPI** to treat it as another body key using `Body`:
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="22"
{!> ../../../docs_src/body_multiple_params/tutorial003.py!}
```Python hl_lines="23"
{!> ../../../docs_src/body_multiple_params/tutorial003_an_py310.py!}
```
=== "Python 3.10 and above"
=== "Python 3.9+"
```Python hl_lines="23"
{!> ../../../docs_src/body_multiple_params/tutorial003_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="24"
{!> ../../../docs_src/body_multiple_params/tutorial003_an.py!}
```
=== "Python 3.10+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="20"
{!> ../../../docs_src/body_multiple_params/tutorial003_py310.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="22"
{!> ../../../docs_src/body_multiple_params/tutorial003.py!}
```
In this case, **FastAPI** will expect a body like:
```JSON
@@ -137,18 +185,42 @@ q: str | None = None
For example:
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="27"
{!> ../../../docs_src/body_multiple_params/tutorial004_an_py310.py!}
```
=== "Python 3.9+"
```Python hl_lines="27"
{!> ../../../docs_src/body_multiple_params/tutorial004_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="28"
{!> ../../../docs_src/body_multiple_params/tutorial004_an.py!}
```
=== "Python 3.10+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="25"
{!> ../../../docs_src/body_multiple_params/tutorial004_py310.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="27"
{!> ../../../docs_src/body_multiple_params/tutorial004.py!}
```
=== "Python 3.10 and above"
```Python hl_lines="26"
{!> ../../../docs_src/body_multiple_params/tutorial004_py310.py!}
```
!!! info
`Body` also has all the same extra validation and metadata parameters as `Query`,`Path` and others you will see later.
@@ -166,18 +238,42 @@ item: Item = Body(embed=True)
as in:
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="17"
{!> ../../../docs_src/body_multiple_params/tutorial005.py!}
{!> ../../../docs_src/body_multiple_params/tutorial005_an_py310.py!}
```
=== "Python 3.10 and above"
=== "Python 3.9+"
```Python hl_lines="17"
{!> ../../../docs_src/body_multiple_params/tutorial005_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="18"
{!> ../../../docs_src/body_multiple_params/tutorial005_an.py!}
```
=== "Python 3.10+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="15"
{!> ../../../docs_src/body_multiple_params/tutorial005_py310.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="17"
{!> ../../../docs_src/body_multiple_params/tutorial005.py!}
```
In this case **FastAPI** will expect a body like:
```JSON hl_lines="2"

View File

@@ -6,18 +6,18 @@ With **FastAPI**, you can define, validate, document, and use arbitrarily deeply
You can define an attribute to be a subtype. For example, a Python `list`:
=== "Python 3.6 and above"
```Python hl_lines="14"
{!> ../../../docs_src/body_nested_models/tutorial001.py!}
```
=== "Python 3.10 and above"
=== "Python 3.10+"
```Python hl_lines="12"
{!> ../../../docs_src/body_nested_models/tutorial001_py310.py!}
```
=== "Python 3.6+"
```Python hl_lines="14"
{!> ../../../docs_src/body_nested_models/tutorial001.py!}
```
This will make `tags` be a list, although it doesn't declare the type of the elements of the list.
## List fields with type parameter
@@ -61,22 +61,22 @@ Use that same standard syntax for model attributes with internal types.
So, in our example, we can make `tags` be specifically a "list of strings":
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="14"
{!> ../../../docs_src/body_nested_models/tutorial002.py!}
```Python hl_lines="12"
{!> ../../../docs_src/body_nested_models/tutorial002_py310.py!}
```
=== "Python 3.9 and above"
=== "Python 3.9+"
```Python hl_lines="14"
{!> ../../../docs_src/body_nested_models/tutorial002_py39.py!}
```
=== "Python 3.10 and above"
=== "Python 3.6+"
```Python hl_lines="12"
{!> ../../../docs_src/body_nested_models/tutorial002_py310.py!}
```Python hl_lines="14"
{!> ../../../docs_src/body_nested_models/tutorial002.py!}
```
## Set types
@@ -87,22 +87,22 @@ And Python has a special data type for sets of unique items, the `set`.
Then we can declare `tags` as a set of strings:
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="1 14"
{!> ../../../docs_src/body_nested_models/tutorial003.py!}
```Python hl_lines="12"
{!> ../../../docs_src/body_nested_models/tutorial003_py310.py!}
```
=== "Python 3.9 and above"
=== "Python 3.9+"
```Python hl_lines="14"
{!> ../../../docs_src/body_nested_models/tutorial003_py39.py!}
```
=== "Python 3.10 and above"
=== "Python 3.6+"
```Python hl_lines="12"
{!> ../../../docs_src/body_nested_models/tutorial003_py310.py!}
```Python hl_lines="1 14"
{!> ../../../docs_src/body_nested_models/tutorial003.py!}
```
With this, even if you receive a request with duplicate data, it will be converted to a set of unique items.
@@ -125,44 +125,44 @@ All that, arbitrarily nested.
For example, we can define an `Image` model:
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="9-11"
{!> ../../../docs_src/body_nested_models/tutorial004.py!}
```Python hl_lines="7-9"
{!> ../../../docs_src/body_nested_models/tutorial004_py310.py!}
```
=== "Python 3.9 and above"
=== "Python 3.9+"
```Python hl_lines="9-11"
{!> ../../../docs_src/body_nested_models/tutorial004_py39.py!}
```
=== "Python 3.10 and above"
=== "Python 3.6+"
```Python hl_lines="7-9"
{!> ../../../docs_src/body_nested_models/tutorial004_py310.py!}
```Python hl_lines="9-11"
{!> ../../../docs_src/body_nested_models/tutorial004.py!}
```
### Use the submodel as a type
And then we can use it as the type of an attribute:
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="20"
{!> ../../../docs_src/body_nested_models/tutorial004.py!}
```Python hl_lines="18"
{!> ../../../docs_src/body_nested_models/tutorial004_py310.py!}
```
=== "Python 3.9 and above"
=== "Python 3.9+"
```Python hl_lines="20"
{!> ../../../docs_src/body_nested_models/tutorial004_py39.py!}
```
=== "Python 3.10 and above"
=== "Python 3.6+"
```Python hl_lines="18"
{!> ../../../docs_src/body_nested_models/tutorial004_py310.py!}
```Python hl_lines="20"
{!> ../../../docs_src/body_nested_models/tutorial004.py!}
```
This would mean that **FastAPI** would expect a body similar to:
@@ -196,22 +196,22 @@ To see all the options you have, checkout the docs for <a href="https://pydantic
For example, as in the `Image` model we have a `url` field, we can declare it to be instead of a `str`, a Pydantic's `HttpUrl`:
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="4 10"
{!> ../../../docs_src/body_nested_models/tutorial005.py!}
```Python hl_lines="2 8"
{!> ../../../docs_src/body_nested_models/tutorial005_py310.py!}
```
=== "Python 3.9 and above"
=== "Python 3.9+"
```Python hl_lines="4 10"
{!> ../../../docs_src/body_nested_models/tutorial005_py39.py!}
```
=== "Python 3.10 and above"
=== "Python 3.6+"
```Python hl_lines="2 8"
{!> ../../../docs_src/body_nested_models/tutorial005_py310.py!}
```Python hl_lines="4 10"
{!> ../../../docs_src/body_nested_models/tutorial005.py!}
```
The string will be checked to be a valid URL, and documented in JSON Schema / OpenAPI as such.
@@ -220,22 +220,22 @@ The string will be checked to be a valid URL, and documented in JSON Schema / Op
You can also use Pydantic models as subtypes of `list`, `set`, etc:
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="20"
{!> ../../../docs_src/body_nested_models/tutorial006.py!}
```Python hl_lines="18"
{!> ../../../docs_src/body_nested_models/tutorial006_py310.py!}
```
=== "Python 3.9 and above"
=== "Python 3.9+"
```Python hl_lines="20"
{!> ../../../docs_src/body_nested_models/tutorial006_py39.py!}
```
=== "Python 3.10 and above"
=== "Python 3.6+"
```Python hl_lines="18"
{!> ../../../docs_src/body_nested_models/tutorial006_py310.py!}
```Python hl_lines="20"
{!> ../../../docs_src/body_nested_models/tutorial006.py!}
```
This will expect (convert, validate, document, etc) a JSON body like:
@@ -271,22 +271,22 @@ This will expect (convert, validate, document, etc) a JSON body like:
You can define arbitrarily deeply nested models:
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="9 14 20 23 27"
{!> ../../../docs_src/body_nested_models/tutorial007.py!}
```Python hl_lines="7 12 18 21 25"
{!> ../../../docs_src/body_nested_models/tutorial007_py310.py!}
```
=== "Python 3.9 and above"
=== "Python 3.9+"
```Python hl_lines="9 14 20 23 27"
{!> ../../../docs_src/body_nested_models/tutorial007_py39.py!}
```
=== "Python 3.10 and above"
=== "Python 3.6+"
```Python hl_lines="7 12 18 21 25"
{!> ../../../docs_src/body_nested_models/tutorial007_py310.py!}
```Python hl_lines="9 14 20 23 27"
{!> ../../../docs_src/body_nested_models/tutorial007.py!}
```
!!! info
@@ -308,18 +308,18 @@ images: list[Image]
as in:
=== "Python 3.6 and above"
```Python hl_lines="15"
{!> ../../../docs_src/body_nested_models/tutorial008.py!}
```
=== "Python 3.9 and above"
=== "Python 3.9+"
```Python hl_lines="13"
{!> ../../../docs_src/body_nested_models/tutorial008_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="15"
{!> ../../../docs_src/body_nested_models/tutorial008.py!}
```
## Editor support everywhere
And you get editor support everywhere.
@@ -348,18 +348,18 @@ That's what we are going to see here.
In this case, you would accept any `dict` as long as it has `int` keys with `float` values:
=== "Python 3.6 and above"
```Python hl_lines="9"
{!> ../../../docs_src/body_nested_models/tutorial009.py!}
```
=== "Python 3.9 and above"
=== "Python 3.9+"
```Python hl_lines="7"
{!> ../../../docs_src/body_nested_models/tutorial009_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="9"
{!> ../../../docs_src/body_nested_models/tutorial009.py!}
```
!!! tip
Have in mind that JSON only supports `str` as keys.

View File

@@ -6,22 +6,22 @@ To update an item you can use the <a href="https://developer.mozilla.org/en-US/d
You can use the `jsonable_encoder` to convert the input data to data that can be stored as JSON (e.g. with a NoSQL database). For example, converting `datetime` to `str`.
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="30-35"
{!> ../../../docs_src/body_updates/tutorial001.py!}
```Python hl_lines="28-33"
{!> ../../../docs_src/body_updates/tutorial001_py310.py!}
```
=== "Python 3.9 and above"
=== "Python 3.9+"
```Python hl_lines="30-35"
{!> ../../../docs_src/body_updates/tutorial001_py39.py!}
```
=== "Python 3.10 and above"
=== "Python 3.6+"
```Python hl_lines="28-33"
{!> ../../../docs_src/body_updates/tutorial001_py310.py!}
```Python hl_lines="30-35"
{!> ../../../docs_src/body_updates/tutorial001.py!}
```
`PUT` is used to receive data that should replace the existing data.
@@ -67,22 +67,22 @@ That would generate a `dict` with only the data that was set when creating the `
Then you can use this to generate a `dict` with only the data that was set (sent in the request), omitting default values:
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="34"
{!> ../../../docs_src/body_updates/tutorial002.py!}
```Python hl_lines="32"
{!> ../../../docs_src/body_updates/tutorial002_py310.py!}
```
=== "Python 3.9 and above"
=== "Python 3.9+"
```Python hl_lines="34"
{!> ../../../docs_src/body_updates/tutorial002_py39.py!}
```
=== "Python 3.10 and above"
=== "Python 3.6+"
```Python hl_lines="32"
{!> ../../../docs_src/body_updates/tutorial002_py310.py!}
```Python hl_lines="34"
{!> ../../../docs_src/body_updates/tutorial002.py!}
```
### Using Pydantic's `update` parameter
@@ -91,22 +91,22 @@ Now, you can create a copy of the existing model using `.copy()`, and pass the `
Like `stored_item_model.copy(update=update_data)`:
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="35"
{!> ../../../docs_src/body_updates/tutorial002.py!}
```Python hl_lines="33"
{!> ../../../docs_src/body_updates/tutorial002_py310.py!}
```
=== "Python 3.9 and above"
=== "Python 3.9+"
```Python hl_lines="35"
{!> ../../../docs_src/body_updates/tutorial002_py39.py!}
```
=== "Python 3.10 and above"
=== "Python 3.6+"
```Python hl_lines="33"
{!> ../../../docs_src/body_updates/tutorial002_py310.py!}
```Python hl_lines="35"
{!> ../../../docs_src/body_updates/tutorial002.py!}
```
### Partial updates recap
@@ -124,22 +124,22 @@ In summary, to apply partial updates you would:
* Save the data to your DB.
* Return the updated model.
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="30-37"
{!> ../../../docs_src/body_updates/tutorial002.py!}
```Python hl_lines="28-35"
{!> ../../../docs_src/body_updates/tutorial002_py310.py!}
```
=== "Python 3.9 and above"
=== "Python 3.9+"
```Python hl_lines="30-37"
{!> ../../../docs_src/body_updates/tutorial002_py39.py!}
```
=== "Python 3.10 and above"
=== "Python 3.6+"
```Python hl_lines="28-35"
{!> ../../../docs_src/body_updates/tutorial002_py310.py!}
```Python hl_lines="30-37"
{!> ../../../docs_src/body_updates/tutorial002.py!}
```
!!! tip

View File

@@ -19,36 +19,36 @@ To declare a **request** body, you use <a href="https://pydantic-docs.helpmanual
First, you need to import `BaseModel` from `pydantic`:
=== "Python 3.6 and above"
```Python hl_lines="4"
{!> ../../../docs_src/body/tutorial001.py!}
```
=== "Python 3.10 and above"
=== "Python 3.10+"
```Python hl_lines="2"
{!> ../../../docs_src/body/tutorial001_py310.py!}
```
=== "Python 3.6+"
```Python hl_lines="4"
{!> ../../../docs_src/body/tutorial001.py!}
```
## Create your data model
Then you declare your data model as a class that inherits from `BaseModel`.
Use standard Python types for all the attributes:
=== "Python 3.6 and above"
```Python hl_lines="7-11"
{!> ../../../docs_src/body/tutorial001.py!}
```
=== "Python 3.10 and above"
=== "Python 3.10+"
```Python hl_lines="5-9"
{!> ../../../docs_src/body/tutorial001_py310.py!}
```
=== "Python 3.6+"
```Python hl_lines="7-11"
{!> ../../../docs_src/body/tutorial001.py!}
```
The same as when declaring query parameters, when a model attribute has a default value, it is not required. Otherwise, it is required. Use `None` to make it just optional.
For example, this model above declares a JSON "`object`" (or Python `dict`) like:
@@ -75,18 +75,18 @@ For example, this model above declares a JSON "`object`" (or Python `dict`) like
To add it to your *path operation*, declare it the same way you declared path and query parameters:
=== "Python 3.6 and above"
```Python hl_lines="18"
{!> ../../../docs_src/body/tutorial001.py!}
```
=== "Python 3.10 and above"
=== "Python 3.10+"
```Python hl_lines="16"
{!> ../../../docs_src/body/tutorial001_py310.py!}
```
=== "Python 3.6+"
```Python hl_lines="18"
{!> ../../../docs_src/body/tutorial001.py!}
```
...and declare its type as the model you created, `Item`.
## Results
@@ -149,54 +149,54 @@ But you would get the same editor support with <a href="https://www.jetbrains.co
Inside of the function, you can access all the attributes of the model object directly:
=== "Python 3.6 and above"
```Python hl_lines="21"
{!> ../../../docs_src/body/tutorial002.py!}
```
=== "Python 3.10 and above"
=== "Python 3.10+"
```Python hl_lines="19"
{!> ../../../docs_src/body/tutorial002_py310.py!}
```
=== "Python 3.6+"
```Python hl_lines="21"
{!> ../../../docs_src/body/tutorial002.py!}
```
## Request body + path parameters
You can declare path parameters and request body at the same time.
**FastAPI** will recognize that the function parameters that match path parameters should be **taken from the path**, and that function parameters that are declared to be Pydantic models should be **taken from the request body**.
=== "Python 3.6 and above"
```Python hl_lines="17-18"
{!> ../../../docs_src/body/tutorial003.py!}
```
=== "Python 3.10 and above"
=== "Python 3.10+"
```Python hl_lines="15-16"
{!> ../../../docs_src/body/tutorial003_py310.py!}
```
=== "Python 3.6+"
```Python hl_lines="17-18"
{!> ../../../docs_src/body/tutorial003.py!}
```
## Request body + path + query parameters
You can also declare **body**, **path** and **query** parameters, all at the same time.
**FastAPI** will recognize each of them and take the data from the correct place.
=== "Python 3.6 and above"
```Python hl_lines="18"
{!> ../../../docs_src/body/tutorial004.py!}
```
=== "Python 3.10 and above"
=== "Python 3.10+"
```Python hl_lines="16"
{!> ../../../docs_src/body/tutorial004_py310.py!}
```
=== "Python 3.6+"
```Python hl_lines="18"
{!> ../../../docs_src/body/tutorial004.py!}
```
The function parameters will be recognized as follows:
* If the parameter is also declared in the **path**, it will be used as a path parameter.

View File

@@ -6,36 +6,84 @@ You can define Cookie parameters the same way you define `Query` and `Path` para
First import `Cookie`:
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="3"
{!> ../../../docs_src/cookie_params/tutorial001.py!}
{!> ../../../docs_src/cookie_params/tutorial001_an_py310.py!}
```
=== "Python 3.10 and above"
=== "Python 3.9+"
```Python hl_lines="3"
{!> ../../../docs_src/cookie_params/tutorial001_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="3"
{!> ../../../docs_src/cookie_params/tutorial001_an.py!}
```
=== "Python 3.10+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="1"
{!> ../../../docs_src/cookie_params/tutorial001_py310.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="3"
{!> ../../../docs_src/cookie_params/tutorial001.py!}
```
## Declare `Cookie` parameters
Then declare the cookie parameters using the same structure as with `Path` and `Query`.
The first value is the default value, you can pass all the extra validation or annotation parameters:
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="9"
{!> ../../../docs_src/cookie_params/tutorial001.py!}
{!> ../../../docs_src/cookie_params/tutorial001_an_py310.py!}
```
=== "Python 3.10 and above"
=== "Python 3.9+"
```Python hl_lines="9"
{!> ../../../docs_src/cookie_params/tutorial001_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="10"
{!> ../../../docs_src/cookie_params/tutorial001_an.py!}
```
=== "Python 3.10+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="7"
{!> ../../../docs_src/cookie_params/tutorial001_py310.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="9"
{!> ../../../docs_src/cookie_params/tutorial001.py!}
```
!!! note "Technical Details"
`Cookie` is a "sister" class of `Path` and `Query`. It also inherits from the same common `Param` class.

View File

@@ -6,18 +6,42 @@ Before diving deeper into the **Dependency Injection** system, let's upgrade the
In the previous example, we were returning a `dict` from our dependency ("dependable"):
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="9"
{!> ../../../docs_src/dependencies/tutorial001.py!}
{!> ../../../docs_src/dependencies/tutorial001_an_py310.py!}
```
=== "Python 3.10 and above"
=== "Python 3.9+"
```Python hl_lines="11"
{!> ../../../docs_src/dependencies/tutorial001_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="12"
{!> ../../../docs_src/dependencies/tutorial001_an.py!}
```
=== "Python 3.10+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="7"
{!> ../../../docs_src/dependencies/tutorial001_py310.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="11"
{!> ../../../docs_src/dependencies/tutorial001.py!}
```
But then we get a `dict` in the parameter `commons` of the *path operation function*.
And we know that editors can't provide a lot of support (like completion) for `dict`s, because they can't know their keys and value types.
@@ -79,46 +103,118 @@ That also applies to callables with no parameters at all. The same as it would b
Then, we can change the dependency "dependable" `common_parameters` from above to the class `CommonQueryParams`:
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="11-15"
{!> ../../../docs_src/dependencies/tutorial002.py!}
{!> ../../../docs_src/dependencies/tutorial002_an_py310.py!}
```
=== "Python 3.10 and above"
=== "Python 3.9+"
```Python hl_lines="11-15"
{!> ../../../docs_src/dependencies/tutorial002_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="12-16"
{!> ../../../docs_src/dependencies/tutorial002_an.py!}
```
=== "Python 3.10+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="9-13"
{!> ../../../docs_src/dependencies/tutorial002_py310.py!}
```
Pay attention to the `__init__` method used to create the instance of the class:
=== "Python 3.6+ non-Annotated"
=== "Python 3.6 and above"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="12"
```Python hl_lines="11-15"
{!> ../../../docs_src/dependencies/tutorial002.py!}
```
=== "Python 3.10 and above"
Pay attention to the `__init__` method used to create the instance of the class:
=== "Python 3.10+"
```Python hl_lines="12"
{!> ../../../docs_src/dependencies/tutorial002_an_py310.py!}
```
=== "Python 3.9+"
```Python hl_lines="12"
{!> ../../../docs_src/dependencies/tutorial002_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="13"
{!> ../../../docs_src/dependencies/tutorial002_an.py!}
```
=== "Python 3.10+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="10"
{!> ../../../docs_src/dependencies/tutorial002_py310.py!}
```
...it has the same parameters as our previous `common_parameters`:
=== "Python 3.6+ non-Annotated"
=== "Python 3.6 and above"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="9"
{!> ../../../docs_src/dependencies/tutorial001.py!}
```Python hl_lines="12"
{!> ../../../docs_src/dependencies/tutorial002.py!}
```
=== "Python 3.10 and above"
...it has the same parameters as our previous `common_parameters`:
=== "Python 3.10+"
```Python hl_lines="8"
{!> ../../../docs_src/dependencies/tutorial001_an_py310.py!}
```
=== "Python 3.9+"
```Python hl_lines="9"
{!> ../../../docs_src/dependencies/tutorial001_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="10"
{!> ../../../docs_src/dependencies/tutorial001_an.py!}
```
=== "Python 3.10+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="6"
{!> ../../../docs_src/dependencies/tutorial001_py310.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="9"
{!> ../../../docs_src/dependencies/tutorial001.py!}
```
Those parameters are what **FastAPI** will use to "solve" the dependency.
In both cases, it will have:
@@ -133,32 +229,67 @@ In both cases the data will be converted, validated, documented on the OpenAPI s
Now you can declare your dependency using this class.
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="19"
{!> ../../../docs_src/dependencies/tutorial002.py!}
{!> ../../../docs_src/dependencies/tutorial002_an_py310.py!}
```
=== "Python 3.10 and above"
=== "Python 3.9+"
```Python hl_lines="19"
{!> ../../../docs_src/dependencies/tutorial002_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="20"
{!> ../../../docs_src/dependencies/tutorial002_an.py!}
```
=== "Python 3.10+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="17"
{!> ../../../docs_src/dependencies/tutorial002_py310.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="19"
{!> ../../../docs_src/dependencies/tutorial002.py!}
```
**FastAPI** calls the `CommonQueryParams` class. This creates an "instance" of that class and the instance will be passed as the parameter `commons` to your function.
## Type annotation vs `Depends`
Notice how we write `CommonQueryParams` twice in the above code:
```Python
commons: CommonQueryParams = Depends(CommonQueryParams)
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python
commons: CommonQueryParams = Depends(CommonQueryParams)
```
=== "Python 3.6+"
```Python
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
```
The last `CommonQueryParams`, in:
```Python
... = Depends(CommonQueryParams)
... Depends(CommonQueryParams)
```
...is what **FastAPI** will actually use to know what is the dependency.
@@ -169,32 +300,78 @@ From it is that FastAPI will extract the declared parameters and that is what Fa
In this case, the first `CommonQueryParams`, in:
```Python
commons: CommonQueryParams ...
```
=== "Python 3.6+"
...doesn't have any special meaning for **FastAPI**. FastAPI won't use it for data conversion, validation, etc. (as it is using the `= Depends(CommonQueryParams)` for that).
```Python
commons: Annotated[CommonQueryParams, ...
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python
commons: CommonQueryParams ...
```
...doesn't have any special meaning for **FastAPI**. FastAPI won't use it for data conversion, validation, etc. (as it is using the `Depends(CommonQueryParams)` for that).
You could actually write just:
```Python
commons = Depends(CommonQueryParams)
```
=== "Python 3.6+"
```Python
commons: Annotated[Any, Depends(CommonQueryParams)]
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python
commons = Depends(CommonQueryParams)
```
..as in:
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="19"
{!> ../../../docs_src/dependencies/tutorial003.py!}
{!> ../../../docs_src/dependencies/tutorial003_an_py310.py!}
```
=== "Python 3.10 and above"
=== "Python 3.9+"
```Python hl_lines="19"
{!> ../../../docs_src/dependencies/tutorial003_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="20"
{!> ../../../docs_src/dependencies/tutorial003_an.py!}
```
=== "Python 3.10+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="17"
{!> ../../../docs_src/dependencies/tutorial003_py310.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="19"
{!> ../../../docs_src/dependencies/tutorial003.py!}
```
But declaring the type is encouraged as that way your editor will know what will be passed as the parameter `commons`, and then it can help you with code completion, type checks, etc:
<img src="/img/tutorial/dependencies/image02.png">
@@ -203,9 +380,20 @@ But declaring the type is encouraged as that way your editor will know what will
But you see that we are having some code repetition here, writing `CommonQueryParams` twice:
```Python
commons: CommonQueryParams = Depends(CommonQueryParams)
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python
commons: CommonQueryParams = Depends(CommonQueryParams)
```
=== "Python 3.6+"
```Python
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
```
**FastAPI** provides a shortcut for these cases, in where the dependency is *specifically* a class that **FastAPI** will "call" to create an instance of the class itself.
@@ -213,32 +401,78 @@ For those specific cases, you can do the following:
Instead of writing:
```Python
commons: CommonQueryParams = Depends(CommonQueryParams)
```
=== "Python 3.6+"
```Python
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python
commons: CommonQueryParams = Depends(CommonQueryParams)
```
...you write:
```Python
commons: CommonQueryParams = Depends()
```
=== "Python 3.6+"
You declare the dependency as the type of the parameter, and you use `Depends()` as its "default" value (that after the `=`) for that function's parameter, without any parameter in `Depends()`, instead of having to write the full class *again* inside of `Depends(CommonQueryParams)`.
```Python
commons: Annotated[CommonQueryParams, Depends()]
```
=== "Python 3.6 non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python
commons: CommonQueryParams = Depends()
```
You declare the dependency as the type of the parameter, and you use `Depends()` without any parameter, instead of having to write the full class *again* inside of `Depends(CommonQueryParams)`.
The same example would then look like:
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="19"
{!> ../../../docs_src/dependencies/tutorial004.py!}
{!> ../../../docs_src/dependencies/tutorial004_an_py310.py!}
```
=== "Python 3.10 and above"
=== "Python 3.9+"
```Python hl_lines="19"
{!> ../../../docs_src/dependencies/tutorial004_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="20"
{!> ../../../docs_src/dependencies/tutorial004_an.py!}
```
=== "Python 3.10+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="17"
{!> ../../../docs_src/dependencies/tutorial004_py310.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="19"
{!> ../../../docs_src/dependencies/tutorial004.py!}
```
...and **FastAPI** will know what to do.
!!! tip

View File

@@ -14,9 +14,26 @@ The *path operation decorator* receives an optional argument `dependencies`.
It should be a `list` of `Depends()`:
```Python hl_lines="17"
{!../../../docs_src/dependencies/tutorial006.py!}
```
=== "Python 3.9+"
```Python hl_lines="19"
{!> ../../../docs_src/dependencies/tutorial006_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="18"
{!> ../../../docs_src/dependencies/tutorial006_an.py!}
```
=== "Python 3.6 non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="17"
{!> ../../../docs_src/dependencies/tutorial006.py!}
```
These dependencies will be executed/solved the same way normal dependencies. But their value (if they return any) won't be passed to your *path operation function*.
@@ -40,17 +57,51 @@ You can use the same dependency *functions* you use normally.
They can declare request requirements (like headers) or other sub-dependencies:
```Python hl_lines="6 11"
{!../../../docs_src/dependencies/tutorial006.py!}
```
=== "Python 3.9+"
```Python hl_lines="8 13"
{!> ../../../docs_src/dependencies/tutorial006_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="7 12"
{!> ../../../docs_src/dependencies/tutorial006_an.py!}
```
=== "Python 3.6 non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="6 11"
{!> ../../../docs_src/dependencies/tutorial006.py!}
```
### Raise exceptions
These dependencies can `raise` exceptions, the same as normal dependencies:
```Python hl_lines="8 13"
{!../../../docs_src/dependencies/tutorial006.py!}
```
=== "Python 3.9+"
```Python hl_lines="10 15"
{!> ../../../docs_src/dependencies/tutorial006_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="9 14"
{!> ../../../docs_src/dependencies/tutorial006_an.py!}
```
=== "Python 3.6 non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="8 13"
{!> ../../../docs_src/dependencies/tutorial006.py!}
```
### Return values
@@ -58,9 +109,26 @@ And they can return values or not, the values won't be used.
So, you can re-use a normal dependency (that returns a value) you already use somewhere else, and even though the value won't be used, the dependency will be executed:
```Python hl_lines="9 14"
{!../../../docs_src/dependencies/tutorial006.py!}
```
=== "Python 3.9+"
```Python hl_lines="11 16"
{!> ../../../docs_src/dependencies/tutorial006_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="10 15"
{!> ../../../docs_src/dependencies/tutorial006_an.py!}
```
=== "Python 3.6 non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="9 14"
{!> ../../../docs_src/dependencies/tutorial006.py!}
```
## Dependencies for a group of *path operations*

View File

@@ -66,9 +66,26 @@ You can have sub-dependencies and "trees" of sub-dependencies of any size and sh
For example, `dependency_c` can have a dependency on `dependency_b`, and `dependency_b` on `dependency_a`:
```Python hl_lines="4 12 20"
{!../../../docs_src/dependencies/tutorial008.py!}
```
=== "Python 3.9+"
```Python hl_lines="6 14 22"
{!> ../../../docs_src/dependencies/tutorial008_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="5 13 21"
{!> ../../../docs_src/dependencies/tutorial008_an.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="4 12 20"
{!> ../../../docs_src/dependencies/tutorial008.py!}
```
And all of them can use `yield`.
@@ -76,9 +93,26 @@ In this case `dependency_c`, to execute its exit code, needs the value from `dep
And, in turn, `dependency_b` needs the value from `dependency_a` (here named `dep_a`) to be available for its exit code.
```Python hl_lines="16-17 24-25"
{!../../../docs_src/dependencies/tutorial008.py!}
```
=== "Python 3.9+"
```Python hl_lines="18-19 26-27"
{!> ../../../docs_src/dependencies/tutorial008_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="17-18 25-26"
{!> ../../../docs_src/dependencies/tutorial008_an.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="16-17 24-25"
{!> ../../../docs_src/dependencies/tutorial008.py!}
```
The same way, you could have dependencies with `yield` and `return` mixed.

View File

@@ -6,9 +6,26 @@ Similar to the way you can [add `dependencies` to the *path operation decorators
In that case, they will be applied to all the *path operations* in the application:
```Python hl_lines="15"
{!../../../docs_src/dependencies/tutorial012.py!}
```
=== "Python 3.9+"
```Python hl_lines="16"
{!> ../../../docs_src/dependencies/tutorial012_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="16"
{!> ../../../docs_src/dependencies/tutorial012_an.py!}
```
=== "Python 3.6 non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="15"
{!> ../../../docs_src/dependencies/tutorial012.py!}
```
And all the ideas in the section about [adding `dependencies` to the *path operation decorators*](dependencies-in-path-operation-decorators.md){.internal-link target=_blank} still apply, but in this case, to all of the *path operations* in the app.

View File

@@ -31,18 +31,42 @@ Let's first focus on the dependency.
It is just a function that can take all the same parameters that a *path operation function* can take:
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="8-11"
{!> ../../../docs_src/dependencies/tutorial001.py!}
```Python hl_lines="8-9"
{!> ../../../docs_src/dependencies/tutorial001_an_py310.py!}
```
=== "Python 3.10 and above"
=== "Python 3.9+"
```Python hl_lines="8-11"
{!> ../../../docs_src/dependencies/tutorial001_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="9-12"
{!> ../../../docs_src/dependencies/tutorial001_an.py!}
```
=== "Python 3.10+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="6-7"
{!> ../../../docs_src/dependencies/tutorial001_py310.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="8-11"
{!> ../../../docs_src/dependencies/tutorial001.py!}
```
That's it.
**2 lines**.
@@ -63,40 +87,90 @@ And then it just returns a `dict` containing those values.
### Import `Depends`
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="3"
{!> ../../../docs_src/dependencies/tutorial001.py!}
{!> ../../../docs_src/dependencies/tutorial001_an_py310.py!}
```
=== "Python 3.10 and above"
=== "Python 3.9+"
```Python hl_lines="3"
{!> ../../../docs_src/dependencies/tutorial001_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="3"
{!> ../../../docs_src/dependencies/tutorial001_an.py!}
```
=== "Python 3.10+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="1"
{!> ../../../docs_src/dependencies/tutorial001_py310.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="3"
{!> ../../../docs_src/dependencies/tutorial001.py!}
```
### Declare the dependency, in the "dependant"
The same way you use `Body`, `Query`, etc. with your *path operation function* parameters, use `Depends` with a new parameter:
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="15 20"
{!> ../../../docs_src/dependencies/tutorial001.py!}
```Python hl_lines="13 18"
{!> ../../../docs_src/dependencies/tutorial001_an_py310.py!}
```
=== "Python 3.10 and above"
=== "Python 3.9+"
```Python hl_lines="15 20"
{!> ../../../docs_src/dependencies/tutorial001_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="16 21"
{!> ../../../docs_src/dependencies/tutorial001_an.py!}
```
=== "Python 3.10+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="11 16"
{!> ../../../docs_src/dependencies/tutorial001_py310.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="15 20"
{!> ../../../docs_src/dependencies/tutorial001.py!}
```
Although you use `Depends` in the parameters of your function the same way you use `Body`, `Query`, etc, `Depends` works a bit differently.
You only give `Depends` a single parameter.
This parameter must be something like a function.
You **don't call it** directly (don't add the parenthesis at the end), you just pass it as a parameter to `Depends()`.
And that function takes parameters in the same way that *path operation functions* do.
!!! tip
@@ -126,6 +200,45 @@ This way you write shared code once and **FastAPI** takes care of calling it for
You just pass it to `Depends` and **FastAPI** knows how to do the rest.
## Share `Annotated` dependencies
In the examples above, you see that there's a tiny bit of **code duplication**.
When you need to use the `common_parameters()` dependency, you have to write the whole parameter with the type annotation and `Depends()`:
```Python
commons: Annotated[dict, Depends(common_parameters)]
```
But because we are using `Annotated`, we can store that `Annotated` value in a variable and use it in multiple places:
=== "Python 3.10+"
```Python hl_lines="12 16 21"
{!> ../../../docs_src/dependencies/tutorial001_02_an_py310.py!}
```
=== "Python 3.9+"
```Python hl_lines="14 18 23"
{!> ../../../docs_src/dependencies/tutorial001_02_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="15 19 24"
{!> ../../../docs_src/dependencies/tutorial001_02_an.py!}
```
!!! tip
This is just standard Python, it's called a "type alias", it's actually not specific to **FastAPI**.
But because **FastAPI** is based on the Python standards, including `Annotated`, you can use this trick in your code. 😎
The dependencies will keep working as expected, and the **best part** is that the **type information will be preserved**, which means that your editor will be able to keep providing you with **autocompletion**, **inline errors**, etc. The same for other tools like `mypy`.
This will be especially useful when you use it in a **large code base** where you use **the same dependencies** over and over again in **many *path operations***.
## To `async` or not to `async`
As dependencies will also be called by **FastAPI** (the same as your *path operation functions*), the same rules apply while defining your functions.

View File

@@ -10,18 +10,42 @@ They can be as **deep** as you need them to be.
You could create a first dependency ("dependable") like:
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="8-9"
{!> ../../../docs_src/dependencies/tutorial005.py!}
{!> ../../../docs_src/dependencies/tutorial005_an_py310.py!}
```
=== "Python 3.10 and above"
=== "Python 3.9+"
```Python hl_lines="8-9"
{!> ../../../docs_src/dependencies/tutorial005_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="9-10"
{!> ../../../docs_src/dependencies/tutorial005_an.py!}
```
=== "Python 3.10 non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="6-7"
{!> ../../../docs_src/dependencies/tutorial005_py310.py!}
```
=== "Python 3.6 non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="8-9"
{!> ../../../docs_src/dependencies/tutorial005.py!}
```
It declares an optional query parameter `q` as a `str`, and then it just returns it.
This is quite simple (not very useful), but will help us focus on how the sub-dependencies work.
@@ -30,18 +54,42 @@ This is quite simple (not very useful), but will help us focus on how the sub-de
Then you can create another dependency function (a "dependable") that at the same time declares a dependency of its own (so it is a "dependant" too):
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="13"
{!> ../../../docs_src/dependencies/tutorial005.py!}
{!> ../../../docs_src/dependencies/tutorial005_an_py310.py!}
```
=== "Python 3.10 and above"
=== "Python 3.9+"
```Python hl_lines="13"
{!> ../../../docs_src/dependencies/tutorial005_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="14"
{!> ../../../docs_src/dependencies/tutorial005_an.py!}
```
=== "Python 3.10 non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="11"
{!> ../../../docs_src/dependencies/tutorial005_py310.py!}
```
=== "Python 3.6 non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="13"
{!> ../../../docs_src/dependencies/tutorial005.py!}
```
Let's focus on the parameters declared:
* Even though this function is a dependency ("dependable") itself, it also declares another dependency (it "depends" on something else).
@@ -53,18 +101,42 @@ Let's focus on the parameters declared:
Then we can use the dependency with:
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="22"
{!> ../../../docs_src/dependencies/tutorial005.py!}
```Python hl_lines="23"
{!> ../../../docs_src/dependencies/tutorial005_an_py310.py!}
```
=== "Python 3.10 and above"
=== "Python 3.9+"
```Python hl_lines="23"
{!> ../../../docs_src/dependencies/tutorial005_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="24"
{!> ../../../docs_src/dependencies/tutorial005_an.py!}
```
=== "Python 3.10 non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="19"
{!> ../../../docs_src/dependencies/tutorial005_py310.py!}
```
=== "Python 3.6 non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="22"
{!> ../../../docs_src/dependencies/tutorial005.py!}
```
!!! info
Notice that we are only declaring one dependency in the *path operation function*, the `query_or_cookie_extractor`.
@@ -89,10 +161,22 @@ And it will save the returned value in a <abbr title="A utility/system to store
In an advanced scenario where you know you need the dependency to be called at every step (possibly multiple times) in the same request instead of using the "cached" value, you can set the parameter `use_cache=False` when using `Depends`:
```Python hl_lines="1"
async def needy_dependency(fresh_value: str = Depends(get_value, use_cache=False)):
return {"fresh_value": fresh_value}
```
=== "Python 3.6+"
```Python hl_lines="1"
async def needy_dependency(fresh_value: Annotated[str, Depends(get_value, use_cache=False)]):
return {"fresh_value": fresh_value}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="1"
async def needy_dependency(fresh_value: str = Depends(get_value, use_cache=False)):
return {"fresh_value": fresh_value}
```
## Recap

View File

@@ -20,18 +20,18 @@ You can use `jsonable_encoder` for that.
It receives an object, like a Pydantic model, and returns a JSON compatible version:
=== "Python 3.6 and above"
```Python hl_lines="5 22"
{!> ../../../docs_src/encoder/tutorial001.py!}
```
=== "Python 3.10 and above"
=== "Python 3.10+"
```Python hl_lines="4 21"
{!> ../../../docs_src/encoder/tutorial001_py310.py!}
```
=== "Python 3.6+"
```Python hl_lines="5 22"
{!> ../../../docs_src/encoder/tutorial001.py!}
```
In this example, it would convert the Pydantic model to a `dict`, and the `datetime` to a `str`.
The result of calling it is something that can be encoded with the Python standard <a href="https://docs.python.org/3/library/json.html#json.dumps" class="external-link" target="_blank">`json.dumps()`</a>.

View File

@@ -55,28 +55,76 @@ Here are some of the additional data types you can use:
Here's an example *path operation* with parameters using some of the above types.
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="1 3 12-16"
{!> ../../../docs_src/extra_data_types/tutorial001.py!}
{!> ../../../docs_src/extra_data_types/tutorial001_an_py310.py!}
```
=== "Python 3.10 and above"
=== "Python 3.9+"
```Python hl_lines="1 3 12-16"
{!> ../../../docs_src/extra_data_types/tutorial001_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="1 3 13-17"
{!> ../../../docs_src/extra_data_types/tutorial001_an.py!}
```
=== "Python 3.10+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="1 2 11-15"
{!> ../../../docs_src/extra_data_types/tutorial001_py310.py!}
```
Note that the parameters inside the function have their natural data type, and you can, for example, perform normal date manipulations, like:
=== "Python 3.6+ non-Annotated"
=== "Python 3.6 and above"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="18-19"
```Python hl_lines="1 2 12-16"
{!> ../../../docs_src/extra_data_types/tutorial001.py!}
```
=== "Python 3.10 and above"
Note that the parameters inside the function have their natural data type, and you can, for example, perform normal date manipulations, like:
=== "Python 3.10+"
```Python hl_lines="18-19"
{!> ../../../docs_src/extra_data_types/tutorial001_an_py310.py!}
```
=== "Python 3.9+"
```Python hl_lines="18-19"
{!> ../../../docs_src/extra_data_types/tutorial001_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="19-20"
{!> ../../../docs_src/extra_data_types/tutorial001_an.py!}
```
=== "Python 3.10+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="17-18"
{!> ../../../docs_src/extra_data_types/tutorial001_py310.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="18-19"
{!> ../../../docs_src/extra_data_types/tutorial001.py!}
```

View File

@@ -17,18 +17,18 @@ This is especially the case for user models, because:
Here's a general idea of how the models could look like with their password fields and the places where they are used:
=== "Python 3.6 and above"
```Python hl_lines="9 11 16 22 24 29-30 33-35 40-41"
{!> ../../../docs_src/extra_models/tutorial001.py!}
```
=== "Python 3.10 and above"
=== "Python 3.10+"
```Python hl_lines="7 9 14 20 22 27-28 31-33 38-39"
{!> ../../../docs_src/extra_models/tutorial001_py310.py!}
```
=== "Python 3.6+"
```Python hl_lines="9 11 16 22 24 29-30 33-35 40-41"
{!> ../../../docs_src/extra_models/tutorial001.py!}
```
### About `**user_in.dict()`
#### Pydantic's `.dict()`
@@ -158,18 +158,18 @@ All the data conversion, validation, documentation, etc. will still work as norm
That way, we can declare just the differences between the models (with plaintext `password`, with `hashed_password` and without password):
=== "Python 3.6 and above"
```Python hl_lines="9 15-16 19-20 23-24"
{!> ../../../docs_src/extra_models/tutorial002.py!}
```
=== "Python 3.10 and above"
=== "Python 3.10+"
```Python hl_lines="7 13-14 17-18 21-22"
{!> ../../../docs_src/extra_models/tutorial002_py310.py!}
```
=== "Python 3.6+"
```Python hl_lines="9 15-16 19-20 23-24"
{!> ../../../docs_src/extra_models/tutorial002.py!}
```
## `Union` or `anyOf`
You can declare a response to be the `Union` of two types, that means, that the response would be any of the two.
@@ -181,18 +181,18 @@ To do that, use the standard Python type hint <a href="https://docs.python.org/3
!!! note
When defining a <a href="https://pydantic-docs.helpmanual.io/usage/types/#unions" class="external-link" target="_blank">`Union`</a>, include the most specific type first, followed by the less specific type. In the example below, the more specific `PlaneItem` comes before `CarItem` in `Union[PlaneItem, CarItem]`.
=== "Python 3.6 and above"
```Python hl_lines="1 14-15 18-20 33"
{!> ../../../docs_src/extra_models/tutorial003.py!}
```
=== "Python 3.10 and above"
=== "Python 3.10+"
```Python hl_lines="1 14-15 18-20 33"
{!> ../../../docs_src/extra_models/tutorial003_py310.py!}
```
=== "Python 3.6+"
```Python hl_lines="1 14-15 18-20 33"
{!> ../../../docs_src/extra_models/tutorial003.py!}
```
### `Union` in Python 3.10
In this example we pass `Union[PlaneItem, CarItem]` as the value of the argument `response_model`.
@@ -213,18 +213,18 @@ The same way, you can declare responses of lists of objects.
For that, use the standard Python `typing.List` (or just `list` in Python 3.9 and above):
=== "Python 3.6 and above"
```Python hl_lines="1 20"
{!> ../../../docs_src/extra_models/tutorial004.py!}
```
=== "Python 3.9 and above"
=== "Python 3.9+"
```Python hl_lines="18"
{!> ../../../docs_src/extra_models/tutorial004_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="1 20"
{!> ../../../docs_src/extra_models/tutorial004.py!}
```
## Response with arbitrary `dict`
You can also declare a response using a plain arbitrary `dict`, declaring just the type of the keys and values, without using a Pydantic model.
@@ -233,18 +233,18 @@ This is useful if you don't know the valid field/attribute names (that would be
In this case, you can use `typing.Dict` (or just `dict` in Python 3.9 and above):
=== "Python 3.6 and above"
```Python hl_lines="1 8"
{!> ../../../docs_src/extra_models/tutorial005.py!}
```
=== "Python 3.9 and above"
=== "Python 3.9+"
```Python hl_lines="6"
{!> ../../../docs_src/extra_models/tutorial005_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="1 8"
{!> ../../../docs_src/extra_models/tutorial005.py!}
```
## Recap
Use multiple Pydantic models and inherit freely for each case.

View File

@@ -6,36 +6,84 @@ You can define Header parameters the same way you define `Query`, `Path` and `Co
First import `Header`:
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="3"
{!> ../../../docs_src/header_params/tutorial001.py!}
{!> ../../../docs_src/header_params/tutorial001_an_py310.py!}
```
=== "Python 3.10 and above"
=== "Python 3.9+"
```Python hl_lines="3"
{!> ../../../docs_src/header_params/tutorial001_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="3"
{!> ../../../docs_src/header_params/tutorial001_an.py!}
```
=== "Python 3.10+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="1"
{!> ../../../docs_src/header_params/tutorial001_py310.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="3"
{!> ../../../docs_src/header_params/tutorial001.py!}
```
## Declare `Header` parameters
Then declare the header parameters using the same structure as with `Path`, `Query` and `Cookie`.
The first value is the default value, you can pass all the extra validation or annotation parameters:
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="9"
{!> ../../../docs_src/header_params/tutorial001.py!}
{!> ../../../docs_src/header_params/tutorial001_an_py310.py!}
```
=== "Python 3.10 and above"
=== "Python 3.9+"
```Python hl_lines="9"
{!> ../../../docs_src/header_params/tutorial001_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="10"
{!> ../../../docs_src/header_params/tutorial001_an.py!}
```
=== "Python 3.10+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="7"
{!> ../../../docs_src/header_params/tutorial001_py310.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="9"
{!> ../../../docs_src/header_params/tutorial001.py!}
```
!!! note "Technical Details"
`Header` is a "sister" class of `Path`, `Query` and `Cookie`. It also inherits from the same common `Param` class.
@@ -60,18 +108,42 @@ So, you can use `user_agent` as you normally would in Python code, instead of ne
If for some reason you need to disable automatic conversion of underscores to hyphens, set the parameter `convert_underscores` of `Header` to `False`:
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="10"
{!> ../../../docs_src/header_params/tutorial002.py!}
{!> ../../../docs_src/header_params/tutorial002_an_py310.py!}
```
=== "Python 3.10 and above"
=== "Python 3.9+"
```Python hl_lines="11"
{!> ../../../docs_src/header_params/tutorial002_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="12"
{!> ../../../docs_src/header_params/tutorial002_an.py!}
```
=== "Python 3.10+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="8"
{!> ../../../docs_src/header_params/tutorial002_py310.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="10"
{!> ../../../docs_src/header_params/tutorial002.py!}
```
!!! warning
Before setting `convert_underscores` to `False`, bear in mind that some HTTP proxies and servers disallow the usage of headers with underscores.
@@ -85,22 +157,49 @@ You will receive all the values from the duplicate header as a Python `list`.
For example, to declare a header of `X-Token` that can appear more than once, you can write:
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="9"
{!> ../../../docs_src/header_params/tutorial003.py!}
{!> ../../../docs_src/header_params/tutorial003_an_py310.py!}
```
=== "Python 3.9 and above"
=== "Python 3.9+"
```Python hl_lines="9"
{!> ../../../docs_src/header_params/tutorial003_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="10"
{!> ../../../docs_src/header_params/tutorial003_an.py!}
```
=== "Python 3.10+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="7"
{!> ../../../docs_src/header_params/tutorial003_py310.py!}
```
=== "Python 3.9+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="9"
{!> ../../../docs_src/header_params/tutorial003_py39.py!}
```
=== "Python 3.10 and above"
=== "Python 3.6+ non-Annotated"
```Python hl_lines="7"
{!> ../../../docs_src/header_params/tutorial003_py310.py!}
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="9"
{!> ../../../docs_src/header_params/tutorial003.py!}
```
If you communicate with that *path operation* sending two HTTP headers like:

View File

@@ -101,7 +101,7 @@ You can configure the two documentation user interfaces included:
* **Swagger UI**: served at `/docs`.
* You can set its URL with the parameter `docs_url`.
* You can disable it by setting `docs_url=None`.
* ReDoc: served at `/redoc`.
* **ReDoc**: served at `/redoc`.
* You can set its URL with the parameter `redoc_url`.
* You can disable it by setting `redoc_url=None`.

View File

@@ -13,22 +13,22 @@ You can pass directly the `int` code, like `404`.
But if you don't remember what each number code is for, you can use the shortcut constants in `status`:
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="3 17"
{!> ../../../docs_src/path_operation_configuration/tutorial001.py!}
```Python hl_lines="1 15"
{!> ../../../docs_src/path_operation_configuration/tutorial001_py310.py!}
```
=== "Python 3.9 and above"
=== "Python 3.9+"
```Python hl_lines="3 17"
{!> ../../../docs_src/path_operation_configuration/tutorial001_py39.py!}
```
=== "Python 3.10 and above"
=== "Python 3.6+"
```Python hl_lines="1 15"
{!> ../../../docs_src/path_operation_configuration/tutorial001_py310.py!}
```Python hl_lines="3 17"
{!> ../../../docs_src/path_operation_configuration/tutorial001.py!}
```
That status code will be used in the response and will be added to the OpenAPI schema.
@@ -42,22 +42,22 @@ That status code will be used in the response and will be added to the OpenAPI s
You can add tags to your *path operation*, pass the parameter `tags` with a `list` of `str` (commonly just one `str`):
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="17 22 27"
{!> ../../../docs_src/path_operation_configuration/tutorial002.py!}
```Python hl_lines="15 20 25"
{!> ../../../docs_src/path_operation_configuration/tutorial002_py310.py!}
```
=== "Python 3.9 and above"
=== "Python 3.9+"
```Python hl_lines="17 22 27"
{!> ../../../docs_src/path_operation_configuration/tutorial002_py39.py!}
```
=== "Python 3.10 and above"
=== "Python 3.6+"
```Python hl_lines="15 20 25"
{!> ../../../docs_src/path_operation_configuration/tutorial002_py310.py!}
```Python hl_lines="17 22 27"
{!> ../../../docs_src/path_operation_configuration/tutorial002.py!}
```
They will be added to the OpenAPI schema and used by the automatic documentation interfaces:
@@ -80,22 +80,22 @@ In these cases, it could make sense to store the tags in an `Enum`.
You can add a `summary` and `description`:
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="20-21"
{!> ../../../docs_src/path_operation_configuration/tutorial003.py!}
```Python hl_lines="18-19"
{!> ../../../docs_src/path_operation_configuration/tutorial003_py310.py!}
```
=== "Python 3.9 and above"
=== "Python 3.9+"
```Python hl_lines="20-21"
{!> ../../../docs_src/path_operation_configuration/tutorial003_py39.py!}
```
=== "Python 3.10 and above"
=== "Python 3.6+"
```Python hl_lines="18-19"
{!> ../../../docs_src/path_operation_configuration/tutorial003_py310.py!}
```Python hl_lines="20-21"
{!> ../../../docs_src/path_operation_configuration/tutorial003.py!}
```
## Description from docstring
@@ -104,22 +104,22 @@ As descriptions tend to be long and cover multiple lines, you can declare the *p
You can write <a href="https://en.wikipedia.org/wiki/Markdown" class="external-link" target="_blank">Markdown</a> in the docstring, it will be interpreted and displayed correctly (taking into account docstring indentation).
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="19-27"
{!> ../../../docs_src/path_operation_configuration/tutorial004.py!}
```Python hl_lines="17-25"
{!> ../../../docs_src/path_operation_configuration/tutorial004_py310.py!}
```
=== "Python 3.9 and above"
=== "Python 3.9+"
```Python hl_lines="19-27"
{!> ../../../docs_src/path_operation_configuration/tutorial004_py39.py!}
```
=== "Python 3.10 and above"
=== "Python 3.6+"
```Python hl_lines="17-25"
{!> ../../../docs_src/path_operation_configuration/tutorial004_py310.py!}
```Python hl_lines="19-27"
{!> ../../../docs_src/path_operation_configuration/tutorial004.py!}
```
It will be used in the interactive docs:
@@ -130,22 +130,22 @@ It will be used in the interactive docs:
You can specify the response description with the parameter `response_description`:
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="21"
{!> ../../../docs_src/path_operation_configuration/tutorial005.py!}
```Python hl_lines="19"
{!> ../../../docs_src/path_operation_configuration/tutorial005_py310.py!}
```
=== "Python 3.9 and above"
=== "Python 3.9+"
```Python hl_lines="21"
{!> ../../../docs_src/path_operation_configuration/tutorial005_py39.py!}
```
=== "Python 3.10 and above"
=== "Python 3.6+"
```Python hl_lines="19"
{!> ../../../docs_src/path_operation_configuration/tutorial005_py310.py!}
```Python hl_lines="21"
{!> ../../../docs_src/path_operation_configuration/tutorial005.py!}
```
!!! info

View File

@@ -4,38 +4,86 @@ In the same way that you can declare more validations and metadata for query par
## Import Path
First, import `Path` from `fastapi`:
First, import `Path` from `fastapi`, and import `Annotated`:
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="3"
{!> ../../../docs_src/path_params_numeric_validations/tutorial001.py!}
```Python hl_lines="1 3"
{!> ../../../docs_src/path_params_numeric_validations/tutorial001_an_py310.py!}
```
=== "Python 3.10 and above"
=== "Python 3.9+"
```Python hl_lines="1 3"
{!> ../../../docs_src/path_params_numeric_validations/tutorial001_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="3-4"
{!> ../../../docs_src/path_params_numeric_validations/tutorial001_an.py!}
```
=== "Python 3.10+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="1"
{!> ../../../docs_src/path_params_numeric_validations/tutorial001_py310.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="3"
{!> ../../../docs_src/path_params_numeric_validations/tutorial001.py!}
```
## Declare metadata
You can declare all the same parameters as for `Query`.
For example, to declare a `title` metadata value for the path parameter `item_id` you can type:
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="10"
{!> ../../../docs_src/path_params_numeric_validations/tutorial001.py!}
{!> ../../../docs_src/path_params_numeric_validations/tutorial001_an_py310.py!}
```
=== "Python 3.10 and above"
=== "Python 3.9+"
```Python hl_lines="10"
{!> ../../../docs_src/path_params_numeric_validations/tutorial001_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="11"
{!> ../../../docs_src/path_params_numeric_validations/tutorial001_an.py!}
```
=== "Python 3.10+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="8"
{!> ../../../docs_src/path_params_numeric_validations/tutorial001_py310.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="10"
{!> ../../../docs_src/path_params_numeric_validations/tutorial001.py!}
```
!!! note
A path parameter is always required as it has to be part of the path.
@@ -45,11 +93,14 @@ For example, to declare a `title` metadata value for the path parameter `item_id
## Order the parameters as you need
!!! tip
This is probably not as important or necessary if you use `Annotated`.
Let's say that you want to declare the query parameter `q` as a required `str`.
And you don't need to declare anything else for that parameter, so you don't really need to use `Query`.
But you still need to use `Path` for the `item_id` path parameter.
But you still need to use `Path` for the `item_id` path parameter. And you don't want to use `Annotated` for some reason.
Python will complain if you put a value with a "default" before a value that doesn't have a "default".
@@ -59,13 +110,44 @@ It doesn't matter for **FastAPI**. It will detect the parameters by their names,
So, you can declare your function as:
```Python hl_lines="7"
{!../../../docs_src/path_params_numeric_validations/tutorial002.py!}
```
=== "Python 3.6 non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="7"
{!> ../../../docs_src/path_params_numeric_validations/tutorial002.py!}
```
But have in mind that if you use `Annotated`, you won't have this problem, it won't matter as you're not using the function parameter default values for `Query()` or `Path()`.
=== "Python 3.9+"
```Python hl_lines="10"
{!> ../../../docs_src/path_params_numeric_validations/tutorial002_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="9"
{!> ../../../docs_src/path_params_numeric_validations/tutorial002_an.py!}
```
## Order the parameters as you need, tricks
If you want to declare the `q` query parameter without a `Query` nor any default value, and the path parameter `item_id` using `Path`, and have them in a different order, Python has a little special syntax for that.
!!! tip
This is probably not as important or necessary if you use `Annotated`.
Here's a **small trick** that can be handy, but you won't need it often.
If you want to:
* declare the `q` query parameter without a `Query` nor any default value
* declare the path parameter `item_id` using `Path`
* have them in a different order
* not use `Annotated`
...Python has a little special syntax for that.
Pass `*`, as the first parameter of the function.
@@ -75,15 +157,48 @@ Python won't do anything with that `*`, but it will know that all the following
{!../../../docs_src/path_params_numeric_validations/tutorial003.py!}
```
### Better with `Annotated`
Have in mind that if you use `Annotated`, as you are not using function parameter default values, you won't have this problem, and yo probably won't need to use `*`.
=== "Python 3.9+"
```Python hl_lines="10"
{!> ../../../docs_src/path_params_numeric_validations/tutorial003_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="9"
{!> ../../../docs_src/path_params_numeric_validations/tutorial003_an.py!}
```
## Number validations: greater than or equal
With `Query` and `Path` (and others you'll see later) you can declare number constraints.
Here, with `ge=1`, `item_id` will need to be an integer number "`g`reater than or `e`qual" to `1`.
```Python hl_lines="8"
{!../../../docs_src/path_params_numeric_validations/tutorial004.py!}
```
=== "Python 3.9+"
```Python hl_lines="10"
{!> ../../../docs_src/path_params_numeric_validations/tutorial004_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="9"
{!> ../../../docs_src/path_params_numeric_validations/tutorial004_an.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="8"
{!> ../../../docs_src/path_params_numeric_validations/tutorial004.py!}
```
## Number validations: greater than and less than or equal
@@ -92,9 +207,26 @@ The same applies for:
* `gt`: `g`reater `t`han
* `le`: `l`ess than or `e`qual
```Python hl_lines="9"
{!../../../docs_src/path_params_numeric_validations/tutorial005.py!}
```
=== "Python 3.9+"
```Python hl_lines="10"
{!> ../../../docs_src/path_params_numeric_validations/tutorial005_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="9"
{!> ../../../docs_src/path_params_numeric_validations/tutorial005_an.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="9"
{!> ../../../docs_src/path_params_numeric_validations/tutorial005.py!}
```
## Number validations: floats, greater than and less than
@@ -106,9 +238,26 @@ So, `0.5` would be a valid value. But `0.0` or `0` would not.
And the same for <abbr title="less than"><code>lt</code></abbr>.
```Python hl_lines="11"
{!../../../docs_src/path_params_numeric_validations/tutorial006.py!}
```
=== "Python 3.9+"
```Python hl_lines="13"
{!> ../../../docs_src/path_params_numeric_validations/tutorial006_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="12"
{!> ../../../docs_src/path_params_numeric_validations/tutorial006_an.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="11"
{!> ../../../docs_src/path_params_numeric_validations/tutorial006.py!}
```
## Recap

View File

@@ -4,18 +4,18 @@
Let's take this application as example:
=== "Python 3.6 and above"
```Python hl_lines="9"
{!> ../../../docs_src/query_params_str_validations/tutorial001.py!}
```
=== "Python 3.10 and above"
=== "Python 3.10+"
```Python hl_lines="7"
{!> ../../../docs_src/query_params_str_validations/tutorial001_py310.py!}
```
=== "Python 3.6+"
```Python hl_lines="9"
{!> ../../../docs_src/query_params_str_validations/tutorial001.py!}
```
The query parameter `q` is of type `Union[str, None]` (or `str | None` in Python 3.10), that means that it's of type `str` but could also be `None`, and indeed, the default value is `None`, so FastAPI will know it's not required.
!!! note
@@ -27,39 +27,117 @@ The query parameter `q` is of type `Union[str, None]` (or `str | None` in Python
We are going to enforce that even though `q` is optional, whenever it is provided, **its length doesn't exceed 50 characters**.
### Import `Query`
### Import `Query` and `Annotated`
To achieve that, first import `Query` from `fastapi`:
To achieve that, first import:
=== "Python 3.6 and above"
* `Query` from `fastapi`
* `Annotated` from `typing` (or from `typing_extensions` in Python below 3.9)
```Python hl_lines="3"
{!> ../../../docs_src/query_params_str_validations/tutorial002.py!}
=== "Python 3.10+"
In Python 3.9 or above, `Annotated` is part of the standard library, so you can import it from `typing`.
```Python hl_lines="1 3"
{!> ../../../docs_src/query_params_str_validations/tutorial002_an_py310.py!}
```
=== "Python 3.10 and above"
=== "Python 3.6+"
```Python hl_lines="1"
{!> ../../../docs_src/query_params_str_validations/tutorial002_py310.py!}
In versions of Python below Python 3.9 you import `Annotation` from `typing_extensions`.
It will already be installed with FastAPI.
```Python hl_lines="3-4"
{!> ../../../docs_src/query_params_str_validations/tutorial002_an.py!}
```
## Use `Query` as the default value
## Use `Annotated` in the type for the `q` parameter
And now use it as the default value of your parameter, setting the parameter `max_length` to 50:
Remember I told you before that `Annotated` can be used to add metadata to your parameters in the [Python Types Intro](../python-types.md#type-hints-with-metadata-annotations){.internal-link target=_blank}?
=== "Python 3.6 and above"
Now it's the time to use it with FastAPI. 🚀
We had this type annotation:
=== "Python 3.10+"
```Python
q: str | None = None
```
=== "Python 3.6+"
```Python
q: Union[str, None] = None
```
What we will do is wrap that with `Annotated`, so it becomes:
=== "Python 3.10+"
```Python
q: Annotated[str | None] = None
```
=== "Python 3.6+"
```Python
q: Annotated[Union[str, None]] = None
```
Both of those versions mean the same thing, `q` is a parameter that can be a `str` or `None`, and by default, it is `None`.
Now let's jump to the fun stuff. 🎉
## Add `Query` to `Annotated` in the `q` parameter
Now that we have this `Annotated` where we can put more metadata, add `Query` to it, and set the parameter `max_length` to 50:
=== "Python 3.10+"
```Python hl_lines="9"
{!> ../../../docs_src/query_params_str_validations/tutorial002.py!}
{!> ../../../docs_src/query_params_str_validations/tutorial002_an_py310.py!}
```
=== "Python 3.10 and above"
=== "Python 3.6+"
```Python hl_lines="10"
{!> ../../../docs_src/query_params_str_validations/tutorial002_an.py!}
```
Notice that the default value is still `None`, so the parameter is still optional.
But now, having `Query(max_length=50)` inside of `Annotated`, we are telling FastAPI that we want it to extract this value from the query parameters (this would have been the default anyway 🤷) and that we want to have **additional validation** for this value (that's why we do this, to get the additional validation). 😎
FastAPI wll now:
* **Validate** the data making sure that the max length is 50 characters
* Show a **clear error** for the client when the data is not valid
* **Document** the parameter in the OpenAPI schema *path operation* (so it will show up in the **automatic docs UI**)
## Alternative (old) `Query` as the default value
Previous versions of FastAPI (before <abbr title="before 2023-03">0.95.0</abbr>) required you to use `Query` as the default value of your parameter, instead of putting it in `Annotated`, there's a high chance that you will see code using it around, so I'll explain it to you.
!!! tip
For new code and whenever possible, use `Annotated` as explained above. There are multiple advantages (explained below) and no disadvantages. 🍰
This is how you would use `Query()` as the default value of your function parameter, setting the parameter `max_length` to 50:
=== "Python 3.10+"
```Python hl_lines="7"
{!> ../../../docs_src/query_params_str_validations/tutorial002_py310.py!}
```
As we have to replace the default value `None` in the function with `Query()`, we can now set the default value with the parameter `Query(default=None)`, it serves the same purpose of defining that default value.
=== "Python 3.6+"
```Python hl_lines="9"
{!> ../../../docs_src/query_params_str_validations/tutorial002.py!}
```
As in this case (without using `Annotated`) we have to replace the default value `None` in the function with `Query()`, we now need to set the default value with the parameter `Query(default=None)`, it serves the same purpose of defining that default value (at least for FastAPI).
So:
@@ -67,7 +145,7 @@ So:
q: Union[str, None] = Query(default=None)
```
...makes the parameter optional, the same as:
...makes the parameter optional, with a default value of `None`, the same as:
```Python
q: Union[str, None] = None
@@ -79,7 +157,7 @@ And in Python 3.10 and above:
q: str | None = Query(default=None)
```
...makes the parameter optional, the same as:
...makes the parameter optional, with a default value of `None`, the same as:
```Python
q: str | None = None
@@ -112,38 +190,124 @@ q: Union[str, None] = Query(default=None, max_length=50)
This will validate the data, show a clear error when the data is not valid, and document the parameter in the OpenAPI schema *path operation*.
### `Query` as the default value or in `Annotated`
Have in mind that when using `Query` inside of `Annotated` you cannot use the `default` parameter for `Query`.
Instead use the actual default value of the function parameter. Otherwise, it would be inconsistent.
For example, this is not allowed:
```Python
q: Annotated[str Query(default="rick")] = "morty"
```
...because it's not clear if the default value should be `"rick"` or `"morty"`.
So, you would use (preferably):
```Python
q: Annotated[str, Query()] = "rick"
```
...or in older code bases you will find:
```Python
q: str = Query(default="rick")
```
### Advantages of `Annotated`
**Using `Annotated` is recommended** instead of the default value in function parameters, it is **better** for multiple reasons. 🤓
The **default** value of the **function parameter** is the **actual default** value, that's more intuitive with Python in general. 😌
You could **call** that same function in **other places** without FastAPI, and it would **work as expected**. If there's a **required** parameter (without a default value), your **editor** will let you know with an error, **Python** will also complain if you run it without passing the required parameter.
When you don't use `Annotated` and instead use the **(old) default value style**, if you call that function without FastAPI in **other place**, you have to **remember** to pass the arguments to the function for it to work correctly, otherwise the values will be different from what you expect (e.g. `QueryInfo` or something similar instead of `str`). And your editor won't complain, and Python won't complain running that function, only when the operations inside error out.
Because `Annotated` can have more than one metadata annotation, you could now even use the same function with other tools, like <a href="https://typer.tiangolo.com/" class="external-link" target="_blank">Typer</a>. 🚀
## Add more validations
You can also add a parameter `min_length`:
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="10"
{!> ../../../docs_src/query_params_str_validations/tutorial003.py!}
{!> ../../../docs_src/query_params_str_validations/tutorial003_an_py310.py!}
```
=== "Python 3.10 and above"
=== "Python 3.9+"
```Python hl_lines="10"
{!> ../../../docs_src/query_params_str_validations/tutorial003_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="11"
{!> ../../../docs_src/query_params_str_validations/tutorial003_an.py!}
```
=== "Python 3.10+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="7"
{!> ../../../docs_src/query_params_str_validations/tutorial003_py310.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="10"
{!> ../../../docs_src/query_params_str_validations/tutorial003.py!}
```
## Add regular expressions
You can define a <abbr title="A regular expression, regex or regexp is a sequence of characters that define a search pattern for strings.">regular expression</abbr> that the parameter should match:
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="11"
{!> ../../../docs_src/query_params_str_validations/tutorial004.py!}
{!> ../../../docs_src/query_params_str_validations/tutorial004_an_py310.py!}
```
=== "Python 3.10 and above"
=== "Python 3.9+"
```Python hl_lines="11"
{!> ../../../docs_src/query_params_str_validations/tutorial004_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="12"
{!> ../../../docs_src/query_params_str_validations/tutorial004_an.py!}
```
=== "Python 3.10+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="9"
{!> ../../../docs_src/query_params_str_validations/tutorial004_py310.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="11"
{!> ../../../docs_src/query_params_str_validations/tutorial004.py!}
```
This specific regular expression checks that the received parameter value:
* `^`: starts with the following characters, doesn't have characters before.
@@ -156,16 +320,33 @@ But whenever you need them and go and learn them, know that you can already use
## Default values
The same way that you can pass `None` as the value for the `default` parameter, you can pass other values.
You can, of course, use default values other than `None`.
Let's say that you want to declare the `q` query parameter to have a `min_length` of `3`, and to have a default value of `"fixedquery"`:
```Python hl_lines="7"
{!../../../docs_src/query_params_str_validations/tutorial005.py!}
```
=== "Python 3.9+"
```Python hl_lines="9"
{!> ../../../docs_src/query_params_str_validations/tutorial005_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="8"
{!> ../../../docs_src/query_params_str_validations/tutorial005_an.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="7"
{!> ../../../docs_src/query_params_str_validations/tutorial005.py!}
```
!!! note
Having a default value also makes the parameter optional.
Having a default value of any type, including `None`, makes the parameter optional (not required).
## Make it required
@@ -183,23 +364,70 @@ q: Union[str, None] = None
But we are now declaring it with `Query`, for example like:
```Python
q: Union[str, None] = Query(default=None, min_length=3)
```
=== "Annotated"
```Python
q: Annotated[Union[str, None], Query(min_length=3)] = None
```
=== "non-Annotated"
```Python
q: Union[str, None] = Query(default=None, min_length=3)
```
So, when you need to declare a value as required while using `Query`, you can simply not declare a default value:
```Python hl_lines="7"
{!../../../docs_src/query_params_str_validations/tutorial006.py!}
```
=== "Python 3.9+"
```Python hl_lines="9"
{!> ../../../docs_src/query_params_str_validations/tutorial006_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="8"
{!> ../../../docs_src/query_params_str_validations/tutorial006_an.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="7"
{!> ../../../docs_src/query_params_str_validations/tutorial006.py!}
```
!!! tip
Notice that, even though in this case the `Query()` is used as the function parameter default value, we don't pass the `default=None` to `Query()`.
Still, probably better to use the `Annotated` version. 😉
### Required with Ellipsis (`...`)
There's an alternative way to explicitly declare that a value is required. You can set the `default` parameter to the literal value `...`:
There's an alternative way to explicitly declare that a value is required. You can set the default to the literal value `...`:
```Python hl_lines="7"
{!../../../docs_src/query_params_str_validations/tutorial006b.py!}
```
=== "Python 3.9+"
```Python hl_lines="9"
{!> ../../../docs_src/query_params_str_validations/tutorial006b_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="8"
{!> ../../../docs_src/query_params_str_validations/tutorial006b_an.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="7"
{!> ../../../docs_src/query_params_str_validations/tutorial006b.py!}
```
!!! info
If you hadn't seen that `...` before: it is a special single value, it is <a href="https://docs.python.org/3/library/constants.html#Ellipsis" class="external-link" target="_blank">part of Python and is called "Ellipsis"</a>.
@@ -212,20 +440,44 @@ This will let **FastAPI** know that this parameter is required.
You can declare that a parameter can accept `None`, but that it's still required. This would force clients to send a value, even if the value is `None`.
To do that, you can declare that `None` is a valid type but still use `default=...`:
To do that, you can declare that `None` is a valid type but still use `...` as the default:
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="9"
{!> ../../../docs_src/query_params_str_validations/tutorial006c.py!}
{!> ../../../docs_src/query_params_str_validations/tutorial006c_an_py310.py!}
```
=== "Python 3.10 and above"
=== "Python 3.9+"
```Python hl_lines="9"
{!> ../../../docs_src/query_params_str_validations/tutorial006c_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="10"
{!> ../../../docs_src/query_params_str_validations/tutorial006c_an.py!}
```
=== "Python 3.10+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="7"
{!> ../../../docs_src/query_params_str_validations/tutorial006c_py310.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="9"
{!> ../../../docs_src/query_params_str_validations/tutorial006c.py!}
```
!!! tip
Pydantic, which is what powers all the data validation and serialization in FastAPI, has a special behavior when you use `Optional` or `Union[Something, None]` without a default value, you can read more about it in the Pydantic docs about <a href="https://pydantic-docs.helpmanual.io/usage/models/#required-optional-fields" class="external-link" target="_blank">Required Optional fields</a>.
@@ -233,12 +485,29 @@ To do that, you can declare that `None` is a valid type but still use `default=.
If you feel uncomfortable using `...`, you can also import and use `Required` from Pydantic:
```Python hl_lines="2 8"
{!../../../docs_src/query_params_str_validations/tutorial006d.py!}
```
=== "Python 3.9+"
```Python hl_lines="4 10"
{!> ../../../docs_src/query_params_str_validations/tutorial006d_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="2 9"
{!> ../../../docs_src/query_params_str_validations/tutorial006d_an.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="2 8"
{!> ../../../docs_src/query_params_str_validations/tutorial006d.py!}
```
!!! tip
Remember that in most of the cases, when something is required, you can simply omit the `default` parameter, so you normally don't have to use `...` nor `Required`.
Remember that in most of the cases, when something is required, you can simply omit the default, so you normally don't have to use `...` nor `Required`.
## Query parameter list / multiple values
@@ -246,22 +515,49 @@ When you define a query parameter explicitly with `Query` you can also declare i
For example, to declare a query parameter `q` that can appear multiple times in the URL, you can write:
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="9"
{!> ../../../docs_src/query_params_str_validations/tutorial011.py!}
{!> ../../../docs_src/query_params_str_validations/tutorial011_an_py310.py!}
```
=== "Python 3.9 and above"
=== "Python 3.9+"
```Python hl_lines="9"
{!> ../../../docs_src/query_params_str_validations/tutorial011_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="10"
{!> ../../../docs_src/query_params_str_validations/tutorial011_an.py!}
```
=== "Python 3.10+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="7"
{!> ../../../docs_src/query_params_str_validations/tutorial011_py310.py!}
```
=== "Python 3.9+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="9"
{!> ../../../docs_src/query_params_str_validations/tutorial011_py39.py!}
```
=== "Python 3.10 and above"
=== "Python 3.6+ non-Annotated"
```Python hl_lines="7"
{!> ../../../docs_src/query_params_str_validations/tutorial011_py310.py!}
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="9"
{!> ../../../docs_src/query_params_str_validations/tutorial011.py!}
```
Then, with a URL like:
@@ -294,18 +590,36 @@ The interactive API docs will update accordingly, to allow multiple values:
And you can also define a default `list` of values if none are provided:
=== "Python 3.6 and above"
=== "Python 3.9+"
```Python hl_lines="9"
{!> ../../../docs_src/query_params_str_validations/tutorial012.py!}
{!> ../../../docs_src/query_params_str_validations/tutorial012_an_py39.py!}
```
=== "Python 3.9 and above"
=== "Python 3.6+"
```Python hl_lines="10"
{!> ../../../docs_src/query_params_str_validations/tutorial012_an.py!}
```
=== "Python 3.9+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="7"
{!> ../../../docs_src/query_params_str_validations/tutorial012_py39.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="9"
{!> ../../../docs_src/query_params_str_validations/tutorial012.py!}
```
If you go to:
```
@@ -327,9 +641,26 @@ the default of `q` will be: `["foo", "bar"]` and your response will be:
You can also use `list` directly instead of `List[str]` (or `list[str]` in Python 3.9+):
```Python hl_lines="7"
{!../../../docs_src/query_params_str_validations/tutorial013.py!}
```
=== "Python 3.9+"
```Python hl_lines="9"
{!> ../../../docs_src/query_params_str_validations/tutorial013_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="8"
{!> ../../../docs_src/query_params_str_validations/tutorial013_an.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="7"
{!> ../../../docs_src/query_params_str_validations/tutorial013.py!}
```
!!! note
Have in mind that in this case, FastAPI won't check the contents of the list.
@@ -349,32 +680,80 @@ That information will be included in the generated OpenAPI and used by the docum
You can add a `title`:
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="10"
{!> ../../../docs_src/query_params_str_validations/tutorial007.py!}
{!> ../../../docs_src/query_params_str_validations/tutorial007_an_py310.py!}
```
=== "Python 3.10 and above"
=== "Python 3.9+"
```Python hl_lines="10"
{!> ../../../docs_src/query_params_str_validations/tutorial007_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="11"
{!> ../../../docs_src/query_params_str_validations/tutorial007_an.py!}
```
=== "Python 3.10+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="8"
{!> ../../../docs_src/query_params_str_validations/tutorial007_py310.py!}
```
And a `description`:
=== "Python 3.6+ non-Annotated"
=== "Python 3.6 and above"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="13"
{!> ../../../docs_src/query_params_str_validations/tutorial008.py!}
```Python hl_lines="10"
{!> ../../../docs_src/query_params_str_validations/tutorial007.py!}
```
=== "Python 3.10 and above"
And a `description`:
=== "Python 3.10+"
```Python hl_lines="14"
{!> ../../../docs_src/query_params_str_validations/tutorial008_an_py310.py!}
```
=== "Python 3.9+"
```Python hl_lines="14"
{!> ../../../docs_src/query_params_str_validations/tutorial008_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="15"
{!> ../../../docs_src/query_params_str_validations/tutorial008_an.py!}
```
=== "Python 3.10+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="12"
{!> ../../../docs_src/query_params_str_validations/tutorial008_py310.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="13"
{!> ../../../docs_src/query_params_str_validations/tutorial008.py!}
```
## Alias parameters
Imagine that you want the parameter to be `item-query`.
@@ -393,18 +772,42 @@ But you still need it to be exactly `item-query`...
Then you can declare an `alias`, and that alias is what will be used to find the parameter value:
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="9"
{!> ../../../docs_src/query_params_str_validations/tutorial009.py!}
{!> ../../../docs_src/query_params_str_validations/tutorial009_an_py310.py!}
```
=== "Python 3.10 and above"
=== "Python 3.9+"
```Python hl_lines="9"
{!> ../../../docs_src/query_params_str_validations/tutorial009_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="10"
{!> ../../../docs_src/query_params_str_validations/tutorial009_an.py!}
```
=== "Python 3.10+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="7"
{!> ../../../docs_src/query_params_str_validations/tutorial009_py310.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="9"
{!> ../../../docs_src/query_params_str_validations/tutorial009.py!}
```
## Deprecating parameters
Now let's say you don't like this parameter anymore.
@@ -413,18 +816,42 @@ You have to leave it there a while because there are clients using it, but you w
Then pass the parameter `deprecated=True` to `Query`:
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="18"
{!> ../../../docs_src/query_params_str_validations/tutorial010.py!}
```Python hl_lines="19"
{!> ../../../docs_src/query_params_str_validations/tutorial010_an_py310.py!}
```
=== "Python 3.10 and above"
=== "Python 3.9+"
```Python hl_lines="19"
{!> ../../../docs_src/query_params_str_validations/tutorial010_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="20"
{!> ../../../docs_src/query_params_str_validations/tutorial010_an.py!}
```
=== "Python 3.10+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="17"
{!> ../../../docs_src/query_params_str_validations/tutorial010_py310.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="18"
{!> ../../../docs_src/query_params_str_validations/tutorial010.py!}
```
The docs will show it like this:
<img src="/img/tutorial/query-params-str-validations/image01.png">
@@ -433,18 +860,42 @@ The docs will show it like this:
To exclude a query parameter from the generated OpenAPI schema (and thus, from the automatic documentation systems), set the parameter `include_in_schema` of `Query` to `False`:
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="10"
{!> ../../../docs_src/query_params_str_validations/tutorial014.py!}
{!> ../../../docs_src/query_params_str_validations/tutorial014_an_py310.py!}
```
=== "Python 3.10 and above"
=== "Python 3.9+"
```Python hl_lines="10"
{!> ../../../docs_src/query_params_str_validations/tutorial014_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="11"
{!> ../../../docs_src/query_params_str_validations/tutorial014_an.py!}
```
=== "Python 3.10+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="8"
{!> ../../../docs_src/query_params_str_validations/tutorial014_py310.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="10"
{!> ../../../docs_src/query_params_str_validations/tutorial014.py!}
```
## Recap
You can declare additional validations and metadata for your parameters.

View File

@@ -63,18 +63,18 @@ The parameter values in your function will be:
The same way, you can declare optional query parameters, by setting their default to `None`:
=== "Python 3.6 and above"
```Python hl_lines="9"
{!> ../../../docs_src/query_params/tutorial002.py!}
```
=== "Python 3.10 and above"
=== "Python 3.10+"
```Python hl_lines="7"
{!> ../../../docs_src/query_params/tutorial002_py310.py!}
```
=== "Python 3.6+"
```Python hl_lines="9"
{!> ../../../docs_src/query_params/tutorial002.py!}
```
In this case, the function parameter `q` will be optional, and will be `None` by default.
!!! check
@@ -84,18 +84,18 @@ In this case, the function parameter `q` will be optional, and will be `None` by
You can also declare `bool` types, and they will be converted:
=== "Python 3.6 and above"
```Python hl_lines="9"
{!> ../../../docs_src/query_params/tutorial003.py!}
```
=== "Python 3.10 and above"
=== "Python 3.10+"
```Python hl_lines="7"
{!> ../../../docs_src/query_params/tutorial003_py310.py!}
```
=== "Python 3.6+"
```Python hl_lines="9"
{!> ../../../docs_src/query_params/tutorial003.py!}
```
In this case, if you go to:
```
@@ -137,18 +137,18 @@ And you don't have to declare them in any specific order.
They will be detected by name:
=== "Python 3.6 and above"
```Python hl_lines="8 10"
{!> ../../../docs_src/query_params/tutorial004.py!}
```
=== "Python 3.10 and above"
=== "Python 3.10+"
```Python hl_lines="6 8"
{!> ../../../docs_src/query_params/tutorial004_py310.py!}
```
=== "Python 3.6+"
```Python hl_lines="8 10"
{!> ../../../docs_src/query_params/tutorial004.py!}
```
## Required query parameters
When you declare a default value for non-path parameters (for now, we have only seen query parameters), then it is not required.
@@ -203,18 +203,18 @@ http://127.0.0.1:8000/items/foo-item?needy=sooooneedy
And of course, you can define some parameters as required, some as having a default value, and some entirely optional:
=== "Python 3.6 and above"
```Python hl_lines="10"
{!> ../../../docs_src/query_params/tutorial006.py!}
```
=== "Python 3.10 and above"
=== "Python 3.10+"
```Python hl_lines="8"
{!> ../../../docs_src/query_params/tutorial006_py310.py!}
```
=== "Python 3.6+"
```Python hl_lines="10"
{!> ../../../docs_src/query_params/tutorial006.py!}
```
In this case, there are 3 query parameters:
* `needy`, a required `str`.

View File

@@ -13,17 +13,51 @@ You can define files to be uploaded by the client using `File`.
Import `File` and `UploadFile` from `fastapi`:
```Python hl_lines="1"
{!../../../docs_src/request_files/tutorial001.py!}
```
=== "Python 3.9+"
```Python hl_lines="3"
{!> ../../../docs_src/request_files/tutorial001_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="1"
{!> ../../../docs_src/request_files/tutorial001_an.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="1"
{!> ../../../docs_src/request_files/tutorial001.py!}
```
## Define `File` Parameters
Create file parameters the same way you would for `Body` or `Form`:
```Python hl_lines="7"
{!../../../docs_src/request_files/tutorial001.py!}
```
=== "Python 3.9+"
```Python hl_lines="9"
{!> ../../../docs_src/request_files/tutorial001_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="8"
{!> ../../../docs_src/request_files/tutorial001_an.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="7"
{!> ../../../docs_src/request_files/tutorial001.py!}
```
!!! info
`File` is a class that inherits directly from `Form`.
@@ -45,9 +79,26 @@ But there are several cases in which you might benefit from using `UploadFile`.
Define a file parameter with a type of `UploadFile`:
```Python hl_lines="12"
{!../../../docs_src/request_files/tutorial001.py!}
```
=== "Python 3.9+"
```Python hl_lines="14"
{!> ../../../docs_src/request_files/tutorial001_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="13"
{!> ../../../docs_src/request_files/tutorial001_an.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="12"
{!> ../../../docs_src/request_files/tutorial001.py!}
```
Using `UploadFile` has several advantages over `bytes`:
@@ -118,25 +169,66 @@ The way HTML forms (`<form></form>`) sends the data to the server normally uses
You can make a file optional by using standard type annotations and setting a default value of `None`:
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="9 17"
{!> ../../../docs_src/request_files/tutorial001_02_an_py310.py!}
```
=== "Python 3.9+"
```Python hl_lines="9 17"
{!> ../../../docs_src/request_files/tutorial001_02_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="10 18"
{!> ../../../docs_src/request_files/tutorial001_02_an.py!}
```
=== "Python 3.10+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="7 15"
{!> ../../../docs_src/request_files/tutorial001_02_py310.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="9 17"
{!> ../../../docs_src/request_files/tutorial001_02.py!}
```
=== "Python 3.10 and above"
```Python hl_lines="7 14"
{!> ../../../docs_src/request_files/tutorial001_02_py310.py!}
```
## `UploadFile` with Additional Metadata
You can also use `File()` with `UploadFile`, for example, to set additional metadata:
```Python hl_lines="13"
{!../../../docs_src/request_files/tutorial001_03.py!}
```
=== "Python 3.9+"
```Python hl_lines="9 15"
{!> ../../../docs_src/request_files/tutorial001_03_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="8 14"
{!> ../../../docs_src/request_files/tutorial001_03_an.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="7 13"
{!> ../../../docs_src/request_files/tutorial001_03.py!}
```
## Multiple File Uploads
@@ -146,18 +238,36 @@ They would be associated to the same "form field" sent using "form data".
To use that, declare a list of `bytes` or `UploadFile`:
=== "Python 3.6 and above"
=== "Python 3.9+"
```Python hl_lines="10 15"
{!> ../../../docs_src/request_files/tutorial002.py!}
{!> ../../../docs_src/request_files/tutorial002_an_py39.py!}
```
=== "Python 3.9 and above"
=== "Python 3.6+"
```Python hl_lines="11 16"
{!> ../../../docs_src/request_files/tutorial002_an.py!}
```
=== "Python 3.9+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="8 13"
{!> ../../../docs_src/request_files/tutorial002_py39.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="10 15"
{!> ../../../docs_src/request_files/tutorial002.py!}
```
You will receive, as declared, a `list` of `bytes` or `UploadFile`s.
!!! note "Technical Details"
@@ -169,18 +279,36 @@ You will receive, as declared, a `list` of `bytes` or `UploadFile`s.
And the same way as before, you can use `File()` to set additional parameters, even for `UploadFile`:
=== "Python 3.6 and above"
=== "Python 3.9+"
```Python hl_lines="18"
{!> ../../../docs_src/request_files/tutorial003.py!}
```Python hl_lines="11 18-20"
{!> ../../../docs_src/request_files/tutorial003_an_py39.py!}
```
=== "Python 3.9 and above"
=== "Python 3.6+"
```Python hl_lines="16"
```Python hl_lines="12 19-21"
{!> ../../../docs_src/request_files/tutorial003_an.py!}
```
=== "Python 3.9+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="9 16"
{!> ../../../docs_src/request_files/tutorial003_py39.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="11 18"
{!> ../../../docs_src/request_files/tutorial003.py!}
```
## Recap
Use `File`, `bytes`, and `UploadFile` to declare files to be uploaded in the request, sent as form data.

View File

@@ -9,17 +9,51 @@ You can define files and form fields at the same time using `File` and `Form`.
## Import `File` and `Form`
```Python hl_lines="1"
{!../../../docs_src/request_forms_and_files/tutorial001.py!}
```
=== "Python 3.9+"
```Python hl_lines="3"
{!> ../../../docs_src/request_forms_and_files/tutorial001_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="1"
{!> ../../../docs_src/request_forms_and_files/tutorial001_an.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="1"
{!> ../../../docs_src/request_forms_and_files/tutorial001.py!}
```
## Define `File` and `Form` parameters
Create file and form parameters the same way you would for `Body` or `Query`:
```Python hl_lines="8"
{!../../../docs_src/request_forms_and_files/tutorial001.py!}
```
=== "Python 3.9+"
```Python hl_lines="10-12"
{!> ../../../docs_src/request_forms_and_files/tutorial001_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="9-11"
{!> ../../../docs_src/request_forms_and_files/tutorial001_an.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="8"
{!> ../../../docs_src/request_forms_and_files/tutorial001.py!}
```
The files and form fields will be uploaded as form data and you will receive the files and form fields.

View File

@@ -11,17 +11,51 @@ When you need to receive form fields instead of JSON, you can use `Form`.
Import `Form` from `fastapi`:
```Python hl_lines="1"
{!../../../docs_src/request_forms/tutorial001.py!}
```
=== "Python 3.9+"
```Python hl_lines="3"
{!> ../../../docs_src/request_forms/tutorial001_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="1"
{!> ../../../docs_src/request_forms/tutorial001_an.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="1"
{!> ../../../docs_src/request_forms/tutorial001.py!}
```
## Define `Form` parameters
Create form parameters the same way you would for `Body` or `Query`:
```Python hl_lines="7"
{!../../../docs_src/request_forms/tutorial001.py!}
```
=== "Python 3.9+"
```Python hl_lines="9"
{!> ../../../docs_src/request_forms/tutorial001_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="8"
{!> ../../../docs_src/request_forms/tutorial001_an.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="7"
{!> ../../../docs_src/request_forms/tutorial001.py!}
```
For example, in one of the ways the OAuth2 specification can be used (called "password flow") it is required to send a `username` and `password` as form fields.

View File

@@ -4,22 +4,22 @@ You can declare the type used for the response by annotating the *path operation
You can use **type annotations** the same way you would for input data in function **parameters**, you can use Pydantic models, lists, dictionaries, scalar values like integers, booleans, etc.
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="18 23"
{!> ../../../docs_src/response_model/tutorial001_01.py!}
```Python hl_lines="16 21"
{!> ../../../docs_src/response_model/tutorial001_01_py310.py!}
```
=== "Python 3.9 and above"
=== "Python 3.9+"
```Python hl_lines="18 23"
{!> ../../../docs_src/response_model/tutorial001_01_py39.py!}
```
=== "Python 3.10 and above"
=== "Python 3.6+"
```Python hl_lines="16 21"
{!> ../../../docs_src/response_model/tutorial001_01_py310.py!}
```Python hl_lines="18 23"
{!> ../../../docs_src/response_model/tutorial001_01.py!}
```
FastAPI will use this return type to:
@@ -53,22 +53,22 @@ You can use the `response_model` parameter in any of the *path operations*:
* `@app.delete()`
* etc.
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="17 22 24-27"
{!> ../../../docs_src/response_model/tutorial001.py!}
{!> ../../../docs_src/response_model/tutorial001_py310.py!}
```
=== "Python 3.9 and above"
=== "Python 3.9+"
```Python hl_lines="17 22 24-27"
{!> ../../../docs_src/response_model/tutorial001_py39.py!}
```
=== "Python 3.10 and above"
=== "Python 3.6+"
```Python hl_lines="17 22 24-27"
{!> ../../../docs_src/response_model/tutorial001_py310.py!}
{!> ../../../docs_src/response_model/tutorial001.py!}
```
!!! note
@@ -95,18 +95,18 @@ You can also use `response_model=None` to disable creating a response model for
Here we are declaring a `UserIn` model, it will contain a plaintext password:
=== "Python 3.6 and above"
```Python hl_lines="9 11"
{!> ../../../docs_src/response_model/tutorial002.py!}
```
=== "Python 3.10 and above"
=== "Python 3.10+"
```Python hl_lines="7 9"
{!> ../../../docs_src/response_model/tutorial002_py310.py!}
```
=== "Python 3.6+"
```Python hl_lines="9 11"
{!> ../../../docs_src/response_model/tutorial002.py!}
```
!!! info
To use `EmailStr`, first install <a href="https://github.com/JoshData/python-email-validator" class="external-link" target="_blank">`email_validator`</a>.
@@ -115,18 +115,18 @@ Here we are declaring a `UserIn` model, it will contain a plaintext password:
And we are using this model to declare our input and the same model to declare our output:
=== "Python 3.6 and above"
```Python hl_lines="18"
{!> ../../../docs_src/response_model/tutorial002.py!}
```
=== "Python 3.10 and above"
=== "Python 3.10+"
```Python hl_lines="16"
{!> ../../../docs_src/response_model/tutorial002_py310.py!}
```
=== "Python 3.6+"
```Python hl_lines="18"
{!> ../../../docs_src/response_model/tutorial002.py!}
```
Now, whenever a browser is creating a user with a password, the API will return the same password in the response.
In this case, it might not be a problem, because it's the same user sending the password.
@@ -140,46 +140,46 @@ But if we use the same model for another *path operation*, we could be sending o
We can instead create an input model with the plaintext password and an output model without it:
=== "Python 3.6 and above"
```Python hl_lines="9 11 16"
{!> ../../../docs_src/response_model/tutorial003.py!}
```
=== "Python 3.10 and above"
=== "Python 3.10+"
```Python hl_lines="9 11 16"
{!> ../../../docs_src/response_model/tutorial003_py310.py!}
```
=== "Python 3.6+"
```Python hl_lines="9 11 16"
{!> ../../../docs_src/response_model/tutorial003.py!}
```
Here, even though our *path operation function* is returning the same input user that contains the password:
=== "Python 3.6 and above"
```Python hl_lines="24"
{!> ../../../docs_src/response_model/tutorial003.py!}
```
=== "Python 3.10 and above"
=== "Python 3.10+"
```Python hl_lines="24"
{!> ../../../docs_src/response_model/tutorial003_py310.py!}
```
=== "Python 3.6+"
```Python hl_lines="24"
{!> ../../../docs_src/response_model/tutorial003.py!}
```
...we declared the `response_model` to be our model `UserOut`, that doesn't include the password:
=== "Python 3.6 and above"
```Python hl_lines="22"
{!> ../../../docs_src/response_model/tutorial003.py!}
```
=== "Python 3.10 and above"
=== "Python 3.10+"
```Python hl_lines="22"
{!> ../../../docs_src/response_model/tutorial003_py310.py!}
```
=== "Python 3.6+"
```Python hl_lines="22"
{!> ../../../docs_src/response_model/tutorial003.py!}
```
So, **FastAPI** will take care of filtering out all the data that is not declared in the output model (using Pydantic).
### `response_model` or Return Type
@@ -202,18 +202,18 @@ But in most of the cases where we need to do something like this, we want the mo
And in those cases, we can use classes and inheritance to take advantage of function **type annotations** to get better support in the editor and tools, and still get the FastAPI **data filtering**.
=== "Python 3.6 and above"
```Python hl_lines="9-13 15-16 20"
{!> ../../../docs_src/response_model/tutorial003_01.py!}
```
=== "Python 3.10 and above"
=== "Python 3.10+"
```Python hl_lines="7-10 13-14 18"
{!> ../../../docs_src/response_model/tutorial003_01_py310.py!}
```
=== "Python 3.6+"
```Python hl_lines="9-13 15-16 20"
{!> ../../../docs_src/response_model/tutorial003_01.py!}
```
With this, we get tooling support, from editors and mypy as this code is correct in terms of types, but we also get the data filtering from FastAPI.
How does this work? Let's check that out. 🤓
@@ -278,18 +278,18 @@ But when you return some other arbitrary object that is not a valid Pydantic typ
The same would happen if you had something like a <abbr title='A union between multiple types means "any of these types".'>union</abbr> between different types where one or more of them are not valid Pydantic types, for example this would fail 💥:
=== "Python 3.6 and above"
```Python hl_lines="10"
{!> ../../../docs_src/response_model/tutorial003_04.py!}
```
=== "Python 3.10 and above"
=== "Python 3.10+"
```Python hl_lines="8"
{!> ../../../docs_src/response_model/tutorial003_04_py310.py!}
```
=== "Python 3.6+"
```Python hl_lines="10"
{!> ../../../docs_src/response_model/tutorial003_04.py!}
```
...this fails because the type annotation is not a Pydantic type and is not just a single `Response` class or subclass, it's a union (any of the two) between a `Response` and a `dict`.
### Disable Response Model
@@ -300,40 +300,40 @@ But you might want to still keep the return type annotation in the function to g
In this case, you can disable the response model generation by setting `response_model=None`:
=== "Python 3.6 and above"
```Python hl_lines="9"
{!> ../../../docs_src/response_model/tutorial003_05.py!}
```
=== "Python 3.10 and above"
=== "Python 3.10+"
```Python hl_lines="7"
{!> ../../../docs_src/response_model/tutorial003_05_py310.py!}
```
=== "Python 3.6+"
```Python hl_lines="9"
{!> ../../../docs_src/response_model/tutorial003_05.py!}
```
This will make FastAPI skip the response model generation and that way you can have any return type annotations you need without it affecting your FastAPI application. 🤓
## Response Model encoding parameters
Your response model could have default values, like:
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="11 13-14"
{!> ../../../docs_src/response_model/tutorial004.py!}
```Python hl_lines="9 11-12"
{!> ../../../docs_src/response_model/tutorial004_py310.py!}
```
=== "Python 3.9 and above"
=== "Python 3.9+"
```Python hl_lines="11 13-14"
{!> ../../../docs_src/response_model/tutorial004_py39.py!}
```
=== "Python 3.10 and above"
=== "Python 3.6+"
```Python hl_lines="9 11-12"
{!> ../../../docs_src/response_model/tutorial004_py310.py!}
```Python hl_lines="11 13-14"
{!> ../../../docs_src/response_model/tutorial004.py!}
```
* `description: Union[str, None] = None` (or `str | None = None` in Python 3.10) has a default of `None`.
@@ -348,22 +348,22 @@ For example, if you have models with many optional attributes in a NoSQL databas
You can set the *path operation decorator* parameter `response_model_exclude_unset=True`:
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="24"
{!> ../../../docs_src/response_model/tutorial004.py!}
```Python hl_lines="22"
{!> ../../../docs_src/response_model/tutorial004_py310.py!}
```
=== "Python 3.9 and above"
=== "Python 3.9+"
```Python hl_lines="24"
{!> ../../../docs_src/response_model/tutorial004_py39.py!}
```
=== "Python 3.10 and above"
=== "Python 3.6+"
```Python hl_lines="22"
{!> ../../../docs_src/response_model/tutorial004_py310.py!}
```Python hl_lines="24"
{!> ../../../docs_src/response_model/tutorial004.py!}
```
and those default values won't be included in the response, only the values actually set.
@@ -441,18 +441,18 @@ This can be used as a quick shortcut if you have only one Pydantic model and wan
This also applies to `response_model_by_alias` that works similarly.
=== "Python 3.6 and above"
```Python hl_lines="31 37"
{!> ../../../docs_src/response_model/tutorial005.py!}
```
=== "Python 3.10 and above"
=== "Python 3.10+"
```Python hl_lines="29 35"
{!> ../../../docs_src/response_model/tutorial005_py310.py!}
```
=== "Python 3.6+"
```Python hl_lines="31 37"
{!> ../../../docs_src/response_model/tutorial005.py!}
```
!!! tip
The syntax `{"name", "description"}` creates a `set` with those two values.
@@ -462,18 +462,18 @@ This can be used as a quick shortcut if you have only one Pydantic model and wan
If you forget to use a `set` and use a `list` or `tuple` instead, FastAPI will still convert it to a `set` and it will work correctly:
=== "Python 3.6 and above"
```Python hl_lines="31 37"
{!> ../../../docs_src/response_model/tutorial006.py!}
```
=== "Python 3.10 and above"
=== "Python 3.10+"
```Python hl_lines="29 35"
{!> ../../../docs_src/response_model/tutorial006_py310.py!}
```
=== "Python 3.6+"
```Python hl_lines="31 37"
{!> ../../../docs_src/response_model/tutorial006.py!}
```
## Recap
Use the *path operation decorator's* parameter `response_model` to define response models and especially to ensure private data is filtered out.

View File

@@ -8,18 +8,18 @@ Here are several ways to do it.
You can declare an `example` for a Pydantic model using `Config` and `schema_extra`, as described in <a href="https://pydantic-docs.helpmanual.io/usage/schema/#schema-customization" class="external-link" target="_blank">Pydantic's docs: Schema customization</a>:
=== "Python 3.6 and above"
```Python hl_lines="15-23"
{!> ../../../docs_src/schema_extra_example/tutorial001.py!}
```
=== "Python 3.10 and above"
=== "Python 3.10+"
```Python hl_lines="13-21"
{!> ../../../docs_src/schema_extra_example/tutorial001_py310.py!}
```
=== "Python 3.6+"
```Python hl_lines="15-23"
{!> ../../../docs_src/schema_extra_example/tutorial001.py!}
```
That extra info will be added as-is to the output **JSON Schema** for that model, and it will be used in the API docs.
!!! tip
@@ -33,18 +33,18 @@ When using `Field()` with Pydantic models, you can also declare extra info for t
You can use this to add `example` for each field:
=== "Python 3.6 and above"
```Python hl_lines="4 10-13"
{!> ../../../docs_src/schema_extra_example/tutorial002.py!}
```
=== "Python 3.10 and above"
=== "Python 3.10+"
```Python hl_lines="2 8-11"
{!> ../../../docs_src/schema_extra_example/tutorial002_py310.py!}
```
=== "Python 3.6+"
```Python hl_lines="4 10-13"
{!> ../../../docs_src/schema_extra_example/tutorial002.py!}
```
!!! warning
Keep in mind that those extra arguments passed won't add any validation, only extra information, for documentation purposes.
@@ -66,18 +66,39 @@ you can also declare a data `example` or a group of `examples` with additional i
Here we pass an `example` of the data expected in `Body()`:
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="20-25"
{!> ../../../docs_src/schema_extra_example/tutorial003.py!}
```Python hl_lines="22-27"
{!> ../../../docs_src/schema_extra_example/tutorial003_an_py310.py!}
```
=== "Python 3.10 and above"
=== "Python 3.9+"
```Python hl_lines="22-27"
{!> ../../../docs_src/schema_extra_example/tutorial003_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="23-28"
{!> ../../../docs_src/schema_extra_example/tutorial003_an.py!}
```
=== "Python 3.10+ non-Annotated"
```Python hl_lines="18-23"
{!> ../../../docs_src/schema_extra_example/tutorial003_py310.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="20-25"
{!> ../../../docs_src/schema_extra_example/tutorial003.py!}
```
### Example in the docs UI
With any of the methods above it would look like this in the `/docs`:
@@ -97,18 +118,39 @@ Each specific example `dict` in the `examples` can contain:
* `value`: This is the actual example shown, e.g. a `dict`.
* `externalValue`: alternative to `value`, a URL pointing to the example. Although this might not be supported by as many tools as `value`.
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="21-47"
{!> ../../../docs_src/schema_extra_example/tutorial004.py!}
```Python hl_lines="23-49"
{!> ../../../docs_src/schema_extra_example/tutorial004_an_py310.py!}
```
=== "Python 3.10 and above"
=== "Python 3.9+"
```Python hl_lines="23-49"
{!> ../../../docs_src/schema_extra_example/tutorial004_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="24-50"
{!> ../../../docs_src/schema_extra_example/tutorial004_an.py!}
```
=== "Python 3.10+ non-Annotated"
```Python hl_lines="19-45"
{!> ../../../docs_src/schema_extra_example/tutorial004_py310.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="21-47"
{!> ../../../docs_src/schema_extra_example/tutorial004.py!}
```
### Examples in the docs UI
With `examples` added to `Body()` the `/docs` would look like:

View File

@@ -20,9 +20,27 @@ Let's first just use the code and see how it works, and then we'll come back to
Copy the example in a file `main.py`:
```Python
{!../../../docs_src/security/tutorial001.py!}
```
=== "Python 3.9+"
```Python
{!> ../../../docs_src/security/tutorial001_an_py39.py!}
```
=== "Python 3.6+"
```Python
{!> ../../../docs_src/security/tutorial001_an.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python
{!> ../../../docs_src/security/tutorial001.py!}
```
## Run it
@@ -116,9 +134,26 @@ In this example we are going to use **OAuth2**, with the **Password** flow, usin
When we create an instance of the `OAuth2PasswordBearer` class we pass in the `tokenUrl` parameter. This parameter contains the URL that the client (the frontend running in the user's browser) will use to send the `username` and `password` in order to get a token.
```Python hl_lines="6"
{!../../../docs_src/security/tutorial001.py!}
```
=== "Python 3.9+"
```Python hl_lines="8"
{!> ../../../docs_src/security/tutorial001_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="7"
{!> ../../../docs_src/security/tutorial001_an.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="6"
{!> ../../../docs_src/security/tutorial001.py!}
```
!!! tip
Here `tokenUrl="token"` refers to a relative URL `token` that we haven't created yet. As it's a relative URL, it's equivalent to `./token`.
@@ -150,9 +185,26 @@ So, it can be used with `Depends`.
Now you can pass that `oauth2_scheme` in a dependency with `Depends`.
```Python hl_lines="10"
{!../../../docs_src/security/tutorial001.py!}
```
=== "Python 3.9+"
```Python hl_lines="12"
{!> ../../../docs_src/security/tutorial001_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="11"
{!> ../../../docs_src/security/tutorial001_an.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="10"
{!> ../../../docs_src/security/tutorial001.py!}
```
This dependency will provide a `str` that is assigned to the parameter `token` of the *path operation function*.

View File

@@ -2,9 +2,26 @@
In the previous chapter the security system (which is based on the dependency injection system) was giving the *path operation function* a `token` as a `str`:
```Python hl_lines="10"
{!../../../docs_src/security/tutorial001.py!}
```
=== "Python 3.9+"
```Python hl_lines="12"
{!> ../../../docs_src/security/tutorial001_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="11"
{!> ../../../docs_src/security/tutorial001_an.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="10"
{!> ../../../docs_src/security/tutorial001.py!}
```
But that is still not that useful.
@@ -16,18 +33,42 @@ First, let's create a Pydantic user model.
The same way we use Pydantic to declare bodies, we can use it anywhere else:
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="5 12-16"
{!> ../../../docs_src/security/tutorial002.py!}
{!> ../../../docs_src/security/tutorial002_an_py310.py!}
```
=== "Python 3.10 and above"
=== "Python 3.9+"
```Python hl_lines="5 12-16"
{!> ../../../docs_src/security/tutorial002_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="5 13-17"
{!> ../../../docs_src/security/tutorial002_an.py!}
```
=== "Python 3.10+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="3 10-14"
{!> ../../../docs_src/security/tutorial002_py310.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="5 12-16"
{!> ../../../docs_src/security/tutorial002.py!}
```
## Create a `get_current_user` dependency
Let's create a dependency `get_current_user`.
@@ -38,50 +79,122 @@ Remember that dependencies can have sub-dependencies?
The same as we were doing before in the *path operation* directly, our new dependency `get_current_user` will receive a `token` as a `str` from the sub-dependency `oauth2_scheme`:
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="25"
{!> ../../../docs_src/security/tutorial002.py!}
{!> ../../../docs_src/security/tutorial002_an_py310.py!}
```
=== "Python 3.10 and above"
=== "Python 3.9+"
```Python hl_lines="25"
{!> ../../../docs_src/security/tutorial002_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="26"
{!> ../../../docs_src/security/tutorial002_an.py!}
```
=== "Python 3.10+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="23"
{!> ../../../docs_src/security/tutorial002_py310.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="25"
{!> ../../../docs_src/security/tutorial002.py!}
```
## Get the user
`get_current_user` will use a (fake) utility function we created, that takes a token as a `str` and returns our Pydantic `User` model:
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="19-22 26-27"
{!> ../../../docs_src/security/tutorial002.py!}
{!> ../../../docs_src/security/tutorial002_an_py310.py!}
```
=== "Python 3.10 and above"
=== "Python 3.9+"
```Python hl_lines="19-22 26-27"
{!> ../../../docs_src/security/tutorial002_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="20-23 27-28"
{!> ../../../docs_src/security/tutorial002_an.py!}
```
=== "Python 3.10+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="17-20 24-25"
{!> ../../../docs_src/security/tutorial002_py310.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="19-22 26-27"
{!> ../../../docs_src/security/tutorial002.py!}
```
## Inject the current user
So now we can use the same `Depends` with our `get_current_user` in the *path operation*:
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="31"
{!> ../../../docs_src/security/tutorial002.py!}
{!> ../../../docs_src/security/tutorial002_an_py310.py!}
```
=== "Python 3.10 and above"
=== "Python 3.9+"
```Python hl_lines="31"
{!> ../../../docs_src/security/tutorial002_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="32"
{!> ../../../docs_src/security/tutorial002_an.py!}
```
=== "Python 3.10+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="29"
{!> ../../../docs_src/security/tutorial002_py310.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="31"
{!> ../../../docs_src/security/tutorial002.py!}
```
Notice that we declare the type of `current_user` as the Pydantic model `User`.
This will help us inside of the function with all the completion and type checks.
@@ -128,18 +241,42 @@ And all of them (or any portion of them that you want) can take the advantage of
And all these thousands of *path operations* can be as small as 3 lines:
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="30-32"
{!> ../../../docs_src/security/tutorial002.py!}
{!> ../../../docs_src/security/tutorial002_an_py310.py!}
```
=== "Python 3.10 and above"
=== "Python 3.9+"
```Python hl_lines="30-32"
{!> ../../../docs_src/security/tutorial002_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="31-33"
{!> ../../../docs_src/security/tutorial002_an.py!}
```
=== "Python 3.10+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="28-30"
{!> ../../../docs_src/security/tutorial002_py310.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="30-32"
{!> ../../../docs_src/security/tutorial002.py!}
```
## Recap
You can now get the current user directly in your *path operation function*.

View File

@@ -109,18 +109,42 @@ And another utility to verify if a received password matches the hash stored.
And another one to authenticate and return a user.
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="7 48 55-56 59-60 69-75"
{!> ../../../docs_src/security/tutorial004.py!}
{!> ../../../docs_src/security/tutorial004_an_py310.py!}
```
=== "Python 3.10 and above"
=== "Python 3.9+"
```Python hl_lines="7 48 55-56 59-60 69-75"
{!> ../../../docs_src/security/tutorial004_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="7 49 56-57 60-61 70-76"
{!> ../../../docs_src/security/tutorial004_an.py!}
```
=== "Python 3.10+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="6 47 54-55 58-59 68-74"
{!> ../../../docs_src/security/tutorial004_py310.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="7 48 55-56 59-60 69-75"
{!> ../../../docs_src/security/tutorial004.py!}
```
!!! note
If you check the new (fake) database `fake_users_db`, you will see how the hashed password looks like now: `"$2b$12$EixZaYVK1fsbw1ZfbX3OXePaWxn96p36WQoeG6Lruj3vjPGga31lW"`.
@@ -152,18 +176,42 @@ Define a Pydantic Model that will be used in the token endpoint for the response
Create a utility function to generate a new access token.
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="6 12-14 28-30 78-86"
{!> ../../../docs_src/security/tutorial004.py!}
{!> ../../../docs_src/security/tutorial004_an_py310.py!}
```
=== "Python 3.10 and above"
=== "Python 3.9+"
```Python hl_lines="6 12-14 28-30 78-86"
{!> ../../../docs_src/security/tutorial004_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="6 13-15 29-31 79-87"
{!> ../../../docs_src/security/tutorial004_an.py!}
```
=== "Python 3.10+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="5 11-13 27-29 77-85"
{!> ../../../docs_src/security/tutorial004_py310.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="6 12-14 28-30 78-86"
{!> ../../../docs_src/security/tutorial004.py!}
```
## Update the dependencies
Update `get_current_user` to receive the same token as before, but this time, using JWT tokens.
@@ -172,36 +220,84 @@ Decode the received token, verify it, and return the current user.
If the token is invalid, return an HTTP error right away.
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="89-106"
{!> ../../../docs_src/security/tutorial004.py!}
{!> ../../../docs_src/security/tutorial004_an_py310.py!}
```
=== "Python 3.10 and above"
=== "Python 3.9+"
```Python hl_lines="89-106"
{!> ../../../docs_src/security/tutorial004_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="90-107"
{!> ../../../docs_src/security/tutorial004_an.py!}
```
=== "Python 3.10+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="88-105"
{!> ../../../docs_src/security/tutorial004_py310.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="89-106"
{!> ../../../docs_src/security/tutorial004.py!}
```
## Update the `/token` *path operation*
Create a `timedelta` with the expiration time of the token.
Create a real JWT access token and return it.
Create a real JWT access token and return it
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="115-128"
{!> ../../../docs_src/security/tutorial004.py!}
```Python hl_lines="117-132"
{!> ../../../docs_src/security/tutorial004_an_py310.py!}
```
=== "Python 3.10 and above"
=== "Python 3.9+"
```Python hl_lines="117-132"
{!> ../../../docs_src/security/tutorial004_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="118-133"
{!> ../../../docs_src/security/tutorial004_an.py!}
```
=== "Python 3.10+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="114-127"
{!> ../../../docs_src/security/tutorial004_py310.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="115-128"
{!> ../../../docs_src/security/tutorial004.py!}
```
### Technical details about the JWT "subject" `sub`
The JWT specification says that there's a key `sub`, with the subject of the token.

View File

@@ -49,18 +49,42 @@ Now let's use the utilities provided by **FastAPI** to handle this.
First, import `OAuth2PasswordRequestForm`, and use it as a dependency with `Depends` in the *path operation* for `/token`:
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="4 76"
{!> ../../../docs_src/security/tutorial003.py!}
```Python hl_lines="4 78"
{!> ../../../docs_src/security/tutorial003_an_py310.py!}
```
=== "Python 3.10 and above"
=== "Python 3.9+"
```Python hl_lines="4 78"
{!> ../../../docs_src/security/tutorial003_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="4 79"
{!> ../../../docs_src/security/tutorial003_an.py!}
```
=== "Python 3.10+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="2 74"
{!> ../../../docs_src/security/tutorial003_py310.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="4 76"
{!> ../../../docs_src/security/tutorial003.py!}
```
`OAuth2PasswordRequestForm` is a class dependency that declares a form body with:
* The `username`.
@@ -98,18 +122,42 @@ If there is no such user, we return an error saying "incorrect username or passw
For the error, we use the exception `HTTPException`:
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="3 77-79"
{!> ../../../docs_src/security/tutorial003.py!}
```Python hl_lines="3 79-81"
{!> ../../../docs_src/security/tutorial003_an_py310.py!}
```
=== "Python 3.10 and above"
=== "Python 3.9+"
```Python hl_lines="3 79-81"
{!> ../../../docs_src/security/tutorial003_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="3 80-82"
{!> ../../../docs_src/security/tutorial003_an.py!}
```
=== "Python 3.10+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="1 75-77"
{!> ../../../docs_src/security/tutorial003_py310.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="3 77-79"
{!> ../../../docs_src/security/tutorial003.py!}
```
### Check the password
At this point we have the user data from our database, but we haven't checked the password.
@@ -134,18 +182,42 @@ If your database is stolen, the thief won't have your users' plaintext passwords
So, the thief won't be able to try to use those same passwords in another system (as many users use the same password everywhere, this would be dangerous).
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="80-83"
{!> ../../../docs_src/security/tutorial003.py!}
```Python hl_lines="82-85"
{!> ../../../docs_src/security/tutorial003_an_py310.py!}
```
=== "Python 3.10 and above"
=== "Python 3.9+"
```Python hl_lines="82-85"
{!> ../../../docs_src/security/tutorial003_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="83-86"
{!> ../../../docs_src/security/tutorial003_an.py!}
```
=== "Python 3.10+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="78-81"
{!> ../../../docs_src/security/tutorial003_py310.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="80-83"
{!> ../../../docs_src/security/tutorial003.py!}
```
#### About `**user_dict`
`UserInDB(**user_dict)` means:
@@ -180,18 +252,42 @@ For this simple example, we are going to just be completely insecure and return
But for now, let's focus on the specific details we need.
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="85"
{!> ../../../docs_src/security/tutorial003.py!}
```Python hl_lines="87"
{!> ../../../docs_src/security/tutorial003_an_py310.py!}
```
=== "Python 3.10 and above"
=== "Python 3.9+"
```Python hl_lines="87"
{!> ../../../docs_src/security/tutorial003_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="88"
{!> ../../../docs_src/security/tutorial003_an.py!}
```
=== "Python 3.10+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="83"
{!> ../../../docs_src/security/tutorial003_py310.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="85"
{!> ../../../docs_src/security/tutorial003.py!}
```
!!! tip
By the spec, you should return a JSON with an `access_token` and a `token_type`, the same as in this example.
@@ -213,18 +309,42 @@ Both of these dependencies will just return an HTTP error if the user doesn't ex
So, in our endpoint, we will only get a user if the user exists, was correctly authenticated, and is active:
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="58-66 69-74 94"
{!> ../../../docs_src/security/tutorial003_an_py310.py!}
```
=== "Python 3.9+"
```Python hl_lines="58-66 69-74 94"
{!> ../../../docs_src/security/tutorial003_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="59-67 70-75 95"
{!> ../../../docs_src/security/tutorial003_an.py!}
```
=== "Python 3.10+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="56-64 67-70 88"
{!> ../../../docs_src/security/tutorial003_py310.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="58-66 69-72 90"
{!> ../../../docs_src/security/tutorial003.py!}
```
=== "Python 3.10 and above"
```Python hl_lines="55-64 67-70 88"
{!> ../../../docs_src/security/tutorial003_py310.py!}
```
!!! info
The additional header `WWW-Authenticate` with value `Bearer` we are returning here is also part of the spec.

View File

@@ -262,22 +262,22 @@ So, the user will also have a `password` when creating it.
But for security, the `password` won't be in other Pydantic *models*, for example, it won't be sent from the API when reading a user.
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="3 6-8 11-12 23-24 27-28"
{!> ../../../docs_src/sql_databases/sql_app/schemas.py!}
```Python hl_lines="1 4-6 9-10 21-22 25-26"
{!> ../../../docs_src/sql_databases/sql_app_py310/schemas.py!}
```
=== "Python 3.9 and above"
=== "Python 3.9+"
```Python hl_lines="3 6-8 11-12 23-24 27-28"
{!> ../../../docs_src/sql_databases/sql_app_py39/schemas.py!}
```
=== "Python 3.10 and above"
=== "Python 3.6+"
```Python hl_lines="1 4-6 9-10 21-22 25-26"
{!> ../../../docs_src/sql_databases/sql_app_py310/schemas.py!}
```Python hl_lines="3 6-8 11-12 23-24 27-28"
{!> ../../../docs_src/sql_databases/sql_app/schemas.py!}
```
#### SQLAlchemy style and Pydantic style
@@ -306,22 +306,22 @@ The same way, when reading a user, we can now declare that `items` will contain
Not only the IDs of those items, but all the data that we defined in the Pydantic *model* for reading items: `Item`.
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="15-17 31-34"
{!> ../../../docs_src/sql_databases/sql_app/schemas.py!}
```Python hl_lines="13-15 29-32"
{!> ../../../docs_src/sql_databases/sql_app_py310/schemas.py!}
```
=== "Python 3.9 and above"
=== "Python 3.9+"
```Python hl_lines="15-17 31-34"
{!> ../../../docs_src/sql_databases/sql_app_py39/schemas.py!}
```
=== "Python 3.10 and above"
=== "Python 3.6+"
```Python hl_lines="13-15 29-32"
{!> ../../../docs_src/sql_databases/sql_app_py310/schemas.py!}
```Python hl_lines="15-17 31-34"
{!> ../../../docs_src/sql_databases/sql_app/schemas.py!}
```
!!! tip
@@ -335,22 +335,22 @@ This <a href="https://pydantic-docs.helpmanual.io/usage/model_config/" class="ex
In the `Config` class, set the attribute `orm_mode = True`.
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="15 19-20 31 36-37"
{!> ../../../docs_src/sql_databases/sql_app/schemas.py!}
```Python hl_lines="13 17-18 29 34-35"
{!> ../../../docs_src/sql_databases/sql_app_py310/schemas.py!}
```
=== "Python 3.9 and above"
=== "Python 3.9+"
```Python hl_lines="15 19-20 31 36-37"
{!> ../../../docs_src/sql_databases/sql_app_py39/schemas.py!}
```
=== "Python 3.10 and above"
=== "Python 3.6+"
```Python hl_lines="13 17-18 29 34-35"
{!> ../../../docs_src/sql_databases/sql_app_py310/schemas.py!}
```Python hl_lines="15 19-20 31 36-37"
{!> ../../../docs_src/sql_databases/sql_app/schemas.py!}
```
!!! tip
@@ -481,18 +481,18 @@ And now in the file `sql_app/main.py` let's integrate and use all the other part
In a very simplistic way create the database tables:
=== "Python 3.6 and above"
```Python hl_lines="9"
{!> ../../../docs_src/sql_databases/sql_app/main.py!}
```
=== "Python 3.9 and above"
=== "Python 3.9+"
```Python hl_lines="7"
{!> ../../../docs_src/sql_databases/sql_app_py39/main.py!}
```
=== "Python 3.6+"
```Python hl_lines="9"
{!> ../../../docs_src/sql_databases/sql_app/main.py!}
```
#### Alembic Note
Normally you would probably initialize your database (create tables, etc) with <a href="https://alembic.sqlalchemy.org/en/latest/" class="external-link" target="_blank">Alembic</a>.
@@ -515,18 +515,18 @@ For that, we will create a new dependency with `yield`, as explained before in t
Our dependency will create a new SQLAlchemy `SessionLocal` that will be used in a single request, and then close it once the request is finished.
=== "Python 3.6 and above"
```Python hl_lines="15-20"
{!> ../../../docs_src/sql_databases/sql_app/main.py!}
```
=== "Python 3.9 and above"
=== "Python 3.9+"
```Python hl_lines="13-18"
{!> ../../../docs_src/sql_databases/sql_app_py39/main.py!}
```
=== "Python 3.6+"
```Python hl_lines="15-20"
{!> ../../../docs_src/sql_databases/sql_app/main.py!}
```
!!! info
We put the creation of the `SessionLocal()` and handling of the requests in a `try` block.
@@ -540,18 +540,18 @@ And then, when using the dependency in a *path operation function*, we declare i
This will then give us better editor support inside the *path operation function*, because the editor will know that the `db` parameter is of type `Session`:
=== "Python 3.6 and above"
```Python hl_lines="24 32 38 47 53"
{!> ../../../docs_src/sql_databases/sql_app/main.py!}
```
=== "Python 3.9 and above"
=== "Python 3.9+"
```Python hl_lines="22 30 36 45 51"
{!> ../../../docs_src/sql_databases/sql_app_py39/main.py!}
```
=== "Python 3.6+"
```Python hl_lines="24 32 38 47 53"
{!> ../../../docs_src/sql_databases/sql_app/main.py!}
```
!!! info "Technical Details"
The parameter `db` is actually of type `SessionLocal`, but this class (created with `sessionmaker()`) is a "proxy" of a SQLAlchemy `Session`, so, the editor doesn't really know what methods are provided.
@@ -561,18 +561,18 @@ This will then give us better editor support inside the *path operation function
Now, finally, here's the standard **FastAPI** *path operations* code.
=== "Python 3.6 and above"
```Python hl_lines="23-28 31-34 37-42 45-49 52-55"
{!> ../../../docs_src/sql_databases/sql_app/main.py!}
```
=== "Python 3.9 and above"
=== "Python 3.9+"
```Python hl_lines="21-26 29-32 35-40 43-47 50-53"
{!> ../../../docs_src/sql_databases/sql_app_py39/main.py!}
```
=== "Python 3.6+"
```Python hl_lines="23-28 31-34 37-42 45-49 52-55"
{!> ../../../docs_src/sql_databases/sql_app/main.py!}
```
We are creating the database session before each request in the dependency with `yield`, and then closing it afterwards.
And then we can create the required dependency in the *path operation function*, to get that session directly.
@@ -654,22 +654,22 @@ For example, in a background task worker with <a href="https://docs.celeryq.dev"
* `sql_app/schemas.py`:
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python
{!> ../../../docs_src/sql_databases/sql_app/schemas.py!}
{!> ../../../docs_src/sql_databases/sql_app_py310/schemas.py!}
```
=== "Python 3.9 and above"
=== "Python 3.9+"
```Python
{!> ../../../docs_src/sql_databases/sql_app_py39/schemas.py!}
```
=== "Python 3.10 and above"
=== "Python 3.6+"
```Python
{!> ../../../docs_src/sql_databases/sql_app_py310/schemas.py!}
{!> ../../../docs_src/sql_databases/sql_app/schemas.py!}
```
* `sql_app/crud.py`:
@@ -680,18 +680,18 @@ For example, in a background task worker with <a href="https://docs.celeryq.dev"
* `sql_app/main.py`:
=== "Python 3.6 and above"
```Python
{!> ../../../docs_src/sql_databases/sql_app/main.py!}
```
=== "Python 3.9 and above"
=== "Python 3.9+"
```Python
{!> ../../../docs_src/sql_databases/sql_app_py39/main.py!}
```
=== "Python 3.6+"
```Python
{!> ../../../docs_src/sql_databases/sql_app/main.py!}
```
## Check it
You can copy this code and use it as is.
@@ -739,18 +739,18 @@ A "middleware" is basically a function that is always executed for each request,
The middleware we'll add (just a function) will create a new SQLAlchemy `SessionLocal` for each request, add it to the request and then close it once the request is finished.
=== "Python 3.6 and above"
```Python hl_lines="14-22"
{!> ../../../docs_src/sql_databases/sql_app/alt_main.py!}
```
=== "Python 3.9 and above"
=== "Python 3.9+"
```Python hl_lines="12-20"
{!> ../../../docs_src/sql_databases/sql_app_py39/alt_main.py!}
```
=== "Python 3.6+"
```Python hl_lines="14-22"
{!> ../../../docs_src/sql_databases/sql_app/alt_main.py!}
```
!!! info
We put the creation of the `SessionLocal()` and handling of the requests in a `try` block.

View File

@@ -110,18 +110,42 @@ It has a `POST` operation that could return several errors.
Both *path operations* require an `X-Token` header.
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python
{!> ../../../docs_src/app_testing/app_b/main.py!}
{!> ../../../docs_src/app_testing/app_b_an_py310/main.py!}
```
=== "Python 3.10 and above"
=== "Python 3.9+"
```Python
{!> ../../../docs_src/app_testing/app_b_an_py39/main.py!}
```
=== "Python 3.6+"
```Python
{!> ../../../docs_src/app_testing/app_b_an/main.py!}
```
=== "Python 3.10+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python
{!> ../../../docs_src/app_testing/app_b_py310/main.py!}
```
=== "Python 3.6+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python
{!> ../../../docs_src/app_testing/app_b/main.py!}
```
### Extended testing file
You could then update `test_main.py` with the extended tests:

View File

@@ -45,6 +45,7 @@ nav:
- fa: /fa/
- fr: /fr/
- he: /he/
- hy: /hy/
- id: /id/
- it: /it/
- ja: /ja/
@@ -55,6 +56,7 @@ nav:
- ru: /ru/
- sq: /sq/
- sv: /sv/
- ta: /ta/
- tr: /tr/
- uk: /uk/
- zh: /zh/
@@ -187,7 +189,7 @@ markdown_extensions:
extra:
analytics:
provider: google
property: UA-133183413-1
property: G-YNEVN69SC3
social:
- icon: fontawesome/brands/github-alt
link: https://github.com/tiangolo/fastapi
@@ -218,6 +220,8 @@ extra:
name: fr - français
- link: /he/
name: he
- link: /hy/
name: hy
- link: /id/
name: id
- link: /it/
@@ -238,6 +242,8 @@ extra:
name: sq - shqip
- link: /sv/
name: sv - svenska
- link: /ta/
name: ta - தமிழ்
- link: /tr/
name: tr - Türkçe
- link: /uk/

View File

@@ -45,6 +45,7 @@ nav:
- fa: /fa/
- fr: /fr/
- he: /he/
- hy: /hy/
- id: /id/
- it: /it/
- ja: /ja/
@@ -55,6 +56,7 @@ nav:
- ru: /ru/
- sq: /sq/
- sv: /sv/
- ta: /ta/
- tr: /tr/
- uk: /uk/
- zh: /zh/
@@ -90,7 +92,7 @@ markdown_extensions:
extra:
analytics:
provider: google
property: UA-133183413-1
property: G-YNEVN69SC3
social:
- icon: fontawesome/brands/github-alt
link: https://github.com/tiangolo/fastapi
@@ -121,6 +123,8 @@ extra:
name: fr - français
- link: /he/
name: he
- link: /hy/
name: hy
- link: /id/
name: id
- link: /it/
@@ -141,6 +145,8 @@ extra:
name: sq - shqip
- link: /sv/
name: sv - svenska
- link: /ta/
name: ta - தமிழ்
- link: /tr/
name: tr - Türkçe
- link: /uk/

View File

@@ -45,6 +45,7 @@ nav:
- fa: /fa/
- fr: /fr/
- he: /he/
- hy: /hy/
- id: /id/
- it: /it/
- ja: /ja/
@@ -55,6 +56,7 @@ nav:
- ru: /ru/
- sq: /sq/
- sv: /sv/
- ta: /ta/
- tr: /tr/
- uk: /uk/
- zh: /zh/
@@ -80,7 +82,7 @@ markdown_extensions:
extra:
analytics:
provider: google
property: UA-133183413-1
property: G-YNEVN69SC3
social:
- icon: fontawesome/brands/github-alt
link: https://github.com/tiangolo/fastapi
@@ -111,6 +113,8 @@ extra:
name: fr - français
- link: /he/
name: he
- link: /hy/
name: hy
- link: /id/
name: id
- link: /it/
@@ -131,6 +135,8 @@ extra:
name: sq - shqip
- link: /sv/
name: sv - svenska
- link: /ta/
name: ta - தமிழ்
- link: /tr/
name: tr - Türkçe
- link: /uk/

View File

@@ -0,0 +1,168 @@
# Configuration avancée des paramètres de chemin
## ID d'opération OpenAPI
!!! Attention
Si vous n'êtes pas un "expert" en OpenAPI, vous n'en avez probablement pas besoin.
Dans OpenAPI, les chemins sont des ressources, tels que /users/ ou /items/, exposées par votre API, et les opérations sont les méthodes HTTP utilisées pour manipuler ces chemins, telles que GET, POST ou DELETE. Les operationId sont des chaînes uniques facultatives utilisées pour identifier une opération d'un chemin. Vous pouvez définir l'OpenAPI `operationId` à utiliser dans votre *opération de chemin* avec le paramètre `operation_id`.
Vous devez vous assurer qu'il est unique pour chaque opération.
```Python hl_lines="6"
{!../../../docs_src/path_operation_advanced_configuration/tutorial001.py!}
```
### Utilisation du nom *path operation function* comme operationId
Si vous souhaitez utiliser les noms de fonction de vos API comme `operationId`, vous pouvez les parcourir tous et remplacer chaque `operation_id` de l'*opération de chemin* en utilisant leur `APIRoute.name`.
Vous devriez le faire après avoir ajouté toutes vos *paramètres de chemin*.
```Python hl_lines="2 12-21 24"
{!../../../docs_src/path_operation_advanced_configuration/tutorial002.py!}
```
!!! Astuce
Si vous appelez manuellement `app.openapi()`, vous devez mettre à jour les `operationId` avant.
!!! Attention
Pour faire cela, vous devez vous assurer que chacun de vos *chemin* ait un nom unique.
Même s'ils se trouvent dans des modules différents (fichiers Python).
## Exclusion d'OpenAPI
Pour exclure un *chemin* du schéma OpenAPI généré (et donc des systèmes de documentation automatiques), utilisez le paramètre `include_in_schema` et assignez-lui la valeur `False` :
```Python hl_lines="6"
{!../../../docs_src/path_operation_advanced_configuration/tutorial003.py!}
```
## Description avancée de docstring
Vous pouvez limiter le texte utilisé de la docstring d'une *fonction de chemin* qui sera affiché sur OpenAPI.
L'ajout d'un `\f` (un caractère d'échappement "form feed") va permettre à **FastAPI** de tronquer la sortie utilisée pour OpenAPI à ce stade.
Il n'apparaîtra pas dans la documentation, mais d'autres outils (tel que Sphinx) pourront utiliser le reste.
```Python hl_lines="19-29"
{!../../../docs_src/path_operation_advanced_configuration/tutorial004.py!}
```
## Réponses supplémentaires
Vous avez probablement vu comment déclarer le `response_model` et le `status_code` pour une *opération de chemin*.
Cela définit les métadonnées sur la réponse principale d'une *opération de chemin*.
Vous pouvez également déclarer des réponses supplémentaires avec leurs modèles, codes de statut, etc.
Il y a un chapitre entier ici dans la documentation à ce sujet, vous pouvez le lire sur [Réponses supplémentaires dans OpenAPI](./additional-responses.md){.internal-link target=_blank}.
## OpenAPI supplémentaire
Lorsque vous déclarez un *chemin* dans votre application, **FastAPI** génère automatiquement les métadonnées concernant ce *chemin* à inclure dans le schéma OpenAPI.
!!! note "Détails techniques"
La spécification OpenAPI appelle ces métaonnées des <a href="https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#operation-object" class="external-link" target="_blank">Objets d'opération</a>.
Il contient toutes les informations sur le *chemin* et est utilisé pour générer automatiquement la documentation.
Il inclut les `tags`, `parameters`, `requestBody`, `responses`, etc.
Ce schéma OpenAPI spécifique aux *operations* est normalement généré automatiquement par **FastAPI**, mais vous pouvez également l'étendre.
!!! Astuce
Si vous avez seulement besoin de déclarer des réponses supplémentaires, un moyen plus pratique de le faire est d'utiliser les [réponses supplémentaires dans OpenAPI](./additional-responses.md){.internal-link target=_blank}.
Vous pouvez étendre le schéma OpenAPI pour une *opération de chemin* en utilisant le paramètre `openapi_extra`.
### Extensions OpenAPI
Cet `openapi_extra` peut être utile, par exemple, pour déclarer [OpenAPI Extensions](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#specificationExtensions) :
```Python hl_lines="6"
{!../../../docs_src/path_operation_advanced_configuration/tutorial005.py!}
```
Si vous ouvrez la documentation automatique de l'API, votre extension apparaîtra au bas du *chemin* spécifique.
<img src="/img/tutorial/path-operation-advanced-configuration/image01.png">
Et dans le fichier openapi généré (`/openapi.json`), vous verrez également votre extension dans le cadre du *chemin* spécifique :
```JSON hl_lines="22"
{
"openapi": "3.0.2",
"info": {
"title": "FastAPI",
"version": "0.1.0"
},
"paths": {
"/items/": {
"get": {
"summary": "Read Items",
"operationId": "read_items_items__get",
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {}
}
}
}
},
"x-aperture-labs-portal": "blue"
}
}
}
}
```
### Personnalisation du Schéma OpenAPI pour un chemin
Le dictionnaire contenu dans la variable `openapi_extra` sera fusionné avec le schéma OpenAPI généré automatiquement pour l'*opération de chemin*.
Ainsi, vous pouvez ajouter des données supplémentaires au schéma généré automatiquement.
Par exemple, vous pouvez décider de lire et de valider la requête avec votre propre code, sans utiliser les fonctionnalités automatiques de validation proposée par Pydantic, mais vous pouvez toujours définir la requête dans le schéma OpenAPI.
Vous pouvez le faire avec `openapi_extra` :
```Python hl_lines="20-37 39-40"
{!../../../docs_src/path_operation_advanced_configuration/tutorial006.py !}
```
Dans cet exemple, nous n'avons déclaré aucun modèle Pydantic. En fait, le corps de la requête n'est même pas <abbr title="converti d'un format simple, comme des octets, en objets Python">parsé</abbr> en tant que JSON, il est lu directement en tant que `bytes`, et la fonction `magic_data_reader()` serait chargé de l'analyser d'une manière ou d'une autre.
Néanmoins, nous pouvons déclarer le schéma attendu pour le corps de la requête.
### Type de contenu OpenAPI personnalisé
En utilisant cette même astuce, vous pouvez utiliser un modèle Pydantic pour définir le schéma JSON qui est ensuite inclus dans la section de schéma OpenAPI personnalisée pour le *chemin* concerné.
Et vous pouvez le faire même si le type de données dans la requête n'est pas au format JSON.
Dans cet exemple, nous n'utilisons pas les fonctionnalités de FastAPI pour extraire le schéma JSON des modèles Pydantic ni la validation automatique pour JSON. En fait, nous déclarons le type de contenu de la requête en tant que YAML, et non JSON :
```Python hl_lines="17-22 24"
{!../../../docs_src/path_operation_advanced_configuration/tutorial007.py!}
```
Néanmoins, bien que nous n'utilisions pas la fonctionnalité par défaut, nous utilisons toujours un modèle Pydantic pour générer manuellement le schéma JSON pour les données que nous souhaitons recevoir en YAML.
Ensuite, nous utilisons directement la requête et extrayons son contenu en tant qu'octets. Cela signifie que FastAPI n'essaiera même pas d'analyser le payload de la requête en tant que JSON.
Et nous analysons directement ce contenu YAML, puis nous utilisons à nouveau le même modèle Pydantic pour valider le contenu YAML :
```Python hl_lines="26-33"
{!../../../docs_src/path_operation_advanced_configuration/tutorial007.py!}
```
!!! Astuce
Ici, nous réutilisons le même modèle Pydantic.
Mais nous aurions pu tout aussi bien pu le valider d'une autre manière.

View File

@@ -0,0 +1,153 @@
# Exécuter un serveur manuellement - Uvicorn
La principale chose dont vous avez besoin pour exécuter une application **FastAPI** sur une machine serveur distante est un programme serveur ASGI tel que **Uvicorn**.
Il existe 3 principales alternatives :
* <a href="https://www.uvicorn.org/" class="external-link" target="_blank">Uvicorn</a> : un serveur ASGI haute performance.
* <a href="https://pgjones.gitlab.io/hypercorn/" class="external-link" target="_blank">Hypercorn</a> : un serveur
ASGI compatible avec HTTP/2 et Trio entre autres fonctionnalités.
* <a href="https://github.com/django/daphne" class="external-link" target="_blank">Daphne</a> : le serveur ASGI
conçu pour Django Channels.
## Machine serveur et programme serveur
Il y a un petit détail sur les noms à garder à l'esprit. 💡
Le mot "**serveur**" est couramment utilisé pour désigner à la fois l'ordinateur distant/cloud (la machine physique ou virtuelle) et également le programme qui s'exécute sur cette machine (par exemple, Uvicorn).
Gardez cela à l'esprit lorsque vous lisez "serveur" en général, cela pourrait faire référence à l'une de ces deux choses.
Lorsqu'on se réfère à la machine distante, il est courant de l'appeler **serveur**, mais aussi **machine**, **VM** (machine virtuelle), **nœud**. Tout cela fait référence à un type de machine distante, exécutant Linux, en règle générale, sur laquelle vous exécutez des programmes.
## Installer le programme serveur
Vous pouvez installer un serveur compatible ASGI avec :
=== "Uvicorn"
* <a href="https://www.uvicorn.org/" class="external-link" target="_blank">Uvicorn</a>, un serveur ASGI rapide comme l'éclair, basé sur uvloop et httptools.
<div class="termy">
```console
$ pip install "uvicorn[standard]"
---> 100%
```
</div>
!!! tip "Astuce"
En ajoutant `standard`, Uvicorn va installer et utiliser quelques dépendances supplémentaires recommandées.
Cela inclut `uvloop`, le remplaçant performant de `asyncio`, qui fournit le gros gain de performance en matière de concurrence.
=== "Hypercorn"
* <a href="https://gitlab.com/pgjones/hypercorn" class="external-link" target="_blank">Hypercorn</a>, un serveur ASGI également compatible avec HTTP/2.
<div class="termy">
```console
$ pip install hypercorn
---> 100%
```
</div>
...ou tout autre serveur ASGI.
## Exécutez le programme serveur
Vous pouvez ensuite exécuter votre application de la même manière que vous l'avez fait dans les tutoriels, mais sans l'option `--reload`, par exemple :
=== "Uvicorn"
<div class="termy">
```console
$ uvicorn main:app --host 0.0.0.0 --port 80
<span style="color: green;">INFO</span>: Uvicorn running on http://0.0.0.0:80 (Press CTRL+C to quit)
```
</div>
=== "Hypercorn"
<div class="termy">
```console
$ hypercorn main:app --bind 0.0.0.0:80
Running on 0.0.0.0:8080 over http (CTRL + C to quit)
```
</div>
!!! warning
N'oubliez pas de supprimer l'option `--reload` si vous l'utilisiez.
L'option `--reload` consomme beaucoup plus de ressources, est plus instable, etc.
Cela aide beaucoup pendant le **développement**, mais vous **ne devriez pas** l'utiliser en **production**.
## Hypercorn avec Trio
Starlette et **FastAPI** sont basés sur
<a href="https://anyio.readthedocs.io/en/stable/" class="external-link" target="_blank">AnyIO</a>, qui les rend
compatibles avec <a href="https://docs.python.org/3/library/asyncio-task.html" class="external-link" target="_blank">asyncio</a>, de la bibliothèque standard Python et
<a href="https://trio.readthedocs.io/en/stable/" class="external-link" target="_blank">Trio</a>.
Néanmoins, Uvicorn n'est actuellement compatible qu'avec asyncio, et il utilise normalement <a href="https://github.
com/MagicStack/uvloop" class="external-link" target="_blank">`uvloop`</a >, le remplaçant hautes performances de `asyncio`.
Mais si vous souhaitez utiliser directement **Trio**, vous pouvez utiliser **Hypercorn** car il le prend en charge. ✨
### Installer Hypercorn avec Trio
Vous devez d'abord installer Hypercorn avec le support Trio :
<div class="termy">
```console
$ pip install "hypercorn[trio]"
---> 100%
```
</div>
### Exécuter avec Trio
Ensuite, vous pouvez passer l'option de ligne de commande `--worker-class` avec la valeur `trio` :
<div class="termy">
```console
$ hypercorn main:app --worker-class trio
```
</div>
Et cela démarrera Hypercorn avec votre application en utilisant Trio comme backend.
Vous pouvez désormais utiliser Trio en interne dans votre application. Ou mieux encore, vous pouvez utiliser AnyIO pour que votre code reste compatible avec Trio et asyncio. 🎉
## Concepts de déploiement
Ces exemples lancent le programme serveur (e.g. Uvicorn), démarrant **un seul processus**, sur toutes les IPs (`0.0.
0.0`) sur un port prédéfini (par example, `80`).
C'est l'idée de base. Mais vous vous préoccuperez probablement de certains concepts supplémentaires, tels que ... :
* la sécurité - HTTPS
* l'exécution au démarrage
* les redémarrages
* la réplication (le nombre de processus en cours d'exécution)
* la mémoire
* les étapes précédant le démarrage
Je vous en dirai plus sur chacun de ces concepts, sur la façon de les aborder, et donnerai quelques exemples concrets avec des stratégies pour les traiter dans les prochains chapitres. 🚀

View File

@@ -0,0 +1,112 @@
# <abbr title="En anglais: Debugging">Débogage</abbr>
Vous pouvez connecter le <abbr title="En anglais: debugger">débogueur</abbr> dans votre éditeur, par exemple avec Visual Studio Code ou PyCharm.
## Faites appel à `uvicorn`
Dans votre application FastAPI, importez et exécutez directement `uvicorn` :
```Python hl_lines="1 15"
{!../../../docs_src/debugging/tutorial001.py!}
```
### À propos de `__name__ == "__main__"`
Le but principal de `__name__ == "__main__"` est d'avoir du code qui est exécuté lorsque votre fichier est appelé avec :
<div class="termy">
```console
$ python myapp.py
```
</div>
mais qui n'est pas appelé lorsqu'un autre fichier l'importe, comme dans :
```Python
from myapp import app
```
#### Pour davantage de détails
Imaginons que votre fichier s'appelle `myapp.py`.
Si vous l'exécutez avec :
<div class="termy">
```console
$ python myapp.py
```
</div>
alors la variable interne `__name__` de votre fichier, créée automatiquement par Python, aura pour valeur la chaîne de caractères `"__main__"`.
Ainsi, la section :
```Python
uvicorn.run(app, host="0.0.0.0", port=8000)
```
va s'exécuter.
---
Cela ne se produira pas si vous importez ce module (fichier).
Par exemple, si vous avez un autre fichier `importer.py` qui contient :
```Python
from myapp import app
# Code supplémentaire
```
dans ce cas, la variable automatique `__name__` à l'intérieur de `myapp.py` n'aura pas la valeur `"__main__"`.
Ainsi, la ligne :
```Python
uvicorn.run(app, host="0.0.0.0", port=8000)
```
ne sera pas exécutée.
!!! info
Pour plus d'informations, consultez <a href="https://docs.python.org/3/library/__main__.html" class="external-link" target="_blank">la documentation officielle de Python</a>.
## Exécutez votre code avec votre <abbr title="En anglais: debugger">débogueur</abbr>
Parce que vous exécutez le serveur Uvicorn directement depuis votre code, vous pouvez appeler votre programme Python (votre application FastAPI) directement depuis le <abbr title="En anglais: debugger">débogueur</abbr>.
---
Par exemple, dans Visual Studio Code, vous pouvez :
- Cliquer sur l'onglet "Debug" de la barre d'activités de Visual Studio Code.
- "Add configuration...".
- Sélectionnez "Python".
- Lancez le <abbr title="En anglais: debugger">débogueur</abbr> avec l'option "`Python: Current File (Integrated Terminal)`".
Il démarrera alors le serveur avec votre code **FastAPI**, s'arrêtera à vos points d'arrêt, etc.
Voici à quoi cela pourrait ressembler :
<img src="/img/tutorial/debugging/image01.png">
---
Si vous utilisez Pycharm, vous pouvez :
- Ouvrir le menu "Run".
- Sélectionnez l'option "Debug...".
- Un menu contextuel s'affiche alors.
- Sélectionnez le fichier à déboguer (dans ce cas, `main.py`).
Il démarrera alors le serveur avec votre code **FastAPI**, s'arrêtera à vos points d'arrêt, etc.
Voici à quoi cela pourrait ressembler :
<img src="/img/tutorial/debugging/image02.png">

View File

@@ -45,6 +45,7 @@ nav:
- fa: /fa/
- fr: /fr/
- he: /he/
- hy: /hy/
- id: /id/
- it: /it/
- ja: /ja/
@@ -55,6 +56,7 @@ nav:
- ru: /ru/
- sq: /sq/
- sv: /sv/
- ta: /ta/
- tr: /tr/
- uk: /uk/
- zh: /zh/
@@ -67,7 +69,9 @@ nav:
- tutorial/query-params.md
- tutorial/body.md
- tutorial/background-tasks.md
- tutorial/debugging.md
- Guide utilisateur avancé:
- advanced/path-operation-advanced-configuration.md
- advanced/additional-status-codes.md
- advanced/additional-responses.md
- async.md
@@ -77,6 +81,7 @@ nav:
- deployment/https.md
- deployment/deta.md
- deployment/docker.md
- deployment/manually.md
- project-generation.md
- alternatives.md
- history-design-future.md
@@ -104,7 +109,7 @@ markdown_extensions:
extra:
analytics:
provider: google
property: UA-133183413-1
property: G-YNEVN69SC3
social:
- icon: fontawesome/brands/github-alt
link: https://github.com/tiangolo/fastapi
@@ -135,6 +140,8 @@ extra:
name: fr - français
- link: /he/
name: he
- link: /hy/
name: hy
- link: /id/
name: id
- link: /it/
@@ -155,6 +162,8 @@ extra:
name: sq - shqip
- link: /sv/
name: sv - svenska
- link: /ta/
name: ta - தமிழ்
- link: /tr/
name: tr - Türkçe
- link: /uk/

View File

@@ -45,6 +45,7 @@ nav:
- fa: /fa/
- fr: /fr/
- he: /he/
- hy: /hy/
- id: /id/
- it: /it/
- ja: /ja/
@@ -55,6 +56,7 @@ nav:
- ru: /ru/
- sq: /sq/
- sv: /sv/
- ta: /ta/
- tr: /tr/
- uk: /uk/
- zh: /zh/
@@ -80,7 +82,7 @@ markdown_extensions:
extra:
analytics:
provider: google
property: UA-133183413-1
property: G-YNEVN69SC3
social:
- icon: fontawesome/brands/github-alt
link: https://github.com/tiangolo/fastapi
@@ -111,6 +113,8 @@ extra:
name: fr - français
- link: /he/
name: he
- link: /hy/
name: hy
- link: /id/
name: id
- link: /it/
@@ -131,6 +135,8 @@ extra:
name: sq - shqip
- link: /sv/
name: sv - svenska
- link: /ta/
name: ta - தமிழ்
- link: /tr/
name: tr - Türkçe
- link: /uk/

467
docs/hy/docs/index.md Normal file
View File

@@ -0,0 +1,467 @@
{!../../../docs/missing-translation.md!}
<p align="center">
<a href="https://fastapi.tiangolo.com"><img src="https://fastapi.tiangolo.com/img/logo-margin/logo-teal.png" alt="FastAPI"></a>
</p>
<p align="center">
<em>FastAPI framework, high performance, easy to learn, fast to code, ready for production</em>
</p>
<p align="center">
<a href="https://github.com/tiangolo/fastapi/actions?query=workflow%3ATest+event%3Apush+branch%3Amaster" target="_blank">
<img src="https://github.com/tiangolo/fastapi/workflows/Test/badge.svg?event=push&branch=master" alt="Test">
</a>
<a href="https://coverage-badge.samuelcolvin.workers.dev/redirect/tiangolo/fastapi" target="_blank">
<img src="https://coverage-badge.samuelcolvin.workers.dev/tiangolo/fastapi.svg" alt="Coverage">
</a>
<a href="https://pypi.org/project/fastapi" target="_blank">
<img src="https://img.shields.io/pypi/v/fastapi?color=%2334D058&label=pypi%20package" alt="Package version">
</a>
<a href="https://pypi.org/project/fastapi" target="_blank">
<img src="https://img.shields.io/pypi/pyversions/fastapi.svg?color=%2334D058" alt="Supported Python versions">
</a>
</p>
---
**Documentation**: <a href="https://fastapi.tiangolo.com" target="_blank">https://fastapi.tiangolo.com</a>
**Source Code**: <a href="https://github.com/tiangolo/fastapi" target="_blank">https://github.com/tiangolo/fastapi</a>
---
FastAPI is a modern, fast (high-performance), web framework for building APIs with Python 3.7+ based on standard Python type hints.
The key features are:
* **Fast**: Very high performance, on par with **NodeJS** and **Go** (thanks to Starlette and Pydantic). [One of the fastest Python frameworks available](#performance).
* **Fast to code**: Increase the speed to develop features by about 200% to 300%. *
* **Fewer bugs**: Reduce about 40% of human (developer) induced errors. *
* **Intuitive**: Great editor support. <abbr title="also known as auto-complete, autocompletion, IntelliSense">Completion</abbr> everywhere. Less time debugging.
* **Easy**: Designed to be easy to use and learn. Less time reading docs.
* **Short**: Minimize code duplication. Multiple features from each parameter declaration. Fewer bugs.
* **Robust**: Get production-ready code. With automatic interactive documentation.
* **Standards-based**: Based on (and fully compatible with) the open standards for APIs: <a href="https://github.com/OAI/OpenAPI-Specification" class="external-link" target="_blank">OpenAPI</a> (previously known as Swagger) and <a href="https://json-schema.org/" class="external-link" target="_blank">JSON Schema</a>.
<small>* estimation based on tests on an internal development team, building production applications.</small>
## Sponsors
<!-- sponsors -->
{% if sponsors %}
{% for sponsor in sponsors.gold -%}
<a href="{{ sponsor.url }}" target="_blank" title="{{ sponsor.title }}"><img src="{{ sponsor.img }}" style="border-radius:15px"></a>
{% endfor -%}
{%- for sponsor in sponsors.silver -%}
<a href="{{ sponsor.url }}" target="_blank" title="{{ sponsor.title }}"><img src="{{ sponsor.img }}" style="border-radius:15px"></a>
{% endfor %}
{% endif %}
<!-- /sponsors -->
<a href="https://fastapi.tiangolo.com/fastapi-people/#sponsors" class="external-link" target="_blank">Other sponsors</a>
## Opinions
"_[...] I'm using **FastAPI** a ton these days. [...] I'm actually planning to use it for all of my team's **ML services at Microsoft**. Some of them are getting integrated into the core **Windows** product and some **Office** products._"
<div style="text-align: right; margin-right: 10%;">Kabir Khan - <strong>Microsoft</strong> <a href="https://github.com/tiangolo/fastapi/pull/26" target="_blank"><small>(ref)</small></a></div>
---
"_We adopted the **FastAPI** library to spawn a **REST** server that can be queried to obtain **predictions**. [for Ludwig]_"
<div style="text-align: right; margin-right: 10%;">Piero Molino, Yaroslav Dudin, and Sai Sumanth Miryala - <strong>Uber</strong> <a href="https://eng.uber.com/ludwig-v0-2/" target="_blank"><small>(ref)</small></a></div>
---
"_**Netflix** is pleased to announce the open-source release of our **crisis management** orchestration framework: **Dispatch**! [built with **FastAPI**]_"
<div style="text-align: right; margin-right: 10%;">Kevin Glisson, Marc Vilanova, Forest Monsen - <strong>Netflix</strong> <a href="https://netflixtechblog.com/introducing-dispatch-da4b8a2a8072" target="_blank"><small>(ref)</small></a></div>
---
"_Im over the moon excited about **FastAPI**. Its so fun!_"
<div style="text-align: right; margin-right: 10%;">Brian Okken - <strong><a href="https://pythonbytes.fm/episodes/show/123/time-to-right-the-py-wrongs?time_in_sec=855" target="_blank">Python Bytes</a> podcast host</strong> <a href="https://twitter.com/brianokken/status/1112220079972728832" target="_blank"><small>(ref)</small></a></div>
---
"_Honestly, what you've built looks super solid and polished. In many ways, it's what I wanted **Hug** to be - it's really inspiring to see someone build that._"
<div style="text-align: right; margin-right: 10%;">Timothy Crosley - <strong><a href="https://www.hug.rest/" target="_blank">Hug</a> creator</strong> <a href="https://news.ycombinator.com/item?id=19455465" target="_blank"><small>(ref)</small></a></div>
---
"_If you're looking to learn one **modern framework** for building REST APIs, check out **FastAPI** [...] It's fast, easy to use and easy to learn [...]_"
"_We've switched over to **FastAPI** for our **APIs** [...] I think you'll like it [...]_"
<div style="text-align: right; margin-right: 10%;">Ines Montani - Matthew Honnibal - <strong><a href="https://explosion.ai" target="_blank">Explosion AI</a> founders - <a href="https://spacy.io" target="_blank">spaCy</a> creators</strong> <a href="https://twitter.com/_inesmontani/status/1144173225322143744" target="_blank"><small>(ref)</small></a> - <a href="https://twitter.com/honnibal/status/1144031421859655680" target="_blank"><small>(ref)</small></a></div>
---
## **Typer**, the FastAPI of CLIs
<a href="https://typer.tiangolo.com" target="_blank"><img src="https://typer.tiangolo.com/img/logo-margin/logo-margin-vector.svg" style="width: 20%;"></a>
If you are building a <abbr title="Command Line Interface">CLI</abbr> app to be used in the terminal instead of a web API, check out <a href="https://typer.tiangolo.com/" class="external-link" target="_blank">**Typer**</a>.
**Typer** is FastAPI's little sibling. And it's intended to be the **FastAPI of CLIs**. ⌨️ 🚀
## Requirements
Python 3.7+
FastAPI stands on the shoulders of giants:
* <a href="https://www.starlette.io/" class="external-link" target="_blank">Starlette</a> for the web parts.
* <a href="https://pydantic-docs.helpmanual.io/" class="external-link" target="_blank">Pydantic</a> for the data parts.
## Installation
<div class="termy">
```console
$ pip install fastapi
---> 100%
```
</div>
You will also need an ASGI server, for production such as <a href="https://www.uvicorn.org" class="external-link" target="_blank">Uvicorn</a> or <a href="https://github.com/pgjones/hypercorn" class="external-link" target="_blank">Hypercorn</a>.
<div class="termy">
```console
$ pip install "uvicorn[standard]"
---> 100%
```
</div>
## Example
### Create it
* Create a file `main.py` with:
```Python
from typing import Union
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def read_root():
return {"Hello": "World"}
@app.get("/items/{item_id}")
def read_item(item_id: int, q: Union[str, None] = None):
return {"item_id": item_id, "q": q}
```
<details markdown="1">
<summary>Or use <code>async def</code>...</summary>
If your code uses `async` / `await`, use `async def`:
```Python hl_lines="9 14"
from typing import Union
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def read_root():
return {"Hello": "World"}
@app.get("/items/{item_id}")
async def read_item(item_id: int, q: Union[str, None] = None):
return {"item_id": item_id, "q": q}
```
**Note**:
If you don't know, check the _"In a hurry?"_ section about <a href="https://fastapi.tiangolo.com/async/#in-a-hurry" target="_blank">`async` and `await` in the docs</a>.
</details>
### Run it
Run the server with:
<div class="termy">
```console
$ uvicorn main:app --reload
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO: Started reloader process [28720]
INFO: Started server process [28722]
INFO: Waiting for application startup.
INFO: Application startup complete.
```
</div>
<details markdown="1">
<summary>About the command <code>uvicorn main:app --reload</code>...</summary>
The command `uvicorn main:app` refers to:
* `main`: the file `main.py` (the Python "module").
* `app`: the object created inside of `main.py` with the line `app = FastAPI()`.
* `--reload`: make the server restart after code changes. Only do this for development.
</details>
### Check it
Open your browser at <a href="http://127.0.0.1:8000/items/5?q=somequery" class="external-link" target="_blank">http://127.0.0.1:8000/items/5?q=somequery</a>.
You will see the JSON response as:
```JSON
{"item_id": 5, "q": "somequery"}
```
You already created an API that:
* Receives HTTP requests in the _paths_ `/` and `/items/{item_id}`.
* Both _paths_ take `GET` <em>operations</em> (also known as HTTP _methods_).
* The _path_ `/items/{item_id}` has a _path parameter_ `item_id` that should be an `int`.
* The _path_ `/items/{item_id}` has an optional `str` _query parameter_ `q`.
### Interactive API docs
Now go to <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
You will see the automatic interactive API documentation (provided by <a href="https://github.com/swagger-api/swagger-ui" class="external-link" target="_blank">Swagger UI</a>):
![Swagger UI](https://fastapi.tiangolo.com/img/index/index-01-swagger-ui-simple.png)
### Alternative API docs
And now, go to <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>.
You will see the alternative automatic documentation (provided by <a href="https://github.com/Rebilly/ReDoc" class="external-link" target="_blank">ReDoc</a>):
![ReDoc](https://fastapi.tiangolo.com/img/index/index-02-redoc-simple.png)
## Example upgrade
Now modify the file `main.py` to receive a body from a `PUT` request.
Declare the body using standard Python types, thanks to Pydantic.
```Python hl_lines="4 9-12 25-27"
from typing import Union
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
price: float
is_offer: Union[bool, None] = None
@app.get("/")
def read_root():
return {"Hello": "World"}
@app.get("/items/{item_id}")
def read_item(item_id: int, q: Union[str, None] = None):
return {"item_id": item_id, "q": q}
@app.put("/items/{item_id}")
def update_item(item_id: int, item: Item):
return {"item_name": item.name, "item_id": item_id}
```
The server should reload automatically (because you added `--reload` to the `uvicorn` command above).
### Interactive API docs upgrade
Now go to <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
* The interactive API documentation will be automatically updated, including the new body:
![Swagger UI](https://fastapi.tiangolo.com/img/index/index-03-swagger-02.png)
* Click on the button "Try it out", it allows you to fill the parameters and directly interact with the API:
![Swagger UI interaction](https://fastapi.tiangolo.com/img/index/index-04-swagger-03.png)
* Then click on the "Execute" button, the user interface will communicate with your API, send the parameters, get the results and show them on the screen:
![Swagger UI interaction](https://fastapi.tiangolo.com/img/index/index-05-swagger-04.png)
### Alternative API docs upgrade
And now, go to <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>.
* The alternative documentation will also reflect the new query parameter and body:
![ReDoc](https://fastapi.tiangolo.com/img/index/index-06-redoc-02.png)
### Recap
In summary, you declare **once** the types of parameters, body, etc. as function parameters.
You do that with standard modern Python types.
You don't have to learn a new syntax, the methods or classes of a specific library, etc.
Just standard **Python 3.7+**.
For example, for an `int`:
```Python
item_id: int
```
or for a more complex `Item` model:
```Python
item: Item
```
...and with that single declaration you get:
* Editor support, including:
* Completion.
* Type checks.
* Validation of data:
* Automatic and clear errors when the data is invalid.
* Validation even for deeply nested JSON objects.
* <abbr title="also known as: serialization, parsing, marshalling">Conversion</abbr> of input data: coming from the network to Python data and types. Reading from:
* JSON.
* Path parameters.
* Query parameters.
* Cookies.
* Headers.
* Forms.
* Files.
* <abbr title="also known as: serialization, parsing, marshalling">Conversion</abbr> of output data: converting from Python data and types to network data (as JSON):
* Convert Python types (`str`, `int`, `float`, `bool`, `list`, etc).
* `datetime` objects.
* `UUID` objects.
* Database models.
* ...and many more.
* Automatic interactive API documentation, including 2 alternative user interfaces:
* Swagger UI.
* ReDoc.
---
Coming back to the previous code example, **FastAPI** will:
* Validate that there is an `item_id` in the path for `GET` and `PUT` requests.
* Validate that the `item_id` is of type `int` for `GET` and `PUT` requests.
* If it is not, the client will see a useful, clear error.
* Check if there is an optional query parameter named `q` (as in `http://127.0.0.1:8000/items/foo?q=somequery`) for `GET` requests.
* As the `q` parameter is declared with `= None`, it is optional.
* Without the `None` it would be required (as is the body in the case with `PUT`).
* For `PUT` requests to `/items/{item_id}`, Read the body as JSON:
* Check that it has a required attribute `name` that should be a `str`.
* Check that it has a required attribute `price` that has to be a `float`.
* Check that it has an optional attribute `is_offer`, that should be a `bool`, if present.
* All this would also work for deeply nested JSON objects.
* Convert from and to JSON automatically.
* Document everything with OpenAPI, that can be used by:
* Interactive documentation systems.
* Automatic client code generation systems, for many languages.
* Provide 2 interactive documentation web interfaces directly.
---
We just scratched the surface, but you already get the idea of how it all works.
Try changing the line with:
```Python
return {"item_name": item.name, "item_id": item_id}
```
...from:
```Python
... "item_name": item.name ...
```
...to:
```Python
... "item_price": item.price ...
```
...and see how your editor will auto-complete the attributes and know their types:
![editor support](https://fastapi.tiangolo.com/img/vscode-completion.png)
For a more complete example including more features, see the <a href="https://fastapi.tiangolo.com/tutorial/">Tutorial - User Guide</a>.
**Spoiler alert**: the tutorial - user guide includes:
* Declaration of **parameters** from other different places as: **headers**, **cookies**, **form fields** and **files**.
* How to set **validation constraints** as `maximum_length` or `regex`.
* A very powerful and easy to use **<abbr title="also known as components, resources, providers, services, injectables">Dependency Injection</abbr>** system.
* Security and authentication, including support for **OAuth2** with **JWT tokens** and **HTTP Basic** auth.
* More advanced (but equally easy) techniques for declaring **deeply nested JSON models** (thanks to Pydantic).
* **GraphQL** integration with <a href="https://strawberry.rocks" class="external-link" target="_blank">Strawberry</a> and other libraries.
* Many extra features (thanks to Starlette) as:
* **WebSockets**
* extremely easy tests based on HTTPX and `pytest`
* **CORS**
* **Cookie Sessions**
* ...and more.
## Performance
Independent TechEmpower benchmarks show **FastAPI** applications running under Uvicorn as <a href="https://www.techempower.com/benchmarks/#section=test&runid=7464e520-0dc2-473d-bd34-dbdfd7e85911&hw=ph&test=query&l=zijzen-7" class="external-link" target="_blank">one of the fastest Python frameworks available</a>, only below Starlette and Uvicorn themselves (used internally by FastAPI). (*)
To understand more about it, see the section <a href="https://fastapi.tiangolo.com/benchmarks/" class="internal-link" target="_blank">Benchmarks</a>.
## Optional Dependencies
Used by Pydantic:
* <a href="https://github.com/esnme/ultrajson" target="_blank"><code>ujson</code></a> - for faster JSON <abbr title="converting the string that comes from an HTTP request into Python data">"parsing"</abbr>.
* <a href="https://github.com/JoshData/python-email-validator" target="_blank"><code>email_validator</code></a> - for email validation.
Used by Starlette:
* <a href="https://www.python-httpx.org" target="_blank"><code>httpx</code></a> - Required if you want to use the `TestClient`.
* <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - Required if you want to use the default template configuration.
* <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - Required if you want to support form <abbr title="converting the string that comes from an HTTP request into Python data">"parsing"</abbr>, with `request.form()`.
* <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - Required for `SessionMiddleware` support.
* <a href="https://pyyaml.org/wiki/PyYAMLDocumentation" target="_blank"><code>pyyaml</code></a> - Required for Starlette's `SchemaGenerator` support (you probably don't need it with FastAPI).
* <a href="https://github.com/esnme/ultrajson" target="_blank"><code>ujson</code></a> - Required if you want to use `UJSONResponse`.
Used by FastAPI / Starlette:
* <a href="https://www.uvicorn.org" target="_blank"><code>uvicorn</code></a> - for the server that loads and serves your application.
* <a href="https://github.com/ijl/orjson" target="_blank"><code>orjson</code></a> - Required if you want to use `ORJSONResponse`.
You can install all of these with `pip install "fastapi[all]"`.
## License
This project is licensed under the terms of the MIT license.

148
docs/hy/mkdocs.yml Normal file
View File

@@ -0,0 +1,148 @@
site_name: FastAPI
site_description: FastAPI framework, high performance, easy to learn, fast to code, ready for production
site_url: https://fastapi.tiangolo.com/hy/
theme:
name: material
custom_dir: overrides
palette:
- media: '(prefers-color-scheme: light)'
scheme: default
primary: teal
accent: amber
toggle:
icon: material/lightbulb
name: Switch to light mode
- media: '(prefers-color-scheme: dark)'
scheme: slate
primary: teal
accent: amber
toggle:
icon: material/lightbulb-outline
name: Switch to dark mode
features:
- search.suggest
- search.highlight
- content.tabs.link
icon:
repo: fontawesome/brands/github-alt
logo: https://fastapi.tiangolo.com/img/icon-white.svg
favicon: https://fastapi.tiangolo.com/img/favicon.png
language: hy
repo_name: tiangolo/fastapi
repo_url: https://github.com/tiangolo/fastapi
edit_uri: ''
plugins:
- search
- markdownextradata:
data: data
nav:
- FastAPI: index.md
- Languages:
- en: /
- az: /az/
- de: /de/
- es: /es/
- fa: /fa/
- fr: /fr/
- he: /he/
- hy: /hy/
- id: /id/
- it: /it/
- ja: /ja/
- ko: /ko/
- nl: /nl/
- pl: /pl/
- pt: /pt/
- ru: /ru/
- sq: /sq/
- sv: /sv/
- tr: /tr/
- uk: /uk/
- zh: /zh/
markdown_extensions:
- toc:
permalink: true
- markdown.extensions.codehilite:
guess_lang: false
- mdx_include:
base_path: docs
- admonition
- codehilite
- extra
- pymdownx.superfences:
custom_fences:
- name: mermaid
class: mermaid
format: !!python/name:pymdownx.superfences.fence_code_format ''
- pymdownx.tabbed:
alternate_style: true
- attr_list
- md_in_html
extra:
analytics:
provider: google
property: UA-133183413-1
social:
- icon: fontawesome/brands/github-alt
link: https://github.com/tiangolo/fastapi
- icon: fontawesome/brands/discord
link: https://discord.gg/VQjSZaeJmf
- icon: fontawesome/brands/twitter
link: https://twitter.com/fastapi
- icon: fontawesome/brands/linkedin
link: https://www.linkedin.com/in/tiangolo
- icon: fontawesome/brands/dev
link: https://dev.to/tiangolo
- icon: fontawesome/brands/medium
link: https://medium.com/@tiangolo
- icon: fontawesome/solid/globe
link: https://tiangolo.com
alternate:
- link: /
name: en - English
- link: /az/
name: az
- link: /de/
name: de
- link: /es/
name: es - español
- link: /fa/
name: fa
- link: /fr/
name: fr - français
- link: /he/
name: he
- link: /hy/
name: hy
- link: /id/
name: id
- link: /it/
name: it - italiano
- link: /ja/
name: ja - 日本語
- link: /ko/
name: ko - 한국어
- link: /nl/
name: nl
- link: /pl/
name: pl
- link: /pt/
name: pt - português
- link: /ru/
name: ru - русский язык
- link: /sq/
name: sq - shqip
- link: /sv/
name: sv - svenska
- link: /tr/
name: tr - Türkçe
- link: /uk/
name: uk - українська мова
- link: /zh/
name: zh - 汉语
extra_css:
- https://fastapi.tiangolo.com/css/termynal.css
- https://fastapi.tiangolo.com/css/custom.css
extra_javascript:
- https://fastapi.tiangolo.com/js/termynal.js
- https://fastapi.tiangolo.com/js/custom.js

0
docs/hy/overrides/.gitignore vendored Normal file
View File

View File

@@ -45,6 +45,7 @@ nav:
- fa: /fa/
- fr: /fr/
- he: /he/
- hy: /hy/
- id: /id/
- it: /it/
- ja: /ja/
@@ -55,6 +56,7 @@ nav:
- ru: /ru/
- sq: /sq/
- sv: /sv/
- ta: /ta/
- tr: /tr/
- uk: /uk/
- zh: /zh/
@@ -80,7 +82,7 @@ markdown_extensions:
extra:
analytics:
provider: google
property: UA-133183413-1
property: G-YNEVN69SC3
social:
- icon: fontawesome/brands/github-alt
link: https://github.com/tiangolo/fastapi
@@ -111,6 +113,8 @@ extra:
name: fr - français
- link: /he/
name: he
- link: /hy/
name: hy
- link: /id/
name: id
- link: /it/
@@ -131,6 +135,8 @@ extra:
name: sq - shqip
- link: /sv/
name: sv - svenska
- link: /ta/
name: ta - தமிழ்
- link: /tr/
name: tr - Türkçe
- link: /uk/

View File

@@ -45,6 +45,7 @@ nav:
- fa: /fa/
- fr: /fr/
- he: /he/
- hy: /hy/
- id: /id/
- it: /it/
- ja: /ja/
@@ -55,6 +56,7 @@ nav:
- ru: /ru/
- sq: /sq/
- sv: /sv/
- ta: /ta/
- tr: /tr/
- uk: /uk/
- zh: /zh/
@@ -80,7 +82,7 @@ markdown_extensions:
extra:
analytics:
provider: google
property: UA-133183413-1
property: G-YNEVN69SC3
social:
- icon: fontawesome/brands/github-alt
link: https://github.com/tiangolo/fastapi
@@ -111,6 +113,8 @@ extra:
name: fr - français
- link: /he/
name: he
- link: /hy/
name: hy
- link: /id/
name: id
- link: /it/
@@ -131,6 +135,8 @@ extra:
name: sq - shqip
- link: /sv/
name: sv - svenska
- link: /ta/
name: ta - தமிழ்
- link: /tr/
name: tr - Türkçe
- link: /uk/

View File

@@ -74,18 +74,18 @@
これらの *path operation* には `X-Token` ヘッダーが必要です。
=== "Python 3.6 and above"
```Python
{!> ../../../docs_src/app_testing/app_b/main.py!}
```
=== "Python 3.10 and above"
=== "Python 3.10+"
```Python
{!> ../../../docs_src/app_testing/app_b_py310/main.py!}
```
=== "Python 3.6+"
```Python
{!> ../../../docs_src/app_testing/app_b/main.py!}
```
### 拡張版テストファイル
次に、先程のものに拡張版のテストを加えた、`test_main_b.py` を作成します。

View File

@@ -45,6 +45,7 @@ nav:
- fa: /fa/
- fr: /fr/
- he: /he/
- hy: /hy/
- id: /id/
- it: /it/
- ja: /ja/
@@ -55,6 +56,7 @@ nav:
- ru: /ru/
- sq: /sq/
- sv: /sv/
- ta: /ta/
- tr: /tr/
- uk: /uk/
- zh: /zh/
@@ -124,7 +126,7 @@ markdown_extensions:
extra:
analytics:
provider: google
property: UA-133183413-1
property: G-YNEVN69SC3
social:
- icon: fontawesome/brands/github-alt
link: https://github.com/tiangolo/fastapi
@@ -155,6 +157,8 @@ extra:
name: fr - français
- link: /he/
name: he
- link: /hy/
name: hy
- link: /id/
name: id
- link: /it/
@@ -175,6 +179,8 @@ extra:
name: sq - shqip
- link: /sv/
name: sv - svenska
- link: /ta/
name: ta - தமிழ்
- link: /tr/
name: tr - Türkçe
- link: /uk/

View File

@@ -45,6 +45,7 @@ nav:
- fa: /fa/
- fr: /fr/
- he: /he/
- hy: /hy/
- id: /id/
- it: /it/
- ja: /ja/
@@ -55,6 +56,7 @@ nav:
- ru: /ru/
- sq: /sq/
- sv: /sv/
- ta: /ta/
- tr: /tr/
- uk: /uk/
- zh: /zh/
@@ -92,7 +94,7 @@ markdown_extensions:
extra:
analytics:
provider: google
property: UA-133183413-1
property: G-YNEVN69SC3
social:
- icon: fontawesome/brands/github-alt
link: https://github.com/tiangolo/fastapi
@@ -123,6 +125,8 @@ extra:
name: fr - français
- link: /he/
name: he
- link: /hy/
name: hy
- link: /id/
name: id
- link: /it/
@@ -143,6 +147,8 @@ extra:
name: sq - shqip
- link: /sv/
name: sv - svenska
- link: /ta/
name: ta - தமிழ்
- link: /tr/
name: tr - Türkçe
- link: /uk/

View File

@@ -45,6 +45,7 @@ nav:
- fa: /fa/
- fr: /fr/
- he: /he/
- hy: /hy/
- id: /id/
- it: /it/
- ja: /ja/
@@ -55,6 +56,7 @@ nav:
- ru: /ru/
- sq: /sq/
- sv: /sv/
- ta: /ta/
- tr: /tr/
- uk: /uk/
- zh: /zh/
@@ -80,7 +82,7 @@ markdown_extensions:
extra:
analytics:
provider: google
property: UA-133183413-1
property: G-YNEVN69SC3
social:
- icon: fontawesome/brands/github-alt
link: https://github.com/tiangolo/fastapi
@@ -111,6 +113,8 @@ extra:
name: fr - français
- link: /he/
name: he
- link: /hy/
name: hy
- link: /id/
name: id
- link: /it/
@@ -131,6 +135,8 @@ extra:
name: sq - shqip
- link: /sv/
name: sv - svenska
- link: /ta/
name: ta - தமிழ்
- link: /tr/
name: tr - Türkçe
- link: /uk/

View File

@@ -45,6 +45,7 @@ nav:
- fa: /fa/
- fr: /fr/
- he: /he/
- hy: /hy/
- id: /id/
- it: /it/
- ja: /ja/
@@ -55,6 +56,7 @@ nav:
- ru: /ru/
- sq: /sq/
- sv: /sv/
- ta: /ta/
- tr: /tr/
- uk: /uk/
- zh: /zh/
@@ -83,7 +85,7 @@ markdown_extensions:
extra:
analytics:
provider: google
property: UA-133183413-1
property: G-YNEVN69SC3
social:
- icon: fontawesome/brands/github-alt
link: https://github.com/tiangolo/fastapi
@@ -114,6 +116,8 @@ extra:
name: fr - français
- link: /he/
name: he
- link: /hy/
name: hy
- link: /id/
name: id
- link: /it/
@@ -134,6 +138,8 @@ extra:
name: sq - shqip
- link: /sv/
name: sv - svenska
- link: /ta/
name: ta - தமிழ்
- link: /tr/
name: tr - Türkçe
- link: /uk/

View File

@@ -8,18 +8,18 @@ Primeiro, é claro, você pode misturar `Path`, `Query` e declarações de parâ
E você também pode declarar parâmetros de corpo como opcionais, definindo o valor padrão com `None`:
=== "Python 3.6 e superiores"
```Python hl_lines="19-21"
{!> ../../../docs_src/body_multiple_params/tutorial001.py!}
```
=== "Python 3.10 e superiores"
=== "Python 3.10+"
```Python hl_lines="17-19"
{!> ../../../docs_src/body_multiple_params/tutorial001_py310.py!}
```
=== "Python 3.6+"
```Python hl_lines="19-21"
{!> ../../../docs_src/body_multiple_params/tutorial001.py!}
```
!!! nota
Repare que, neste caso, o `item` que seria capturado a partir do corpo é opcional. Visto que ele possui `None` como valor padrão.
@@ -38,18 +38,18 @@ No exemplo anterior, as *operações de rota* esperariam um JSON no corpo conten
Mas você pode também declarar múltiplos parâmetros de corpo, por exemplo, `item` e `user`:
=== "Python 3.6 e superiores"
```Python hl_lines="22"
{!> ../../../docs_src/body_multiple_params/tutorial002.py!}
```
=== "Python 3.10 e superiores"
=== "Python 3.10+"
```Python hl_lines="20"
{!> ../../../docs_src/body_multiple_params/tutorial002_py310.py!}
```
=== "Python 3.6+"
```Python hl_lines="22"
{!> ../../../docs_src/body_multiple_params/tutorial002.py!}
```
Neste caso, o **FastAPI** perceberá que existe mais de um parâmetro de corpo na função (dois parâmetros que são modelos Pydantic).
Então, ele usará o nome dos parâmetros como chaves (nome dos campos) no corpo, e espera um corpo como:
@@ -87,13 +87,13 @@ Se você declará-lo como é, porque é um valor singular, o **FastAPI** assumir
Mas você pode instruir o **FastAPI** para tratá-lo como outra chave do corpo usando `Body`:
=== "Python 3.6 e superiores"
=== "Python 3.6+"
```Python hl_lines="22"
{!> ../../../docs_src/body_multiple_params/tutorial003.py!}
```
=== "Python 3.10 e superiores"
=== "Python 3.10+"
```Python hl_lines="20"
{!> ../../../docs_src/body_multiple_params/tutorial003_py310.py!}
@@ -137,18 +137,18 @@ q: str | None = None
Por exemplo:
=== "Python 3.6 e superiores"
```Python hl_lines="27"
{!> ../../../docs_src/body_multiple_params/tutorial004.py!}
```
=== "Python 3.10 e superiores"
=== "Python 3.10+"
```Python hl_lines="26"
{!> ../../../docs_src/body_multiple_params/tutorial004_py310.py!}
```
=== "Python 3.6+"
```Python hl_lines="27"
{!> ../../../docs_src/body_multiple_params/tutorial004.py!}
```
!!! info "Informação"
`Body` também possui todas as validações adicionais e metadados de parâmetros como em `Query`,`Path` e outras que você verá depois.
@@ -166,18 +166,18 @@ item: Item = Body(embed=True)
como em:
=== "Python 3.6 e superiores"
```Python hl_lines="17"
{!> ../../../docs_src/body_multiple_params/tutorial005.py!}
```
=== "Python 3.10 e superiores"
=== "Python 3.10+"
```Python hl_lines="15"
{!> ../../../docs_src/body_multiple_params/tutorial005_py310.py!}
```
=== "Python 3.6+"
```Python hl_lines="17"
{!> ../../../docs_src/body_multiple_params/tutorial005.py!}
```
Neste caso o **FastAPI** esperará um corpo como:
```JSON hl_lines="2"

View File

@@ -20,18 +20,18 @@ Você pode usar a função `jsonable_encoder` para resolver isso.
A função recebe um objeto, como um modelo Pydantic e retorna uma versão compatível com JSON:
=== "Python 3.6 e acima"
```Python hl_lines="5 22"
{!> ../../../docs_src/encoder/tutorial001.py!}
```
=== "Python 3.10 e acima"
=== "Python 3.10+"
```Python hl_lines="4 21"
{!> ../../../docs_src/encoder/tutorial001_py310.py!}
```
=== "Python 3.6+"
```Python hl_lines="5 22"
{!> ../../../docs_src/encoder/tutorial001.py!}
```
Neste exemplo, ele converteria o modelo Pydantic em um `dict`, e o `datetime` em um `str`.
O resultado de chamar a função é algo que pode ser codificado com o padrão do Python <a href="https://docs.python.org/3/library/json.html#json.dumps" class="external-link" target="_blank">`json.dumps()`</a>.

View File

@@ -6,36 +6,36 @@ Você pode definir parâmetros de Cabeçalho da mesma maneira que define paramê
Primeiro importe `Header`:
=== "Python 3.6 and above"
```Python hl_lines="3"
{!> ../../../docs_src/header_params/tutorial001.py!}
```
=== "Python 3.10 and above"
=== "Python 3.10+"
```Python hl_lines="1"
{!> ../../../docs_src/header_params/tutorial001_py310.py!}
```
=== "Python 3.6+"
```Python hl_lines="3"
{!> ../../../docs_src/header_params/tutorial001.py!}
```
## Declare parâmetros de `Header`
Então declare os paramêtros de cabeçalho usando a mesma estrutura que em `Path`, `Query` e `Cookie`.
O primeiro valor é o valor padrão, você pode passar todas as validações adicionais ou parâmetros de anotação:
=== "Python 3.6 and above"
```Python hl_lines="9"
{!> ../../../docs_src/header_params/tutorial001.py!}
```
=== "Python 3.10 and above"
=== "Python 3.10+"
```Python hl_lines="7"
{!> ../../../docs_src/header_params/tutorial001_py310.py!}
```
=== "Python 3.6+"
```Python hl_lines="9"
{!> ../../../docs_src/header_params/tutorial001.py!}
```
!!! note "Detalhes Técnicos"
`Header` é uma classe "irmã" de `Path`, `Query` e `Cookie`. Ela também herda da mesma classe em comum `Param`.
@@ -60,18 +60,18 @@ Portanto, você pode usar `user_agent` como faria normalmente no código Python,
Se por algum motivo você precisar desabilitar a conversão automática de sublinhados para hífens, defina o parâmetro `convert_underscores` de `Header` para `False`:
=== "Python 3.6 and above"
```Python hl_lines="10"
{!> ../../../docs_src/header_params/tutorial002.py!}
```
=== "Python 3.10 and above"
=== "Python 3.10+"
```Python hl_lines="8"
{!> ../../../docs_src/header_params/tutorial002_py310.py!}
```
=== "Python 3.6+"
```Python hl_lines="10"
{!> ../../../docs_src/header_params/tutorial002.py!}
```
!!! warning "Aviso"
Antes de definir `convert_underscores` como `False`, lembre-se de que alguns proxies e servidores HTTP não permitem o uso de cabeçalhos com sublinhados.
@@ -85,22 +85,22 @@ Você receberá todos os valores do cabeçalho duplicado como uma `list` Python.
Por exemplo, para declarar um cabeçalho de `X-Token` que pode aparecer mais de uma vez, você pode escrever:
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="9"
{!> ../../../docs_src/header_params/tutorial003.py!}
```Python hl_lines="7"
{!> ../../../docs_src/header_params/tutorial003_py310.py!}
```
=== "Python 3.9 and above"
=== "Python 3.9+"
```Python hl_lines="9"
{!> ../../../docs_src/header_params/tutorial003_py39.py!}
```
=== "Python 3.10 and above"
=== "Python 3.6+"
```Python hl_lines="7"
{!> ../../../docs_src/header_params/tutorial003_py310.py!}
```Python hl_lines="9"
{!> ../../../docs_src/header_params/tutorial003.py!}
```
Se você se comunicar com essa *operação de caminho* enviando dois cabeçalhos HTTP como:

View File

@@ -6,36 +6,36 @@ Do mesmo modo que você pode declarar mais validações e metadados para parâme
Primeiro, importe `Path` de `fastapi`:
=== "Python 3.6 e superiores"
```Python hl_lines="3"
{!> ../../../docs_src/path_params_numeric_validations/tutorial001.py!}
```
=== "Python 3.10 e superiores"
=== "Python 3.10+"
```Python hl_lines="1"
{!> ../../../docs_src/path_params_numeric_validations/tutorial001_py310.py!}
```
=== "Python 3.6+"
```Python hl_lines="3"
{!> ../../../docs_src/path_params_numeric_validations/tutorial001.py!}
```
## Declare metadados
Você pode declarar todos os parâmetros da mesma maneira que na `Query`.
Por exemplo para declarar um valor de metadado `title` para o parâmetro de rota `item_id` você pode digitar:
=== "Python 3.6 e superiores"
```Python hl_lines="10"
{!> ../../../docs_src/path_params_numeric_validations/tutorial001.py!}
```
=== "Python 3.10 e superiores"
=== "Python 3.10+"
```Python hl_lines="8"
{!> ../../../docs_src/path_params_numeric_validations/tutorial001_py310.py!}
```
=== "Python 3.6+"
```Python hl_lines="10"
{!> ../../../docs_src/path_params_numeric_validations/tutorial001.py!}
```
!!! note "Nota"
Um parâmetro de rota é sempre obrigatório, como se fizesse parte da rota.

View File

@@ -63,18 +63,18 @@ Os valores dos parâmetros na sua função serão:
Da mesma forma, você pode declarar parâmetros de consulta opcionais, definindo o valor padrão para `None`:
=== "Python 3.6 and above"
```Python hl_lines="9"
{!> ../../../docs_src/query_params/tutorial002.py!}
```
=== "Python 3.10 and above"
=== "Python 3.10+"
```Python hl_lines="7"
{!> ../../../docs_src/query_params/tutorial002_py310.py!}
```
=== "Python 3.6+"
```Python hl_lines="9"
{!> ../../../docs_src/query_params/tutorial002.py!}
```
Nesse caso, o parâmetro da função `q` será opcional, e `None` será o padrão.
!!! check "Verificar"
@@ -85,18 +85,18 @@ Nesse caso, o parâmetro da função `q` será opcional, e `None` será o padrã
Você também pode declarar tipos `bool`, e eles serão convertidos:
=== "Python 3.6 and above"
```Python hl_lines="9"
{!> ../../../docs_src/query_params/tutorial003.py!}
```
=== "Python 3.10 and above"
=== "Python 3.10+"
```Python hl_lines="7"
{!> ../../../docs_src/query_params/tutorial003_py310.py!}
```
=== "Python 3.6+"
```Python hl_lines="9"
{!> ../../../docs_src/query_params/tutorial003.py!}
```
Nesse caso, se você for para:
```
@@ -137,18 +137,18 @@ E você não precisa declarar eles em nenhuma ordem específica.
Eles serão detectados pelo nome:
=== "Python 3.6 and above"
```Python hl_lines="8 10"
{!> ../../../docs_src/query_params/tutorial004.py!}
```
=== "Python 3.10 and above"
=== "Python 3.10+"
```Python hl_lines="6 8"
{!> ../../../docs_src/query_params/tutorial004_py310.py!}
```
=== "Python 3.6+"
```Python hl_lines="8 10"
{!> ../../../docs_src/query_params/tutorial004.py!}
```
## Parâmetros de consulta obrigatórios
Quando você declara um valor padrão para parâmetros que não são de rota (até agora, nós vimos apenas parâmetros de consulta), então eles não são obrigatórios.
@@ -203,17 +203,18 @@ http://127.0.0.1:8000/items/foo-item?needy=sooooneedy
E claro, você pode definir alguns parâmetros como obrigatórios, alguns possuindo um valor padrão, e outros sendo totalmente opcionais:
=== "Python 3.6 and above"
=== "Python 3.10+"
```Python hl_lines="8"
{!> ../../../docs_src/query_params/tutorial006_py310.py!}
```
=== "Python 3.6+"
```Python hl_lines="10"
{!> ../../../docs_src/query_params/tutorial006.py!}
```
=== "Python 3.10 and above"
```Python hl_lines="8"
{!> ../../../docs_src/query_params/tutorial006_py310.py!}
```
Nesse caso, existem 3 parâmetros de consulta:
* `needy`, um `str` obrigatório.

View File

@@ -45,6 +45,7 @@ nav:
- fa: /fa/
- fr: /fr/
- he: /he/
- hy: /hy/
- id: /id/
- it: /it/
- ja: /ja/
@@ -55,6 +56,7 @@ nav:
- ru: /ru/
- sq: /sq/
- sv: /sv/
- ta: /ta/
- tr: /tr/
- uk: /uk/
- zh: /zh/
@@ -117,7 +119,7 @@ markdown_extensions:
extra:
analytics:
provider: google
property: UA-133183413-1
property: G-YNEVN69SC3
social:
- icon: fontawesome/brands/github-alt
link: https://github.com/tiangolo/fastapi
@@ -148,6 +150,8 @@ extra:
name: fr - français
- link: /he/
name: he
- link: /hy/
name: hy
- link: /id/
name: id
- link: /it/
@@ -168,6 +172,8 @@ extra:
name: sq - shqip
- link: /sv/
name: sv - svenska
- link: /ta/
name: ta - தமிழ்
- link: /tr/
name: tr - Türkçe
- link: /uk/

View File

@@ -0,0 +1,77 @@
# История создания и дальнейшее развитие
Однажды, <a href="https://github.com/tiangolo/fastapi/issues/3#issuecomment-454956920" class="external-link" target="_blank">один из пользователей **FastAPI** задал вопрос</a>:
> Какова история этого проекта? Создаётся впечатление, что он явился из ниоткуда и завоевал мир за несколько недель [...]
Что ж, вот небольшая часть истории проекта.
## Альтернативы
В течение нескольких лет я, возглавляя различные команды разработчиков, создавал довольно сложные API для машинного обучения, распределённых систем, асинхронных задач, баз данных NoSQL и т.д.
В рамках работы над этими проектами я исследовал, проверял и использовал многие фреймворки.
Во многом история **FastAPI** - история его предшественников.
Как написано в разделе [Альтернативы](alternatives.md){.internal-link target=_blank}:
<blockquote markdown="1">
**FastAPI** не существовал бы, если б не было более ранних работ других людей.
Они создали большое количество инструментов, которые и вдохновили меня на создание **FastAPI**.
Я всячески избегал создания нового фреймворка в течение нескольких лет. Сначала я пытался собрать все нужные возможности, которые ныне есть в **FastAPI**, используя множество различных фреймворков, плагинов и инструментов.
Но в какой-то момент не осталось другого выбора, кроме как создать что-то, что предоставляло бы все эти возможности сразу. Взять самые лучшие идеи из предыдущих инструментов и, используя введённые в Python подсказки типов (которых не было до версии 3.6), объединить их.
</blockquote>
## Исследования
Благодаря опыту использования существующих альтернатив, мы с коллегами изучили их основные идеи и скомбинировали собранные знания наилучшим образом.
Например, стало ясно, что необходимо брать за основу стандартные подсказки типов Python, а самым лучшим подходом является использование уже существующих стандартов.
Итак, прежде чем приступить к написанию **FastAPI**, я потратил несколько месяцев на изучение OpenAPI, JSON Schema, OAuth2, и т.п. для понимания их взаимосвязей, совпадений и различий.
## Дизайн
Затем я потратил некоторое время на придумывание "API" разработчика, который я хотел иметь как пользователь (как разработчик, использующий FastAPI).
Я проверил несколько идей на самых популярных редакторах кода среди Python-разработчиков: PyCharm, VS Code, Jedi.
Данные по редакторам я взял из <a href="https://www.jetbrains.com/research/python-developers-survey-2018/#development-tools" class="external-link" target="_blank">опроса Python-разработчиков</a>, который охватываает около 80% пользователей.
Это означает, что **FastAPI** был специально проверен на редакторах, используемых 80% Python-разработчиками. И поскольку большинство других редакторов, как правило, работают аналогичным образом, все его преимущества должны работать практически для всех редакторов.
Таким образом, я смог найти наилучшие способы сократить дублирование кода, обеспечить повсеместное автодополнение, проверку типов и ошибок и т.д.
И все это, чтобы все пользователи могли получать наилучший опыт разработки.
## Зависимости
Протестировав несколько вариантов, я решил, что в качестве основы буду использовать <a href="https://pydantic-docs.helpmanual.io/" class="external-link" target="_blank">**Pydantic**</a> и его преимущества.
По моим предложениям был изменён код этого фреймворка, чтобы сделать его полностью совместимым с JSON Schema, поддержать различные способы определения ограничений и улучшить помощь редакторов (проверки типов, автозаполнение).
В то же время, я принимал участие в разработке <a href="https://www.starlette.io/" class="external-link" target="_blank">**Starlette**</a>, ещё один из основных компонентов FastAPI.
## Разработка
К тому времени, когда я начал создавать **FastAPI**, большинство необходимых деталей уже существовало, дизайн был определён, зависимости и прочие инструменты были готовы, а знания о стандартах и спецификациях были четкими и свежими.
## Будущее
Сейчас уже ясно, что **FastAPI** со своими идеями стал полезен многим людям.
При сравнении с альтернативами, выбор падает на него, поскольку он лучше подходит для множества вариантов использования.
Многие разработчики и команды уже используют **FastAPI** в своих проектах (включая меня и мою команду).
Но, тем не менее, грядёт добавление ещё многих улучшений и возможностей.
У **FastAPI** великое будущее.
И [ваш вклад в это](help-fastapi.md){.internal-link target=_blank} - очень ценнен.

View File

@@ -57,18 +57,18 @@
**FastAPI** знает, что нужно сделать в каждом случае и как переиспользовать тот же объект `BackgroundTasks`, так чтобы все фоновые задачи собрались и запустились вместе в фоне:
=== "Python 3.6 и выше"
```Python hl_lines="13 15 22 25"
{!> ../../../docs_src/background_tasks/tutorial002.py!}
```
=== "Python 3.10 и выше"
=== "Python 3.10+"
```Python hl_lines="11 13 20 23"
{!> ../../../docs_src/background_tasks/tutorial002_py310.py!}
```
=== "Python 3.6+"
```Python hl_lines="13 15 22 25"
{!> ../../../docs_src/background_tasks/tutorial002.py!}
```
В этом примере сообщения будут записаны в `log.txt` *после* того, как ответ сервера был отправлен.
Если бы в запросе была очередь `q`, она бы первой записалась в `log.txt` фоновой задачей (потому что вызывается в зависимости `get_query`).

View File

@@ -6,18 +6,18 @@
Сначала вы должны импортировать его:
=== "Python 3.6 и выше"
```Python hl_lines="4"
{!> ../../../docs_src/body_fields/tutorial001.py!}
```
=== "Python 3.10 и выше"
=== "Python 3.10+"
```Python hl_lines="2"
{!> ../../../docs_src/body_fields/tutorial001_py310.py!}
```
=== "Python 3.6+"
```Python hl_lines="4"
{!> ../../../docs_src/body_fields/tutorial001.py!}
```
!!! warning "Внимание"
Обратите внимание, что функция `Field` импортируется непосредственно из `pydantic`, а не из `fastapi`, как все остальные функции (`Query`, `Path`, `Body` и т.д.).
@@ -25,18 +25,18 @@
Вы можете использовать функцию `Field` с атрибутами модели:
=== "Python 3.6 и выше"
```Python hl_lines="11-14"
{!> ../../../docs_src/body_fields/tutorial001.py!}
```
=== "Python 3.10 и выше"
=== "Python 3.10+"
```Python hl_lines="9-12"
{!> ../../../docs_src/body_fields/tutorial001_py310.py!}
```
=== "Python 3.6+"
```Python hl_lines="11-14"
{!> ../../../docs_src/body_fields/tutorial001.py!}
```
Функция `Field` работает так же, как `Query`, `Path` и `Body`, у ее такие же параметры и т.д.
!!! note "Технические детали"

View File

@@ -6,36 +6,36 @@
Сначала импортируйте `Cookie`:
=== "Python 3.6 и выше"
```Python hl_lines="3"
{!> ../../../docs_src/cookie_params/tutorial001.py!}
```
=== "Python 3.10 и выше"
=== "Python 3.10+"
```Python hl_lines="1"
{!> ../../../docs_src/cookie_params/tutorial001_py310.py!}
```
=== "Python 3.6+"
```Python hl_lines="3"
{!> ../../../docs_src/cookie_params/tutorial001.py!}
```
## Объявление параметров `Cookie`
Затем объявляйте параметры cookie, используя ту же структуру, что и с `Path` и `Query`.
Первое значение - это значение по умолчанию, вы можете передать все дополнительные параметры проверки или аннотации:
=== "Python 3.6 и выше"
```Python hl_lines="9"
{!> ../../../docs_src/cookie_params/tutorial001.py!}
```
=== "Python 3.10 и выше"
=== "Python 3.10+"
```Python hl_lines="7"
{!> ../../../docs_src/cookie_params/tutorial001_py310.py!}
```
=== "Python 3.6+"
```Python hl_lines="9"
{!> ../../../docs_src/cookie_params/tutorial001.py!}
```
!!! note "Технические детали"
`Cookie` - это класс, родственный `Path` и `Query`. Он также наследуется от общего класса `Param`.

View File

@@ -45,6 +45,7 @@ nav:
- fa: /fa/
- fr: /fr/
- he: /he/
- hy: /hy/
- id: /id/
- it: /it/
- ja: /ja/
@@ -55,6 +56,7 @@ nav:
- ru: /ru/
- sq: /sq/
- sv: /sv/
- ta: /ta/
- tr: /tr/
- uk: /uk/
- zh: /zh/
@@ -69,6 +71,7 @@ nav:
- Развёртывание:
- deployment/index.md
- deployment/versions.md
- history-design-future.md
- external-links.md
- contributing.md
markdown_extensions:
@@ -93,7 +96,7 @@ markdown_extensions:
extra:
analytics:
provider: google
property: UA-133183413-1
property: G-YNEVN69SC3
social:
- icon: fontawesome/brands/github-alt
link: https://github.com/tiangolo/fastapi
@@ -124,6 +127,8 @@ extra:
name: fr - français
- link: /he/
name: he
- link: /hy/
name: hy
- link: /id/
name: id
- link: /it/
@@ -144,6 +149,8 @@ extra:
name: sq - shqip
- link: /sv/
name: sv - svenska
- link: /ta/
name: ta - தமிழ்
- link: /tr/
name: tr - Türkçe
- link: /uk/

View File

@@ -45,6 +45,7 @@ nav:
- fa: /fa/
- fr: /fr/
- he: /he/
- hy: /hy/
- id: /id/
- it: /it/
- ja: /ja/
@@ -55,6 +56,7 @@ nav:
- ru: /ru/
- sq: /sq/
- sv: /sv/
- ta: /ta/
- tr: /tr/
- uk: /uk/
- zh: /zh/
@@ -80,7 +82,7 @@ markdown_extensions:
extra:
analytics:
provider: google
property: UA-133183413-1
property: G-YNEVN69SC3
social:
- icon: fontawesome/brands/github-alt
link: https://github.com/tiangolo/fastapi
@@ -111,6 +113,8 @@ extra:
name: fr - français
- link: /he/
name: he
- link: /hy/
name: hy
- link: /id/
name: id
- link: /it/
@@ -131,6 +135,8 @@ extra:
name: sq - shqip
- link: /sv/
name: sv - svenska
- link: /ta/
name: ta - தமிழ்
- link: /tr/
name: tr - Türkçe
- link: /uk/

View File

@@ -45,6 +45,7 @@ nav:
- fa: /fa/
- fr: /fr/
- he: /he/
- hy: /hy/
- id: /id/
- it: /it/
- ja: /ja/
@@ -55,6 +56,7 @@ nav:
- ru: /ru/
- sq: /sq/
- sv: /sv/
- ta: /ta/
- tr: /tr/
- uk: /uk/
- zh: /zh/
@@ -80,7 +82,7 @@ markdown_extensions:
extra:
analytics:
provider: google
property: UA-133183413-1
property: G-YNEVN69SC3
social:
- icon: fontawesome/brands/github-alt
link: https://github.com/tiangolo/fastapi
@@ -111,6 +113,8 @@ extra:
name: fr - français
- link: /he/
name: he
- link: /hy/
name: hy
- link: /id/
name: id
- link: /it/
@@ -131,6 +135,8 @@ extra:
name: sq - shqip
- link: /sv/
name: sv - svenska
- link: /ta/
name: ta - தமிழ்
- link: /tr/
name: tr - Türkçe
- link: /uk/

148
docs/ta/mkdocs.yml Normal file
View File

@@ -0,0 +1,148 @@
site_name: FastAPI
site_description: FastAPI framework, high performance, easy to learn, fast to code, ready for production
site_url: https://fastapi.tiangolo.com/ta/
theme:
name: material
custom_dir: overrides
palette:
- media: '(prefers-color-scheme: light)'
scheme: default
primary: teal
accent: amber
toggle:
icon: material/lightbulb
name: Switch to light mode
- media: '(prefers-color-scheme: dark)'
scheme: slate
primary: teal
accent: amber
toggle:
icon: material/lightbulb-outline
name: Switch to dark mode
features:
- search.suggest
- search.highlight
- content.tabs.link
icon:
repo: fontawesome/brands/github-alt
logo: https://fastapi.tiangolo.com/img/icon-white.svg
favicon: https://fastapi.tiangolo.com/img/favicon.png
language: en
repo_name: tiangolo/fastapi
repo_url: https://github.com/tiangolo/fastapi
edit_uri: ''
plugins:
- search
- markdownextradata:
data: data
nav:
- FastAPI: index.md
- Languages:
- en: /
- az: /az/
- de: /de/
- es: /es/
- fa: /fa/
- fr: /fr/
- he: /he/
- id: /id/
- it: /it/
- ja: /ja/
- ko: /ko/
- nl: /nl/
- pl: /pl/
- pt: /pt/
- ru: /ru/
- sq: /sq/
- sv: /sv/
- ta: /ta/
- tr: /tr/
- uk: /uk/
- zh: /zh/
markdown_extensions:
- toc:
permalink: true
- markdown.extensions.codehilite:
guess_lang: false
- mdx_include:
base_path: docs
- admonition
- codehilite
- extra
- pymdownx.superfences:
custom_fences:
- name: mermaid
class: mermaid
format: !!python/name:pymdownx.superfences.fence_code_format ''
- pymdownx.tabbed:
alternate_style: true
- attr_list
- md_in_html
extra:
analytics:
provider: google
property: UA-133183413-1
social:
- icon: fontawesome/brands/github-alt
link: https://github.com/tiangolo/fastapi
- icon: fontawesome/brands/discord
link: https://discord.gg/VQjSZaeJmf
- icon: fontawesome/brands/twitter
link: https://twitter.com/fastapi
- icon: fontawesome/brands/linkedin
link: https://www.linkedin.com/in/tiangolo
- icon: fontawesome/brands/dev
link: https://dev.to/tiangolo
- icon: fontawesome/brands/medium
link: https://medium.com/@tiangolo
- icon: fontawesome/solid/globe
link: https://tiangolo.com
alternate:
- link: /
name: en - English
- link: /az/
name: az
- link: /de/
name: de
- link: /es/
name: es - español
- link: /fa/
name: fa
- link: /fr/
name: fr - français
- link: /he/
name: he
- link: /id/
name: id
- link: /it/
name: it - italiano
- link: /ja/
name: ja - 日本語
- link: /ko/
name: ko - 한국어
- link: /nl/
name: nl
- link: /pl/
name: pl
- link: /pt/
name: pt - português
- link: /ru/
name: ru - русский язык
- link: /sq/
name: sq - shqip
- link: /sv/
name: sv - svenska
- link: /ta/
name: ta - தமிழ்
- link: /tr/
name: tr - Türkçe
- link: /uk/
name: uk - українська мова
- link: /zh/
name: zh - 汉语
extra_css:
- https://fastapi.tiangolo.com/css/termynal.css
- https://fastapi.tiangolo.com/css/custom.css
extra_javascript:
- https://fastapi.tiangolo.com/js/termynal.js
- https://fastapi.tiangolo.com/js/custom.js

0
docs/ta/overrides/.gitignore vendored Normal file
View File

View File

@@ -45,6 +45,7 @@ nav:
- fa: /fa/
- fr: /fr/
- he: /he/
- hy: /hy/
- id: /id/
- it: /it/
- ja: /ja/
@@ -55,6 +56,7 @@ nav:
- ru: /ru/
- sq: /sq/
- sv: /sv/
- ta: /ta/
- tr: /tr/
- uk: /uk/
- zh: /zh/
@@ -85,7 +87,7 @@ markdown_extensions:
extra:
analytics:
provider: google
property: UA-133183413-1
property: G-YNEVN69SC3
social:
- icon: fontawesome/brands/github-alt
link: https://github.com/tiangolo/fastapi
@@ -116,6 +118,8 @@ extra:
name: fr - français
- link: /he/
name: he
- link: /hy/
name: hy
- link: /id/
name: id
- link: /it/
@@ -136,6 +140,8 @@ extra:
name: sq - shqip
- link: /sv/
name: sv - svenska
- link: /ta/
name: ta - தமிழ்
- link: /tr/
name: tr - Türkçe
- link: /uk/

View File

@@ -45,6 +45,7 @@ nav:
- fa: /fa/
- fr: /fr/
- he: /he/
- hy: /hy/
- id: /id/
- it: /it/
- ja: /ja/
@@ -55,6 +56,7 @@ nav:
- ru: /ru/
- sq: /sq/
- sv: /sv/
- ta: /ta/
- tr: /tr/
- uk: /uk/
- zh: /zh/
@@ -80,7 +82,7 @@ markdown_extensions:
extra:
analytics:
provider: google
property: UA-133183413-1
property: G-YNEVN69SC3
social:
- icon: fontawesome/brands/github-alt
link: https://github.com/tiangolo/fastapi
@@ -111,6 +113,8 @@ extra:
name: fr - français
- link: /he/
name: he
- link: /hy/
name: hy
- link: /id/
name: id
- link: /it/
@@ -131,6 +135,8 @@ extra:
name: sq - shqip
- link: /sv/
name: sv - svenska
- link: /ta/
name: ta - தமிழ்
- link: /tr/
name: tr - Türkçe
- link: /uk/

View File

@@ -6,18 +6,18 @@
在前面的例子中, 我们从依赖项 ("可依赖对象") 中返回了一个 `dict`:
=== "Python 3.6 以及 以上"
```Python hl_lines="9"
{!> ../../../docs_src/dependencies/tutorial001.py!}
```
=== "Python 3.10 以及以上"
=== "Python 3.10+"
```Python hl_lines="7"
{!> ../../../docs_src/dependencies/tutorial001_py310.py!}
```
=== "Python 3.6+"
```Python hl_lines="9"
{!> ../../../docs_src/dependencies/tutorial001.py!}
```
但是后面我们在路径操作函数的参数 `commons` 中得到了一个 `dict`。
我们知道编辑器不能为 `dict` 提供很多支持(比如补全),因为编辑器不知道 `dict` 的键和值类型。
@@ -79,46 +79,46 @@ fluffy = Cat(name="Mr Fluffy")
所以,我们可以将上面的依赖项 "可依赖对象" `common_parameters` 更改为类 `CommonQueryParams`:
=== "Python 3.6 以及 以上"
```Python hl_lines="11-15"
{!> ../../../docs_src/dependencies/tutorial002.py!}
```
=== "Python 3.10 以及 以上"
=== "Python 3.10+"
```Python hl_lines="9-13"
{!> ../../../docs_src/dependencies/tutorial002_py310.py!}
```
注意用于创建类实例的 `__init__` 方法:
=== "Python 3.6+"
=== "Python 3.6 以及 以上"
```Python hl_lines="12"
```Python hl_lines="11-15"
{!> ../../../docs_src/dependencies/tutorial002.py!}
```
=== "Python 3.10 以及 以上"
注意用于创建类实例的 `__init__` 方法:
=== "Python 3.10+"
```Python hl_lines="10"
{!> ../../../docs_src/dependencies/tutorial002_py310.py!}
```
...它与我们以前的 `common_parameters` 具有相同的参数:
=== "Python 3.6+"
=== "Python 3.6 以及 以上"
```Python hl_lines="9"
{!> ../../../docs_src/dependencies/tutorial001.py!}
```Python hl_lines="12"
{!> ../../../docs_src/dependencies/tutorial002.py!}
```
=== "Python 3.10 以及 以上"
...它与我们以前的 `common_parameters` 具有相同的参数:
=== "Python 3.10+"
```Python hl_lines="6"
{!> ../../../docs_src/dependencies/tutorial001_py310.py!}
```
=== "Python 3.6+"
```Python hl_lines="9"
{!> ../../../docs_src/dependencies/tutorial001.py!}
```
这些参数就是 **FastAPI** 用来 "处理" 依赖项的。
在两个例子下,都有:
@@ -133,18 +133,18 @@ fluffy = Cat(name="Mr Fluffy")
现在,您可以使用这个类来声明你的依赖项了。
=== "Python 3.6 以及 以上"
```Python hl_lines="19"
{!> ../../../docs_src/dependencies/tutorial002.py!}
```
=== "Python 3.10 以及 以上"
=== "Python 3.10+"
```Python hl_lines="17"
{!> ../../../docs_src/dependencies/tutorial002_py310.py!}
```
=== "Python 3.6+"
```Python hl_lines="19"
{!> ../../../docs_src/dependencies/tutorial002.py!}
```
**FastAPI** 调用 `CommonQueryParams` 类。这将创建该类的一个 "实例",该实例将作为参数 `commons` 被传递给你的函数。
## 类型注解 vs `Depends`
@@ -183,18 +183,18 @@ commons = Depends(CommonQueryParams)
..就像:
=== "Python 3.6 以及 以上"
```Python hl_lines="19"
{!> ../../../docs_src/dependencies/tutorial003.py!}
```
=== "Python 3.10 以及 以上"
=== "Python 3.10+"
```Python hl_lines="17"
{!> ../../../docs_src/dependencies/tutorial003_py310.py!}
```
=== "Python 3.6+"
```Python hl_lines="19"
{!> ../../../docs_src/dependencies/tutorial003.py!}
```
但是声明类型是被鼓励的,因为那样你的编辑器就会知道将传递什么作为参数 `commons` ,然后它可以帮助你完成代码,类型检查,等等:
<img src="/img/tutorial/dependencies/image02.png">
@@ -227,18 +227,18 @@ commons: CommonQueryParams = Depends()
同样的例子看起来像这样:
=== "Python 3.6 以及 以上"
```Python hl_lines="19"
{!> ../../../docs_src/dependencies/tutorial004.py!}
```
=== "Python 3.10 以及 以上"
=== "Python 3.10+"
```Python hl_lines="17"
{!> ../../../docs_src/dependencies/tutorial004_py310.py!}
```
=== "Python 3.6+"
```Python hl_lines="19"
{!> ../../../docs_src/dependencies/tutorial004.py!}
```
... **FastAPI** 会知道怎么处理。
!!! tip

View File

@@ -20,18 +20,18 @@
它接收一个对象比如Pydantic模型并会返回一个JSON兼容的版本
=== "Python 3.6 and above"
```Python hl_lines="5 22"
{!> ../../../docs_src/encoder/tutorial001.py!}
```
=== "Python 3.10 and above"
=== "Python 3.10+"
```Python hl_lines="4 21"
{!> ../../../docs_src/encoder/tutorial001_py310.py!}
```
=== "Python 3.6+"
```Python hl_lines="5 22"
{!> ../../../docs_src/encoder/tutorial001.py!}
```
在这个例子中它将Pydantic模型转换为`dict`,并将`datetime`转换为`str`。
调用它的结果后就可以使用Python标准编码中的<a href="https://docs.python.org/3/library/json.html#json.dumps" class="external-link" target="_blank">`json.dumps()`</a>。

View File

@@ -124,18 +124,18 @@ contents = myfile.file.read()
您可以通过使用标准类型注解并将 None 作为默认值的方式将一个文件参数设为可选:
=== "Python 3.6 及以上版本"
```Python hl_lines="9 17"
{!> ../../../docs_src/request_files/tutorial001_02.py!}
```
=== "Python 3.9 及以上版本"
=== "Python 3.9+"
```Python hl_lines="7 14"
{!> ../../../docs_src/request_files/tutorial001_02_py310.py!}
```
=== "Python 3.6+"
```Python hl_lines="9 17"
{!> ../../../docs_src/request_files/tutorial001_02.py!}
```
## 带有额外元数据的 `UploadFile`
您也可以将 `File()` 与 `UploadFile` 一起使用,例如,设置额外的元数据:
@@ -152,18 +152,18 @@ FastAPI 支持同时上传多个文件。
上传多个文件时,要声明含 `bytes` 或 `UploadFile` 的列表(`List`
=== "Python 3.6 及以上版本"
```Python hl_lines="10 15"
{!> ../../../docs_src/request_files/tutorial002.py!}
```
=== "Python 3.9 及以上版本"
=== "Python 3.9+"
```Python hl_lines="8 13"
{!> ../../../docs_src/request_files/tutorial002_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="10 15"
{!> ../../../docs_src/request_files/tutorial002.py!}
```
接收的也是含 `bytes` 或 `UploadFile` 的列表(`list`)。
@@ -177,18 +177,18 @@ FastAPI 支持同时上传多个文件。
和之前的方式一样, 您可以为 `File()` 设置额外参数, 即使是 `UploadFile`:
=== "Python 3.6 及以上版本"
```Python hl_lines="18"
{!> ../../../docs_src/request_files/tutorial003.py!}
```
=== "Python 3.9 及以上版本"
=== "Python 3.9+"
```Python hl_lines="16"
{!> ../../../docs_src/request_files/tutorial003_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="18"
{!> ../../../docs_src/request_files/tutorial003.py!}
```
## 小结
本节介绍了如何用 `File` 把上传文件声明为(表单数据的)输入参数。

View File

@@ -246,22 +246,22 @@ connect_args={"check_same_thread": False}
但是为了安全起见,`password`不会出现在其他同类 Pydantic*模型*中,例如用户请求时不应该从 API 返回响应中包含它。
=== "Python 3.6 及以上版本"
=== "Python 3.10+"
```Python hl_lines="3 6-8 11-12 23-24 27-28"
{!> ../../../docs_src/sql_databases/sql_app/schemas.py!}
```Python hl_lines="1 4-6 9-10 21-22 25-26"
{!> ../../../docs_src/sql_databases/sql_app_py310/schemas.py!}
```
=== "Python 3.9 及以上版本"
=== "Python 3.9+"
```Python hl_lines="3 6-8 11-12 23-24 27-28"
{!> ../../../docs_src/sql_databases/sql_app_py39/schemas.py!}
```
=== "Python 3.10 及以上版本"
=== "Python 3.6+"
```Python hl_lines="1 4-6 9-10 21-22 25-26"
{!> ../../../docs_src/sql_databases/sql_app_py310/schemas.py!}
```Python hl_lines="3 6-8 11-12 23-24 27-28"
{!> ../../../docs_src/sql_databases/sql_app/schemas.py!}
```
#### SQLAlchemy 风格和 Pydantic 风格
@@ -290,22 +290,22 @@ name: str
不仅是这些项目的 ID还有我们在 Pydantic*模型*中定义的用于读取项目的所有数据:`Item`.
=== "Python 3.6 及以上版本"
=== "Python 3.10+"
```Python hl_lines="15-17 31-34"
{!> ../../../docs_src/sql_databases/sql_app/schemas.py!}
```Python hl_lines="13-15 29-32"
{!> ../../../docs_src/sql_databases/sql_app_py310/schemas.py!}
```
=== "Python 3.9 及以上版本"
=== "Python 3.9+"
```Python hl_lines="15-17 31-34"
{!> ../../../docs_src/sql_databases/sql_app_py39/schemas.py!}
```
=== "Python 3.10 及以上版本"
=== "Python 3.6+"
```Python hl_lines="13-15 29-32"
{!> ../../../docs_src/sql_databases/sql_app_py310/schemas.py!}
```Python hl_lines="15-17 31-34"
{!> ../../../docs_src/sql_databases/sql_app/schemas.py!}
```
!!! tip
@@ -319,22 +319,22 @@ name: str
在`Config`类中,设置属性`orm_mode = True`。
=== "Python 3.6 及以上版本"
=== "Python 3.10+"
```Python hl_lines="15 19-20 31 36-37"
{!> ../../../docs_src/sql_databases/sql_app/schemas.py!}
```Python hl_lines="13 17-18 29 34-35"
{!> ../../../docs_src/sql_databases/sql_app_py310/schemas.py!}
```
=== "Python 3.9 及以上版本"
=== "Python 3.9+"
```Python hl_lines="15 19-20 31 36-37"
{!> ../../../docs_src/sql_databases/sql_app_py39/schemas.py!}
```
=== "Python 3.10 及以上版本"
=== "Python 3.6+"
```Python hl_lines="13 17-18 29 34-35"
{!> ../../../docs_src/sql_databases/sql_app_py310/schemas.py!}
```Python hl_lines="15 19-20 31 36-37"
{!> ../../../docs_src/sql_databases/sql_app/schemas.py!}
```
!!! tip
@@ -465,18 +465,18 @@ current_user.items
以非常简单的方式创建数据库表:
=== "Python 3.6 及以上版本"
```Python hl_lines="9"
{!> ../../../docs_src/sql_databases/sql_app/main.py!}
```
=== "Python 3.9 及以上版本"
=== "Python 3.9+"
```Python hl_lines="7"
{!> ../../../docs_src/sql_databases/sql_app_py39/main.py!}
```
=== "Python 3.6+"
```Python hl_lines="9"
{!> ../../../docs_src/sql_databases/sql_app/main.py!}
```
#### Alembic 注意
通常你可能会使用 <a href="https://alembic.sqlalchemy.org/en/latest/" class="external-link" target="_blank">Alembic</a>,来进行格式化数据库(创建表等)。
@@ -499,18 +499,18 @@ current_user.items
我们的依赖项将创建一个新的 SQLAlchemy `SessionLocal`,它将在单个请求中使用,然后在请求完成后关闭它。
=== "Python 3.6 及以上版本"
```Python hl_lines="15-20"
{!> ../../../docs_src/sql_databases/sql_app/main.py!}
```
=== "Python 3.9 及以上版本"
=== "Python 3.9+"
```Python hl_lines="13-18"
{!> ../../../docs_src/sql_databases/sql_app_py39/main.py!}
```
=== "Python 3.6+"
```Python hl_lines="15-20"
{!> ../../../docs_src/sql_databases/sql_app/main.py!}
```
!!! info
我们将`SessionLocal()`请求的创建和处理放在一个`try`块中。
@@ -524,18 +524,18 @@ current_user.items
*这将为我们在路径操作函数*中提供更好的编辑器支持,因为编辑器将知道`db`参数的类型`Session`
=== "Python 3.6 及以上版本"
```Python hl_lines="24 32 38 47 53"
{!> ../../../docs_src/sql_databases/sql_app/main.py!}
```
=== "Python 3.9 及以上版本"
=== "Python 3.9+"
```Python hl_lines="22 30 36 45 51"
{!> ../../../docs_src/sql_databases/sql_app_py39/main.py!}
```
=== "Python 3.6+"
```Python hl_lines="24 32 38 47 53"
{!> ../../../docs_src/sql_databases/sql_app/main.py!}
```
!!! info "技术细节"
参数`db`实际上是 type `SessionLocal`,但是这个类(用 创建`sessionmaker()`)是 SQLAlchemy 的“代理” `Session`,所以,编辑器并不真正知道提供了哪些方法。
@@ -545,18 +545,18 @@ current_user.items
现在,到了最后,编写标准的**FastAPI** *路径操作*代码。
=== "Python 3.6 及以上版本"
```Python hl_lines="23-28 31-34 37-42 45-49 52-55"
{!> ../../../docs_src/sql_databases/sql_app/main.py!}
```
=== "Python 3.9 及以上版本"
=== "Python 3.9+"
```Python hl_lines="21-26 29-32 35-40 43-47 50-53"
{!> ../../../docs_src/sql_databases/sql_app_py39/main.py!}
```
=== "Python 3.6+"
```Python hl_lines="23-28 31-34 37-42 45-49 52-55"
{!> ../../../docs_src/sql_databases/sql_app/main.py!}
```
我们在依赖项中的每个请求之前利用`yield`创建数据库会话,然后关闭它。
所以我们就可以在*路径操作函数*中创建需要的依赖,就能直接获取会话。
@@ -638,22 +638,22 @@ def read_user(user_id: int, db: Session = Depends(get_db)):
* `sql_app/schemas.py`:
=== "Python 3.6 及以上版本"
=== "Python 3.10+"
```Python
{!> ../../../docs_src/sql_databases/sql_app/schemas.py!}
{!> ../../../docs_src/sql_databases/sql_app_py310/schemas.py!}
```
=== "Python 3.9 及以上版本"
=== "Python 3.9+"
```Python
{!> ../../../docs_src/sql_databases/sql_app_py39/schemas.py!}
```
=== "Python 3.10 及以上版本"
=== "Python 3.6+"
```Python
{!> ../../../docs_src/sql_databases/sql_app_py310/schemas.py!}
{!> ../../../docs_src/sql_databases/sql_app/schemas.py!}
```
* `sql_app/crud.py`:
@@ -664,18 +664,18 @@ def read_user(user_id: int, db: Session = Depends(get_db)):
* `sql_app/main.py`:
=== "Python 3.6 及以上版本"
```Python
{!> ../../../docs_src/sql_databases/sql_app/main.py!}
```
=== "Python 3.9 及以上版本"
=== "Python 3.9+"
```Python
{!> ../../../docs_src/sql_databases/sql_app_py39/main.py!}
```
=== "Python 3.6+"
```Python
{!> ../../../docs_src/sql_databases/sql_app/main.py!}
```
## 执行项目
您可以复制这些代码并按原样使用它。
@@ -723,18 +723,18 @@ $ uvicorn sql_app.main:app --reload
我们将添加中间件(只是一个函数)将为每个请求创建一个新的 SQLAlchemy`SessionLocal`,将其添加到请求中,然后在请求完成后关闭它。
=== "Python 3.6 及以上版本"
```Python hl_lines="14-22"
{!> ../../../docs_src/sql_databases/sql_app/alt_main.py!}
```
=== "Python 3.9 及以上版本"
=== "Python 3.9+"
```Python hl_lines="12-20"
{!> ../../../docs_src/sql_databases/sql_app_py39/alt_main.py!}
```
=== "Python 3.6+"
```Python hl_lines="14-22"
{!> ../../../docs_src/sql_databases/sql_app/alt_main.py!}
```
!!! info
我们将`SessionLocal()`请求的创建和处理放在一个`try`块中。

View File

@@ -45,6 +45,7 @@ nav:
- fa: /fa/
- fr: /fr/
- he: /he/
- hy: /hy/
- id: /id/
- it: /it/
- ja: /ja/
@@ -55,6 +56,7 @@ nav:
- ru: /ru/
- sq: /sq/
- sv: /sv/
- ta: /ta/
- tr: /tr/
- uk: /uk/
- zh: /zh/
@@ -137,7 +139,7 @@ markdown_extensions:
extra:
analytics:
provider: google
property: UA-133183413-1
property: G-YNEVN69SC3
social:
- icon: fontawesome/brands/github-alt
link: https://github.com/tiangolo/fastapi
@@ -168,6 +170,8 @@ extra:
name: fr - français
- link: /he/
name: he
- link: /hy/
name: hy
- link: /id/
name: id
- link: /it/
@@ -188,6 +192,8 @@ extra:
name: sq - shqip
- link: /sv/
name: sv - svenska
- link: /ta/
name: ta - தமிழ்
- link: /tr/
name: tr - Türkçe
- link: /uk/

View File

@@ -0,0 +1,26 @@
from typing import Union
from fastapi import Body, FastAPI, status
from fastapi.responses import JSONResponse
from typing_extensions import Annotated
app = FastAPI()
items = {"foo": {"name": "Fighters", "size": 6}, "bar": {"name": "Tenders", "size": 3}}
@app.put("/items/{item_id}")
async def upsert_item(
item_id: str,
name: Annotated[Union[str, None], Body()] = None,
size: Annotated[Union[int, None], Body()] = None,
):
if item_id in items:
item = items[item_id]
item["name"] = name
item["size"] = size
return item
else:
item = {"name": name, "size": size}
items[item_id] = item
return JSONResponse(status_code=status.HTTP_201_CREATED, content=item)

Some files were not shown because too many files have changed in this diff Show More