diff --git a/.github/DISCUSSION_TEMPLATE/translations.yml b/.github/DISCUSSION_TEMPLATE/translations.yml
new file mode 100644
index 0000000000..16e304d998
--- /dev/null
+++ b/.github/DISCUSSION_TEMPLATE/translations.yml
@@ -0,0 +1,45 @@
+labels: [lang-all]
+body:
+ - type: markdown
+ attributes:
+ value: |
+ Thanks for your interest in helping translate the FastAPI docs! 🌍
+
+ Please follow these instructions carefully to propose a new language translation. 🙏
+
+ This structured process helps ensure translations can be properly maintained long-term.
+ - type: checkboxes
+ id: checks
+ attributes:
+ label: Initial Checks
+ description: Please confirm and check all the following options.
+ options:
+ - label: I checked that this language is not already being translated in FastAPI docs.
+ required: true
+ - label: I searched existing discussions to ensure no one else proposed this language.
+ required: true
+ - label: I am a native speaker of the language I want to help translate.
+ required: true
+ - type: input
+ id: language
+ attributes:
+ label: Target Language
+ description: What language do you want to translate the FastAPI docs into?
+ placeholder: e.g. Latin
+ validations:
+ required: true
+ - type: textarea
+ id: additional_info
+ attributes:
+ label: Additional Information
+ description: Any other relevant information about your translation proposal
+ - type: markdown
+ attributes:
+ value: |
+ Translations are automatized with AI and then reviewed by native speakers. 🤖 🙋
+
+ This allows us to keep them consistent and up-to-date.
+
+ If there are several native speakers commenting on this discussion and
+ committing to help review new translations, the FastAPI team will review it
+ and potentially make it an official translation. 😎
diff --git a/.github/actions/notify-translations/Dockerfile b/.github/actions/notify-translations/Dockerfile
deleted file mode 100644
index b68b4bb1a2..0000000000
--- a/.github/actions/notify-translations/Dockerfile
+++ /dev/null
@@ -1,7 +0,0 @@
-FROM python:3.9
-
-RUN pip install httpx PyGithub "pydantic==1.5.1" "pyyaml>=5.3.1,<6.0.0"
-
-COPY ./app /app
-
-CMD ["python", "/app/main.py"]
diff --git a/.github/actions/notify-translations/action.yml b/.github/actions/notify-translations/action.yml
deleted file mode 100644
index c3579977c5..0000000000
--- a/.github/actions/notify-translations/action.yml
+++ /dev/null
@@ -1,10 +0,0 @@
-name: "Notify Translations"
-description: "Notify in the issue for a translation when there's a new PR available"
-author: "Sebastián Ramírez
-
+
@@ -46,24 +46,22 @@ The key features are:
-
-
-
-
+
-
-
-
-
-
+
-
-
-
+
+
+
+
+
+
-
+
+
+
@@ -91,13 +89,13 @@ The key features are:
"_I’m over the moon excited about **FastAPI**. It’s so fun!_"
-
fastapi dev main.py...jinja2 - Required if you want to use the default template configuration.
* python-multipart - Required if you want to support form "parsing", with `request.form()`.
-Used by FastAPI / Starlette:
+Used by FastAPI:
-* uvicorn - for the server that loads and serves your application. This includes `uvicorn[standard]`, which includes some dependencies (e.g. `uvloop`) needed for high performance serving.
-* `fastapi-cli` - to provide the `fastapi` command.
+* uvicorn - for the server that loads and serves your application. This includes `uvicorn[standard]`, which includes some dependencies (e.g. `uvloop`) needed for high performance serving.
+* `fastapi-cli[standard]` - to provide the `fastapi` command.
+ * This includes `fastapi-cloud-cli`, which allows you to deploy your FastAPI application to FastAPI Cloud.
### Without `standard` Dependencies
If you don't want to include the `standard` optional dependencies, you can install with `pip install fastapi` instead of `pip install "fastapi[standard]"`.
+### Without `fastapi-cloud-cli`
+
+If you want to install FastAPI with the standard dependencies but without the `fastapi-cloud-cli`, you can install with `pip install "fastapi[standard-no-fastapi-cloud-cli]"`.
+
### Additional Optional Dependencies
There are some additional dependencies you might want to install.
diff --git a/SECURITY.md b/SECURITY.md
index db412cf2c8..87e87e0ca1 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -16,7 +16,7 @@ You can learn more about [FastAPI versions and how to pin and upgrade them](http
If you think you found a vulnerability, and even if you are not sure about it, please report it right away by sending an email to: security@tiangolo.com. Please try to be as explicit as possible, describing all the steps and example code to reproduce the security issue.
-I (the author, [@tiangolo](https://twitter.com/tiangolo)) will review it thoroughly and get back to you.
+I (the author, [@tiangolo](https://x.com/tiangolo)) will review it thoroughly and get back to you.
## Public Discussions
diff --git a/docs/az/docs/index.md b/docs/az/docs/index.md
deleted file mode 100644
index b5d7f8f92f..0000000000
--- a/docs/az/docs/index.md
+++ /dev/null
@@ -1,467 +0,0 @@
-
-- FastAPI framework, yüksək məshuldarlı, öyrənməsi asan, çevik kodlama, istifadəyə hazırdır -
- - ---- - -**Sənədlər**: https://fastapi.tiangolo.com - -**Qaynaq Kodu**: https://github.com/fastapi/fastapi - ---- - -FastAPI Python ilə API yaratmaq üçün standart Python tip məsləhətlərinə əsaslanan, müasir, sürətli (yüksək performanslı) framework-dür. - -Əsas xüsusiyyətləri bunlardır: - -* **Sürətli**: Çox yüksək performans, **NodeJS** və **Go** səviyyəsində (Starlette və Pydantic-ə təşəkkürlər). [Ən sürətli Python frameworklərindən biridir](#performans). -* **Çevik kodlama**: Funksiyanallıqları inkişaf etdirmək sürətini təxminən 200%-dən 300%-ə qədər artırın. * -* **Daha az xəta**: İnsan (developer) tərəfindən törədilən səhvlərin təxminən 40% -ni azaldın. * -* **İntuitiv**: Əla redaktor dəstəyi. Hər yerdə otomatik tamamlama. Xətaları müəyyənləşdirməyə daha az vaxt sərf edəcəksiniz. -* **Asan**: İstifadəsi və öyrənilməsi asan olması üçün nəzərdə tutulmuşdur. Sənədləri oxumaq üçün daha az vaxt ayıracaqsınız. -* **Qısa**: Kod təkrarlanmasını minimuma endirin. Hər bir parametr tərifində birdən çox xüsusiyyət ilə və daha az səhvlə qarşılaşacaqsınız. -* **Güclü**: Avtomatik və interaktiv sənədlərlə birlikdə istifadəyə hazır kod əldə edə bilərsiniz. -* **Standartlara əsaslanan**: API-lar üçün açıq standartlara əsaslanır (və tam uyğun gəlir): OpenAPI (əvvəlki adı ilə Swagger) və JSON Schema. - -* Bu fikirlər daxili development komandasının hazırladıqları məhsulların sınaqlarına əsaslanır. - -## Sponsorlar - - - -{% if sponsors %} -{% for sponsor in sponsors.gold -%} -async def...uvicorn main:app --reload əmri haqqında...email-validator - e-poçtun yoxlanılması üçün.
-* pydantic-settings - parametrlərin idarə edilməsi üçün.
-* pydantic-extra-types - Pydantic ilə istifadə edilə bilən əlavə tiplər üçün.
-
-Starlette tərəfindən istifadə olunanlar:
-
-* httpx - Əgər `TestClient` strukturundan istifadə edəcəksinizsə, tələb olunur.
-* jinja2 - Standart şablon konfiqurasiyasından istifadə etmək istəyirsinizsə, tələb olunur.
-* python-multipart - `request.form()` ilə forma "çevirmə" dəstəyindən istifadə etmək istəyirsinizsə, tələb olunur.
-* itsdangerous - `SessionMiddleware` dəstəyi üçün tələb olunur.
-* pyyaml - `SchemaGenerator` dəstəyi üçün tələb olunur (Çox güman ki, FastAPI istifadə edərkən buna ehtiyacınız olmayacaq).
-* ujson - `UJSONResponse` istifadə etmək istəyirsinizsə, tələb olunur.
-
-Həm FastAPI, həm də Starlette tərəfindən istifadə olunur:
-
-* uvicorn - Yaratdığımız proqramı servis edəcək veb server kimi fəaliyyət göstərir.
-* orjson - `ORJSONResponse` istifadə edəcəksinizsə tələb olunur.
-
-Bütün bunları `pip install fastapi[all]` ilə quraşdıra bilərsiniz.
-
-## Lisenziya
-
-Bu layihə MIT lisenziyasının şərtlərinə əsasən lisenziyalaşdırılıb.
diff --git a/docs/az/docs/learn/index.md b/docs/az/docs/learn/index.md
deleted file mode 100644
index cc32108bf1..0000000000
--- a/docs/az/docs/learn/index.md
+++ /dev/null
@@ -1,5 +0,0 @@
-# Öyrən
-
-Burada **FastAPI** öyrənmək üçün giriş bölmələri və dərsliklər yer alır.
-
-Siz bunu kitab, kurs, FastAPI öyrənmək üçün rəsmi və tövsiyə olunan üsul hesab edə bilərsiniz. 😎
diff --git a/docs/az/mkdocs.yml b/docs/az/mkdocs.yml
deleted file mode 100644
index de18856f44..0000000000
--- a/docs/az/mkdocs.yml
+++ /dev/null
@@ -1 +0,0 @@
-INHERIT: ../en/mkdocs.yml
diff --git a/docs/bn/docs/index.md b/docs/bn/docs/index.md
deleted file mode 100644
index c882506ff8..0000000000
--- a/docs/bn/docs/index.md
+++ /dev/null
@@ -1,463 +0,0 @@
-
-- FastAPI উচ্চক্ষমতা সম্পন্ন, সহজে শেখার এবং দ্রুত কোড করে প্রোডাকশনের জন্য ফ্রামওয়ার্ক। -
- - ---- - -**নির্দেশিকা নথি**: https://fastapi.tiangolo.com - -**সোর্স কোড**: https://github.com/fastapi/fastapi - ---- - -FastAPI একটি আধুনিক, দ্রুত ( বেশি ক্ষমতা ) সম্পন্ন, Python 3.6+ দিয়ে API তৈরির জন্য স্ট্যান্ডার্ড পাইথন টাইপ ইঙ্গিত ভিত্তিক ওয়েব ফ্রেমওয়ার্ক। - -এর মূল বৈশিষ্ট্য গুলো হলঃ - -- **গতি**: এটি **NodeJS** এবং **Go** এর মত কার্যক্ষমতা সম্পন্ন (Starlette এবং Pydantic এর সাহায্যে)। [পাইথন এর দ্রুততম ফ্রেমওয়ার্ক গুলোর মধ্যে এটি একটি](#_11)। -- **দ্রুত কোড করা**:বৈশিষ্ট্য তৈরির গতি ২০০% থেকে ৩০০% বৃদ্ধি করে৷ \* -- **স্বল্প bugs**: মানুব (ডেভেলপার) সৃষ্ট ত্রুটির প্রায় ৪০% হ্রাস করে। \* -- **স্বজ্ঞাত**: দুর্দান্ত এডিটর সাহায্য Completion নামেও পরিচিত। দ্রুত ডিবাগ করা যায়। - -- **সহজ**: এটি এমন ভাবে সজানো হয়েছে যেন নির্দেশিকা নথি পড়ে সহজে শেখা এবং ব্যবহার করা যায়। -- **সংক্ষিপ্ত**: কোড পুনরাবৃত্তি কমানোর পাশাপাশি, bug কমায় এবং প্রতিটি প্যারামিটার ঘোষণা থেকে একাধিক ফিচার পাওয়া যায় । -- **জোরালো**: স্বয়ংক্রিয় ভাবে তৈরি ক্রিয়াশীল নির্দেশনা নথি (documentation) সহ উৎপাদন উপযোগি (Production-ready) কোড পাওয়া যায়। -- **মান-ভিত্তিক**: এর ভিত্তি OpenAPI (যা পুর্বে Swagger নামে পরিচিত ছিল) এবং JSON Schema এর আদর্শের মানের ওপর - -\* উৎপাদনমুখি এপ্লিকেশন বানানোর এক দল ডেভেলপার এর মতামত ভিত্তিক ফলাফল। - -## স্পনসর গণ - - - -{% if sponsors %} -{% for sponsor in sponsors.gold -%} -async def...uvicorn main:app --reload...email-validator - ইমেল যাচাইকরণের জন্য।
-
-স্টারলেট দ্বারা ব্যবহৃত:
-
-- httpx - আপনি যদি `TestClient` ব্যবহার করতে চান তাহলে আবশ্যক।
-- jinja2 - আপনি যদি প্রদত্ত টেমপ্লেট রূপরেখা ব্যবহার করতে চান তাহলে প্রয়োজন।
-- python-multipart - আপনি যদি ফর্ম সহায়তা করতে চান তাহলে প্রয়োজন "parsing", `request.form()` সহ।
-- itsdangerous - `SessionMiddleware` সহায়তার জন্য প্রয়োজন।
-- pyyaml - স্টারলেটের SchemaGenerator সাপোর্ট এর জন্য প্রয়োজন (আপনার সম্ভাবত FastAPI প্রয়োজন নেই)।
-- graphene - `GraphQLApp` সহায়তার জন্য প্রয়োজন।
-
-FastAPI / Starlette দ্বারা ব্যবহৃত:
-
-- uvicorn - সার্ভারের জন্য যা আপনার অ্যাপ্লিকেশন লোড করে এবং পরিবেশন করে।
-- orjson - আপনি `ORJSONResponse` ব্যবহার করতে চাইলে প্রয়োজন।
-- ujson - আপনি `UJSONResponse` ব্যবহার করতে চাইলে প্রয়োজন।
-
-আপনি এই সব ইনস্টল করতে পারেন `pip install fastapi[all]` দিয়ে.
-
-## লাইসেন্স
-
-এই প্রজেক্ট MIT লাইসেন্স নীতিমালার অধীনে শর্তায়িত।
diff --git a/docs/bn/docs/learn/index.md b/docs/bn/docs/learn/index.md
deleted file mode 100644
index 4e4c62038c..0000000000
--- a/docs/bn/docs/learn/index.md
+++ /dev/null
@@ -1,5 +0,0 @@
-# শিখুন
-
-এখানে **FastAPI** শিখার জন্য প্রাথমিক বিভাগগুলি এবং টিউটোরিয়ালগুলি রয়েছে।
-
-আপনি এটিকে একটি **বই**, একটি **কোর্স**, এবং FastAPI শিখার **অফিসিয়াল** এবং প্রস্তাবিত উপায় বিবেচনা করতে পারেন। 😎
diff --git a/docs/bn/docs/python-types.md b/docs/bn/docs/python-types.md
deleted file mode 100644
index d5304a65e3..0000000000
--- a/docs/bn/docs/python-types.md
+++ /dev/null
@@ -1,596 +0,0 @@
-# পাইথন এর টাইপ্স পরিচিতি
-
-Python-এ ঐচ্ছিক "টাইপ হিন্ট" (যা "টাইপ অ্যানোটেশন" নামেও পরিচিত) এর জন্য সাপোর্ট রয়েছে।
-
-এই **"টাইপ হিন্ট"** বা অ্যানোটেশনগুলি এক ধরণের বিশেষ সিনট্যাক্স যা একটি ভেরিয়েবলের টাইপ ঘোষণা করতে দেয়।
-
-ভেরিয়েবলগুলির জন্য টাইপ ঘোষণা করলে, এডিটর এবং টুলগুলি আপনাকে আরও ভালো সাপোর্ট দিতে পারে।
-
-এটি পাইথন টাইপ হিন্ট সম্পর্কে একটি দ্রুত **টিউটোরিয়াল / রিফ্রেশার** মাত্র। এটি **FastAPI** এর সাথে ব্যবহার করার জন্য শুধুমাত্র ন্যূনতম প্রয়োজনীয়তা কভার করে... যা আসলে খুব একটা বেশি না।
-
-**FastAPI** এই টাইপ হিন্টগুলির উপর ভিত্তি করে নির্মিত, যা এটিকে অনেক সুবিধা এবং লাভ প্রদান করে।
-
-তবে, আপনি যদি কখনো **FastAPI** ব্যবহার নাও করেন, তবুও এগুলি সম্পর্কে একটু শেখা আপনার উপকারে আসবে।
-
-/// note
-
-যদি আপনি একজন Python বিশেষজ্ঞ হন, এবং টাইপ হিন্ট সম্পর্কে সবকিছু জানেন, তাহলে পরবর্তী অধ্যায়ে চলে যান।
-
-///
-
-## প্রেরণা
-
-চলুন একটি সাধারণ উদাহরণ দিয়ে শুরু করি:
-
-```Python
-{!../../../docs_src/python_types/tutorial001.py!}
-```
-
-এই প্রোগ্রামটি কল করলে আউটপুট হয়:
-
-```
-John Doe
-```
-
-ফাংশনটি নিম্নলিখিত কাজ করে:
-
-* `first_name` এবং `last_name` নেয়।
-* প্রতিটির প্রথম অক্ষরকে `title()` ব্যবহার করে বড় হাতের অক্ষরে রূপান্তর করে।
-* তাদেরকে মাঝখানে একটি স্পেস দিয়ে concatenate করে।
-
-```Python hl_lines="2"
-{!../../../docs_src/python_types/tutorial001.py!}
-```
-
-### এটি সম্পাদনা করুন
-
-এটি একটি খুব সাধারণ প্রোগ্রাম।
-
-কিন্তু এখন কল্পনা করুন যে আপনি এটি শুরু থেকে লিখছিলেন।
-
-এক পর্যায়ে আপনি ফাংশনের সংজ্ঞা শুরু করেছিলেন, আপনার প্যারামিটারগুলি প্রস্তুত ছিল...
-
-কিন্তু তারপর আপনাকে "সেই method কল করতে হবে যা প্রথম অক্ষরকে বড় হাতের অক্ষরে রূপান্তর করে"।
-
-এটা কি `upper` ছিল? নাকি `uppercase`? `first_uppercase`? `capitalize`?
-
-তারপর, আপনি পুরোনো প্রোগ্রামারের বন্ধু, এডিটর অটোকমপ্লিশনের সাহায্যে নেওয়ার চেষ্টা করেন।
-
-আপনি ফাংশনের প্রথম প্যারামিটার `first_name` টাইপ করেন, তারপর একটি ডট (`.`) টাইপ করেন এবং `Ctrl+Space` চাপেন অটোকমপ্লিশন ট্রিগার করার জন্য।
-
-কিন্তু, দুর্ভাগ্যবশত, আপনি কিছুই উপযোগী পান না:
-
-
-
-### টাইপ যোগ করুন
-
-আসুন আগের সংস্করণ থেকে একটি লাইন পরিবর্তন করি।
-
-আমরা ঠিক এই অংশটি পরিবর্তন করব অর্থাৎ ফাংশনের প্যারামিটারগুলি, এইগুলি:
-
-```Python
- first_name, last_name
-```
-
-থেকে এইগুলি:
-
-```Python
- first_name: str, last_name: str
-```
-
-ব্যাস।
-
-এগুলিই "টাইপ হিন্ট":
-
-```Python hl_lines="1"
-{!../../../docs_src/python_types/tutorial002.py!}
-```
-
-এটি ডিফল্ট ভ্যালু ঘোষণা করার মত নয় যেমন:
-
-```Python
- first_name="john", last_name="doe"
-```
-
-এটি একটি ভিন্ন জিনিস।
-
-আমরা সমান (`=`) নয়, কোলন (`:`) ব্যবহার করছি।
-
-এবং টাইপ হিন্ট যোগ করা সাধারণত তেমন কিছু পরিবর্তন করে না যা টাইপ হিন্ট ছাড়াই ঘটত।
-
-কিন্তু এখন, কল্পনা করুন আপনি আবার সেই ফাংশন তৈরির মাঝখানে আছেন, কিন্তু টাইপ হিন্ট সহ।
-
-একই পর্যায়ে, আপনি অটোকমপ্লিট ট্রিগার করতে `Ctrl+Space` চাপেন এবং আপনি দেখতে পান:
-
-
-
-এর সাথে, আপনি অপশনগুলি দেখে, স্ক্রল করতে পারেন, যতক্ষণ না আপনি এমন একটি অপশন খুঁজে পান যা কিছু মনে পরিয়ে দেয়:
-
-
-
-## আরও প্রেরণা
-
-এই ফাংশনটি দেখুন, এটিতে ইতিমধ্যে টাইপ হিন্ট রয়েছে:
-
-```Python hl_lines="1"
-{!../../../docs_src/python_types/tutorial003.py!}
-```
-
-এডিটর ভেরিয়েবলগুলির টাইপ জানার কারণে, আপনি শুধুমাত্র অটোকমপ্লিশনই পান না, আপনি এরর চেকও পান:
-
-
-
-এখন আপনি জানেন যে আপনাকে এটি ঠিক করতে হবে, `age`-কে একটি স্ট্রিং হিসেবে রূপান্তর করতে `str(age)` ব্যবহার করতে হবে:
-
-```Python hl_lines="2"
-{!../../../docs_src/python_types/tutorial004.py!}
-```
-
-## টাইপ ঘোষণা
-
-আপনি এতক্ষন টাইপ হিন্ট ঘোষণা করার মূল স্থানটি দেখে ফেলেছেন-- ফাংশন প্যারামিটার হিসেবে।
-
-সাধারণত এটি **FastAPI** এর ক্ষেত্রেও একই।
-
-### সিম্পল টাইপ
-
-আপনি `str` ছাড়াও সমস্ত স্ট্যান্ডার্ড পাইথন টাইপ ঘোষণা করতে পারেন।
-
-উদাহরণস্বরূপ, আপনি এগুলো ব্যবহার করতে পারেন:
-
-* `int`
-* `float`
-* `bool`
-* `bytes`
-
-```Python hl_lines="1"
-{!../../../docs_src/python_types/tutorial005.py!}
-```
-
-### টাইপ প্যারামিটার সহ জেনেরিক টাইপ
-
-কিছু ডাটা স্ট্রাকচার অন্যান্য মান ধারণ করতে পারে, যেমন `dict`, `list`, `set` এবং `tuple`। এবং অভ্যন্তরীণ মানগুলোরও নিজেদের টাইপ থাকতে পারে।
-
-এই ধরনের টাইপগুলিকে বলা হয় "**জেনেরিক**" টাইপ এবং এগুলিকে তাদের অভ্যন্তরীণ টাইপগুলি সহ ঘোষণা করা সম্ভব।
-
-এই টাইপগুলি এবং অভ্যন্তরীণ টাইপগুলি ঘোষণা করতে, আপনি Python মডিউল `typing` ব্যবহার করতে পারেন। এটি বিশেষভাবে এই টাইপ হিন্টগুলি সমর্থন করার জন্য রয়েছে।
-
-#### Python এর নতুন সংস্করণ
-
-`typing` ব্যবহার করা সিনট্যাক্সটি Python 3.6 থেকে সর্বশেষ সংস্করণগুলি পর্যন্ত, অর্থাৎ Python 3.9, Python 3.10 ইত্যাদি সহ সকল সংস্করণের সাথে **সামঞ্জস্যপূর্ণ**।
-
-Python যত এগিয়ে যাচ্ছে, **নতুন সংস্করণগুলি** এই টাইপ অ্যানোটেশনগুলির জন্য তত উন্নত সাপোর্ট নিয়ে আসছে এবং অনেক ক্ষেত্রে আপনাকে টাইপ অ্যানোটেশন ঘোষণা করতে `typing` মডিউল ইম্পোর্ট এবং ব্যবহার করার প্রয়োজন হবে না।
-
-যদি আপনি আপনার প্রজেক্টের জন্য Python-এর আরও সাম্প্রতিক সংস্করণ নির্বাচন করতে পারেন, তাহলে আপনি সেই অতিরিক্ত সরলতা থেকে সুবিধা নিতে পারবেন।
-
-ডক্সে রয়েছে Python-এর প্রতিটি সংস্করণের সাথে সামঞ্জস্যপূর্ণ উদাহরণগুলি (যখন পার্থক্য আছে)।
-
-উদাহরণস্বরূপ, "**Python 3.6+**" মানে এটি Python 3.6 বা তার উপরে সামঞ্জস্যপূর্ণ (যার মধ্যে 3.7, 3.8, 3.9, 3.10, ইত্যাদি অন্তর্ভুক্ত)। এবং "**Python 3.9+**" মানে এটি Python 3.9 বা তার উপরে সামঞ্জস্যপূর্ণ (যার মধ্যে 3.10, ইত্যাদি অন্তর্ভুক্ত)।
-
-যদি আপনি Python-এর **সর্বশেষ সংস্করণগুলি ব্যবহার করতে পারেন**, তাহলে সর্বশেষ সংস্করণের জন্য উদাহরণগুলি ব্যবহার করুন, সেগুলি আপনাকে **সর্বোত্তম এবং সহজতম সিনট্যাক্স** প্রদান করবে, যেমন, "**Python 3.10+**"।
-
-#### লিস্ট
-
-উদাহরণস্বরূপ, একটি ভেরিয়েবলকে `str`-এর একটি `list` হিসেবে সংজ্ঞায়িত করা যাক।
-
-//// tab | Python 3.9+
-
-ভেরিয়েবলটি ঘোষণা করুন, একই কোলন (`:`) সিনট্যাক্স ব্যবহার করে।
-
-টাইপ হিসেবে, `list` ব্যবহার করুন।
-
-যেহেতু লিস্ট এমন একটি টাইপ যা অভ্যন্তরীণ টাইপগুলি ধারণ করে, আপনি তাদের স্কোয়ার ব্রাকেটের ভিতরে ব্যবহার করুন:
-
-```Python hl_lines="1"
-{!> ../../../docs_src/python_types/tutorial006_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-`typing` থেকে `List` (বড় হাতের `L` দিয়ে) ইমপোর্ট করুন:
-
-``` Python hl_lines="1"
-{!> ../../../docs_src/python_types/tutorial006.py!}
-```
-
-ভেরিয়েবলটি ঘোষণা করুন, একই কোলন (`:`) সিনট্যাক্স ব্যবহার করে।
-
-টাইপ হিসেবে, `typing` থেকে আপনার ইম্পোর্ট করা `List` ব্যবহার করুন।
-
-যেহেতু লিস্ট এমন একটি টাইপ যা অভ্যন্তরীণ টাইপগুলি ধারণ করে, আপনি তাদের স্কোয়ার ব্রাকেটের ভিতরে করুন:
-
-```Python hl_lines="4"
-{!> ../../../docs_src/python_types/tutorial006.py!}
-```
-
-////
-
-/// info
-
-স্কোয়ার ব্রাকেট এর ভিতরে ব্যবহৃত এইসব অভন্তরীন টাইপগুলোকে "ইন্টারনাল টাইপ" বলে।
-
-এই উদাহরণে, এটি হচ্ছে `List`(অথবা পাইথন ৩.৯ বা তার উপরের সংস্করণের ক্ষেত্রে `list`) এ পাস করা টাইপ প্যারামিটার।
-
-///
-
-এর অর্থ হচ্ছে: "ভেরিয়েবল `items` একটি `list`, এবং এই লিস্টের প্রতিটি আইটেম একটি `str`।"
-
-/// tip
-
-যদি আপনি Python 3.9 বা তার উপরে ব্যবহার করেন, আপনার `typing` থেকে `List` আমদানি করতে হবে না, আপনি সাধারণ `list` ওই টাইপের পরিবর্তে ব্যবহার করতে পারেন।
-
-///
-
-এর মাধ্যমে, আপনার এডিটর লিস্ট থেকে আইটেম প্রসেস করার সময় সাপোর্ট প্রদান করতে পারবে:
-
-
-
-টাইপগুলি ছাড়া, এটি করা প্রায় অসম্ভব।
-
-লক্ষ্য করুন যে ভেরিয়েবল `item` হল `items` লিস্টের একটি এলিমেন্ট।
-
-তবুও, এডিটর জানে যে এটি একটি `str`, এবং তার জন্য সাপোর্ট প্রদান করে।
-
-#### টাপল এবং সেট
-
-আপনি `tuple` এবং `set` ঘোষণা করার জন্য একই প্রক্রিয়া অনুসরণ করবেন:
-
-//// tab | Python 3.9+
-
-```Python hl_lines="1"
-{!> ../../../docs_src/python_types/tutorial007_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="1 4"
-{!> ../../../docs_src/python_types/tutorial007.py!}
-```
-
-////
-
-এর মানে হল:
-
-* ভেরিয়েবল `items_t` হল একটি `tuple` যা ৩টি আইটেম ধারণ করে, একটি `int`, অন্য একটি `int`, এবং একটি `str`।
-* ভেরিয়েবল `items_s` হল একটি `set`, এবং এর প্রতিটি আইটেম হল `bytes` টাইপের।
-
-#### ডিক্ট
-
-একটি `dict` সংজ্ঞায়িত করতে, আপনি ২টি টাইপ প্যারামিটার কমা দ্বারা পৃথক করে দেবেন।
-
-প্রথম টাইপ প্যারামিটারটি হল `dict`-এর কীগুলির জন্য।
-
-দ্বিতীয় টাইপ প্যারামিটারটি হল `dict`-এর মানগুলির জন্য:
-
-//// tab | Python 3.9+
-
-```Python hl_lines="1"
-{!> ../../../docs_src/python_types/tutorial008_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="1 4"
-{!> ../../../docs_src/python_types/tutorial008.py!}
-```
-
-////
-
-এর মানে হল:
-
-* ভেরিয়েবল `prices` হল একটি `dict`:
- * এই `dict`-এর কীগুলি হল `str` টাইপের (ধরা যাক, প্রতিটি আইটেমের নাম)।
- * এই `dict`-এর মানগুলি হল `float` টাইপের (ধরা যাক, প্রতিটি আইটেমের দাম)।
-
-#### ইউনিয়ন
-
-আপনি একটি ভেরিয়েবলকে এমনভাবে ঘোষণা করতে পারেন যেন তা **একাধিক টাইপের** হয়, উদাহরণস্বরূপ, একটি `int` অথবা `str`।
-
-Python 3.6 এবং তার উপরের সংস্করণগুলিতে (Python 3.10 অন্তর্ভুক্ত) আপনি `typing` থেকে `Union` টাইপ ব্যবহার করতে পারেন এবং স্কোয়ার ব্র্যাকেটের মধ্যে গ্রহণযোগ্য টাইপগুলি রাখতে পারেন।
-
-Python 3.10-এ একটি **নতুন সিনট্যাক্স** আছে যেখানে আপনি সম্ভাব্য টাইপগুলিকে একটি ভার্টিকাল বার (`|`) দ্বারা পৃথক করতে পারেন।
-
-//// tab | Python 3.10+
-
-```Python hl_lines="1"
-{!> ../../../docs_src/python_types/tutorial008b_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="1 4"
-{!> ../../../docs_src/python_types/tutorial008b.py!}
-```
-
-////
-
-উভয় ক্ষেত্রেই এর মানে হল যে `item` হতে পারে একটি `int` অথবা `str`।
-
-#### সম্ভবত `None`
-
-আপনি এমনভাবে ঘোষণা করতে পারেন যে একটি মান হতে পারে এক টাইপের, যেমন `str`, আবার এটি `None`-ও হতে পারে।
-
-Python 3.6 এবং তার উপরের সংস্করণগুলিতে (Python 3.10 অনতর্ভুক্ত) আপনি `typing` মডিউল থেকে `Optional` ইমপোর্ট করে এটি ঘোষণা এবং ব্যবহার করতে পারেন।
-
-```Python hl_lines="1 4"
-{!../../../docs_src/python_types/tutorial009.py!}
-```
-
-`Optional[str]` ব্যবহার করা মানে হল শুধু `str` নয়, এটি হতে পারে `None`-ও, যা আপনার এডিটরকে সেই ত্রুটিগুলি শনাক্ত করতে সাহায্য করবে যেখানে আপনি ধরে নিচ্ছেন যে একটি মান সবসময় `str` হবে, অথচ এটি `None`-ও হতে পারেও।
-
-`Optional[Something]` মূলত `Union[Something, None]`-এর একটি শর্টকাট, এবং তারা সমতুল্য।
-
-এর মানে হল, Python 3.10-এ, আপনি টাইপগুলির ইউনিয়ন ঘোষণা করতে `Something | None` ব্যবহার করতে পারেন:
-
-//// tab | Python 3.10+
-
-```Python hl_lines="1"
-{!> ../../../docs_src/python_types/tutorial009_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="1 4"
-{!> ../../../docs_src/python_types/tutorial009.py!}
-```
-
-////
-
-//// tab | Python 3.8+ বিকল্প
-
-```Python hl_lines="1 4"
-{!> ../../../docs_src/python_types/tutorial009b.py!}
-```
-
-////
-
-#### `Union` বা `Optional` ব্যবহার
-
-যদি আপনি Python 3.10-এর নীচের সংস্করণ ব্যবহার করেন, তবে এখানে আমার খুবই **ব্যক্তিগত** দৃষ্টিভঙ্গি থেকে একটি টিপস:
-
-* 🚨 `Optional[SomeType]` ব্যবহার এড়িয়ে চলুন।
-* এর পরিবর্তে ✨ **`Union[SomeType, None]` ব্যবহার করুন** ✨।
-
-উভয়ই সমতুল্য এবং মূলে একই, কিন্তু আমি `Union`-এর পক্ষে সুপারিশ করব কারণ "**অপশনাল**" শব্দটি মনে হতে পারে যে মানটি ঐচ্ছিক,অথচ এটি আসলে মানে "এটি হতে পারে `None`", এমনকি যদি এটি ঐচ্ছিক না হয়েও আবশ্যিক হয়।
-
-আমি মনে করি `Union[SomeType, None]` এর অর্থ আরও স্পষ্টভাবে প্রকাশ করে।
-
-এটি কেবল শব্দ এবং নামের ব্যাপার। কিন্তু সেই শব্দগুলি আপনি এবং আপনার সহকর্মীরা কোড সম্পর্কে কীভাবে চিন্তা করেন তা প্রভাবিত করতে পারে।
-
-একটি উদাহরণ হিসেবে, এই ফাংশনটি নিন:
-
-```Python hl_lines="1 4"
-{!../../../docs_src/python_types/tutorial009c.py!}
-```
-
-`name` প্যারামিটারটি `Optional[str]` হিসেবে সংজ্ঞায়িত হয়েছে, কিন্তু এটি **অপশনাল নয়**, আপনি প্যারামিটার ছাড়া ফাংশনটি কল করতে পারবেন না:
-
-```Python
-say_hi() # ওহ না, এটি একটি ত্রুটি নিক্ষেপ করবে! 😱
-```
-
-`name` প্যারামিটারটি **এখনও আবশ্যিক** (নন-অপশনাল) কারণ এটির কোনো ডিফল্ট মান নেই। তবুও, `name` এর মান হিসেবে `None` গ্রহণযোগ্য:
-
-```Python
-say_hi(name=None) # এটি কাজ করে, None বৈধ 🎉
-```
-
-সুখবর হল, একবার আপনি Python 3.10 ব্যবহার করা শুরু করলে, আপনাকে এগুলোর ব্যাপারে আর চিন্তা করতে হবে না, যেহুতু আপনি | ব্যবহার করেই ইউনিয়ন ঘোষণা করতে পারবেন:
-
-```Python hl_lines="1 4"
-{!../../../docs_src/python_types/tutorial009c_py310.py!}
-```
-
-এবং তারপর আপনাকে নামগুলি যেমন `Optional` এবং `Union` নিয়ে আর চিন্তা করতে হবে না। 😎
-
-#### জেনেরিক টাইপস
-
-স্কোয়ার ব্র্যাকেটে টাইপ প্যারামিটার নেওয়া এই টাইপগুলিকে **জেনেরিক টাইপ** বা **জেনেরিকস** বলা হয়, যেমন:
-
-//// tab | Python 3.10+
-
-আপনি সেই একই বিল্টইন টাইপ জেনেরিক্স হিসেবে ব্যবহার করতে পারবেন(ভিতরে টাইপ সহ স্কয়ারে ব্রাকেট দিয়ে):
-
-* `list`
-* `tuple`
-* `set`
-* `dict`
-
-এবং Python 3.8 এর মতোই, `typing` মডিউল থেকে:
-
-* `Union`
-* `Optional` (Python 3.8 এর মতোই)
-* ...এবং অন্যান্য।
-
-Python 3.10-এ, `Union` এবং `Optional` জেনেরিকস ব্যবহার করার বিকল্প হিসেবে, আপনি টাইপগুলির ইউনিয়ন ঘোষণা করতে ভার্টিকাল বার (`|`) ব্যবহার করতে পারেন, যা ওদের থেকে অনেক ভালো এবং সহজ।
-
-////
-
-//// tab | Python 3.9+
-
-আপনি সেই একই বিল্টইন টাইপ জেনেরিক্স হিসেবে ব্যবহার করতে পারবেন(ভিতরে টাইপ সহ স্কয়ারে ব্রাকেট দিয়ে):
-
-* `list`
-* `tuple`
-* `set`
-* `dict`
-
-এবং Python 3.8 এর মতোই, `typing` মডিউল থেকে:
-
-* `Union`
-* `Optional`
-* ...এবং অন্যান্য।
-
-////
-
-//// tab | Python 3.8+
-
-* `List`
-* `Tuple`
-* `Set`
-* `Dict`
-* `Union`
-* `Optional`
-* ...এবং অন্যান্য।
-
-////
-
-### ক্লাস হিসেবে টাইপস
-
-আপনি একটি ভেরিয়েবলের টাইপ হিসেবে একটি ক্লাস ঘোষণা করতে পারেন।
-
-ধরুন আপনার কাছে `Person` নামে একটি ক্লাস আছে, যার একটি নাম আছে:
-
-```Python hl_lines="1-3"
-{!../../../docs_src/python_types/tutorial010.py!}
-```
-
-তারপর আপনি একটি ভেরিয়েবলকে `Person` টাইপের হিসেবে ঘোষণা করতে পারেন:
-
-```Python hl_lines="6"
-{!../../../docs_src/python_types/tutorial010.py!}
-```
-
-এবং তারপর, আবার, আপনি এডিটর সাপোর্ট পেয়ে যাবেন:
-
-
-
-লক্ষ্য করুন যে এর মানে হল "`one_person` হল ক্লাস `Person`-এর একটি **ইন্সট্যান্স**।"
-
-এর মানে এটি নয় যে "`one_person` হল **ক্লাস** যাকে বলা হয় `Person`।"
-
-## Pydantic মডেল
-
-[Pydantic](https://docs.pydantic.dev/) হল একটি Python লাইব্রেরি যা ডাটা ভ্যালিডেশন সম্পাদন করে।
-
-আপনি ডাটার "আকার" এট্রিবিউট সহ ক্লাস হিসেবে ঘোষণা করেন।
-
-এবং প্রতিটি এট্রিবিউট এর একটি টাইপ থাকে।
-
-তারপর আপনি যদি কিছু মান দিয়ে সেই ক্লাসের একটি ইন্সট্যান্স তৈরি করেন-- এটি মানগুলিকে ভ্যালিডেট করবে, প্রয়োজন অনুযায়ী তাদেরকে উপযুক্ত টাইপে রূপান্তর করবে এবং আপনাকে সমস্ত ডাটা সহ একটি অবজেক্ট প্রদান করবে।
-
-এবং আপনি সেই ফলাফল অবজেক্টের সাথে এডিটর সাপোর্ট পাবেন।
-
-অফিসিয়াল Pydantic ডক্স থেকে একটি উদাহরণ:
-
-//// tab | Python 3.10+
-
-```Python
-{!> ../../../docs_src/python_types/tutorial011_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python
-{!> ../../../docs_src/python_types/tutorial011_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python
-{!> ../../../docs_src/python_types/tutorial011.py!}
-```
-
-////
-
-/// info
-
-[Pydantic সম্পর্কে আরও জানতে, এর ডকুমেন্টেশন দেখুন](https://docs.pydantic.dev/)।
-
-///
-
-**FastAPI** মূলত Pydantic-এর উপর নির্মিত।
-
-আপনি এই সমস্ত কিছুর অনেক বাস্তবসম্মত উদাহরণ পাবেন [টিউটোরিয়াল - ইউজার গাইডে](https://fastapi.tiangolo.com/tutorial/)।
-
-/// tip
-
-যখন আপনি `Optional` বা `Union[Something, None]` ব্যবহার করেন এবং কোনো ডিফল্ট মান না থাকে, Pydantic-এর একটি বিশেষ আচরণ রয়েছে, আপনি Pydantic ডকুমেন্টেশনে [Required Optional fields](https://docs.pydantic.dev/latest/concepts/models/#required-optional-fields) সম্পর্কে আরও পড়তে পারেন।
-
-///
-
-## মেটাডাটা অ্যানোটেশন সহ টাইপ হিন্টস
-
-Python-এ এমন একটি ফিচার আছে যা `Annotated` ব্যবহার করে এই টাইপ হিন্টগুলিতে **অতিরিক্ত মেটাডাটা** রাখতে দেয়।
-
-//// tab | Python 3.9+
-
-Python 3.9-এ, `Annotated` স্ট্যান্ডার্ড লাইব্রেরিতে অন্তর্ভুক্ত, তাই আপনি এটি `typing` থেকে ইমপোর্ট করতে পারেন।
-
-```Python hl_lines="1 4"
-{!> ../../../docs_src/python_types/tutorial013_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-Python 3.9-এর নীচের সংস্করণগুলিতে, আপনি `Annotated`-কে `typing_extensions` থেকে ইমপোর্ট করেন।
-
-এটি **FastAPI** এর সাথে ইতিমদ্ধে ইনস্টল হয়ে থাকবে।
-
-```Python hl_lines="1 4"
-{!> ../../../docs_src/python_types/tutorial013.py!}
-```
-
-////
-
-Python নিজে এই `Annotated` দিয়ে কিছুই করে না। এবং এডিটর এবং অন্যান্য টুলগুলির জন্য, টাইপটি এখনও `str`।
-
-কিন্তু আপনি এই `Annotated` এর মধ্যকার জায়গাটির মধ্যে **FastAPI**-এ কীভাবে আপনার অ্যাপ্লিকেশন আচরণ করুক তা সম্পর্কে অতিরিক্ত মেটাডাটা প্রদান করার জন্য ব্যবহার করতে পারেন।
-
-মনে রাখার গুরুত্বপূর্ণ বিষয় হল যে **প্রথম *টাইপ প্যারামিটার*** আপনি `Annotated`-এ পাস করেন সেটি হল **আসল টাইপ**। বাকি শুধুমাত্র অন্যান্য টুলগুলির জন্য মেটাডাটা।
-
-এখন আপনার কেবল জানা প্রয়োজন যে `Annotated` বিদ্যমান, এবং এটি স্ট্যান্ডার্ড Python। 😎
-
-পরবর্তীতে আপনি দেখবেন এটি কতটা **শক্তিশালী** হতে পারে।
-
-/// tip
-
-এটি **স্ট্যান্ডার্ড Python** হওয়ার মানে হল আপনি আপনার এডিটরে, আপনি যে টুলগুলি কোড বিশ্লেষণ এবং রিফ্যাক্টর করার জন্য ব্যবহার করেন তাতে **সেরা সম্ভাব্য ডেভেলপার এক্সপেরিয়েন্স** পাবেন। ✨
-
-এবং এছাড়াও আপনার কোড অন্যান্য অনেক Python টুল এবং লাইব্রেরিগুলির সাথে খুব সামঞ্জস্যপূর্ণ হবে। 🚀
-
-///
-
-## **FastAPI**-এ টাইপ হিন্টস
-
-**FastAPI** এই টাইপ হিন্টগুলি ব্যবহার করে বেশ কিছু জিনিস করে।
-
-**FastAPI**-এ আপনি টাইপ হিন্টগুলি সহ প্যারামিটার ঘোষণা করেন এবং আপনি পান:
-
-* **এডিটর সাপোর্ট**।
-* **টাইপচেক**।
-
-...এবং **FastAPI** একই ঘোষণাগুলি ব্যবহার করে:
-
-* **রিকুইরেমেন্টস সংজ্ঞায়িত করে**: রিকোয়েস্ট পাথ প্যারামিটার, কুয়েরি প্যারামিটার, হেডার, বডি, ডিপেন্ডেন্সিস, ইত্যাদি থেকে।
-* **ডেটা রূপান্তর করে**: রিকোয়েস্ট থেকে প্রয়োজনীয় টাইপে ডেটা।
-* **ডেটা যাচাই করে**: প্রতিটি রিকোয়েস্ট থেকে আসা ডেটা:
- * যখন ডেটা অবৈধ হয় তখন **স্বয়ংক্রিয় ত্রুটি** গ্রাহকের কাছে ফেরত পাঠানো।
-* **API ডকুমেন্টেশন তৈরি করে**: OpenAPI ব্যবহার করে:
- * যা স্বয়ংক্রিয় ইন্টার্যাক্টিভ ডকুমেন্টেশন ইউজার ইন্টারফেস দ্বারা ব্যবহৃত হয়।
-
-এই সব কিছু আপনার কাছে অস্পষ্ট মনে হতে পারে। চিন্তা করবেন না। আপনি [টিউটোরিয়াল - ইউজার গাইড](https://fastapi.tiangolo.com/tutorial/) এ এই সব কিছু প্র্যাকটিসে দেখতে পাবেন।
-
-গুরুত্বপূর্ণ বিষয় হল, আপনি যদি স্ট্যান্ডার্ড Python টাইপগুলি ব্যবহার করেন, তবে আরও বেশি ক্লাস, ডেকোরেটর ইত্যাদি যোগ না করেই একই স্থানে **FastAPI** আপনার অনেক কাজ করে দিবে।
-
-/// info
-
-যদি আপনি টিউটোরিয়ালের সমস্ত বিষয় পড়ে ফেলে থাকেন এবং টাইপ সম্পর্কে আরও জানতে চান, তবে একটি ভালো রিসোর্স হল [mypy এর "cheat sheet"](https://mypy.readthedocs.io/en/latest/cheat_sheet_py3.html)। এই "cheat sheet" এ আপনি Python টাইপ হিন্ট সম্পর্কে বেসিক থেকে উন্নত লেভেলের ধারণা পেতে পারেন, যা আপনার কোডে টাইপ সেফটি এবং স্পষ্টতা বাড়াতে সাহায্য করবে।
-
-///
diff --git a/docs/bn/mkdocs.yml b/docs/bn/mkdocs.yml
deleted file mode 100644
index de18856f44..0000000000
--- a/docs/bn/mkdocs.yml
+++ /dev/null
@@ -1 +0,0 @@
-INHERIT: ../en/mkdocs.yml
diff --git a/docs/de/docs/_llm-test.md b/docs/de/docs/_llm-test.md
new file mode 100644
index 0000000000..4a5e5392c5
--- /dev/null
+++ b/docs/de/docs/_llm-test.md
@@ -0,0 +1,503 @@
+# LLM-Testdatei { #llm-test-file }
+
+Dieses Dokument testet, ob das LLM, das die Dokumentation übersetzt, den `general_prompt` in `scripts/translate.py` und den sprachspezifischen Prompt in `docs/{language code}/llm-prompt.md` versteht. Der sprachspezifische Prompt wird an `general_prompt` angehängt.
+
+Hier hinzugefügte Tests werden von allen Erstellern sprachspezifischer Prompts gesehen.
+
+So verwenden:
+
+* Einen sprachspezifischen Prompt haben – `docs/{language code}/llm-prompt.md`.
+* Eine frische Übersetzung dieses Dokuments in die gewünschte Zielsprache durchführen (siehe z. B. das Kommando `translate-page` der `translate.py`). Dadurch wird die Übersetzung unter `docs/{language code}/docs/_llm-test.md` erstellt.
+* Prüfen Sie, ob in der Übersetzung alles in Ordnung ist.
+* Verbessern Sie bei Bedarf Ihren sprachsspezifischen Prompt, den allgemeinen Prompt oder das englische Dokument.
+* Beheben Sie anschließend manuell die verbleibenden Probleme in der Übersetzung, sodass es eine gute Übersetzung ist.
+* Übersetzen Sie erneut, nachdem die gute Übersetzung vorliegt. Das ideale Ergebnis wäre, dass das LLM an der Übersetzung keine Änderungen mehr vornimmt. Das bedeutet, dass der allgemeine Prompt und Ihr sprachsspezifischer Prompt so gut sind, wie sie sein können (Es wird manchmal ein paar scheinbar zufällige Änderungen machen, der Grund ist, dass LLMs keine deterministischen Algorithmen sind).
+
+Die Tests:
+
+## Codeschnipsel { #code-snippets}
+
+//// tab | Test
+
+Dies ist ein Codeschnipsel: `foo`. Und dies ist ein weiteres Codeschnipsel: `bar`. Und noch eins: `baz quux`.
+
+////
+
+//// tab | Info
+
+Der Inhalt von Codeschnipseln sollte unverändert bleiben.
+
+Siehe Abschnitt `### Content of code snippets` im allgemeinen Prompt in `scripts/translate.py`.
+
+////
+
+## Anführungszeichen { #quotes }
+
+//// tab | Test
+
+Gestern schrieb mein Freund: „Wenn man unkorrekt korrekt schreibt, hat man es unkorrekt geschrieben“. Worauf ich antwortete: „Korrekt, aber ‚unkorrekt‘ ist unkorrekterweise nicht ‚„unkorrekt“‘“.
+
+/// note | Hinweis
+
+Das LLM wird dies wahrscheinlich falsch übersetzen. Interessant ist nur, ob es die korrigierte Übersetzung bei einer erneuten Übersetzung beibehält.
+
+///
+
+////
+
+//// tab | Info
+
+Der Prompt-Designer kann entscheiden, ob neutrale Anführungszeichen in typografische Anführungszeichen umgewandelt werden sollen. Es ist in Ordnung, sie unverändert zu lassen.
+
+Siehe zum Beispiel den Abschnitt `### Quotes` in `docs/de/llm-prompt.md`.
+
+////
+
+## Anführungszeichen in Codeschnipseln { #quotes-in-code-snippets}
+
+//// tab | Test
+
+`pip install "foo[bar]"`
+
+Beispiele für Stringliterale in Codeschnipseln: `"this"`, `'that'`.
+
+Ein schwieriges Beispiel für Stringliterale in Codeschnipseln: `f"I like {'oranges' if orange else "apples"}"`
+
+Hardcore: `Yesterday, my friend wrote: "If you spell incorrectly correctly, you have spelled it incorrectly". To which I answered: "Correct, but 'incorrectly' is incorrectly not '"incorrectly"'"`
+
+////
+
+//// tab | Info
+
+... Allerdings müssen Anführungszeichen in Codeschnipseln unverändert bleiben.
+
+////
+
+## Codeblöcke { #code-blocks }
+
+//// tab | Test
+
+Ein Bash-Codebeispiel ...
+
+```bash
+# Eine Begrüßung an das Universum ausgeben
+echo "Hello universe"
+```
+
+... und ein Konsolen-Codebeispiel ...
+
+```console
+$ fastapi run main.py
+ FastAPI Starting server
+ Searching for package file structure
+```
+
+... und noch ein Konsolen-Codebeispiel ...
+
+```console
+// Ein Verzeichnis „Code“ erstellen
+$ mkdir code
+// In dieses Verzeichnis wechseln
+$ cd code
+```
+
+... und ein Python-Codebeispiel ...
+
+```Python
+wont_work() # Das wird nicht funktionieren 😱
+works(foo="bar") # Das funktioniert 🎉
+```
+
+... und das war's.
+
+////
+
+//// tab | Info
+
+Code in Codeblöcken sollte nicht verändert werden, mit Ausnahme von Kommentaren.
+
+Siehe Abschnitt `### Content of code blocks` im allgemeinen Prompt in `scripts/translate.py`.
+
+////
+
+## Tabs und farbige Boxen { #tabs-and-colored-boxes }
+
+//// tab | Test
+
+/// info | Info
+Etwas Text
+///
+
+/// note | Hinweis
+Etwas Text
+///
+
+/// note | Technische Details
+Etwas Text
+///
+
+/// check | Testen
+Etwas Text
+///
+
+/// tip | Tipp
+Etwas Text
+///
+
+/// warning | Achtung
+Etwas Text
+///
+
+/// danger | Gefahr
+Etwas Text
+///
+
+////
+
+//// tab | Info
+
+Tabs und `Info`/`Note`/`Warning`/usw. Blöcke sollten die Übersetzung ihres Titels nach einem vertikalen Strich (`|`) erhalten.
+
+Siehe die Abschnitte `### Special blocks` und `### Tab blocks` im allgemeinen Prompt in `scripts/translate.py`.
+
+////
+
+## Web- und interne Links { #web-and-internal-links }
+
+//// tab | Test
+
+Der Linktext sollte übersetzt werden, die Linkadresse sollte unverändert bleiben:
+
+* [Link zur Überschrift oben](#code-snippets)
+* [Interner Link](index.md#installation){.internal-link target=_blank}
+* Externer Link
+* Link zu einem Stil
+* Link zu einem Skript
+* Link zu einem Bild
+
+Der Linktext sollte übersetzt werden, die Linkadresse sollte auf die Übersetzung zeigen:
+
+* FastAPI-Link
+
+////
+
+//// tab | Info
+
+Links sollten übersetzt werden, aber ihre Adresse soll unverändert bleiben. Eine Ausnahme sind absolute Links zu Seiten der FastAPI-Dokumentation. In diesem Fall sollte auf die Übersetzung verlinkt werden.
+
+Siehe Abschnitt `### Links` im allgemeinen Prompt in `scripts/translate.py`.
+
+////
+
+## HTML „abbr“-Elemente { #html-abbr-elements }
+
+//// tab | Test
+
+Hier einige Dinge, die in HTML-„abbr“-Elemente gepackt sind (einige sind erfunden):
+
+### Das abbr gibt eine vollständige Phrase { #the-abbr-gives-a-full-phrase }
+
+* GTD
+* lt
+* XWT
+* PSGI
+
+### Das abbr gibt eine Erklärung { #the-abbr-gives-an-explanation }
+
+* Cluster
+* Deep Learning
+
+### Das abbr gibt eine vollständige Phrase und eine Erklärung { #the-abbr-gives-a-full-phrase-and-an-explanation }
+
+* MDN
+* I/O.
+
+////
+
+//// tab | Info
+
+„title“-Attribute von „abbr“-Elementen werden nach bestimmten Anweisungen übersetzt.
+
+Übersetzungen können eigene „abbr“-Elemente hinzufügen, die das LLM nicht entfernen soll. Z. B. um englische Wörter zu erklären.
+
+Siehe Abschnitt `### HTML abbr elements` im allgemeinen Prompt in `scripts/translate.py`.
+
+////
+
+## Überschriften { #headings }
+
+//// tab | Test
+
+### Eine Webapp entwickeln – ein Tutorial { #develop-a-webapp-a-tutorial }
+
+Hallo.
+
+### Typhinweise und -annotationen { #type-hints-and-annotations }
+
+Hallo wieder.
+
+### Super- und Subklassen { #super-and-subclasses }
+
+Hallo wieder.
+
+////
+
+//// tab | Info
+
+Die einzige strenge Regel für Überschriften ist, dass das LLM den Hash-Teil in geschweiften Klammern unverändert lässt, damit Links nicht kaputtgehen.
+
+Siehe Abschnitt `### Headings` im allgemeinen Prompt in `scripts/translate.py`.
+
+Für einige sprachspezifische Anweisungen, siehe z. B. den Abschnitt `### Headings` in `docs/de/llm-prompt.md`.
+
+////
+
+## In der Dokumentation verwendete Begriffe { #terms-used-in-the-docs }
+
+//// tab | Test
+
+* Sie
+* Ihr
+
+* z. B.
+* usw.
+
+* `foo` vom Typ `int`
+* `bar` vom Typ `str`
+* `baz` vom Typ `list`
+
+* das Tutorial – Benutzerhandbuch
+* das Handbuch für fortgeschrittene Benutzer
+* die SQLModel-Dokumentation
+* die API-Dokumentation
+* die automatische Dokumentation
+
+* Data Science
+* Deep Learning
+* Machine Learning
+* Dependency Injection
+* HTTP Basic-Authentifizierung
+* HTTP Digest
+* ISO-Format
+* der JSON-Schema-Standard
+* das JSON-Schema
+* die Schema-Definition
+* Password Flow
+* Mobile
+
+* deprecatet
+* designt
+* ungültig
+* on the fly
+* Standard
+* Default
+* Groß-/Kleinschreibung ist relevant
+* Groß-/Kleinschreibung ist nicht relevant
+
+* die Anwendung bereitstellen
+* die Seite ausliefern
+
+* die App
+* die Anwendung
+
+* der Request
+* die Response
+* die Error-Response
+
+* die Pfadoperation
+* der Pfadoperation-Dekorator
+* die Pfadoperation-Funktion
+
+* der Body
+* der Requestbody
+* der Responsebody
+* der JSON-Body
+* der Formularbody
+* der Dateibody
+* der Funktionskörper
+
+* der Parameter
+* der Body-Parameter
+* der Pfad-Parameter
+* der Query-Parameter
+* der Cookie-Parameter
+* der Header-Parameter
+* der Formular-Parameter
+* der Funktionsparameter
+
+* das Event
+* das Startup-Event
+* das Hochfahren des Servers
+* das Shutdown-Event
+* das Lifespan-Event
+
+* der Handler
+* der Eventhandler
+* der Exceptionhandler
+* handhaben
+
+* das Modell
+* das Pydantic-Modell
+* das Datenmodell
+* das Datenbankmodell
+* das Formularmodell
+* das Modellobjekt
+
+* die Klasse
+* die Basisklasse
+* die Elternklasse
+* die Subklasse
+* die Kindklasse
+* die Geschwisterklasse
+* die Klassenmethode
+
+* der Header
+* die Header
+* der Autorisierungsheader
+* der `Authorization`-Header
+* der Forwarded-Header
+
+* das Dependency-Injection-System
+* die Dependency
+* das Dependable
+* der Dependant
+
+* I/O-lastig
+* CPU-lastig
+* Nebenläufigkeit
+* Parallelität
+* Multiprocessing
+
+* die Umgebungsvariable
+* die Umgebungsvariable
+* der `PATH`
+* die `PATH`-Umgebungsvariable
+
+* die Authentifizierung
+* der Authentifizierungsanbieter
+* die Autorisierung
+* das Anmeldeformular
+* der Autorisierungsanbieter
+* der Benutzer authentisiert sich
+* das System authentifiziert den Benutzer
+
+* Das CLI
+* Das Kommandozeileninterface
+
+* der Server
+* der Client
+
+* der Cloudanbieter
+* der Clouddienst
+
+* die Entwicklung
+* die Entwicklungsphasen
+
+* das Dict
+* das Dictionary
+* die Enumeration
+* das Enum
+* das Enum-Member
+
+* der Encoder
+* der Decoder
+* kodieren
+* dekodieren
+
+* die Exception
+* werfen
+
+* der Ausdruck
+* die Anweisung
+
+* das Frontend
+* das Backend
+
+* die GitHub-Diskussion
+* das GitHub-Issue
+
+* die Leistung
+* die Leistungsoptimierung
+
+* der Rückgabetyp
+* der Rückgabewert
+
+* die Sicherheit
+* das Sicherheitsschema
+
+* der Task
+* der Hintergrundtask
+* die Taskfunktion
+
+* das Template
+* die Template-Engine
+
+* die Typannotation
+* der Typhinweis
+
+* der Serverworker
+* der Uvicorn-Worker
+* der Gunicorn-Worker
+* der Workerprozess
+* die Workerklasse
+* die Workload
+
+* das Deployment
+* bereitstellen
+
+* das SDK
+* das Software Development Kit
+
+* der `APIRouter`
+* die `requirements.txt`
+* das Bearer-Token
+* der Breaking Change
+* der Bug
+* der Button
+* das Callable
+* der Code
+* der Commit
+* der Contextmanager
+* die Coroutine
+* die Datenbank-Session
+* die Festplatte
+* die Domain
+* die Engine
+* das Fake-X
+* die HTTP-GET-Methode
+* das Item
+* die Bibliothek
+* der Lifespan
+* der Lock
+* die Middleware
+* die Mobile-Anwendung
+* das Modul
+* das Mounten
+* das Netzwerk
+* das Origin
+* Die Überschreibung
+* die Payload
+* der Prozessor
+* die Property
+* der Proxy
+* der Pull Request
+* die Query
+* der RAM
+* der entfernte Rechner
+* der Statuscode
+* der String
+* der Tag
+* das Webframework
+* die Wildcard
+* zurückgeben
+* validieren
+
+////
+
+//// tab | Info
+
+Dies ist eine nicht vollständige und nicht normative Liste von (meist) technischen Begriffen, die in der Dokumentation vorkommen. Sie kann dem Prompt-Designer helfen herauszufinden, bei welchen Begriffen das LLM Unterstützung braucht. Zum Beispiel, wenn es eine gute Übersetzung immer wieder auf eine suboptimale Übersetzung zurücksetzt. Oder wenn es Probleme hat, einen Begriff in Ihrer Sprache zu konjugieren/deklinieren.
+
+Siehe z. B. den Abschnitt `### List of English terms and their preferred German translations` in `docs/de/llm-prompt.md`.
+
+////
diff --git a/docs/de/docs/about/index.md b/docs/de/docs/about/index.md
index 4c309e02a5..5e9c6b6a0a 100644
--- a/docs/de/docs/about/index.md
+++ b/docs/de/docs/about/index.md
@@ -1,3 +1,3 @@
-# Über
+# Über { #about }
Über FastAPI, sein Design, seine Inspiration und mehr. 🤓
diff --git a/docs/de/docs/advanced/additional-responses.md b/docs/de/docs/advanced/additional-responses.md
index 6f2c4b2dd8..218dd6c4fb 100644
--- a/docs/de/docs/advanced/additional-responses.md
+++ b/docs/de/docs/advanced/additional-responses.md
@@ -1,6 +1,6 @@
-# Zusätzliche Responses in OpenAPI
+# Zusätzliche Responses in OpenAPI { #additional-responses-in-openapi }
-/// warning | "Achtung"
+/// warning | Achtung
Dies ist ein eher fortgeschrittenes Thema.
@@ -8,17 +8,17 @@ Wenn Sie mit **FastAPI** beginnen, benötigen Sie dies möglicherweise nicht.
///
-Sie können zusätzliche Responses mit zusätzlichen Statuscodes, Medientypen, Beschreibungen, usw. deklarieren.
+Sie können zusätzliche Responses mit zusätzlichen Statuscodes, Medientypen, Beschreibungen, usw. deklarieren.
Diese zusätzlichen Responses werden in das OpenAPI-Schema aufgenommen, sodass sie auch in der API-Dokumentation erscheinen.
Für diese zusätzlichen Responses müssen Sie jedoch sicherstellen, dass Sie eine `Response`, wie etwa `JSONResponse`, direkt zurückgeben, mit Ihrem Statuscode und Inhalt.
-## Zusätzliche Response mit `model`
+## Zusätzliche Response mit `model` { #additional-response-with-model }
Sie können Ihren *Pfadoperation-Dekoratoren* einen Parameter `responses` übergeben.
-Der nimmt ein `dict` entgegen, die Schlüssel sind Statuscodes für jede Response, wie etwa `200`, und die Werte sind andere `dict`s mit den Informationen für jede Response.
+Der nimmt ein `dict` entgegen, die Schlüssel sind Statuscodes für jede Response, wie etwa `200`, und die Werte sind andere `dict`s mit den Informationen für jede Response.
Jedes dieser Response-`dict`s kann einen Schlüssel `model` haben, welcher ein Pydantic-Modell enthält, genau wie `response_model`.
@@ -26,17 +26,15 @@ Jedes dieser Response-`dict`s kann einen Schlüssel `model` haben, welcher ein P
Um beispielsweise eine weitere Response mit dem Statuscode `404` und einem Pydantic-Modell `Message` zu deklarieren, können Sie schreiben:
-```Python hl_lines="18 22"
-{!../../../docs_src/additional_responses/tutorial001.py!}
-```
+{* ../../docs_src/additional_responses/tutorial001.py hl[18,22] *}
-/// note | "Hinweis"
+/// note | Hinweis
Beachten Sie, dass Sie die `JSONResponse` direkt zurückgeben müssen.
///
-/// info
+/// info | Info
Der `model`-Schlüssel ist nicht Teil von OpenAPI.
@@ -171,23 +169,21 @@ Die Schemas werden von einer anderen Stelle innerhalb des OpenAPI-Schemas refere
}
```
-## Zusätzliche Medientypen für die Haupt-Response
+## Zusätzliche Medientypen für die Haupt-Response { #additional-media-types-for-the-main-response }
Sie können denselben `responses`-Parameter verwenden, um verschiedene Medientypen für dieselbe Haupt-Response hinzuzufügen.
Sie können beispielsweise einen zusätzlichen Medientyp `image/png` hinzufügen und damit deklarieren, dass Ihre *Pfadoperation* ein JSON-Objekt (mit dem Medientyp `application/json`) oder ein PNG-Bild zurückgeben kann:
-```Python hl_lines="19-24 28"
-{!../../../docs_src/additional_responses/tutorial002.py!}
-```
+{* ../../docs_src/additional_responses/tutorial002.py hl[19:24,28] *}
-/// note | "Hinweis"
+/// note | Hinweis
Beachten Sie, dass Sie das Bild direkt mit einer `FileResponse` zurückgeben müssen.
///
-/// info
+/// info | Info
Sofern Sie in Ihrem Parameter `responses` nicht explizit einen anderen Medientyp angeben, geht FastAPI davon aus, dass die Response denselben Medientyp wie die Haupt-Response-Klasse hat (Standardmäßig `application/json`).
@@ -195,7 +191,7 @@ Wenn Sie jedoch eine benutzerdefinierte Response-Klasse mit `None` als Medientyp
///
-## Informationen kombinieren
+## Informationen kombinieren { #combining-information }
Sie können auch Response-Informationen von mehreren Stellen kombinieren, einschließlich der Parameter `response_model`, `status_code` und `responses`.
@@ -207,15 +203,13 @@ Sie können beispielsweise eine Response mit dem Statuscode `404` deklarieren, d
Und eine Response mit dem Statuscode `200`, die Ihr `response_model` verwendet, aber ein benutzerdefiniertes Beispiel (`example`) enthält:
-```Python hl_lines="20-31"
-{!../../../docs_src/additional_responses/tutorial003.py!}
-```
+{* ../../docs_src/additional_responses/tutorial003.py hl[20:31] *}
Es wird alles kombiniert und in Ihre OpenAPI eingebunden und in der API-Dokumentation angezeigt:
-## Vordefinierte und benutzerdefinierte Responses kombinieren
+## Vordefinierte und benutzerdefinierte Responses kombinieren { #combine-predefined-responses-and-custom-ones }
Möglicherweise möchten Sie einige vordefinierte Responses haben, die für viele *Pfadoperationen* gelten, Sie möchten diese jedoch mit benutzerdefinierten Responses kombinieren, die für jede *Pfadoperation* erforderlich sind.
@@ -243,13 +237,11 @@ Mit dieser Technik können Sie einige vordefinierte Responses in Ihren *Pfadoper
Zum Beispiel:
-```Python hl_lines="13-17 26"
-{!../../../docs_src/additional_responses/tutorial004.py!}
-```
+{* ../../docs_src/additional_responses/tutorial004.py hl[13:17,26] *}
-## Weitere Informationen zu OpenAPI-Responses
+## Weitere Informationen zu OpenAPI-Responses { #more-information-about-openapi-responses }
Um zu sehen, was genau Sie in die Responses aufnehmen können, können Sie die folgenden Abschnitte in der OpenAPI-Spezifikation überprüfen:
-* OpenAPI Responses Object, enthält das `Response Object`.
-* OpenAPI Response Object, Sie können alles davon direkt in jede Response innerhalb Ihres `responses`-Parameter einfügen. Einschließlich `description`, `headers`, `content` (darin deklarieren Sie verschiedene Medientypen und JSON-Schemas) und `links`.
+* OpenAPI Responses Object, enthält das `Response Object`.
+* OpenAPI Response Object, Sie können alles davon direkt in jede Response innerhalb Ihres `responses`-Parameter einfügen. Einschließlich `description`, `headers`, `content` (darin deklarieren Sie verschiedene Medientypen und JSON-Schemas) und `links`.
diff --git a/docs/de/docs/advanced/additional-status-codes.md b/docs/de/docs/advanced/additional-status-codes.md
index 672efee51c..f948e18629 100644
--- a/docs/de/docs/advanced/additional-status-codes.md
+++ b/docs/de/docs/advanced/additional-status-codes.md
@@ -1,72 +1,22 @@
-# Zusätzliche Statuscodes
+# Zusätzliche Statuscodes { #additional-status-codes }
-Standardmäßig liefert **FastAPI** die Rückgabewerte (Responses) als `JSONResponse` zurück und fügt den Inhalt der jeweiligen *Pfadoperation* in das `JSONResponse` Objekt ein.
+Standardmäßig liefert **FastAPI** die Responses als `JSONResponse` zurück und fügt den Inhalt, den Sie aus Ihrer *Pfadoperation* zurückgeben, in diese `JSONResponse` ein.
Es wird der Default-Statuscode oder derjenige verwendet, den Sie in Ihrer *Pfadoperation* festgelegt haben.
-## Zusätzliche Statuscodes
+## Zusätzliche Statuscodes { #additional-status-codes_1 }
Wenn Sie neben dem Hauptstatuscode weitere Statuscodes zurückgeben möchten, können Sie dies tun, indem Sie direkt eine `Response` zurückgeben, wie etwa eine `JSONResponse`, und den zusätzlichen Statuscode direkt festlegen.
Angenommen, Sie möchten eine *Pfadoperation* haben, die das Aktualisieren von Artikeln ermöglicht und bei Erfolg den HTTP-Statuscode 200 „OK“ zurückgibt.
-Sie möchten aber auch, dass sie neue Artikel akzeptiert. Und wenn die Elemente vorher nicht vorhanden waren, werden diese Elemente erstellt und der HTTP-Statuscode 201 „Created“ zurückgegeben.
+Sie möchten aber auch, dass sie neue Artikel akzeptiert. Und wenn die Artikel vorher nicht vorhanden waren, werden diese Artikel erstellt und der HTTP-Statuscode 201 „Created“ zurückgegeben.
Um dies zu erreichen, importieren Sie `JSONResponse`, und geben Sie Ihren Inhalt direkt zurück, indem Sie den gewünschten `status_code` setzen:
-//// tab | Python 3.10+
+{* ../../docs_src/additional_status_codes/tutorial001_an_py310.py hl[4,25] *}
-```Python hl_lines="4 25"
-{!> ../../../docs_src/additional_status_codes/tutorial001_an_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="4 25"
-{!> ../../../docs_src/additional_status_codes/tutorial001_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="4 26"
-{!> ../../../docs_src/additional_status_codes/tutorial001_an.py!}
-```
-
-////
-
-//// tab | Python 3.10+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="2 23"
-{!> ../../../docs_src/additional_status_codes/tutorial001_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="4 25"
-{!> ../../../docs_src/additional_status_codes/tutorial001.py!}
-```
-
-////
-
-/// warning | "Achtung"
+/// warning | Achtung
Wenn Sie eine `Response` direkt zurückgeben, wie im obigen Beispiel, wird sie direkt zurückgegeben.
@@ -76,15 +26,15 @@ Stellen Sie sicher, dass sie die gewünschten Daten enthält und dass die Werte
///
-/// note | "Technische Details"
+/// note | Technische Details
Sie können auch `from starlette.responses import JSONResponse` verwenden.
-**FastAPI** bietet dieselben `starlette.responses` auch via `fastapi.responses` an, als Annehmlichkeit für Sie, den Entwickler. Die meisten verfügbaren Responses kommen aber direkt von Starlette. Das Gleiche gilt für `status`.
+**FastAPI** bietet dieselben `starlette.responses` auch via `fastapi.responses` an, als Annehmlichkeit für Sie, den Entwickler. Die meisten verfügbaren Responses kommen aber direkt von Starlette. Dasselbe gilt für `status`.
///
-## OpenAPI- und API-Dokumentation
+## OpenAPI- und API-Dokumentation { #openapi-and-api-docs }
Wenn Sie zusätzliche Statuscodes und Responses direkt zurückgeben, werden diese nicht in das OpenAPI-Schema (die API-Dokumentation) aufgenommen, da FastAPI keine Möglichkeit hat, im Voraus zu wissen, was Sie zurückgeben werden.
diff --git a/docs/de/docs/advanced/advanced-dependencies.md b/docs/de/docs/advanced/advanced-dependencies.md
index f299708726..da5f28c7c2 100644
--- a/docs/de/docs/advanced/advanced-dependencies.md
+++ b/docs/de/docs/advanced/advanced-dependencies.md
@@ -1,6 +1,6 @@
-# Fortgeschrittene Abhängigkeiten
+# Fortgeschrittene Abhängigkeiten { #advanced-dependencies }
-## Parametrisierte Abhängigkeiten
+## Parametrisierte Abhängigkeiten { #parameterized-dependencies }
Alle Abhängigkeiten, die wir bisher gesehen haben, waren festgelegte Funktionen oder Klassen.
@@ -10,119 +10,35 @@ Stellen wir uns vor, wir möchten eine Abhängigkeit haben, die prüft, ob ein Q
Aber wir wollen diesen vordefinierten Inhalt per Parameter festlegen können.
-## Eine „aufrufbare“ Instanz
+## Eine „aufrufbare“ Instanz { #a-callable-instance }
-In Python gibt es eine Möglichkeit, eine Instanz einer Klasse „aufrufbar“ („callable“) zu machen.
+In Python gibt es eine Möglichkeit, eine Instanz einer Klasse „aufrufbar“ zu machen.
Nicht die Klasse selbst (die bereits aufrufbar ist), sondern eine Instanz dieser Klasse.
Dazu deklarieren wir eine Methode `__call__`:
-//// tab | Python 3.9+
-
-```Python hl_lines="12"
-{!> ../../../docs_src/dependencies/tutorial011_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="11"
-{!> ../../../docs_src/dependencies/tutorial011_an.py!}
-```
-
-////
-
-//// tab | Python 3.8+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="10"
-{!> ../../../docs_src/dependencies/tutorial011.py!}
-```
-
-////
+{* ../../docs_src/dependencies/tutorial011_an_py39.py hl[12] *}
In diesem Fall ist dieses `__call__` das, was **FastAPI** verwendet, um nach zusätzlichen Parametern und Unterabhängigkeiten zu suchen, und das ist es auch, was später aufgerufen wird, um einen Wert an den Parameter in Ihrer *Pfadoperation-Funktion* zu übergeben.
-## Die Instanz parametrisieren
+## Die Instanz parametrisieren { #parameterize-the-instance }
-Und jetzt können wir `__init__` verwenden, um die Parameter der Instanz zu deklarieren, die wir zum `Parametrisieren` der Abhängigkeit verwenden können:
+Und jetzt können wir `__init__` verwenden, um die Parameter der Instanz zu deklarieren, die wir zum „Parametrisieren“ der Abhängigkeit verwenden können:
-//// tab | Python 3.9+
-
-```Python hl_lines="9"
-{!> ../../../docs_src/dependencies/tutorial011_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="8"
-{!> ../../../docs_src/dependencies/tutorial011_an.py!}
-```
-
-////
-
-//// tab | Python 3.8+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="7"
-{!> ../../../docs_src/dependencies/tutorial011.py!}
-```
-
-////
+{* ../../docs_src/dependencies/tutorial011_an_py39.py hl[9] *}
In diesem Fall wird **FastAPI** `__init__` nie berühren oder sich darum kümmern, wir werden es direkt in unserem Code verwenden.
-## Eine Instanz erstellen
+## Eine Instanz erstellen { #create-an-instance }
Wir könnten eine Instanz dieser Klasse erstellen mit:
-//// tab | Python 3.9+
-
-```Python hl_lines="18"
-{!> ../../../docs_src/dependencies/tutorial011_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="17"
-{!> ../../../docs_src/dependencies/tutorial011_an.py!}
-```
-
-////
-
-//// tab | Python 3.8+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="16"
-{!> ../../../docs_src/dependencies/tutorial011.py!}
-```
-
-////
+{* ../../docs_src/dependencies/tutorial011_an_py39.py hl[18] *}
Und auf diese Weise können wir unsere Abhängigkeit „parametrisieren“, die jetzt `"bar"` enthält, als das Attribut `checker.fixed_content`.
-## Die Instanz als Abhängigkeit verwenden
+## Die Instanz als Abhängigkeit verwenden { #use-the-instance-as-a-dependency }
Dann könnten wir diesen `checker` in einem `Depends(checker)` anstelle von `Depends(FixedContentQueryChecker)` verwenden, da die Abhängigkeit die Instanz `checker` und nicht die Klasse selbst ist.
@@ -134,37 +50,9 @@ checker(q="somequery")
... und übergibt, was immer das als Wert dieser Abhängigkeit in unserer *Pfadoperation-Funktion* zurückgibt, als den Parameter `fixed_content_included`:
-//// tab | Python 3.9+
+{* ../../docs_src/dependencies/tutorial011_an_py39.py hl[22] *}
-```Python hl_lines="22"
-{!> ../../../docs_src/dependencies/tutorial011_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="21"
-{!> ../../../docs_src/dependencies/tutorial011_an.py!}
-```
-
-////
-
-//// tab | Python 3.8+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="20"
-{!> ../../../docs_src/dependencies/tutorial011.py!}
-```
-
-////
-
-/// tip | "Tipp"
+/// tip | Tipp
Das alles mag gekünstelt wirken. Und es ist möglicherweise noch nicht ganz klar, welchen Nutzen das hat.
@@ -175,3 +63,91 @@ In den Kapiteln zum Thema Sicherheit gibt es Hilfsfunktionen, die auf die gleich
Wenn Sie das hier alles verstanden haben, wissen Sie bereits, wie diese Sicherheits-Hilfswerkzeuge unter der Haube funktionieren.
///
+
+## Abhängigkeiten mit `yield`, `HTTPException`, `except` und Hintergrundtasks { #dependencies-with-yield-httpexception-except-and-background-tasks }
+
+/// warning | Achtung
+
+Sie benötigen diese technischen Details höchstwahrscheinlich nicht.
+
+Diese Details sind hauptsächlich nützlich, wenn Sie eine FastAPI-Anwendung haben, die älter als 0.118.0 ist, und Sie auf Probleme mit Abhängigkeiten mit `yield` stoßen.
+
+///
+
+Abhängigkeiten mit `yield` haben sich im Laufe der Zeit weiterentwickelt, um verschiedene Anwendungsfälle abzudecken und einige Probleme zu beheben, hier ist eine Zusammenfassung der Änderungen.
+
+### Abhängigkeiten mit `yield` und `StreamingResponse`, Technische Details { #dependencies-with-yield-and-streamingresponse-technical-details }
+
+Vor FastAPI 0.118.0 wurde bei Verwendung einer Abhängigkeit mit `yield` der Exit-Code nach der *Pfadoperation-Funktion* ausgeführt, aber unmittelbar bevor die Response gesendet wurde.
+
+Die Absicht war, Ressourcen nicht länger als nötig zu halten, während darauf gewartet wird, dass die Response durchs Netzwerk reist.
+
+Diese Änderung bedeutete auch, dass bei Rückgabe einer `StreamingResponse` der Exit-Code der Abhängigkeit mit `yield` bereits ausgeführt worden wäre.
+
+Wenn Sie beispielsweise eine Datenbanksession in einer Abhängigkeit mit `yield` hatten, konnte die `StreamingResponse` diese Session während des Streamens von Daten nicht verwenden, weil die Session im Exit-Code nach `yield` bereits geschlossen worden wäre.
+
+Dieses Verhalten wurde in 0.118.0 zurückgenommen, sodass der Exit-Code nach `yield` ausgeführt wird, nachdem die Response gesendet wurde.
+
+/// info | Info
+
+Wie Sie unten sehen werden, ähnelt dies sehr dem Verhalten vor Version 0.106.0, jedoch mit mehreren Verbesserungen und Bugfixes für Sonderfälle.
+
+///
+
+#### Anwendungsfälle mit frühem Exit-Code { #use-cases-with-early-exit-code }
+
+Es gibt einige Anwendungsfälle mit spezifischen Bedingungen, die vom alten Verhalten profitieren könnten, den Exit-Code von Abhängigkeiten mit `yield` vor dem Senden der Response auszuführen.
+
+Stellen Sie sich zum Beispiel vor, Sie haben Code, der in einer Abhängigkeit mit `yield` eine Datenbanksession verwendet, nur um einen Benutzer zu verifizieren, die Datenbanksession wird aber in der *Pfadoperation-Funktion* nie wieder verwendet, sondern nur in der Abhängigkeit, und die Response benötigt lange, um gesendet zu werden, wie eine `StreamingResponse`, die Daten langsam sendet, aus irgendeinem Grund aber die Datenbank nicht verwendet.
+
+In diesem Fall würde die Datenbanksession gehalten, bis das Senden der Response abgeschlossen ist, aber wenn Sie sie nicht verwenden, wäre es nicht notwendig, sie zu halten.
+
+So könnte es aussehen:
+
+{* ../../docs_src/dependencies/tutorial013_an_py310.py *}
+
+Der Exit-Code, das automatische Schließen der `Session` in:
+
+{* ../../docs_src/dependencies/tutorial013_an_py310.py ln[19:21] *}
+
+... würde ausgeführt, nachdem die Response das langsame Senden der Daten beendet:
+
+{* ../../docs_src/dependencies/tutorial013_an_py310.py ln[30:38] hl[31:33] *}
+
+Da `generate_stream()` die Datenbanksession jedoch nicht verwendet, ist es nicht wirklich notwendig, die Session während des Sendens der Response offen zu halten.
+
+Wenn Sie diesen spezifischen Anwendungsfall mit SQLModel (oder SQLAlchemy) haben, könnten Sie die Session explizit schließen, nachdem Sie sie nicht mehr benötigen:
+
+{* ../../docs_src/dependencies/tutorial014_an_py310.py ln[24:28] hl[28] *}
+
+Auf diese Weise würde die Session die Datenbankverbindung freigeben, sodass andere Requests sie verwenden könnten.
+
+Wenn Sie einen anderen Anwendungsfall haben, der ein frühes Beenden aus einer Abhängigkeit mit `yield` benötigt, erstellen Sie bitte eine GitHub-Diskussion-Frage mit Ihrem spezifischen Anwendungsfall und warum Sie von einem frühen Schließen für Abhängigkeiten mit `yield` profitieren würden.
+
+Wenn es überzeugende Anwendungsfälle für ein frühes Schließen bei Abhängigkeiten mit `yield` gibt, würde ich erwägen, eine neue Möglichkeit hinzuzufügen, um ein frühes Schließen optional zu aktivieren.
+
+### Abhängigkeiten mit `yield` und `except`, Technische Details { #dependencies-with-yield-and-except-technical-details }
+
+Vor FastAPI 0.110.0 war es so, dass wenn Sie eine Abhängigkeit mit `yield` verwendet und dann in dieser Abhängigkeit mit `except` eine Exception abgefangen haben und die Exception nicht erneut geworfen haben, die Exception automatisch an beliebige Exceptionhandler oder den Handler für interne Serverfehler weitergereicht/weitergeworfen wurde.
+
+Dies wurde in Version 0.110.0 geändert, um unbehandelten Speicherverbrauch durch weitergeleitete Exceptions ohne Handler (interne Serverfehler) zu beheben und um es mit dem Verhalten von normalem Python-Code konsistent zu machen.
+
+### Hintergrundtasks und Abhängigkeiten mit `yield`, Technische Details { #background-tasks-and-dependencies-with-yield-technical-details }
+
+Vor FastAPI 0.106.0 war das Werfen von Exceptions nach `yield` nicht möglich, der Exit-Code in Abhängigkeiten mit `yield` wurde ausgeführt, nachdem die Response gesendet wurde, sodass [Exceptionhandler](../handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank} bereits ausgeführt worden wären.
+
+Dies war so designt, hauptsächlich um die Verwendung derselben von Abhängigkeiten „geyieldeten“ Objekte in Hintergrundtasks zu ermöglichen, da der Exit-Code erst ausgeführt wurde, nachdem die Hintergrundtasks abgeschlossen waren.
+
+Dies wurde in FastAPI 0.106.0 geändert mit der Absicht, keine Ressourcen zu halten, während darauf gewartet wird, dass die Response durchs Netzwerk reist.
+
+/// tip | Tipp
+
+Zusätzlich ist ein Hintergrundtask normalerweise ein unabhängiger Logikblock, der separat gehandhabt werden sollte, mit eigenen Ressourcen (z. B. eigener Datenbankverbindung).
+
+So haben Sie wahrscheinlich saubereren Code.
+
+///
+
+Wenn Sie sich bisher auf dieses Verhalten verlassen haben, sollten Sie jetzt die Ressourcen für Hintergrundtasks innerhalb des Hintergrundtasks selbst erstellen und intern nur Daten verwenden, die nicht von den Ressourcen von Abhängigkeiten mit `yield` abhängen.
+
+Anstatt beispielsweise dieselbe Datenbanksession zu verwenden, würden Sie innerhalb des Hintergrundtasks eine neue Datenbanksession erstellen und die Objekte aus der Datenbank mithilfe dieser neuen Session beziehen. Und anstatt das Objekt aus der Datenbank als Parameter an die Hintergrundtask-Funktion zu übergeben, würden Sie die ID dieses Objekts übergeben und das Objekt dann innerhalb der Hintergrundtask-Funktion erneut beziehen.
diff --git a/docs/de/docs/advanced/async-tests.md b/docs/de/docs/advanced/async-tests.md
index 9f0bd4aa2e..ad82452052 100644
--- a/docs/de/docs/advanced/async-tests.md
+++ b/docs/de/docs/advanced/async-tests.md
@@ -1,24 +1,24 @@
-# Asynchrone Tests
+# Asynchrone Tests { #async-tests }
-Sie haben bereits gesehen, wie Sie Ihre **FastAPI**-Anwendungen mit dem bereitgestellten `TestClient` testen. Bisher haben Sie nur gesehen, wie man synchrone Tests schreibt, ohne `async`hrone Funktionen zu verwenden.
+Sie haben bereits gesehen, wie Sie Ihre **FastAPI**-Anwendungen mit dem bereitgestellten `TestClient` testen. Bisher haben Sie nur gesehen, wie man synchrone Tests schreibt, ohne `async`-Funktionen zu verwenden.
-Die Möglichkeit, in Ihren Tests asynchrone Funktionen zu verwenden, könnte beispielsweise nützlich sein, wenn Sie Ihre Datenbank asynchron abfragen. Stellen Sie sich vor, Sie möchten das Senden von Requests an Ihre FastAPI-Anwendung testen und dann überprüfen, ob Ihr Backend die richtigen Daten erfolgreich in die Datenbank geschrieben hat, während Sie eine asynchrone Datenbankbibliothek verwenden.
+Die Möglichkeit, in Ihren Tests asynchrone Funktionen zu verwenden, könnte beispielsweise nützlich sein, wenn Sie Ihre Datenbank asynchron abfragen. Stellen Sie sich vor, Sie möchten das Senden von Requests an Ihre FastAPI-Anwendung testen und dann überprüfen, ob Ihr Backend die richtigen Daten erfolgreich in die Datenbank geschrieben hat, während Sie eine asynchrone Datenbankbibliothek verwenden.
Schauen wir uns an, wie wir das machen können.
-## pytest.mark.anyio
+## pytest.mark.anyio { #pytest-mark-anyio }
Wenn wir in unseren Tests asynchrone Funktionen aufrufen möchten, müssen unsere Testfunktionen asynchron sein. AnyIO stellt hierfür ein nettes Plugin zur Verfügung, mit dem wir festlegen können, dass einige Testfunktionen asynchron aufgerufen werden sollen.
-## HTTPX
+## HTTPX { #httpx }
-Auch wenn Ihre **FastAPI**-Anwendung normale `def`-Funktionen anstelle von `async def` verwendet, handelt es sich darunter immer noch um eine `async`hrone Anwendung.
+Auch wenn Ihre **FastAPI**-Anwendung normale `def`-Funktionen anstelle von `async def` verwendet, handelt es sich darunter immer noch um eine `async`-Anwendung.
-Der `TestClient` macht unter der Haube magisches, um die asynchrone FastAPI-Anwendung in Ihren normalen `def`-Testfunktionen, mithilfe von Standard-Pytest aufzurufen. Aber diese Magie funktioniert nicht mehr, wenn wir sie in asynchronen Funktionen verwenden. Durch die asynchrone Ausführung unserer Tests können wir den `TestClient` nicht mehr in unseren Testfunktionen verwenden.
+Der `TestClient` betreibt unter der Haube etwas Magie, um die asynchrone FastAPI-Anwendung in Ihren normalen `def`-Testfunktionen, mithilfe von Standard-Pytest aufzurufen. Aber diese Magie funktioniert nicht mehr, wenn wir sie in asynchronen Funktionen verwenden. Durch die asynchrone Ausführung unserer Tests können wir den `TestClient` nicht mehr in unseren Testfunktionen verwenden.
-Der `TestClient` basiert auf HTTPX und glücklicherweise können wir ihn direkt verwenden, um die API zu testen.
+Der `TestClient` basiert auf HTTPX und glücklicherweise können wir es direkt verwenden, um die API zu testen.
-## Beispiel
+## Beispiel { #example }
Betrachten wir als einfaches Beispiel eine Dateistruktur ähnlich der in [Größere Anwendungen](../tutorial/bigger-applications.md){.internal-link target=_blank} und [Testen](../tutorial/testing.md){.internal-link target=_blank}:
@@ -32,17 +32,13 @@ Betrachten wir als einfaches Beispiel eine Dateistruktur ähnlich der in [Größ
Die Datei `main.py` hätte als Inhalt:
-```Python
-{!../../../docs_src/async_tests/main.py!}
-```
+{* ../../docs_src/async_tests/main.py *}
Die Datei `test_main.py` hätte die Tests für `main.py`, das könnte jetzt so aussehen:
-```Python
-{!../../../docs_src/async_tests/test_main.py!}
-```
+{* ../../docs_src/async_tests/test_main.py *}
-## Es ausführen
+## Es ausführen { #run-it }
Sie können Ihre Tests wie gewohnt ausführen mit:
@@ -56,15 +52,13 @@ $ pytest
-## Details
+## Im Detail { #in-detail }
Der Marker `@pytest.mark.anyio` teilt pytest mit, dass diese Testfunktion asynchron aufgerufen werden soll:
-```Python hl_lines="7"
-{!../../../docs_src/async_tests/test_main.py!}
-```
+{* ../../docs_src/async_tests/test_main.py hl[7] *}
-/// tip | "Tipp"
+/// tip | Tipp
Beachten Sie, dass die Testfunktion jetzt `async def` ist und nicht nur `def` wie zuvor, wenn Sie den `TestClient` verwenden.
@@ -72,9 +66,7 @@ Beachten Sie, dass die Testfunktion jetzt `async def` ist und nicht nur `def` wi
Dann können wir einen `AsyncClient` mit der App erstellen und mit `await` asynchrone Requests an ihn senden.
-```Python hl_lines="9-10"
-{!../../../docs_src/async_tests/test_main.py!}
-```
+{* ../../docs_src/async_tests/test_main.py hl[9:12] *}
Das ist das Äquivalent zu:
@@ -84,23 +76,23 @@ response = client.get('/')
... welches wir verwendet haben, um unsere Requests mit dem `TestClient` zu machen.
-/// tip | "Tipp"
+/// tip | Tipp
Beachten Sie, dass wir async/await mit dem neuen `AsyncClient` verwenden – der Request ist asynchron.
///
-/// warning | "Achtung"
+/// warning | Achtung
Falls Ihre Anwendung auf Lifespan-Events angewiesen ist, der `AsyncClient` löst diese Events nicht aus. Um sicherzustellen, dass sie ausgelöst werden, verwenden Sie `LifespanManager` von florimondmanca/asgi-lifespan.
///
-## Andere asynchrone Funktionsaufrufe
+## Andere asynchrone Funktionsaufrufe { #other-asynchronous-function-calls }
-Da die Testfunktion jetzt asynchron ist, können Sie in Ihren Tests neben dem Senden von Requests an Ihre FastAPI-Anwendung jetzt auch andere `async`hrone Funktionen aufrufen (und `await`en), genau so, wie Sie diese an anderer Stelle in Ihrem Code aufrufen würden.
+Da die Testfunktion jetzt asynchron ist, können Sie in Ihren Tests neben dem Senden von Requests an Ihre FastAPI-Anwendung jetzt auch andere `async`-Funktionen aufrufen (und `await`en), genau so, wie Sie diese an anderer Stelle in Ihrem Code aufrufen würden.
-/// tip | "Tipp"
+/// tip | Tipp
Wenn Sie einen `RuntimeError: Task attached to a different loop` erhalten, wenn Sie asynchrone Funktionsaufrufe in Ihre Tests integrieren (z. B. bei Verwendung von MongoDBs MotorClient), dann denken Sie daran, Objekte zu instanziieren, die einen Event Loop nur innerhalb asynchroner Funktionen benötigen, z. B. einen `@app.on_event("startup")`-Callback.
diff --git a/docs/de/docs/advanced/behind-a-proxy.md b/docs/de/docs/advanced/behind-a-proxy.md
index 18f90ebde8..036916cbe2 100644
--- a/docs/de/docs/advanced/behind-a-proxy.md
+++ b/docs/de/docs/advanced/behind-a-proxy.md
@@ -1,34 +1,129 @@
-# Hinter einem Proxy
+# Hinter einem Proxy { #behind-a-proxy }
-In manchen Situationen müssen Sie möglicherweise einen **Proxy**-Server wie Traefik oder Nginx verwenden, mit einer Konfiguration, die ein zusätzliches Pfadpräfix hinzufügt, das von Ihrer Anwendung nicht gesehen wird.
+In vielen Situationen würden Sie einen **Proxy** wie Traefik oder Nginx vor Ihrer FastAPI-App verwenden.
-In diesen Fällen können Sie `root_path` verwenden, um Ihre Anwendung zu konfigurieren.
+Diese Proxys könnten HTTPS-Zertifikate und andere Dinge handhaben.
-Der `root_path` („Wurzelpfad“) ist ein Mechanismus, der von der ASGI-Spezifikation bereitgestellt wird (auf der FastAPI via Starlette aufbaut).
+## Proxy-Forwarded-Header { #proxy-forwarded-headers }
+
+Ein **Proxy** vor Ihrer Anwendung würde normalerweise einige Header on-the-fly setzen, bevor er die Requests an den **Server** sendet, um den Server wissen zu lassen, dass der Request vom Proxy **weitergeleitet** wurde, einschließlich der ursprünglichen (öffentlichen) URL, inklusive der Domain, dass HTTPS verwendet wird, usw.
+
+Das **Server**-Programm (z. B. **Uvicorn** via **FastAPI CLI**) ist in der Lage, diese Header zu interpretieren und diese Information dann an Ihre Anwendung weiterzugeben.
+
+Aber aus Sicherheitsgründen, da der Server nicht weiß, dass er hinter einem vertrauenswürdigen Proxy läuft, wird er diese Header nicht interpretieren.
+
+/// note | Technische Details
+
+Die Proxy-Header sind:
+
+* X-Forwarded-For
+* X-Forwarded-Proto
+* X-Forwarded-Host
+
+///
+
+### Proxy-Forwarded-Header aktivieren { #enable-proxy-forwarded-headers }
+
+Sie können FastAPI CLI mit der *CLI-Option* `--forwarded-allow-ips` starten und die IP-Adressen übergeben, denen vertraut werden soll, um diese Forwarded-Header zu lesen.
+
+Wenn Sie es auf `--forwarded-allow-ips="*"` setzen, würde es allen eingehenden IPs vertrauen.
+
+Wenn Ihr **Server** hinter einem vertrauenswürdigen **Proxy** sitzt und nur der Proxy mit ihm spricht, würde dies dazu führen, dass er die IP dieses **Proxys** akzeptiert, was auch immer sie ist.
+
+
-## Verfügbare Responses
+## Verfügbare Responses { #available-responses }
Hier sind einige der verfügbaren Responses.
Bedenken Sie, dass Sie `Response` verwenden können, um alles andere zurückzugeben, oder sogar eine benutzerdefinierte Unterklasse zu erstellen.
-/// note | "Technische Details"
+/// note | Technische Details
Sie können auch `from starlette.responses import HTMLResponse` verwenden.
@@ -129,7 +121,7 @@ Sie können auch `from starlette.responses import HTMLResponse` verwenden.
///
-### `Response`
+### `Response` { #response }
Die Hauptklasse `Response`, alle anderen Responses erben von ihr.
@@ -144,70 +136,71 @@ Sie akzeptiert die folgenden Parameter:
FastAPI (eigentlich Starlette) fügt automatisch einen Content-Length-Header ein. Außerdem wird es einen Content-Type-Header einfügen, der auf dem media_type basiert, und für Texttypen einen Zeichensatz (charset) anfügen.
-```Python hl_lines="1 18"
-{!../../../docs_src/response_directly/tutorial002.py!}
-```
+{* ../../docs_src/response_directly/tutorial002.py hl[1,18] *}
-### `HTMLResponse`
+### `HTMLResponse` { #htmlresponse }
Nimmt Text oder Bytes entgegen und gibt eine HTML-Response zurück, wie Sie oben gelesen haben.
-### `PlainTextResponse`
+### `PlainTextResponse` { #plaintextresponse }
Nimmt Text oder Bytes entgegen und gibt eine Plain-Text-Response zurück.
-```Python hl_lines="2 7 9"
-{!../../../docs_src/custom_response/tutorial005.py!}
-```
+{* ../../docs_src/custom_response/tutorial005.py hl[2,7,9] *}
-### `JSONResponse`
+### `JSONResponse` { #jsonresponse }
Nimmt einige Daten entgegen und gibt eine `application/json`-codierte Response zurück.
Dies ist die Standard-Response, die in **FastAPI** verwendet wird, wie Sie oben gelesen haben.
-### `ORJSONResponse`
+### `ORJSONResponse` { #orjsonresponse }
Eine schnelle alternative JSON-Response mit `orjson`, wie Sie oben gelesen haben.
-### `UJSONResponse`
+/// info | Info
+
+Dazu muss `orjson` installiert werden, z. B. mit `pip install orjson`.
+
+///
+
+### `UJSONResponse` { #ujsonresponse }
Eine alternative JSON-Response mit `ujson`.
-/// warning | "Achtung"
+/// info | Info
+
+Dazu muss `ujson` installiert werden, z. B. mit `pip install ujson`.
+
+///
+
+/// warning | Achtung
`ujson` ist bei der Behandlung einiger Sonderfälle weniger sorgfältig als Pythons eingebaute Implementierung.
///
-```Python hl_lines="2 7"
-{!../../../docs_src/custom_response/tutorial001.py!}
-```
+{* ../../docs_src/custom_response/tutorial001.py hl[2,7] *}
-/// tip | "Tipp"
+/// tip | Tipp
Möglicherweise ist `ORJSONResponse` eine schnellere Alternative.
///
-### `RedirectResponse`
+### `RedirectResponse` { #redirectresponse }
Gibt eine HTTP-Weiterleitung (HTTP-Redirect) zurück. Verwendet standardmäßig den Statuscode 307 – Temporäre Weiterleitung (Temporary Redirect).
Sie können eine `RedirectResponse` direkt zurückgeben:
-```Python hl_lines="2 9"
-{!../../../docs_src/custom_response/tutorial006.py!}
-```
+{* ../../docs_src/custom_response/tutorial006.py hl[2,9] *}
---
Oder Sie können sie im Parameter `response_class` verwenden:
-
-```Python hl_lines="2 7 9"
-{!../../../docs_src/custom_response/tutorial006b.py!}
-```
+{* ../../docs_src/custom_response/tutorial006b.py hl[2,7,9] *}
Wenn Sie das tun, können Sie die URL direkt von Ihrer *Pfadoperation*-Funktion zurückgeben.
@@ -217,45 +210,39 @@ In diesem Fall ist der verwendete `status_code` der Standardcode für die `Redir
Sie können den Parameter `status_code` auch in Kombination mit dem Parameter `response_class` verwenden:
-```Python hl_lines="2 7 9"
-{!../../../docs_src/custom_response/tutorial006c.py!}
-```
+{* ../../docs_src/custom_response/tutorial006c.py hl[2,7,9] *}
-### `StreamingResponse`
+### `StreamingResponse` { #streamingresponse }
Nimmt einen asynchronen Generator oder einen normalen Generator/Iterator und streamt den Responsebody.
-```Python hl_lines="2 14"
-{!../../../docs_src/custom_response/tutorial007.py!}
-```
+{* ../../docs_src/custom_response/tutorial007.py hl[2,14] *}
-#### Verwendung von `StreamingResponse` mit dateiähnlichen Objekten
+#### Verwendung von `StreamingResponse` mit dateiartigen Objekten { #using-streamingresponse-with-file-like-objects }
-Wenn Sie ein dateiähnliches (file-like) Objekt haben (z. B. das von `open()` zurückgegebene Objekt), können Sie eine Generatorfunktion erstellen, um über dieses dateiähnliche Objekt zu iterieren.
+Wenn Sie ein dateiartiges (file-like) Objekt haben (z. B. das von `open()` zurückgegebene Objekt), können Sie eine Generatorfunktion erstellen, um über dieses dateiartige Objekt zu iterieren.
Auf diese Weise müssen Sie nicht alles zuerst in den Arbeitsspeicher lesen und können diese Generatorfunktion an `StreamingResponse` übergeben und zurückgeben.
Das umfasst viele Bibliotheken zur Interaktion mit Cloud-Speicher, Videoverarbeitung und anderen.
-```{ .python .annotate hl_lines="2 10-12 14" }
-{!../../../docs_src/custom_response/tutorial008.py!}
-```
+{* ../../docs_src/custom_response/tutorial008.py hl[2,10:12,14] *}
1. Das ist die Generatorfunktion. Es handelt sich um eine „Generatorfunktion“, da sie `yield`-Anweisungen enthält.
-2. Durch die Verwendung eines `with`-Blocks stellen wir sicher, dass das dateiähnliche Objekt geschlossen wird, nachdem die Generatorfunktion fertig ist. Also, nachdem sie mit dem Senden der Response fertig ist.
+2. Durch die Verwendung eines `with`-Blocks stellen wir sicher, dass das dateiartige Objekt geschlossen wird, nachdem die Generatorfunktion fertig ist. Also, nachdem sie mit dem Senden der Response fertig ist.
3. Dieses `yield from` weist die Funktion an, über das Ding namens `file_like` zu iterieren. Und dann für jeden iterierten Teil, diesen Teil so zurückzugeben, als wenn er aus dieser Generatorfunktion (`iterfile`) stammen würde.
Es handelt sich also hier um eine Generatorfunktion, die die „generierende“ Arbeit intern auf etwas anderes überträgt.
Auf diese Weise können wir das Ganze in einen `with`-Block einfügen und so sicherstellen, dass das dateiartige Objekt nach Abschluss geschlossen wird.
-/// tip | "Tipp"
+/// tip | Tipp
Beachten Sie, dass wir, da wir Standard-`open()` verwenden, welches `async` und `await` nicht unterstützt, hier die Pfadoperation mit normalen `def` deklarieren.
///
-### `FileResponse`
+### `FileResponse` { #fileresponse }
Streamt eine Datei asynchron als Response.
@@ -268,19 +255,15 @@ Nimmt zur Instanziierung einen anderen Satz von Argumenten entgegen als die ande
Datei-Responses enthalten die entsprechenden `Content-Length`-, `Last-Modified`- und `ETag`-Header.
-```Python hl_lines="2 10"
-{!../../../docs_src/custom_response/tutorial009.py!}
-```
+{* ../../docs_src/custom_response/tutorial009.py hl[2,10] *}
Sie können auch den Parameter `response_class` verwenden:
-```Python hl_lines="2 8 10"
-{!../../../docs_src/custom_response/tutorial009b.py!}
-```
+{* ../../docs_src/custom_response/tutorial009b.py hl[2,8,10] *}
In diesem Fall können Sie den Dateipfad direkt von Ihrer *Pfadoperation*-Funktion zurückgeben.
-## Benutzerdefinierte Response-Klasse
+## Benutzerdefinierte Response-Klasse { #custom-response-class }
Sie können Ihre eigene benutzerdefinierte Response-Klasse erstellen, die von `Response` erbt und diese verwendet.
@@ -290,9 +273,7 @@ Sie möchten etwa, dass Ihre Response eingerücktes und formatiertes JSON zurüc
Sie könnten eine `CustomORJSONResponse` erstellen. Das Wichtigste, was Sie tun müssen, ist, eine `Response.render(content)`-Methode zu erstellen, die den Inhalt als `bytes` zurückgibt:
-```Python hl_lines="9-14 17"
-{!../../../docs_src/custom_response/tutorial009c.py!}
-```
+{* ../../docs_src/custom_response/tutorial009c.py hl[9:14,17] *}
Statt:
@@ -310,7 +291,7 @@ Statt:
Natürlich werden Sie wahrscheinlich viel bessere Möglichkeiten finden, Vorteil daraus zu ziehen, als JSON zu formatieren. 😉
-## Standard-Response-Klasse
+## Standard-Response-Klasse { #default-response-class }
Beim Erstellen einer **FastAPI**-Klasseninstanz oder eines `APIRouter`s können Sie angeben, welche Response-Klasse standardmäßig verwendet werden soll.
@@ -318,16 +299,14 @@ Der Parameter, der das definiert, ist `default_response_class`.
Im folgenden Beispiel verwendet **FastAPI** standardmäßig `ORJSONResponse` in allen *Pfadoperationen*, anstelle von `JSONResponse`.
-```Python hl_lines="2 4"
-{!../../../docs_src/custom_response/tutorial010.py!}
-```
+{* ../../docs_src/custom_response/tutorial010.py hl[2,4] *}
-/// tip | "Tipp"
+/// tip | Tipp
Sie können dennoch weiterhin `response_class` in *Pfadoperationen* überschreiben, wie bisher.
///
-## Zusätzliche Dokumentation
+## Zusätzliche Dokumentation { #additional-documentation }
Sie können auch den Medientyp und viele andere Details in OpenAPI mit `responses` deklarieren: [Zusätzliche Responses in OpenAPI](additional-responses.md){.internal-link target=_blank}.
diff --git a/docs/de/docs/advanced/dataclasses.md b/docs/de/docs/advanced/dataclasses.md
index d5a6634852..12ea8e9eca 100644
--- a/docs/de/docs/advanced/dataclasses.md
+++ b/docs/de/docs/advanced/dataclasses.md
@@ -1,26 +1,24 @@
-# Verwendung von Datenklassen
+# Verwendung von Datenklassen { #using-dataclasses }
-FastAPI basiert auf **Pydantic** und ich habe Ihnen gezeigt, wie Sie Pydantic-Modelle verwenden können, um Requests und Responses zu deklarieren.
+FastAPI basiert auf **Pydantic**, und ich habe Ihnen gezeigt, wie Sie Pydantic-Modelle verwenden können, um Requests und Responses zu deklarieren.
Aber FastAPI unterstützt auf die gleiche Weise auch die Verwendung von `dataclasses`:
-```Python hl_lines="1 7-12 19-20"
-{!../../../docs_src/dataclasses/tutorial001.py!}
-```
+{* ../../docs_src/dataclasses/tutorial001.py hl[1,7:12,19:20] *}
-Das ist dank **Pydantic** ebenfalls möglich, da es `dataclasses` intern unterstützt.
+Das ist dank **Pydantic** ebenfalls möglich, da es `dataclasses` intern unterstützt.
-Auch wenn im obige Code Pydantic nicht explizit vorkommt, verwendet FastAPI Pydantic, um diese Standard-Datenklassen in Pydantics eigene Variante von Datenklassen zu konvertieren.
+Auch wenn im obigen Code Pydantic nicht explizit vorkommt, verwendet FastAPI Pydantic, um diese Standard-Datenklassen in Pydantics eigene Variante von Datenklassen zu konvertieren.
Und natürlich wird das gleiche unterstützt:
-* Validierung der Daten
-* Serialisierung der Daten
-* Dokumentation der Daten, usw.
+* Datenvalidierung
+* Datenserialisierung
+* Datendokumentation, usw.
Das funktioniert genauso wie mit Pydantic-Modellen. Und tatsächlich wird es unter der Haube mittels Pydantic auf die gleiche Weise bewerkstelligt.
-/// info
+/// info | Info
Bedenken Sie, dass Datenklassen nicht alles können, was Pydantic-Modelle können.
@@ -30,13 +28,11 @@ Wenn Sie jedoch eine Menge Datenklassen herumliegen haben, ist dies ein guter Tr
///
-## Datenklassen als `response_model`
+## Datenklassen in `response_model` { #dataclasses-in-response-model }
Sie können `dataclasses` auch im Parameter `response_model` verwenden:
-```Python hl_lines="1 7-13 19"
-{!../../../docs_src/dataclasses/tutorial002.py!}
-```
+{* ../../docs_src/dataclasses/tutorial002.py hl[1,7:13,19] *}
Die Datenklasse wird automatisch in eine Pydantic-Datenklasse konvertiert.
@@ -44,7 +40,7 @@ Auf diese Weise wird deren Schema in der Benutzeroberfläche der API-Dokumentati
-## Datenklassen in verschachtelten Datenstrukturen
+## Datenklassen in verschachtelten Datenstrukturen { #dataclasses-in-nested-data-structures }
Sie können `dataclasses` auch mit anderen Typannotationen kombinieren, um verschachtelte Datenstrukturen zu erstellen.
@@ -52,9 +48,7 @@ In einigen Fällen müssen Sie möglicherweise immer noch Pydantics Version von
In diesem Fall können Sie einfach die Standard-`dataclasses` durch `pydantic.dataclasses` ersetzen, was einen direkten Ersatz darstellt:
-```{ .python .annotate hl_lines="1 5 8-11 14-17 23-25 28" }
-{!../../../docs_src/dataclasses/tutorial003.py!}
-```
+{* ../../docs_src/dataclasses/tutorial003.py hl[1,5,8:11,14:17,23:25,28] *}
1. Wir importieren `field` weiterhin von Standard-`dataclasses`.
@@ -68,7 +62,7 @@ In diesem Fall können Sie einfach die Standard-`dataclasses` durch `pydantic.da
In diesem Fall handelt es sich um eine Liste von `Item`-Datenklassen.
-6. Hier geben wir ein Dictionary zurück, das `items` enthält, welches eine Liste von Datenklassen ist.
+6. Hier geben wir ein Dictionary zurück, das `items` enthält, welches eine Liste von Datenklassen ist.
FastAPI ist weiterhin in der Lage, die Daten nach JSON zu serialisieren.
@@ -80,7 +74,7 @@ In diesem Fall können Sie einfach die Standard-`dataclasses` durch `pydantic.da
Wie immer können Sie in FastAPI `def` und `async def` beliebig kombinieren.
- Wenn Sie eine Auffrischung darüber benötigen, wann welche Anwendung sinnvoll ist, lesen Sie den Abschnitt „In Eile?“ in der Dokumentation zu [`async` und `await`](../async.md#in-eile){.internal-link target=_blank}.
+ Wenn Sie eine Auffrischung darüber benötigen, wann welche Anwendung sinnvoll ist, lesen Sie den Abschnitt „In Eile?“ in der Dokumentation zu [`async` und `await`](../async.md#in-a-hurry){.internal-link target=_blank}.
9. Diese *Pfadoperation-Funktion* gibt keine Datenklassen zurück (obwohl dies möglich wäre), sondern eine Liste von Dictionarys mit internen Daten.
@@ -90,12 +84,12 @@ Sie können `dataclasses` mit anderen Typannotationen auf vielfältige Weise kom
Weitere Einzelheiten finden Sie in den Bemerkungen im Quellcode oben.
-## Mehr erfahren
+## Mehr erfahren { #learn-more }
Sie können `dataclasses` auch mit anderen Pydantic-Modellen kombinieren, von ihnen erben, sie in Ihre eigenen Modelle einbinden, usw.
-Weitere Informationen finden Sie in der Pydantic-Dokumentation zu Datenklassen.
+Weitere Informationen finden Sie in der Pydantic-Dokumentation zu Datenklassen.
-## Version
+## Version { #version }
Dies ist verfügbar seit FastAPI-Version `0.67.0`. 🔖
diff --git a/docs/de/docs/advanced/events.md b/docs/de/docs/advanced/events.md
index e898db49b2..f94526b4fc 100644
--- a/docs/de/docs/advanced/events.md
+++ b/docs/de/docs/advanced/events.md
@@ -1,14 +1,14 @@
-# Lifespan-Events
+# Lifespan-Events { #lifespan-events }
-Sie können Logik (Code) definieren, die ausgeführt werden soll, bevor die Anwendung **hochfährt**. Dies bedeutet, dass dieser Code **einmal** ausgeführt wird, **bevor** die Anwendung **beginnt, Requests entgegenzunehmen**.
+Sie können Logik (Code) definieren, die ausgeführt werden soll, bevor die Anwendung **hochfährt**. Dies bedeutet, dass dieser Code **einmal** ausgeführt wird, **bevor** die Anwendung **beginnt, Requests entgegenzunehmen**.
Auf die gleiche Weise können Sie Logik (Code) definieren, die ausgeführt werden soll, wenn die Anwendung **heruntergefahren** wird. In diesem Fall wird dieser Code **einmal** ausgeführt, **nachdem** möglicherweise **viele Requests** bearbeitet wurden.
-Da dieser Code ausgeführt wird, bevor die Anwendung **beginnt**, Requests entgegenzunehmen, und unmittelbar, nachdem sie die Bearbeitung von Requests **abgeschlossen hat**, deckt er die gesamte **Lebensdauer – „Lifespan“** – der Anwendung ab (das Wort „Lifespan“ wird gleich wichtig sein 😉).
+Da dieser Code ausgeführt wird, bevor die Anwendung **beginnt**, Requests entgegenzunehmen, und unmittelbar, nachdem sie die Bearbeitung von Requests **abgeschlossen hat**, deckt er den gesamten Anwendungs-**Lifespan** ab (das Wort „Lifespan“ wird gleich wichtig sein 😉).
-Dies kann sehr nützlich sein, um **Ressourcen** einzurichten, die Sie in der gesamten Anwendung verwenden wollen und die von Requests **gemeinsam genutzt** werden und/oder die Sie anschließend **aufräumen** müssen. Zum Beispiel ein Pool von Datenbankverbindungen oder das Laden eines gemeinsam genutzten Modells für maschinelles Lernen.
+Dies kann sehr nützlich sein, um **Ressourcen** einzurichten, die Sie in der gesamten App verwenden wollen und die von Requests **gemeinsam genutzt** werden und/oder die Sie anschließend **aufräumen** müssen. Zum Beispiel ein Pool von Datenbankverbindungen oder das Laden eines gemeinsam genutzten Modells für maschinelles Lernen.
-## Anwendungsfall
+## Anwendungsfall { #use-case }
Beginnen wir mit einem Beispiel-**Anwendungsfall** und schauen uns dann an, wie wir ihn mit dieser Methode implementieren können.
@@ -22,51 +22,45 @@ Sie könnten das auf der obersten Ebene des Moduls/der Datei machen, aber das w
Das wollen wir besser machen: Laden wir das Modell, bevor die Requests bearbeitet werden, aber unmittelbar bevor die Anwendung beginnt, Requests zu empfangen, und nicht, während der Code geladen wird.
-## Lifespan
+## Lifespan { #lifespan }
-Sie können diese Logik beim *Hochfahren* und *Herunterfahren* mithilfe des `lifespan`-Parameters der `FastAPI`-App und eines „Kontextmanagers“ definieren (ich zeige Ihnen gleich, was das ist).
+Sie können diese Logik beim *Startup* und *Shutdown* mithilfe des `lifespan`-Parameters der `FastAPI`-App und eines „Kontextmanagers“ definieren (ich zeige Ihnen gleich, was das ist).
Beginnen wir mit einem Beispiel und sehen es uns dann im Detail an.
Wir erstellen eine asynchrone Funktion `lifespan()` mit `yield` wie folgt:
-```Python hl_lines="16 19"
-{!../../../docs_src/events/tutorial003.py!}
-```
+{* ../../docs_src/events/tutorial003.py hl[16,19] *}
-Hier simulieren wir das langsame *Hochfahren*, das Laden des Modells, indem wir die (Fake-)Modellfunktion vor dem `yield` in das Dictionary mit Modellen für maschinelles Lernen einfügen. Dieser Code wird ausgeführt, **bevor** die Anwendung **beginnt, Requests entgegenzunehmen**, während des *Hochfahrens*.
+Hier simulieren wir den langsamen *Startup*, das Laden des Modells, indem wir die (Fake-)Modellfunktion vor dem `yield` in das Dictionary mit Modellen für maschinelles Lernen einfügen. Dieser Code wird ausgeführt, **bevor** die Anwendung **beginnt, Requests entgegenzunehmen**, während des *Startups*.
-Und dann, direkt nach dem `yield`, entladen wir das Modell. Dieser Code wird unmittelbar vor dem *Herunterfahren* ausgeführt, **nachdem** die Anwendung **die Bearbeitung von Requests abgeschlossen hat**. Dadurch könnten beispielsweise Ressourcen wie Arbeitsspeicher oder eine GPU freigegeben werden.
+Und dann, direkt nach dem `yield`, entladen wir das Modell. Dieser Code wird ausgeführt, **nachdem** die Anwendung **die Bearbeitung von Requests abgeschlossen hat**, direkt vor dem *Shutdown*. Dadurch könnten beispielsweise Ressourcen wie Arbeitsspeicher oder eine GPU freigegeben werden.
-/// tip | "Tipp"
+/// tip | Tipp
-Das *Herunterfahren* würde erfolgen, wenn Sie die Anwendung **stoppen**.
+Das `shutdown` würde erfolgen, wenn Sie die Anwendung **stoppen**.
Möglicherweise müssen Sie eine neue Version starten, oder Sie haben es einfach satt, sie auszuführen. 🤷
///
-### Lifespan-Funktion
+### Lifespan-Funktion { #lifespan-function }
Das Erste, was auffällt, ist, dass wir eine asynchrone Funktion mit `yield` definieren. Das ist sehr ähnlich zu Abhängigkeiten mit `yield`.
-```Python hl_lines="14-19"
-{!../../../docs_src/events/tutorial003.py!}
-```
+{* ../../docs_src/events/tutorial003.py hl[14:19] *}
Der erste Teil der Funktion, vor dem `yield`, wird ausgeführt **bevor** die Anwendung startet.
Und der Teil nach `yield` wird ausgeführt, **nachdem** die Anwendung beendet ist.
-### Asynchroner Kontextmanager
+### Asynchroner Kontextmanager { #async-context-manager }
Wie Sie sehen, ist die Funktion mit einem `@asynccontextmanager` versehen.
Dadurch wird die Funktion in einen sogenannten „**asynchronen Kontextmanager**“ umgewandelt.
-```Python hl_lines="1 13"
-{!../../../docs_src/events/tutorial003.py!}
-```
+{* ../../docs_src/events/tutorial003.py hl[1,13] *}
Ein **Kontextmanager** in Python ist etwas, das Sie in einer `with`-Anweisung verwenden können, zum Beispiel kann `open()` als Kontextmanager verwendet werden:
@@ -88,57 +82,51 @@ In unserem obigen Codebeispiel verwenden wir ihn nicht direkt, sondern übergebe
Der Parameter `lifespan` der `FastAPI`-App benötigt einen **asynchronen Kontextmanager**, wir können ihm also unseren neuen asynchronen Kontextmanager `lifespan` übergeben.
-```Python hl_lines="22"
-{!../../../docs_src/events/tutorial003.py!}
-```
+{* ../../docs_src/events/tutorial003.py hl[22] *}
-## Alternative Events (deprecated)
+## Alternative Events (deprecatet) { #alternative-events-deprecated }
-/// warning | "Achtung"
+/// warning | Achtung
-Der empfohlene Weg, das *Hochfahren* und *Herunterfahren* zu handhaben, ist die Verwendung des `lifespan`-Parameters der `FastAPI`-App, wie oben beschrieben. Wenn Sie einen `lifespan`-Parameter übergeben, werden die `startup`- und `shutdown`-Eventhandler nicht mehr aufgerufen. Es ist entweder alles `lifespan` oder alles Events, nicht beides.
+Der empfohlene Weg, den *Startup* und *Shutdown* zu handhaben, ist die Verwendung des `lifespan`-Parameters der `FastAPI`-App, wie oben beschrieben. Wenn Sie einen `lifespan`-Parameter übergeben, werden die `startup`- und `shutdown`-Eventhandler nicht mehr aufgerufen. Es ist entweder alles `lifespan` oder alles Events, nicht beides.
Sie können diesen Teil wahrscheinlich überspringen.
///
-Es gibt eine alternative Möglichkeit, diese Logik zu definieren, sodass sie beim *Hochfahren* und beim *Herunterfahren* ausgeführt wird.
+Es gibt eine alternative Möglichkeit, diese Logik zu definieren, sodass sie beim *Startup* und beim *Shutdown* ausgeführt wird.
-Sie können Eventhandler (Funktionen) definieren, die ausgeführt werden sollen, bevor die Anwendung hochgefahren wird oder wenn die Anwendung heruntergefahren wird.
+Sie können Eventhandler (Funktionen) definieren, die ausgeführt werden sollen, bevor die Anwendung hochgefahren wird oder wenn die Anwendung heruntergefahren wird.
Diese Funktionen können mit `async def` oder normalem `def` deklariert werden.
-### `startup`-Event
+### `startup`-Event { #startup-event }
Um eine Funktion hinzuzufügen, die vor dem Start der Anwendung ausgeführt werden soll, deklarieren Sie diese mit dem Event `startup`:
-```Python hl_lines="8"
-{!../../../docs_src/events/tutorial001.py!}
-```
+{* ../../docs_src/events/tutorial001.py hl[8] *}
In diesem Fall initialisiert die Eventhandler-Funktion `startup` die „Datenbank“ der Items (nur ein `dict`) mit einigen Werten.
Sie können mehr als eine Eventhandler-Funktion hinzufügen.
-Und Ihre Anwendung empfängt erst dann Anfragen, wenn alle `startup`-Eventhandler abgeschlossen sind.
+Und Ihre Anwendung empfängt erst dann Requests, wenn alle `startup`-Eventhandler abgeschlossen sind.
-### `shutdown`-Event
+### `shutdown`-Event { #shutdown-event }
-Um eine Funktion hinzuzufügen, die beim Herunterfahren der Anwendung ausgeführt werden soll, deklarieren Sie sie mit dem Event `shutdown`:
+Um eine Funktion hinzuzufügen, die beim Shutdown der Anwendung ausgeführt werden soll, deklarieren Sie sie mit dem Event `shutdown`:
-```Python hl_lines="6"
-{!../../../docs_src/events/tutorial002.py!}
-```
+{* ../../docs_src/events/tutorial002.py hl[6] *}
Hier schreibt die `shutdown`-Eventhandler-Funktion eine Textzeile `"Application shutdown"` in eine Datei `log.txt`.
-/// info
+/// info | Info
In der Funktion `open()` bedeutet `mode="a"` „append“ („anhängen“), sodass die Zeile nach dem, was sich in dieser Datei befindet, hinzugefügt wird, ohne den vorherigen Inhalt zu überschreiben.
///
-/// tip | "Tipp"
+/// tip | Tipp
Beachten Sie, dass wir in diesem Fall eine Standard-Python-Funktion `open()` verwenden, die mit einer Datei interagiert.
@@ -150,28 +138,28 @@ Daher deklarieren wir die Eventhandler-Funktion mit Standard-`def` statt mit `as
///
-### `startup` und `shutdown` zusammen
+### `startup` und `shutdown` zusammen { #startup-and-shutdown-together }
-Es besteht eine hohe Wahrscheinlichkeit, dass die Logik für Ihr *Hochfahren* und *Herunterfahren* miteinander verknüpft ist. Vielleicht möchten Sie etwas beginnen und es dann beenden, eine Ressource laden und sie dann freigeben usw.
+Es besteht eine hohe Wahrscheinlichkeit, dass die Logik für Ihr *Startup* und *Shutdown* miteinander verknüpft ist. Vielleicht möchten Sie etwas beginnen und es dann beenden, eine Ressource laden und sie dann freigeben usw.
Bei getrennten Funktionen, die keine gemeinsame Logik oder Variablen haben, ist dies schwieriger, da Sie Werte in globalen Variablen speichern oder ähnliche Tricks verwenden müssen.
Aus diesem Grund wird jetzt empfohlen, stattdessen `lifespan` wie oben erläutert zu verwenden.
-## Technische Details
+## Technische Details { #technical-details }
Nur ein technisches Detail für die neugierigen Nerds. 🤓
In der technischen ASGI-Spezifikation ist dies Teil des Lifespan Protokolls und definiert Events namens `startup` und `shutdown`.
-/// info
+/// info | Info
-Weitere Informationen zu Starlettes `lifespan`-Handlern finden Sie in Starlettes Lifespan-Dokumentation.
+Weitere Informationen zu Starlettes `lifespan`-Handlern finden Sie in Starlettes Lifespan-Dokumentation.
Einschließlich, wie man Lifespan-Zustand handhabt, der in anderen Bereichen Ihres Codes verwendet werden kann.
///
-## Unteranwendungen
+## Unteranwendungen { #sub-applications }
-🚨 Beachten Sie, dass diese Lifespan-Events (Hochfahren und Herunterfahren) nur für die Hauptanwendung ausgeführt werden, nicht für [Unteranwendungen – Mounts](sub-applications.md){.internal-link target=_blank}.
+🚨 Beachten Sie, dass diese Lifespan-Events (Startup und Shutdown) nur für die Hauptanwendung ausgeführt werden, nicht für [Unteranwendungen – Mounts](sub-applications.md){.internal-link target=_blank}.
diff --git a/docs/de/docs/advanced/generate-clients.md b/docs/de/docs/advanced/generate-clients.md
index b8d66fdd70..d8836295b6 100644
--- a/docs/de/docs/advanced/generate-clients.md
+++ b/docs/de/docs/advanced/generate-clients.md
@@ -1,135 +1,86 @@
-# Clients generieren
+# SDKs generieren { #generating-sdks }
-Da **FastAPI** auf der OpenAPI-Spezifikation basiert, erhalten Sie automatische Kompatibilität mit vielen Tools, einschließlich der automatischen API-Dokumentation (bereitgestellt von Swagger UI).
+Da **FastAPI** auf der **OpenAPI**-Spezifikation basiert, können dessen APIs in einem standardisierten Format beschrieben werden, das viele Tools verstehen.
-Ein besonderer Vorteil, der nicht unbedingt offensichtlich ist, besteht darin, dass Sie für Ihre API **Clients generieren** können (manchmal auch **SDKs** genannt), für viele verschiedene **Programmiersprachen**.
+Dies vereinfacht es, aktuelle **Dokumentation** und Client-Bibliotheken (**SDKs**) in verschiedenen Sprachen zu generieren sowie **Test-** oder **Automatisierungs-Workflows**, die mit Ihrem Code synchron bleiben.
-## OpenAPI-Client-Generatoren
+In diesem Leitfaden erfahren Sie, wie Sie ein **TypeScript-SDK** für Ihr FastAPI-Backend generieren.
-Es gibt viele Tools zum Generieren von Clients aus **OpenAPI**.
+## Open Source SDK-Generatoren { #open-source-sdk-generators }
-Ein gängiges Tool ist OpenAPI Generator.
+Eine vielseitige Möglichkeit ist der OpenAPI Generator, der **viele Programmiersprachen** unterstützt und SDKs aus Ihrer OpenAPI-Spezifikation generieren kann.
-Wenn Sie ein **Frontend** erstellen, ist openapi-ts eine sehr interessante Alternative.
+Für **TypeScript-Clients** ist Hey API eine speziell entwickelte Lösung, die ein optimiertes Erlebnis für das TypeScript-Ökosystem bietet.
-## Client- und SDK-Generatoren – Sponsor
+Weitere SDK-Generatoren finden Sie auf OpenAPI.Tools.
-Es gibt auch einige **vom Unternehmen entwickelte** Client- und SDK-Generatoren, die auf OpenAPI (FastAPI) basieren. In einigen Fällen können diese Ihnen **weitere Funktionalität** zusätzlich zu qualitativ hochwertigen generierten SDKs/Clients bieten.
+/// tip | Tipp
-Einige von diesen ✨ [**sponsern FastAPI**](../help-fastapi.md#den-autor-sponsern){.internal-link target=_blank} ✨, das gewährleistet die kontinuierliche und gesunde **Entwicklung** von FastAPI und seinem **Ökosystem**.
+FastAPI generiert automatisch **OpenAPI 3.1**-Spezifikationen, daher muss jedes von Ihnen verwendete Tool diese Version unterstützen.
-Und es zeigt deren wahres Engagement für FastAPI und seine **Community** (Sie), da diese Ihnen nicht nur einen **guten Service** bieten möchten, sondern auch sicherstellen möchten, dass Sie über ein **gutes und gesundes Framework** verfügen, FastAPI. 🙇
+///
-Beispielsweise könnten Sie Speakeasy ausprobieren.
+## SDK-Generatoren von FastAPI-Sponsoren { #sdk-generators-from-fastapi-sponsors }
-Es gibt auch mehrere andere Unternehmen, welche ähnliche Dienste anbieten und die Sie online suchen und finden können. 🤓
+Dieser Abschnitt hebt **venture-unterstützte** und **firmengestützte** Lösungen hervor, die von Unternehmen entwickelt werden, welche FastAPI sponsern. Diese Produkte bieten **zusätzliche Funktionen** und **Integrationen** zusätzlich zu hochwertig generierten SDKs.
-## Einen TypeScript-Frontend-Client generieren
+Durch das ✨ [**Sponsoring von FastAPI**](../help-fastapi.md#sponsor-the-author){.internal-link target=_blank} ✨ helfen diese Unternehmen sicherzustellen, dass das Framework und sein **Ökosystem** gesund und **nachhaltig** bleiben.
+
+Ihr Sponsoring zeigt auch ein starkes Engagement für die FastAPI-**Community** (Sie), was bedeutet, dass sie nicht nur einen **großartigen Service** bieten möchten, sondern auch ein **robustes und florierendes Framework**, FastAPI, unterstützen möchten. 🙇
+
+Zum Beispiel könnten Sie ausprobieren:
+
+* Speakeasy
+* Stainless
+* liblab
+
+Einige dieser Lösungen sind möglicherweise auch Open Source oder bieten kostenlose Tarife an, sodass Sie diese ohne finanzielle Verpflichtung ausprobieren können. Andere kommerzielle SDK-Generatoren sind online verfügbar und können dort gefunden werden. 🤓
+
+## Ein TypeScript-SDK erstellen { #create-a-typescript-sdk }
Beginnen wir mit einer einfachen FastAPI-Anwendung:
-//// tab | Python 3.9+
+{* ../../docs_src/generate_clients/tutorial001_py39.py hl[7:9,12:13,16:17,21] *}
-```Python hl_lines="7-9 12-13 16-17 21"
-{!> ../../../docs_src/generate_clients/tutorial001_py39.py!}
-```
+Beachten Sie, dass die *Pfadoperationen* die Modelle definieren, die sie für die Request- und Response-Payload verwenden, indem sie die Modelle `Item` und `ResponseMessage` verwenden.
-////
+### API-Dokumentation { #api-docs }
-//// tab | Python 3.8+
-
-```Python hl_lines="9-11 14-15 18 19 23"
-{!> ../../../docs_src/generate_clients/tutorial001.py!}
-```
-
-////
-
-Beachten Sie, dass die *Pfadoperationen* die Modelle definieren, welche diese für die Request- und Response-Payload verwenden, indem sie die Modelle `Item` und `ResponseMessage` verwenden.
-
-### API-Dokumentation
-
-Wenn Sie zur API-Dokumentation gehen, werden Sie sehen, dass diese die **Schemas** für die Daten enthält, welche in Requests gesendet und in Responses empfangen werden:
+Wenn Sie zu `/docs` gehen, sehen Sie, dass es die **Schemas** für die Daten enthält, die in Requests gesendet und in Responses empfangen werden:
-Sie können diese Schemas sehen, da sie mit den Modellen in der Anwendung deklariert wurden.
+Sie können diese Schemas sehen, da sie mit den Modellen in der App deklariert wurden.
-Diese Informationen sind im **OpenAPI-Schema** der Anwendung verfügbar und werden dann in der API-Dokumentation angezeigt (von Swagger UI).
+Diese Informationen sind im **OpenAPI-Schema** der Anwendung verfügbar und werden in der API-Dokumentation angezeigt.
-Und dieselben Informationen aus den Modellen, die in OpenAPI enthalten sind, können zum **Generieren des Client-Codes** verwendet werden.
+Diese Informationen aus den Modellen, die in OpenAPI enthalten sind, können verwendet werden, um **den Client-Code zu generieren**.
-### Einen TypeScript-Client generieren
+### Hey API { #hey-api }
-Nachdem wir nun die Anwendung mit den Modellen haben, können wir den Client-Code für das Frontend generieren.
+Sobald wir eine FastAPI-App mit den Modellen haben, können wir Hey API verwenden, um einen TypeScript-Client zu generieren. Der schnellste Weg das zu tun, ist über npx.
-#### `openapi-ts` installieren
-
-Sie können `openapi-ts` in Ihrem Frontend-Code installieren mit:
-
-
-Sie erhalten außerdem automatische Vervollständigung für die zu sendende Payload:
+Sie werden auch eine automatische Vervollständigung für die zu sendende Payload erhalten:
-/// tip | "Tipp"
+/// tip | Tipp
-Beachten Sie die automatische Vervollständigung für `name` und `price`, welche in der FastAPI-Anwendung im `Item`-Modell definiert wurden.
+Beachten Sie die automatische Vervollständigung für `name` und `price`, die in der FastAPI-Anwendung im `Item`-Modell definiert wurden.
///
@@ -141,31 +92,17 @@ Das Response-Objekt hat auch automatische Vervollständigung:
-## FastAPI-Anwendung mit Tags
+## FastAPI-Anwendung mit Tags { #fastapi-app-with-tags }
-In vielen Fällen wird Ihre FastAPI-Anwendung größer sein und Sie werden wahrscheinlich Tags verwenden, um verschiedene Gruppen von *Pfadoperationen* zu separieren.
+In vielen Fällen wird Ihre FastAPI-App größer sein und Sie werden wahrscheinlich Tags verwenden, um verschiedene Gruppen von *Pfadoperationen* zu separieren.
-Beispielsweise könnten Sie einen Abschnitt für **Items (Artikel)** und einen weiteren Abschnitt für **Users (Benutzer)** haben, und diese könnten durch Tags getrennt sein:
+Zum Beispiel könnten Sie einen Abschnitt für **Items (Artikel)** und einen weiteren Abschnitt für **Users (Benutzer)** haben, und diese könnten durch Tags getrennt sein:
-//// tab | Python 3.9+
+{* ../../docs_src/generate_clients/tutorial002_py39.py hl[21,26,34] *}
-```Python hl_lines="21 26 34"
-{!> ../../../docs_src/generate_clients/tutorial002_py39.py!}
-```
+### Einen TypeScript-Client mit Tags generieren { #generate-a-typescript-client-with-tags }
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="23 28 36"
-{!> ../../../docs_src/generate_clients/tutorial002.py!}
-```
-
-////
-
-### Einen TypeScript-Client mit Tags generieren
-
-Wenn Sie unter Verwendung von Tags einen Client für eine FastAPI-Anwendung generieren, wird normalerweise auch der Client-Code anhand der Tags getrennt.
+Wenn Sie einen Client für eine FastAPI-App generieren, die Tags verwendet, wird normalerweise der Client-Code auch anhand der Tags getrennt.
Auf diese Weise können Sie die Dinge für den Client-Code richtig ordnen und gruppieren:
@@ -176,7 +113,7 @@ In diesem Fall haben Sie:
* `ItemsService`
* `UsersService`
-### Client-Methodennamen
+### Client-Methodennamen { #client-method-names }
Im Moment sehen die generierten Methodennamen wie `createItemItemsPost` nicht sehr sauber aus:
@@ -186,45 +123,31 @@ ItemsService.createItemItemsPost({name: "Plumbus", price: 5})
... das liegt daran, dass der Client-Generator für jede *Pfadoperation* die OpenAPI-interne **Operation-ID** verwendet.
-OpenAPI erfordert, dass jede Operation-ID innerhalb aller *Pfadoperationen* eindeutig ist. Daher verwendet FastAPI den **Funktionsnamen**, den **Pfad** und die **HTTP-Methode/-Operation**, um diese Operation-ID zu generieren. Denn so kann sichergestellt werden, dass die Operation-IDs eindeutig sind.
+OpenAPI erfordert, dass jede Operation-ID innerhalb aller *Pfadoperationen* einzigartig ist. Daher verwendet FastAPI den **Funktionsnamen**, den **Pfad** und die **HTTP-Methode/-Operation**, um diese Operation-ID zu generieren. Denn so kann sichergestellt werden, dass die Operation-IDs einzigartig sind.
-Aber ich zeige Ihnen als nächstes, wie Sie das verbessern können. 🤓
+Aber ich zeige Ihnen als Nächstes, wie Sie das verbessern können. 🤓
-## Benutzerdefinierte Operation-IDs und bessere Methodennamen
+## Benutzerdefinierte Operation-IDs und bessere Methodennamen { #custom-operation-ids-and-better-method-names }
Sie können die Art und Weise, wie diese Operation-IDs **generiert** werden, **ändern**, um sie einfacher zu machen und **einfachere Methodennamen** in den Clients zu haben.
-In diesem Fall müssen Sie auf andere Weise sicherstellen, dass jede Operation-ID **eindeutig** ist.
+In diesem Fall müssen Sie auf andere Weise sicherstellen, dass jede Operation-ID **einzigartig** ist.
-Sie könnten beispielsweise sicherstellen, dass jede *Pfadoperation* einen Tag hat, und dann die Operation-ID basierend auf dem **Tag** und dem **Namen** der *Pfadoperation* (dem Funktionsnamen) generieren.
+Zum Beispiel könnten Sie sicherstellen, dass jede *Pfadoperation* einen Tag hat, und dann die Operation-ID basierend auf dem **Tag** und dem *Pfadoperation*-**Namen** (dem Funktionsnamen) generieren.
-### Funktion zum Generieren einer eindeutigen ID erstellen
+### Eine benutzerdefinierte Funktion zur Erzeugung einer eindeutigen ID erstellen { #custom-generate-unique-id-function }
-FastAPI verwendet eine **eindeutige ID** für jede *Pfadoperation*, diese wird für die **Operation-ID** und auch für die Namen aller benötigten benutzerdefinierten Modelle für Requests oder Responses verwendet.
+FastAPI verwendet eine **eindeutige ID** für jede *Pfadoperation*, die für die **Operation-ID** und auch für die Namen aller benötigten benutzerdefinierten Modelle für Requests oder Responses verwendet wird.
-Sie können diese Funktion anpassen. Sie nimmt eine `APIRoute` und gibt einen String zurück.
+Sie können diese Funktion anpassen. Sie nimmt ein `APIRoute` und gibt einen String zurück.
-Hier verwendet sie beispielsweise den ersten Tag (Sie werden wahrscheinlich nur einen Tag haben) und den Namen der *Pfadoperation* (den Funktionsnamen).
+Hier verwendet sie beispielsweise den ersten Tag (Sie werden wahrscheinlich nur einen Tag haben) und den *Pfadoperation*-Namen (den Funktionsnamen).
-Anschließend können Sie diese benutzerdefinierte Funktion als Parameter `generate_unique_id_function` an **FastAPI** übergeben:
+Anschließend können Sie diese benutzerdefinierte Funktion als `generate_unique_id_function`-Parameter an **FastAPI** übergeben:
-//// tab | Python 3.9+
+{* ../../docs_src/generate_clients/tutorial003_py39.py hl[6:7,10] *}
-```Python hl_lines="6-7 10"
-{!> ../../../docs_src/generate_clients/tutorial003_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="8-9 12"
-{!> ../../../docs_src/generate_clients/tutorial003.py!}
-```
-
-////
-
-### Einen TypeScript-Client mit benutzerdefinierten Operation-IDs generieren
+### Einen TypeScript-Client mit benutzerdefinierten Operation-IDs generieren { #generate-a-typescript-client-with-custom-operation-ids }
Wenn Sie nun den Client erneut generieren, werden Sie feststellen, dass er über die verbesserten Methodennamen verfügt:
@@ -232,74 +155,54 @@ Wenn Sie nun den Client erneut generieren, werden Sie feststellen, dass er über
Wie Sie sehen, haben die Methodennamen jetzt den Tag und dann den Funktionsnamen, aber keine Informationen aus dem URL-Pfad und der HTTP-Operation.
-### Vorab-Modifikation der OpenAPI-Spezifikation für den Client-Generator
+### Die OpenAPI-Spezifikation für den Client-Generator vorab modifizieren { #preprocess-the-openapi-specification-for-the-client-generator }
-Der generierte Code enthält immer noch etwas **verdoppelte Information**.
+Der generierte Code enthält immer noch einige **verdoppelte Informationen**.
-Wir wissen bereits, dass diese Methode mit den **Items** zusammenhängt, da sich dieses Wort in `ItemsService` befindet (vom Tag übernommen), aber wir haben auch immer noch den Tagnamen im Methodennamen vorangestellt. 😕
+Wir wissen bereits, dass diese Methode mit den **Items** zusammenhängt, weil dieses Wort in `ItemsService` enthalten ist (vom Tag übernommen), aber wir haben den Tag-Namen dennoch im Methodennamen vorangestellt. 😕
-Wir werden das wahrscheinlich weiterhin für OpenAPI im Allgemeinen beibehalten wollen, da dadurch sichergestellt wird, dass die Operation-IDs **eindeutig** sind.
+Wir werden das wahrscheinlich weiterhin für OpenAPI allgemein beibehalten wollen, da dadurch sichergestellt wird, dass die Operation-IDs **einzigartig** sind.
Aber für den generierten Client könnten wir die OpenAPI-Operation-IDs direkt vor der Generierung der Clients **modifizieren**, um diese Methodennamen schöner und **sauberer** zu machen.
-Wir könnten das OpenAPI-JSON in eine Datei `openapi.json` herunterladen und dann mit einem Skript wie dem folgenden **den vorangestellten Tag entfernen**:
+Wir könnten das OpenAPI-JSON in eine Datei `openapi.json` herunterladen und dann mit einem Skript wie dem folgenden **den präfixierten Tag entfernen**:
-//// tab | Python
-
-```Python
-{!> ../../../docs_src/generate_clients/tutorial004.py!}
-```
-
-////
+{* ../../docs_src/generate_clients/tutorial004.py *}
//// tab | Node.js
```Javascript
-{!> ../../../docs_src/generate_clients/tutorial004.js!}
+{!> ../../docs_src/generate_clients/tutorial004.js!}
```
////
Damit würden die Operation-IDs von Dingen wie `items-get_items` in `get_items` umbenannt, sodass der Client-Generator einfachere Methodennamen generieren kann.
-### Einen TypeScript-Client mit der modifizierten OpenAPI generieren
+### Einen TypeScript-Client mit der modifizierten OpenAPI generieren { #generate-a-typescript-client-with-the-preprocessed-openapi }
-Da das Endergebnis nun in einer Datei `openapi.json` vorliegt, würden Sie die `package.json` ändern, um diese lokale Datei zu verwenden, zum Beispiel:
+Da das Endergebnis nun in einer `openapi.json`-Datei vorliegt, müssen Sie Ihren Eingabeort aktualisieren:
-```JSON hl_lines="7"
-{
- "name": "frontend-app",
- "version": "1.0.0",
- "description": "",
- "main": "index.js",
- "scripts": {
- "generate-client": "openapi-ts --input ./openapi.json --output ./src/client --client axios"
- },
- "author": "",
- "license": "",
- "devDependencies": {
- "@hey-api/openapi-ts": "^0.27.38",
- "typescript": "^4.6.2"
- }
-}
+```sh
+npx @hey-api/openapi-ts -i ./openapi.json -o src/client
```
-Nach der Generierung des neuen Clients hätten Sie nun **saubere Methodennamen** mit allen **Autovervollständigungen**, **Inline-Fehlerberichten**, usw.:
+Nach der Generierung des neuen Clients haben Sie jetzt **saubere Methodennamen**, mit allen **Autovervollständigungen**, **Inline-Fehlerberichten**, usw.:
-## Vorteile
+## Vorteile { #benefits }
-Wenn Sie die automatisch generierten Clients verwenden, erhalten Sie **automatische Codevervollständigung** für:
+Wenn Sie die automatisch generierten Clients verwenden, erhalten Sie **Autovervollständigung** für:
* Methoden.
* Request-Payloads im Body, Query-Parameter, usw.
* Response-Payloads.
-Außerdem erhalten Sie für alles **Inline-Fehlerberichte**.
+Sie erhalten auch **Inline-Fehlerberichte** für alles.
-Und wann immer Sie den Backend-Code aktualisieren und das Frontend **neu generieren**, stehen alle neuen *Pfadoperationen* als Methoden zur Verfügung, die alten werden entfernt und alle anderen Änderungen werden im generierten Code reflektiert. 🤓
+Und wann immer Sie den Backend-Code aktualisieren und **das Frontend neu generieren**, stehen alle neuen *Pfadoperationen* als Methoden zur Verfügung, die alten werden entfernt und alle anderen Änderungen werden im generierten Code reflektiert. 🤓
-Das bedeutet auch, dass, wenn sich etwas ändert, dies automatisch im Client-Code **reflektiert** wird. Und wenn Sie den Client **erstellen**, kommt es zu einer Fehlermeldung, wenn die verwendeten Daten **nicht übereinstimmen**.
+Das bedeutet auch, dass, wenn sich etwas ändert, dies automatisch im Client-Code **reflektiert** wird. Und wenn Sie den Client **erstellen**, wird eine Fehlermeldung ausgegeben, wenn die verwendeten Daten **nicht übereinstimmen**.
-Sie würden also sehr früh im Entwicklungszyklus **viele Fehler erkennen**, anstatt darauf warten zu müssen, dass die Fehler Ihren Endbenutzern in der Produktion angezeigt werden, und dann zu versuchen, zu debuggen, wo das Problem liegt. ✨
+Sie würden also **viele Fehler sehr früh** im Entwicklungszyklus erkennen, anstatt darauf warten zu müssen, dass die Fehler Ihren Endbenutzern in der Produktion angezeigt werden, und dann zu versuchen, zu debuggen, wo das Problem liegt. ✨
diff --git a/docs/de/docs/advanced/index.md b/docs/de/docs/advanced/index.md
index 953ad317d6..98fc7bc2fa 100644
--- a/docs/de/docs/advanced/index.md
+++ b/docs/de/docs/advanced/index.md
@@ -1,12 +1,12 @@
-# Handbuch für fortgeschrittene Benutzer
+# Handbuch für fortgeschrittene Benutzer { #advanced-user-guide }
-## Zusatzfunktionen
+## Zusatzfunktionen { #additional-features }
Das Haupt-[Tutorial – Benutzerhandbuch](../tutorial/index.md){.internal-link target=_blank} sollte ausreichen, um Ihnen einen Überblick über alle Hauptfunktionen von **FastAPI** zu geben.
In den nächsten Abschnitten sehen Sie weitere Optionen, Konfigurationen und zusätzliche Funktionen.
-/// tip | "Tipp"
+/// tip | Tipp
Die nächsten Abschnitte sind **nicht unbedingt „fortgeschritten“**.
@@ -14,23 +14,8 @@ Und es ist möglich, dass für Ihren Anwendungsfall die Lösung in einem davon l
///
-## Lesen Sie zuerst das Tutorial
+## Das Tutorial zuerst lesen { #read-the-tutorial-first }
Sie können immer noch die meisten Funktionen in **FastAPI** mit den Kenntnissen aus dem Haupt-[Tutorial – Benutzerhandbuch](../tutorial/index.md){.internal-link target=_blank} nutzen.
-Und in den nächsten Abschnitten wird davon ausgegangen, dass Sie es bereits gelesen haben und dass Sie diese Haupt-Ideen kennen.
-
-## Externe Kurse
-
-Obwohl das [Tutorial – Benutzerhandbuch](../tutorial/index.md){.internal-link target=_blank} und dieses **Handbuch für fortgeschrittene Benutzer** als geführtes Tutorial (wie ein Buch) geschrieben sind und für Sie ausreichen sollten, um **FastAPI zu lernen**, möchten Sie sie vielleicht durch zusätzliche Kurse ergänzen.
-
-Oder Sie belegen einfach lieber andere Kurse, weil diese besser zu Ihrem Lernstil passen.
-
-Einige Kursanbieter ✨ [**sponsern FastAPI**](../help-fastapi.md#den-autor-sponsern){.internal-link target=_blank} ✨, dies gewährleistet die kontinuierliche und gesunde **Entwicklung** von FastAPI und seinem **Ökosystem**.
-
-Und es zeigt deren wahres Engagement für FastAPI und seine **Gemeinschaft** (Sie), da diese Ihnen nicht nur eine **gute Lernerfahrung** bieten möchten, sondern auch sicherstellen möchten, dass Sie über ein **gutes und gesundes Framework verfügen **, FastAPI. 🙇
-
-Vielleicht möchten Sie ihre Kurse ausprobieren:
-
-* Talk Python Training
-* Test-Driven Development
+Und die nächsten Abschnitte setzen voraus, dass Sie es bereits gelesen haben und dass Sie diese Hauptideen kennen.
diff --git a/docs/de/docs/advanced/middleware.md b/docs/de/docs/advanced/middleware.md
index 8912225fbf..8396a626b6 100644
--- a/docs/de/docs/advanced/middleware.md
+++ b/docs/de/docs/advanced/middleware.md
@@ -1,4 +1,4 @@
-# Fortgeschrittene Middleware
+# Fortgeschrittene Middleware { #advanced-middleware }
Im Haupttutorial haben Sie gelesen, wie Sie Ihrer Anwendung [benutzerdefinierte Middleware](../tutorial/middleware.md){.internal-link target=_blank} hinzufügen können.
@@ -6,15 +6,15 @@ Und dann auch, wie man [CORS mittels der `CORSMiddleware`](../tutorial/cors.md){
In diesem Abschnitt werden wir sehen, wie man andere Middlewares verwendet.
-## ASGI-Middleware hinzufügen
+## ASGI-Middleware hinzufügen { #adding-asgi-middlewares }
-Da **FastAPI** auf Starlette basiert und die ASGI-Spezifikation implementiert, können Sie jede ASGI-Middleware verwenden.
+Da **FastAPI** auf Starlette basiert und die ASGI-Spezifikation implementiert, können Sie jede ASGI-Middleware verwenden.
Eine Middleware muss nicht speziell für FastAPI oder Starlette gemacht sein, um zu funktionieren, solange sie der ASGI-Spezifikation genügt.
Im Allgemeinen handelt es sich bei ASGI-Middleware um Klassen, die als erstes Argument eine ASGI-Anwendung erwarten.
-In der Dokumentation für ASGI-Middlewares von Drittanbietern wird Ihnen wahrscheinlich gesagt, etwa Folgendes zu tun:
+In der Dokumentation für ASGI-Middlewares von Drittanbietern wird Ihnen wahrscheinlich gesagt, dass Sie etwa Folgendes tun sollen:
```Python
from unicorn import UnicornMiddleware
@@ -39,11 +39,11 @@ app.add_middleware(UnicornMiddleware, some_config="rainbow")
`app.add_middleware()` empfängt eine Middleware-Klasse als erstes Argument und dann alle weiteren Argumente, die an die Middleware übergeben werden sollen.
-## Integrierte Middleware
+## Integrierte Middleware { #integrated-middlewares }
**FastAPI** enthält mehrere Middlewares für gängige Anwendungsfälle. Wir werden als Nächstes sehen, wie man sie verwendet.
-/// note | "Technische Details"
+/// note | Technische Details
Für die nächsten Beispiele könnten Sie auch `from starlette.middleware.something import SomethingMiddleware` verwenden.
@@ -51,45 +51,41 @@ Für die nächsten Beispiele könnten Sie auch `from starlette.middleware.someth
///
-## `HTTPSRedirectMiddleware`
+## `HTTPSRedirectMiddleware` { #httpsredirectmiddleware }
-Erzwingt, dass alle eingehenden Requests entweder `https` oder `wss` sein müssen.
+Erzwingt, dass alle eingehenden Requests entweder `https` oder `wss` sein müssen.
Alle eingehenden Requests an `http` oder `ws` werden stattdessen an das sichere Schema umgeleitet.
-```Python hl_lines="2 6"
-{!../../../docs_src/advanced_middleware/tutorial001.py!}
-```
+{* ../../docs_src/advanced_middleware/tutorial001.py hl[2,6] *}
-## `TrustedHostMiddleware`
+## `TrustedHostMiddleware` { #trustedhostmiddleware }
Erzwingt, dass alle eingehenden Requests einen korrekt gesetzten `Host`-Header haben, um sich vor HTTP-Host-Header-Angriffen zu schützen.
-```Python hl_lines="2 6-8"
-{!../../../docs_src/advanced_middleware/tutorial002.py!}
-```
+{* ../../docs_src/advanced_middleware/tutorial002.py hl[2,6:8] *}
Die folgenden Argumente werden unterstützt:
* `allowed_hosts` – Eine Liste von Domain-Namen, die als Hostnamen zulässig sein sollten. Wildcard-Domains wie `*.example.com` werden unterstützt, um Subdomains zu matchen. Um jeden Hostnamen zu erlauben, verwenden Sie entweder `allowed_hosts=["*"]` oder lassen Sie diese Middleware weg.
+* `www_redirect` – Wenn auf True gesetzt, werden Requests an Nicht-www-Versionen der erlaubten Hosts zu deren www-Gegenstücken umgeleitet. Der Defaultwert ist `True`.
-Wenn ein eingehender Request nicht korrekt validiert wird, wird eine „400“-Response gesendet.
+Wenn ein eingehender Request nicht korrekt validiert wird, wird eine `400`-Response gesendet.
-## `GZipMiddleware`
+## `GZipMiddleware` { #gzipmiddleware }
Verarbeitet GZip-Responses für alle Requests, die `"gzip"` im `Accept-Encoding`-Header enthalten.
Diese Middleware verarbeitet sowohl Standard- als auch Streaming-Responses.
-```Python hl_lines="2 6"
-{!../../../docs_src/advanced_middleware/tutorial003.py!}
-```
+{* ../../docs_src/advanced_middleware/tutorial003.py hl[2,6] *}
Die folgenden Argumente werden unterstützt:
-* `minimum_size` – Antworten, die kleiner als diese Mindestgröße in Bytes sind, nicht per GZip komprimieren. Der Defaultwert ist `500`.
+* `minimum_size` – Responses, die kleiner als diese Mindestgröße in Bytes sind, nicht per GZip komprimieren. Der Defaultwert ist `500`.
+* `compresslevel` – Wird während der GZip-Kompression verwendet. Es ist ein Ganzzahlwert zwischen 1 und 9. Der Defaultwert ist `9`. Ein niedrigerer Wert resultiert in schnellerer Kompression, aber größeren Dateigrößen, während ein höherer Wert langsamere Kompression, aber kleinere Dateigrößen zur Folge hat.
-## Andere Middlewares
+## Andere Middlewares { #other-middlewares }
Es gibt viele andere ASGI-Middlewares.
@@ -98,4 +94,4 @@ Zum Beispiel:
* Uvicorns `ProxyHeadersMiddleware`
* MessagePack
-Um mehr über weitere verfügbare Middlewares herauszufinden, besuchen Sie Starlettes Middleware-Dokumentation und die ASGI Awesome List.
+Um mehr über weitere verfügbare Middlewares herauszufinden, besuchen Sie Starlettes Middleware-Dokumentation und die ASGI Awesome List.
diff --git a/docs/de/docs/advanced/openapi-callbacks.md b/docs/de/docs/advanced/openapi-callbacks.md
index d7b5bc885a..afc48bbb82 100644
--- a/docs/de/docs/advanced/openapi-callbacks.md
+++ b/docs/de/docs/advanced/openapi-callbacks.md
@@ -1,12 +1,12 @@
-# OpenAPI-Callbacks
+# OpenAPI-Callbacks { #openapi-callbacks }
-Sie könnten eine API mit einer *Pfadoperation* erstellen, die einen Request an eine *externe API* auslösen könnte, welche von jemand anderem erstellt wurde (wahrscheinlich derselbe Entwickler, der Ihre API *verwenden* würde).
+Sie könnten eine API mit einer *Pfadoperation* erstellen, die einen Request an eine *externe API* auslösen könnte, welche von jemand anderem erstellt wurde (wahrscheinlich derselbe Entwickler, der Ihre API *verwenden* würde).
-Der Vorgang, der stattfindet, wenn Ihre API-Anwendung die *externe API* aufruft, wird als „Callback“ („Rückruf“) bezeichnet. Denn die Software, die der externe Entwickler geschrieben hat, sendet einen Request an Ihre API und dann *ruft Ihre API zurück* (*calls back*) und sendet einen Request an eine *externe API* (die wahrscheinlich vom selben Entwickler erstellt wurde).
+Der Vorgang, der stattfindet, wenn Ihre API-Anwendung die *externe API* aufruft, wird als „Callback“ bezeichnet. Denn die Software, die der externe Entwickler geschrieben hat, sendet einen Request an Ihre API und dann *ruft Ihre API zurück* (*calls back*) und sendet einen Request an eine *externe API* (die wahrscheinlich vom selben Entwickler erstellt wurde).
-In diesem Fall möchten Sie möglicherweise dokumentieren, wie diese externe API aussehen *sollte*. Welche *Pfadoperation* sie haben sollte, welchen Body sie erwarten sollte, welche Response sie zurückgeben sollte, usw.
+In diesem Fall möchten Sie möglicherweise dokumentieren, wie diese externe API aussehen *sollte*. Welche *Pfadoperation* sie haben sollte, welchen Body sie erwarten sollte, welche Response sie zurückgeben sollte, usw.
-## Eine Anwendung mit Callbacks
+## Eine Anwendung mit Callbacks { #an-app-with-callbacks }
Sehen wir uns das alles anhand eines Beispiels an.
@@ -16,14 +16,14 @@ Diese Rechnungen haben eine `id`, einen optionalen `title`, einen `customer` (Ku
Der Benutzer Ihrer API (ein externer Entwickler) erstellt mit einem POST-Request eine Rechnung in Ihrer API.
-Dann wird Ihre API (beispielsweise):
+Dann wird Ihre API (stellen wir uns vor):
* die Rechnung an einen Kunden des externen Entwicklers senden.
* das Geld einsammeln.
* eine Benachrichtigung an den API-Benutzer (den externen Entwickler) zurücksenden.
* Dies erfolgt durch Senden eines POST-Requests (von *Ihrer API*) an eine *externe API*, die von diesem externen Entwickler bereitgestellt wird (das ist der „Callback“).
-## Die normale **FastAPI**-Anwendung
+## Die normale **FastAPI**-Anwendung { #the-normal-fastapi-app }
Sehen wir uns zunächst an, wie die normale API-Anwendung aussehen würde, bevor wir den Callback hinzufügen.
@@ -31,11 +31,9 @@ Sie verfügt über eine *Pfadoperation*, die einen `Invoice`-Body empfängt, und
Dieser Teil ist ziemlich normal, der größte Teil des Codes ist Ihnen wahrscheinlich bereits bekannt:
-```Python hl_lines="9-13 36-53"
-{!../../../docs_src/openapi_callbacks/tutorial001.py!}
-```
+{* ../../docs_src/openapi_callbacks/tutorial001.py hl[9:13,36:53] *}
-/// tip | "Tipp"
+/// tip | Tipp
Der Query-Parameter `callback_url` verwendet einen Pydantic-Url-Typ.
@@ -43,7 +41,7 @@ Der Query-Parameter `callback_url` verwendet einen Pydantic-OpenAPI-3-Ausdruck enthalten (mehr dazu weiter unten), wo er Variablen mit Parametern und Teilen des ursprünglichen Requests verwenden kann, der an *Ihre API* gesendet wurde.
-### Der Callback-Pfadausdruck
+### Der Callback-Pfadausdruck { #the-callback-path-expression }
Der Callback-*Pfad* kann einen OpenAPI-3-Ausdruck enthalten, welcher Teile des ursprünglichen Requests enthalten kann, der an *Ihre API* gesendet wurde.
@@ -163,31 +157,29 @@ und sie würde eine Response von dieser *externen API* mit einem JSON-Body wie d
}
```
-/// tip | "Tipp"
+/// tip | Tipp
Beachten Sie, dass die verwendete Callback-URL die URL enthält, die als Query-Parameter in `callback_url` (`https://www.external.org/events`) empfangen wurde, und auch die Rechnungs-`id` aus dem JSON-Body (`2expen51ve`).
///
-### Den Callback-Router hinzufügen
+### Den Callback-Router hinzufügen { #add-the-callback-router }
An diesem Punkt haben Sie die benötigte(n) *Callback-Pfadoperation(en)* (diejenige(n), die der *externe Entwickler* in der *externen API* implementieren sollte) im Callback-Router, den Sie oben erstellt haben.
Verwenden Sie nun den Parameter `callbacks` im *Pfadoperation-Dekorator Ihrer API*, um das Attribut `.routes` (das ist eigentlich nur eine `list`e von Routen/*Pfadoperationen*) dieses Callback-Routers zu übergeben:
-```Python hl_lines="35"
-{!../../../docs_src/openapi_callbacks/tutorial001.py!}
-```
+{* ../../docs_src/openapi_callbacks/tutorial001.py hl[35] *}
-/// tip | "Tipp"
+/// tip | Tipp
Beachten Sie, dass Sie nicht den Router selbst (`invoices_callback_router`) an `callback=` übergeben, sondern das Attribut `.routes`, wie in `invoices_callback_router.routes`.
///
-### Es in der Dokumentation ansehen
+### Es in der Dokumentation testen { #check-the-docs }
-Jetzt können Sie Ihre Anwendung mit Uvicorn starten und auf http://127.0.0.1:8000/docs gehen.
+Jetzt können Sie Ihre Anwendung starten und auf http://127.0.0.1:8000/docs gehen.
Sie sehen Ihre Dokumentation, einschließlich eines Abschnitts „Callbacks“ für Ihre *Pfadoperation*, der zeigt, wie die *externe API* aussehen sollte:
diff --git a/docs/de/docs/advanced/openapi-webhooks.md b/docs/de/docs/advanced/openapi-webhooks.md
index fb0daa9086..a09a675ed6 100644
--- a/docs/de/docs/advanced/openapi-webhooks.md
+++ b/docs/de/docs/advanced/openapi-webhooks.md
@@ -1,56 +1,54 @@
-# OpenAPI-Webhooks
+# OpenAPI Webhooks { #openapi-webhooks }
-Es gibt Fälle, in denen Sie Ihren API-Benutzern mitteilen möchten, dass Ihre Anwendung mit einigen Daten *deren* Anwendung aufrufen (ein Request senden) könnte, normalerweise um über ein bestimmtes **Event** zu **benachrichtigen**.
+Es gibt Fälle, in denen Sie Ihren API-**Benutzern** mitteilen möchten, dass Ihre App *deren* App mit einigen Daten aufrufen (einen Request senden) könnte, normalerweise um über ein bestimmtes **Event** zu **benachrichtigen**.
-Das bedeutet, dass anstelle des normalen Prozesses, bei dem Benutzer Requests an Ihre API senden, **Ihre API** (oder Ihre Anwendung) **Requests an deren System** (an deren API, deren Anwendung) senden könnte.
+Das bedeutet, dass anstelle des normalen Prozesses, bei dem Ihre Benutzer Requests an Ihre API senden, **Ihre API** (oder Ihre App) **Requests an deren System** (an deren API, deren App) senden könnte.
-Das wird normalerweise als **Webhook** bezeichnet.
+Das wird normalerweise als **Webhook** bezeichnet.
-## Webhooks-Schritte
+## Webhooks-Schritte { #webhooks-steps }
Der Prozess besteht normalerweise darin, dass **Sie in Ihrem Code definieren**, welche Nachricht Sie senden möchten, den **Body des Requests**.
-Sie definieren auch auf irgendeine Weise, zu welchen **Momenten** Ihre Anwendung diese Requests oder Events sendet.
+Sie definieren auch auf irgendeine Weise, in welchen **Momenten** Ihre App diese Requests oder Events senden wird.
-Und **Ihre Benutzer** definieren auf irgendeine Weise (zum Beispiel irgendwo in einem Web-Dashboard) die **URL**, an die Ihre Anwendung diese Requests senden soll.
+Und **Ihre Benutzer** definieren auf irgendeine Weise (zum Beispiel irgendwo in einem Web-Dashboard) die **URL**, an die Ihre App diese Requests senden soll.
Die gesamte **Logik** zur Registrierung der URLs für Webhooks und der Code zum tatsächlichen Senden dieser Requests liegt bei Ihnen. Sie schreiben es so, wie Sie möchten, in **Ihrem eigenen Code**.
-## Webhooks mit **FastAPI** und OpenAPI dokumentieren
+## Webhooks mit **FastAPI** und OpenAPI dokumentieren { #documenting-webhooks-with-fastapi-and-openapi }
-Mit **FastAPI** können Sie mithilfe von OpenAPI die Namen dieser Webhooks, die Arten von HTTP-Operationen, die Ihre Anwendung senden kann (z. B. `POST`, `PUT`, usw.) und die Request**bodys** definieren, die Ihre Anwendung senden würde.
+Mit **FastAPI**, mithilfe von OpenAPI, können Sie die Namen dieser Webhooks, die Arten von HTTP-Operationen, die Ihre App senden kann (z. B. `POST`, `PUT`, usw.) und die Request**bodys** definieren, die Ihre App senden würde.
-Dies kann es Ihren Benutzern viel einfacher machen, **deren APIs zu implementieren**, um Ihre **Webhook**-Requests zu empfangen. Möglicherweise können diese sogar einen Teil des eigenem API-Codes automatisch generieren.
+Dies kann es Ihren Benutzern viel einfacher machen, **deren APIs zu implementieren**, um Ihre **Webhook**-Requests zu empfangen. Möglicherweise können diese sogar einen Teil ihres eigenen API-Codes automatisch generieren.
-/// info
+/// info | Info
Webhooks sind in OpenAPI 3.1.0 und höher verfügbar und werden von FastAPI `0.99.0` und höher unterstützt.
///
-## Eine Anwendung mit Webhooks
+## Eine App mit Webhooks { #an-app-with-webhooks }
-Wenn Sie eine **FastAPI**-Anwendung erstellen, gibt es ein `webhooks`-Attribut, mit dem Sie *Webhooks* definieren können, genauso wie Sie *Pfadoperationen* definieren würden, zum Beispiel mit `@app.webhooks.post()`.
+Wenn Sie eine **FastAPI**-Anwendung erstellen, gibt es ein `webhooks`-Attribut, das Sie verwenden können, um *Webhooks* zu definieren, genauso wie Sie *Pfadoperationen* definieren würden, zum Beispiel mit `@app.webhooks.post()`.
-```Python hl_lines="9-13 36-53"
-{!../../../docs_src/openapi_webhooks/tutorial001.py!}
-```
+{* ../../docs_src/openapi_webhooks/tutorial001.py hl[9:13,36:53] *}
Die von Ihnen definierten Webhooks landen im **OpenAPI**-Schema und der automatischen **Dokumentations-Oberfläche**.
-/// info
+/// info | Info
-Das `app.webhooks`-Objekt ist eigentlich nur ein `APIRouter`, derselbe Typ, den Sie verwenden würden, wenn Sie Ihre Anwendung mit mehreren Dateien strukturieren.
+Das `app.webhooks`-Objekt ist eigentlich nur ein `APIRouter`, derselbe Typ, den Sie verwenden würden, wenn Sie Ihre App mit mehreren Dateien strukturieren.
///
-Beachten Sie, dass Sie bei Webhooks tatsächlich keinen *Pfad* (wie `/items/`) deklarieren, sondern dass der Text, den Sie dort übergeben, lediglich eine **Kennzeichnung** des Webhooks (der Name des Events) ist. Zum Beispiel ist in `@app.webhooks.post("new-subscription")` der Webhook-Name `new-subscription`.
+Beachten Sie, dass Sie bei Webhooks tatsächlich keinen *Pfad* (wie `/items/`) deklarieren, der Text, den Sie dort übergeben, ist lediglich eine **Kennzeichnung** des Webhooks (der Name des Events). Zum Beispiel ist in `@app.webhooks.post("new-subscription")` der Webhook-Name `new-subscription`.
-Das liegt daran, dass erwartet wird, dass **Ihre Benutzer** den tatsächlichen **URL-Pfad**, an dem diese den Webhook-Request empfangen möchten, auf andere Weise definieren (z. B. über ein Web-Dashboard).
+Das liegt daran, dass erwartet wird, dass **Ihre Benutzer** den tatsächlichen **URL-Pfad**, an dem sie den Webhook-Request empfangen möchten, auf andere Weise definieren (z. B. über ein Web-Dashboard).
-### Es in der Dokumentation ansehen
+### Die Dokumentation testen { #check-the-docs }
-Jetzt können Sie Ihre Anwendung mit Uvicorn starten und auf http://127.0.0.1:8000/docs gehen.
+Jetzt können Sie Ihre App starten und auf http://127.0.0.1:8000/docs gehen.
Sie werden sehen, dass Ihre Dokumentation die normalen *Pfadoperationen* und jetzt auch einige **Webhooks** enthält:
diff --git a/docs/de/docs/advanced/path-operation-advanced-configuration.md b/docs/de/docs/advanced/path-operation-advanced-configuration.md
index c9cb82fe3d..f5ec7c49ea 100644
--- a/docs/de/docs/advanced/path-operation-advanced-configuration.md
+++ b/docs/de/docs/advanced/path-operation-advanced-configuration.md
@@ -1,8 +1,8 @@
-# Fortgeschrittene Konfiguration der Pfadoperation
+# Fortgeschrittene Konfiguration der Pfadoperation { #path-operation-advanced-configuration }
-## OpenAPI operationId
+## OpenAPI operationId { #openapi-operationid }
-/// warning | "Achtung"
+/// warning | Achtung
Wenn Sie kein „Experte“ für OpenAPI sind, brauchen Sie dies wahrscheinlich nicht.
@@ -12,27 +12,23 @@ Mit dem Parameter `operation_id` können Sie die OpenAPI `operationId` festlegen
Sie müssten sicherstellen, dass sie für jede Operation eindeutig ist.
-```Python hl_lines="6"
-{!../../../docs_src/path_operation_advanced_configuration/tutorial001.py!}
-```
+{* ../../docs_src/path_operation_advanced_configuration/tutorial001.py hl[6] *}
-### Verwendung des Namens der *Pfadoperation-Funktion* als operationId
+### Verwendung des Namens der *Pfadoperation-Funktion* als operationId { #using-the-path-operation-function-name-as-the-operationid }
Wenn Sie die Funktionsnamen Ihrer API als `operationId`s verwenden möchten, können Sie über alle iterieren und die `operation_id` jeder *Pfadoperation* mit deren `APIRoute.name` überschreiben.
Sie sollten dies tun, nachdem Sie alle Ihre *Pfadoperationen* hinzugefügt haben.
-```Python hl_lines="2 12-21 24"
-{!../../../docs_src/path_operation_advanced_configuration/tutorial002.py!}
-```
+{* ../../docs_src/path_operation_advanced_configuration/tutorial002.py hl[2, 12:21, 24] *}
-/// tip | "Tipp"
+/// tip | Tipp
Wenn Sie `app.openapi()` manuell aufrufen, sollten Sie vorher die `operationId`s aktualisiert haben.
///
-/// warning | "Achtung"
+/// warning | Achtung
Wenn Sie dies tun, müssen Sie sicherstellen, dass jede Ihrer *Pfadoperation-Funktionen* einen eindeutigen Namen hat.
@@ -40,15 +36,13 @@ Auch wenn diese sich in unterschiedlichen Modulen (Python-Dateien) befinden.
///
-## Von OpenAPI ausschließen
+## Von OpenAPI ausschließen { #exclude-from-openapi }
Um eine *Pfadoperation* aus dem generierten OpenAPI-Schema (und damit aus den automatischen Dokumentationssystemen) auszuschließen, verwenden Sie den Parameter `include_in_schema` und setzen Sie ihn auf `False`:
-```Python hl_lines="6"
-{!../../../docs_src/path_operation_advanced_configuration/tutorial003.py!}
-```
+{* ../../docs_src/path_operation_advanced_configuration/tutorial003.py hl[6] *}
-## Fortgeschrittene Beschreibung mittels Docstring
+## Fortgeschrittene Beschreibung mittels Docstring { #advanced-description-from-docstring }
Sie können die verwendeten Zeilen aus dem Docstring einer *Pfadoperation-Funktion* einschränken, die für OpenAPI verwendet werden.
@@ -56,25 +50,23 @@ Das Hinzufügen eines `\f` (ein maskiertes „Form Feed“-Zeichen) führt dazu,
Sie wird nicht in der Dokumentation angezeigt, aber andere Tools (z. B. Sphinx) können den Rest verwenden.
-```Python hl_lines="19-29"
-{!../../../docs_src/path_operation_advanced_configuration/tutorial004.py!}
-```
+{* ../../docs_src/path_operation_advanced_configuration/tutorial004.py hl[19:29] *}
-## Zusätzliche Responses
+## Zusätzliche Responses { #additional-responses }
Sie haben wahrscheinlich gesehen, wie man das `response_model` und den `status_code` für eine *Pfadoperation* deklariert.
-Das definiert die Metadaten der Haupt-Response einer *Pfadoperation*.
+Das definiert die Metadaten der Haupt-Response einer *Pfadoperation*.
Sie können auch zusätzliche Responses mit deren Modellen, Statuscodes usw. deklarieren.
Es gibt hier in der Dokumentation ein ganzes Kapitel darüber, Sie können es unter [Zusätzliche Responses in OpenAPI](additional-responses.md){.internal-link target=_blank} lesen.
-## OpenAPI-Extra
+## OpenAPI-Extra { #openapi-extra }
Wenn Sie in Ihrer Anwendung eine *Pfadoperation* deklarieren, generiert **FastAPI** automatisch die relevanten Metadaten dieser *Pfadoperation*, die in das OpenAPI-Schema aufgenommen werden sollen.
-/// note | "Technische Details"
+/// note | Technische Details
In der OpenAPI-Spezifikation wird das Operationsobjekt genannt.
@@ -86,9 +78,9 @@ Es enthält `tags`, `parameters`, `requestBody`, `responses`, usw.
Dieses *Pfadoperation*-spezifische OpenAPI-Schema wird normalerweise automatisch von **FastAPI** generiert, Sie können es aber auch erweitern.
-/// tip | "Tipp"
+/// tip | Tipp
-Dies ist ein Low-Level Erweiterungspunkt.
+Dies ist ein Low-Level-Erweiterungspunkt.
Wenn Sie nur zusätzliche Responses deklarieren müssen, können Sie dies bequemer mit [Zusätzliche Responses in OpenAPI](additional-responses.md){.internal-link target=_blank} tun.
@@ -96,13 +88,11 @@ Wenn Sie nur zusätzliche Responses deklarieren müssen, können Sie dies bequem
Sie können das OpenAPI-Schema für eine *Pfadoperation* erweitern, indem Sie den Parameter `openapi_extra` verwenden.
-### OpenAPI-Erweiterungen
+### OpenAPI-Erweiterungen { #openapi-extensions }
-Dieses `openapi_extra` kann beispielsweise hilfreich sein, um OpenAPI-Erweiterungen zu deklarieren:
+Dieses `openapi_extra` kann beispielsweise hilfreich sein, um [OpenAPI-Erweiterungen](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#specificationExtensions) zu deklarieren:
-```Python hl_lines="6"
-{!../../../docs_src/path_operation_advanced_configuration/tutorial005.py!}
-```
+{* ../../docs_src/path_operation_advanced_configuration/tutorial005.py hl[6] *}
Wenn Sie die automatische API-Dokumentation öffnen, wird Ihre Erweiterung am Ende der spezifischen *Pfadoperation* angezeigt.
@@ -139,49 +129,43 @@ Und wenn Sie die resultierende OpenAPI sehen (unter `/openapi.json` in Ihrer API
}
```
-### Benutzerdefiniertes OpenAPI-*Pfadoperation*-Schema
+### Benutzerdefiniertes OpenAPI-*Pfadoperation*-Schema { #custom-openapi-path-operation-schema }
-Das Dictionary in `openapi_extra` wird mit dem automatisch generierten OpenAPI-Schema für die *Pfadoperation* zusammengeführt (mittels Deep Merge).
+Das Dictionary in `openapi_extra` wird mit dem automatisch generierten OpenAPI-Schema für die *Pfadoperation* zusammengeführt (mittels Deep Merge).
Sie können dem automatisch generierten Schema also zusätzliche Daten hinzufügen.
-Sie könnten sich beispielsweise dafür entscheiden, den Request mit Ihrem eigenen Code zu lesen und zu validieren, ohne die automatischen Funktionen von FastAPI mit Pydantic zu verwenden, aber Sie könnten den Request trotzdem im OpenAPI-Schema definieren wollen.
+Sie könnten sich beispielsweise dafür entscheiden, den Request mit Ihrem eigenen Code zu lesen und zu validieren, ohne die automatischen Funktionen von FastAPI mit Pydantic zu verwenden, aber Sie könnten den Request trotzdem im OpenAPI-Schema definieren wollen.
Das könnte man mit `openapi_extra` machen:
-```Python hl_lines="20-37 39-40"
-{!../../../docs_src/path_operation_advanced_configuration/tutorial006.py!}
-```
+{* ../../docs_src/path_operation_advanced_configuration/tutorial006.py hl[19:36, 39:40] *}
-In diesem Beispiel haben wir kein Pydantic-Modell deklariert. Tatsächlich wird der Requestbody nicht einmal als JSON geparst, sondern direkt als `bytes` gelesen und die Funktion `magic_data_reader ()` wäre dafür verantwortlich, ihn in irgendeiner Weise zu parsen.
+In diesem Beispiel haben wir kein Pydantic-Modell deklariert. Tatsächlich wird der Requestbody nicht einmal als JSON geparst, sondern direkt als `bytes` gelesen und die Funktion `magic_data_reader()` wäre dafür verantwortlich, ihn in irgendeiner Weise zu parsen.
Dennoch können wir das zu erwartende Schema für den Requestbody deklarieren.
-### Benutzerdefinierter OpenAPI-Content-Type
+### Benutzerdefinierter OpenAPI-Content-Type { #custom-openapi-content-type }
Mit demselben Trick könnten Sie ein Pydantic-Modell verwenden, um das JSON-Schema zu definieren, das dann im benutzerdefinierten Abschnitt des OpenAPI-Schemas für die *Pfadoperation* enthalten ist.
-Und Sie könnten dies auch tun, wenn der Datentyp in der Anfrage nicht JSON ist.
+Und Sie könnten dies auch tun, wenn der Datentyp im Request nicht JSON ist.
In der folgenden Anwendung verwenden wir beispielsweise weder die integrierte Funktionalität von FastAPI zum Extrahieren des JSON-Schemas aus Pydantic-Modellen noch die automatische Validierung für JSON. Tatsächlich deklarieren wir den Request-Content-Type als YAML und nicht als JSON:
//// tab | Pydantic v2
-```Python hl_lines="17-22 24"
-{!> ../../../docs_src/path_operation_advanced_configuration/tutorial007.py!}
-```
+{* ../../docs_src/path_operation_advanced_configuration/tutorial007.py hl[17:22, 24] *}
////
//// tab | Pydantic v1
-```Python hl_lines="17-22 24"
-{!> ../../../docs_src/path_operation_advanced_configuration/tutorial007_pv1.py!}
-```
+{* ../../docs_src/path_operation_advanced_configuration/tutorial007_pv1.py hl[17:22, 24] *}
////
-/// info
+/// info | Info
In Pydantic Version 1 hieß die Methode zum Abrufen des JSON-Schemas für ein Modell `Item.schema()`, in Pydantic Version 2 heißt die Methode `Item.model_json_schema()`.
@@ -195,27 +179,23 @@ Und dann parsen wir in unserem Code diesen YAML-Inhalt direkt und verwenden dann
//// tab | Pydantic v2
-```Python hl_lines="26-33"
-{!> ../../../docs_src/path_operation_advanced_configuration/tutorial007.py!}
-```
+{* ../../docs_src/path_operation_advanced_configuration/tutorial007.py hl[26:33] *}
////
//// tab | Pydantic v1
-```Python hl_lines="26-33"
-{!> ../../../docs_src/path_operation_advanced_configuration/tutorial007_pv1.py!}
-```
+{* ../../docs_src/path_operation_advanced_configuration/tutorial007_pv1.py hl[26:33] *}
////
-/// info
+/// info | Info
In Pydantic Version 1 war die Methode zum Parsen und Validieren eines Objekts `Item.parse_obj()`, in Pydantic Version 2 heißt die Methode `Item.model_validate()`.
///
-/// tip | "Tipp"
+/// tip | Tipp
Hier verwenden wir dasselbe Pydantic-Modell wieder.
diff --git a/docs/de/docs/advanced/response-change-status-code.md b/docs/de/docs/advanced/response-change-status-code.md
index bba908a3ee..b079e241d1 100644
--- a/docs/de/docs/advanced/response-change-status-code.md
+++ b/docs/de/docs/advanced/response-change-status-code.md
@@ -1,33 +1,31 @@
-# Response – Statuscode ändern
+# Response – Statuscode ändern { #response-change-status-code }
-Sie haben wahrscheinlich schon vorher gelesen, dass Sie einen Standard-[Response-Statuscode](../tutorial/response-status-code.md){.internal-link target=_blank} festlegen können.
+Sie haben wahrscheinlich schon vorher gelesen, dass Sie einen Default-[Response-Statuscode](../tutorial/response-status-code.md){.internal-link target=_blank} festlegen können.
-In manchen Fällen müssen Sie jedoch einen anderen als den Standard-Statuscode zurückgeben.
+In manchen Fällen müssen Sie jedoch einen anderen als den Default-Statuscode zurückgeben.
-## Anwendungsfall
+## Anwendungsfall { #use-case }
Stellen Sie sich zum Beispiel vor, Sie möchten standardmäßig den HTTP-Statuscode „OK“ `200` zurückgeben.
-Wenn die Daten jedoch nicht vorhanden waren, möchten Sie diese erstellen und den HTTP-Statuscode „CREATED“ `201` zurückgeben.
+Wenn die Daten jedoch nicht vorhanden sind, möchten Sie diese erstellen und den HTTP-Statuscode „CREATED“ `201` zurückgeben.
Sie möchten aber dennoch in der Lage sein, die von Ihnen zurückgegebenen Daten mit einem `response_model` zu filtern und zu konvertieren.
In diesen Fällen können Sie einen `Response`-Parameter verwenden.
-## Einen `Response`-Parameter verwenden
+## Einen `Response`-Parameter verwenden { #use-a-response-parameter }
Sie können einen Parameter vom Typ `Response` in Ihrer *Pfadoperation-Funktion* deklarieren (wie Sie es auch für Cookies und Header tun können).
-Anschließend können Sie den `status_code` in diesem *vorübergehenden* Response-Objekt festlegen.
+Anschließend können Sie den `status_code` in diesem *vorübergehenden* Response-Objekt festlegen.
-```Python hl_lines="1 9 12"
-{!../../../docs_src/response_change_status_code/tutorial001.py!}
-```
+{* ../../docs_src/response_change_status_code/tutorial001.py hl[1,9,12] *}
-Und dann können Sie wie gewohnt jedes benötigte Objekt zurückgeben (ein `dict`, ein Datenbankmodell usw.).
+Und dann können Sie jedes benötigte Objekt zurückgeben, wie Sie es normalerweise tun würden (ein `dict`, ein Datenbankmodell usw.).
Und wenn Sie ein `response_model` deklariert haben, wird es weiterhin zum Filtern und Konvertieren des von Ihnen zurückgegebenen Objekts verwendet.
**FastAPI** verwendet diese *vorübergehende* Response, um den Statuscode (auch Cookies und Header) zu extrahieren und fügt diese in die endgültige Response ein, die den von Ihnen zurückgegebenen Wert enthält, gefiltert nach einem beliebigen `response_model`.
-Sie können den Parameter `Response` auch in Abhängigkeiten deklarieren und den Statuscode darin festlegen. Bedenken Sie jedoch, dass der gewinnt, welcher zuletzt gesetzt wird.
+Sie können den Parameter `Response` auch in Abhängigkeiten deklarieren und den Statuscode darin festlegen. Bedenken Sie jedoch, dass der zuletzt gesetzte gewinnt.
diff --git a/docs/de/docs/advanced/response-cookies.md b/docs/de/docs/advanced/response-cookies.md
index 3d2043565b..02fe99c26a 100644
--- a/docs/de/docs/advanced/response-cookies.md
+++ b/docs/de/docs/advanced/response-cookies.md
@@ -1,14 +1,12 @@
-# Response-Cookies
+# Response-Cookies { #response-cookies }
-## Einen `Response`-Parameter verwenden
+## Einen `Response`-Parameter verwenden { #use-a-response-parameter }
Sie können einen Parameter vom Typ `Response` in Ihrer *Pfadoperation-Funktion* deklarieren.
-Und dann können Sie Cookies in diesem *vorübergehenden* Response-Objekt setzen.
+Und dann können Sie Cookies in diesem *vorübergehenden* Response-Objekt setzen.
-```Python hl_lines="1 8-9"
-{!../../../docs_src/response_cookies/tutorial002.py!}
-```
+{* ../../docs_src/response_cookies/tutorial002.py hl[1, 8:9] *}
Anschließend können Sie wie gewohnt jedes gewünschte Objekt zurückgeben (ein `dict`, ein Datenbankmodell, usw.).
@@ -18,7 +16,7 @@ Und wenn Sie ein `response_model` deklariert haben, wird es weiterhin zum Filter
Sie können den `Response`-Parameter auch in Abhängigkeiten deklarieren und darin Cookies (und Header) setzen.
-## Eine `Response` direkt zurückgeben
+## Eine `Response` direkt zurückgeben { #return-a-response-directly }
Sie können Cookies auch erstellen, wenn Sie eine `Response` direkt in Ihrem Code zurückgeben.
@@ -26,11 +24,9 @@ Dazu können Sie eine Response erstellen, wie unter [Eine Response direkt zurüc
Setzen Sie dann Cookies darin und geben Sie sie dann zurück:
-```Python hl_lines="10-12"
-{!../../../docs_src/response_cookies/tutorial001.py!}
-```
+{* ../../docs_src/response_cookies/tutorial001.py hl[10:12] *}
-/// tip | "Tipp"
+/// tip | Tipp
Beachten Sie, dass, wenn Sie eine Response direkt zurückgeben, anstatt den `Response`-Parameter zu verwenden, FastAPI diese direkt zurückgibt.
@@ -40,9 +36,9 @@ Und auch, dass Sie keine Daten senden, die durch ein `response_model` hätten ge
///
-### Mehr Informationen
+### Mehr Informationen { #more-info }
-/// note | "Technische Details"
+/// note | Technische Details
Sie können auch `from starlette.responses import Response` oder `from starlette.responses import JSONResponse` verwenden.
@@ -52,4 +48,4 @@ Und da die `Response` häufig zum Setzen von Headern und Cookies verwendet wird,
///
-Um alle verfügbaren Parameter und Optionen anzuzeigen, sehen Sie sich deren Dokumentation in Starlette an.
+Um alle verfügbaren Parameter und Optionen anzuzeigen, sehen Sie sich deren Dokumentation in Starlette an.
diff --git a/docs/de/docs/advanced/response-directly.md b/docs/de/docs/advanced/response-directly.md
index 377490b569..d995173730 100644
--- a/docs/de/docs/advanced/response-directly.md
+++ b/docs/de/docs/advanced/response-directly.md
@@ -1,20 +1,20 @@
-# Eine Response direkt zurückgeben
+# Eine Response direkt zurückgeben { #return-a-response-directly }
-Wenn Sie eine **FastAPI** *Pfadoperation* erstellen, können Sie normalerweise beliebige Daten davon zurückgeben: ein `dict`, eine `list`e, ein Pydantic-Modell, ein Datenbankmodell, usw.
+Wenn Sie eine **FastAPI** *Pfadoperation* erstellen, können Sie normalerweise beliebige Daten davon zurückgeben: ein `dict`, eine `list`, ein Pydantic-Modell, ein Datenbankmodell, usw.
Standardmäßig konvertiert **FastAPI** diesen Rückgabewert automatisch nach JSON, mithilfe des `jsonable_encoder`, der in [JSON-kompatibler Encoder](../tutorial/encoder.md){.internal-link target=_blank} erläutert wird.
-Dann würde es hinter den Kulissen diese JSON-kompatiblen Daten (z. B. ein `dict`) in eine `JSONResponse` einfügen, die zum Senden der Response an den Client verwendet würde.
+Dann würde es hinter den Kulissen diese JSON-kompatiblen Daten (z. B. ein `dict`) in eine `JSONResponse` einfügen, die zum Senden der Response an den Client verwendet wird.
Sie können jedoch direkt eine `JSONResponse` von Ihren *Pfadoperationen* zurückgeben.
Das kann beispielsweise nützlich sein, um benutzerdefinierte Header oder Cookies zurückzugeben.
-## Eine `Response` zurückgeben
+## Eine `Response` zurückgeben { #return-a-response }
Tatsächlich können Sie jede `Response` oder jede Unterklasse davon zurückgeben.
-/// tip | "Tipp"
+/// tip | Tipp
`JSONResponse` selbst ist eine Unterklasse von `Response`.
@@ -26,7 +26,7 @@ Es wird keine Datenkonvertierung mit Pydantic-Modellen durchführen, es wird den
Dadurch haben Sie viel Flexibilität. Sie können jeden Datentyp zurückgeben, jede Datendeklaration oder -validierung überschreiben, usw.
-## Verwendung des `jsonable_encoder` in einer `Response`
+## Verwendung des `jsonable_encoder` in einer `Response` { #using-the-jsonable-encoder-in-a-response }
Da **FastAPI** keine Änderungen an einer von Ihnen zurückgegebenen `Response` vornimmt, müssen Sie sicherstellen, dass deren Inhalt dafür bereit ist.
@@ -34,19 +34,17 @@ Sie können beispielsweise kein Pydantic-Modell in eine `JSONResponse` einfügen
In diesen Fällen können Sie den `jsonable_encoder` verwenden, um Ihre Daten zu konvertieren, bevor Sie sie an eine Response übergeben:
-```Python hl_lines="6-7 21-22"
-{!../../../docs_src/response_directly/tutorial001.py!}
-```
+{* ../../docs_src/response_directly/tutorial001.py hl[6:7,21:22] *}
-/// note | "Technische Details"
+/// note | Technische Details
-Sie können auch `from starlette.responses import JSONResponse` verwenden.
+Sie könnten auch `from starlette.responses import JSONResponse` verwenden.
**FastAPI** bietet dieselben `starlette.responses` auch via `fastapi.responses` an, als Annehmlichkeit für Sie, den Entwickler. Die meisten verfügbaren Responses kommen aber direkt von Starlette.
///
-## Eine benutzerdefinierte `Response` zurückgeben
+## Eine benutzerdefinierte `Response` zurückgeben { #returning-a-custom-response }
Das obige Beispiel zeigt alle Teile, die Sie benötigen, ist aber noch nicht sehr nützlich, da Sie das `item` einfach direkt hätten zurückgeben können, und **FastAPI** würde es für Sie in eine `JSONResponse` einfügen, es in ein `dict` konvertieren, usw. All das standardmäßig.
@@ -56,11 +54,9 @@ Nehmen wir an, Sie möchten eine Response-Objekt festlegen.
-```Python hl_lines="1 7-8"
-{!../../../docs_src/response_headers/tutorial002.py!}
-```
+{* ../../docs_src/response_headers/tutorial002.py hl[1, 7:8] *}
Anschließend können Sie wie gewohnt jedes gewünschte Objekt zurückgeben (ein `dict`, ein Datenbankmodell, usw.).
@@ -18,17 +16,15 @@ Und wenn Sie ein `response_model` deklariert haben, wird es weiterhin zum Filter
Sie können den Parameter `Response` auch in Abhängigkeiten deklarieren und darin Header (und Cookies) festlegen.
-## Eine `Response` direkt zurückgeben
+## Eine `Response` direkt zurückgeben { #return-a-response-directly }
Sie können auch Header hinzufügen, wenn Sie eine `Response` direkt zurückgeben.
Erstellen Sie eine Response wie in [Eine Response direkt zurückgeben](response-directly.md){.internal-link target=_blank} beschrieben und übergeben Sie die Header als zusätzlichen Parameter:
-```Python hl_lines="10-12"
-{!../../../docs_src/response_headers/tutorial001.py!}
-```
+{* ../../docs_src/response_headers/tutorial001.py hl[10:12] *}
-/// note | "Technische Details"
+/// note | Technische Details
Sie können auch `from starlette.responses import Response` oder `from starlette.responses import JSONResponse` verwenden.
@@ -38,8 +34,8 @@ Und da die `Response` häufig zum Setzen von Headern und Cookies verwendet wird,
///
-## Benutzerdefinierte Header
+## Benutzerdefinierte Header { #custom-headers }
-Beachten Sie, dass benutzerdefinierte proprietäre Header mittels des Präfix 'X-' hinzugefügt werden können.
+Beachten Sie, dass benutzerdefinierte proprietäre Header mittels des Präfix `X-` hinzugefügt werden können.
-Wenn Sie jedoch benutzerdefinierte Header haben, die ein Client in einem Browser sehen können soll, müssen Sie diese zu Ihren CORS-Konfigurationen hinzufügen (weitere Informationen finden Sie unter [CORS (Cross-Origin Resource Sharing)](../tutorial/cors.md){.internal-link target=_blank}), unter Verwendung des Parameters `expose_headers`, dokumentiert in Starlettes CORS-Dokumentation.
+Wenn Sie jedoch benutzerdefinierte Header haben, die ein Client in einem Browser sehen können soll, müssen Sie diese zu Ihrer CORS-Konfiguration hinzufügen (weitere Informationen finden Sie unter [CORS (Cross-Origin Resource Sharing)](../tutorial/cors.md){.internal-link target=_blank}), unter Verwendung des Parameters `expose_headers`, dokumentiert in Starlettes CORS-Dokumentation.
diff --git a/docs/de/docs/advanced/security/http-basic-auth.md b/docs/de/docs/advanced/security/http-basic-auth.md
index 3e7aeb8a03..f906ecd92b 100644
--- a/docs/de/docs/advanced/security/http-basic-auth.md
+++ b/docs/de/docs/advanced/security/http-basic-auth.md
@@ -1,4 +1,4 @@
-# HTTP Basic Auth
+# HTTP Basic Auth { #http-basic-auth }
Für die einfachsten Fälle können Sie HTTP Basic Auth verwenden.
@@ -6,13 +6,13 @@ Bei HTTP Basic Auth erwartet die Anwendung einen Header, der einen Benutzernamen
Wenn sie diesen nicht empfängt, gibt sie den HTTP-Error 401 „Unauthorized“ zurück.
-Und gibt einen Header `WWW-Authenticate` mit dem Wert `Basic` und einem optionalen `realm`-Parameter („Bereich“) zurück.
+Und gibt einen Header `WWW-Authenticate` mit dem Wert `Basic` und einem optionalen `realm`-Parameter zurück.
Dadurch wird der Browser angewiesen, die integrierte Eingabeaufforderung für einen Benutzernamen und ein Passwort anzuzeigen.
Wenn Sie dann den Benutzernamen und das Passwort eingeben, sendet der Browser diese automatisch im Header.
-## Einfaches HTTP Basic Auth
+## Einfaches HTTP Basic Auth { #simple-http-basic-auth }
* Importieren Sie `HTTPBasic` und `HTTPBasicCredentials`.
* Erstellen Sie mit `HTTPBasic` ein „`security`-Schema“.
@@ -20,41 +20,13 @@ Wenn Sie dann den Benutzernamen und das Passwort eingeben, sendet der Browser di
* Diese gibt ein Objekt vom Typ `HTTPBasicCredentials` zurück:
* Es enthält den gesendeten `username` und das gesendete `password`.
-//// tab | Python 3.9+
-
-```Python hl_lines="4 8 12"
-{!> ../../../docs_src/security/tutorial006_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="2 7 11"
-{!> ../../../docs_src/security/tutorial006_an.py!}
-```
-
-////
-
-//// tab | Python 3.8+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="2 6 10"
-{!> ../../../docs_src/security/tutorial006.py!}
-```
-
-////
+{* ../../docs_src/security/tutorial006_an_py39.py hl[4,8,12] *}
Wenn Sie versuchen, die URL zum ersten Mal zu öffnen (oder in der Dokumentation auf den Button „Execute“ zu klicken), wird der Browser Sie nach Ihrem Benutzernamen und Passwort fragen:
-## Den Benutzernamen überprüfen
+## Den Benutzernamen überprüfen { #check-the-username }
Hier ist ein vollständigeres Beispiel.
@@ -68,35 +40,7 @@ Um dies zu lösen, konvertieren wir zunächst den `username` und das `password`
Dann können wir `secrets.compare_digest()` verwenden, um sicherzustellen, dass `credentials.username` `"stanleyjobson"` und `credentials.password` `"swordfish"` ist.
-//// tab | Python 3.9+
-
-```Python hl_lines="1 12-24"
-{!> ../../../docs_src/security/tutorial007_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="1 12-24"
-{!> ../../../docs_src/security/tutorial007_an.py!}
-```
-
-////
-
-//// tab | Python 3.8+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="1 11-21"
-{!> ../../../docs_src/security/tutorial007.py!}
-```
-
-////
+{* ../../docs_src/security/tutorial007_an_py39.py hl[1,12:24] *}
Dies wäre das gleiche wie:
@@ -108,13 +52,13 @@ if not (credentials.username == "stanleyjobson") or not (credentials.password ==
Aber durch die Verwendung von `secrets.compare_digest()` ist dieser Code sicher vor einer Art von Angriffen, die „Timing-Angriffe“ genannt werden.
-### Timing-Angriffe
+### Timing-Angriffe { #timing-attacks }
Aber was ist ein „Timing-Angriff“?
Stellen wir uns vor, dass einige Angreifer versuchen, den Benutzernamen und das Passwort zu erraten.
-Und sie senden eine Anfrage mit dem Benutzernamen `johndoe` und dem Passwort `love123`.
+Und sie senden einen Request mit dem Benutzernamen `johndoe` und dem Passwort `love123`.
Dann würde der Python-Code in Ihrer Anwendung etwa so aussehen:
@@ -134,21 +78,21 @@ if "stanleyjobsox" == "stanleyjobson" and "love123" == "swordfish":
...
```
-Python muss das gesamte `stanleyjobso` in `stanleyjobsox` und `stanleyjobson` vergleichen, bevor es erkennt, dass beide Zeichenfolgen nicht gleich sind. Daher wird es einige zusätzliche Mikrosekunden dauern, bis die Antwort „Incorrect username or password“ erfolgt.
+Python muss das gesamte `stanleyjobso` in `stanleyjobsox` und `stanleyjobson` vergleichen, bevor es erkennt, dass beide Zeichenfolgen nicht gleich sind. Daher wird es einige zusätzliche Mikrosekunden dauern, bis die Response „Incorrect username or password“ erfolgt.
-#### Die Zeit zum Antworten hilft den Angreifern
+#### Die Zeit zum Antworten hilft den Angreifern { #the-time-to-answer-helps-the-attackers }
-Wenn die Angreifer zu diesem Zeitpunkt feststellen, dass der Server einige Mikrosekunden länger braucht, um die Antwort „Incorrect username or password“ zu senden, wissen sie, dass sie _etwas_ richtig gemacht haben, einige der Anfangsbuchstaben waren richtig.
+Wenn die Angreifer zu diesem Zeitpunkt feststellen, dass der Server einige Mikrosekunden länger braucht, um die Response „Incorrect username or password“ zu senden, wissen sie, dass sie _etwas_ richtig gemacht haben, einige der Anfangsbuchstaben waren richtig.
Und dann können sie es noch einmal versuchen, wohl wissend, dass es wahrscheinlich eher etwas mit `stanleyjobsox` als mit `johndoe` zu tun hat.
-#### Ein „professioneller“ Angriff
+#### Ein „professioneller“ Angriff { #a-professional-attack }
Natürlich würden die Angreifer das alles nicht von Hand versuchen, sondern ein Programm dafür schreiben, möglicherweise mit Tausenden oder Millionen Tests pro Sekunde. Und würden jeweils nur einen zusätzlichen richtigen Buchstaben erhalten.
Aber so hätten die Angreifer in wenigen Minuten oder Stunden mit der „Hilfe“ unserer Anwendung den richtigen Benutzernamen und das richtige Passwort erraten, indem sie die Zeitspanne zur Hilfe nehmen, die diese zur Beantwortung benötigt.
-#### Das Problem beheben mittels `secrets.compare_digest()`
+#### Das Problem beheben mittels `secrets.compare_digest()` { #fix-it-with-secrets-compare-digest }
Aber in unserem Code verwenden wir tatsächlich `secrets.compare_digest()`.
@@ -156,36 +100,8 @@ Damit wird, kurz gesagt, der Vergleich von `stanleyjobsox` mit `stanleyjobson` g
So ist Ihr Anwendungscode, dank der Verwendung von `secrets.compare_digest()`, vor dieser ganzen Klasse von Sicherheitsangriffen geschützt.
-### Den Error zurückgeben
+### Den Error zurückgeben { #return-the-error }
Nachdem Sie festgestellt haben, dass die Anmeldeinformationen falsch sind, geben Sie eine `HTTPException` mit dem Statuscode 401 zurück (derselbe, der auch zurückgegeben wird, wenn keine Anmeldeinformationen angegeben werden) und fügen den Header `WWW-Authenticate` hinzu, damit der Browser die Anmeldeaufforderung erneut anzeigt:
-//// tab | Python 3.9+
-
-```Python hl_lines="26-30"
-{!> ../../../docs_src/security/tutorial007_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="26-30"
-{!> ../../../docs_src/security/tutorial007_an.py!}
-```
-
-////
-
-//// tab | Python 3.8+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="23-27"
-{!> ../../../docs_src/security/tutorial007.py!}
-```
-
-////
+{* ../../docs_src/security/tutorial007_an_py39.py hl[26:30] *}
diff --git a/docs/de/docs/advanced/security/index.md b/docs/de/docs/advanced/security/index.md
index 380e48bbf4..d7e6332314 100644
--- a/docs/de/docs/advanced/security/index.md
+++ b/docs/de/docs/advanced/security/index.md
@@ -1,10 +1,10 @@
-# Fortgeschrittene Sicherheit
+# Fortgeschrittene Sicherheit { #advanced-security }
-## Zusatzfunktionen
+## Zusatzfunktionen { #additional-features }
Neben den in [Tutorial – Benutzerhandbuch: Sicherheit](../../tutorial/security/index.md){.internal-link target=_blank} behandelten Funktionen gibt es noch einige zusätzliche Funktionen zur Handhabung der Sicherheit.
-/// tip | "Tipp"
+/// tip | Tipp
Die nächsten Abschnitte sind **nicht unbedingt „fortgeschritten“**.
@@ -12,8 +12,8 @@ Und es ist möglich, dass für Ihren Anwendungsfall die Lösung in einem davon l
///
-## Lesen Sie zuerst das Tutorial
+## Das Tutorial zuerst lesen { #read-the-tutorial-first }
-In den nächsten Abschnitten wird davon ausgegangen, dass Sie das Haupt-[Tutorial – Benutzerhandbuch: Sicherheit](../../tutorial/security/index.md){.internal-link target=_blank} bereits gelesen haben.
+Die nächsten Abschnitte setzen voraus, dass Sie das Haupt-[Tutorial – Benutzerhandbuch: Sicherheit](../../tutorial/security/index.md){.internal-link target=_blank} bereits gelesen haben.
Sie basieren alle auf den gleichen Konzepten, ermöglichen jedoch einige zusätzliche Funktionalitäten.
diff --git a/docs/de/docs/advanced/security/oauth2-scopes.md b/docs/de/docs/advanced/security/oauth2-scopes.md
index f02707698e..b96715d5a4 100644
--- a/docs/de/docs/advanced/security/oauth2-scopes.md
+++ b/docs/de/docs/advanced/security/oauth2-scopes.md
@@ -1,16 +1,16 @@
-# OAuth2-Scopes
+# OAuth2-Scopes { #oauth2-scopes }
Sie können OAuth2-Scopes direkt in **FastAPI** verwenden, sie sind nahtlos integriert.
Das ermöglicht es Ihnen, ein feingranuliertes Berechtigungssystem nach dem OAuth2-Standard in Ihre OpenAPI-Anwendung (und deren API-Dokumentation) zu integrieren.
-OAuth2 mit Scopes ist der Mechanismus, der von vielen großen Authentifizierungsanbietern wie Facebook, Google, GitHub, Microsoft, Twitter usw. verwendet wird. Sie verwenden ihn, um Benutzern und Anwendungen spezifische Berechtigungen zu erteilen.
+OAuth2 mit Scopes ist der Mechanismus, der von vielen großen Authentifizierungsanbietern wie Facebook, Google, GitHub, Microsoft, X (Twitter) usw. verwendet wird. Sie verwenden ihn, um Benutzern und Anwendungen spezifische Berechtigungen zu erteilen.
-Jedes Mal, wenn Sie sich mit Facebook, Google, GitHub, Microsoft oder Twitter anmelden („log in with“), verwendet die entsprechende Anwendung OAuth2 mit Scopes.
+Jedes Mal, wenn Sie sich mit Facebook, Google, GitHub, Microsoft oder X (Twitter) anmelden („log in with“), verwendet die entsprechende Anwendung OAuth2 mit Scopes.
In diesem Abschnitt erfahren Sie, wie Sie Authentifizierung und Autorisierung mit demselben OAuth2, mit Scopes in Ihrer **FastAPI**-Anwendung verwalten.
-/// warning | "Achtung"
+/// warning | Achtung
Dies ist ein mehr oder weniger fortgeschrittener Abschnitt. Wenn Sie gerade erst anfangen, können Sie ihn überspringen.
@@ -26,7 +26,7 @@ Aber wenn Sie wissen, dass Sie es brauchen oder neugierig sind, lesen Sie weiter
///
-## OAuth2-Scopes und OpenAPI
+## OAuth2-Scopes und OpenAPI { #oauth2-scopes-and-openapi }
Die OAuth2-Spezifikation definiert „Scopes“ als eine Liste von durch Leerzeichen getrennten Strings.
@@ -46,7 +46,7 @@ Er wird normalerweise verwendet, um bestimmte Sicherheitsberechtigungen zu dekla
* `instagram_basic` wird von Facebook / Instagram verwendet.
* `https://www.googleapis.com/auth/drive` wird von Google verwendet.
-/// info
+/// info | Info
In OAuth2 ist ein „Scope“ nur ein String, der eine bestimmte erforderliche Berechtigung deklariert.
@@ -58,149 +58,21 @@ Für OAuth2 sind es einfach nur Strings.
///
-## Gesamtübersicht
+## Gesamtübersicht { #global-view }
Sehen wir uns zunächst kurz die Teile an, die sich gegenüber den Beispielen im Haupt-**Tutorial – Benutzerhandbuch** für [OAuth2 mit Password (und Hashing), Bearer mit JWT-Tokens](../../tutorial/security/oauth2-jwt.md){.internal-link target=_blank} ändern. Diesmal verwenden wir OAuth2-Scopes:
-//// tab | 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!}
-```
-
-////
-
-//// tab | 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!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="2 4 8 12 47 65 106 108-116 122-125 129-135 140 156"
-{!> ../../../docs_src/security/tutorial005_an.py!}
-```
-
-////
-
-//// tab | Python 3.10+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="3 7 11 45 63 104 106-114 120-123 127-133 138 154"
-{!> ../../../docs_src/security/tutorial005_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="2 4 8 12 46 64 105 107-115 121-124 128-134 139 155"
-{!> ../../../docs_src/security/tutorial005_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="2 4 8 12 46 64 105 107-115 121-124 128-134 139 155"
-{!> ../../../docs_src/security/tutorial005.py!}
-```
-
-////
+{* ../../docs_src/security/tutorial005_an_py310.py hl[5,9,13,47,65,106,108:116,122:126,130:136,141,157] *}
Sehen wir uns diese Änderungen nun Schritt für Schritt an.
-## OAuth2-Sicherheitsschema
+## OAuth2-Sicherheitsschema { #oauth2-security-scheme }
Die erste Änderung ist, dass wir jetzt das OAuth2-Sicherheitsschema mit zwei verfügbaren Scopes deklarieren: `me` und `items`.
-Der `scopes`-Parameter erhält ein `dict` mit jedem Scope als Schlüssel und dessen Beschreibung als Wert:
+Der `scopes`-Parameter erhält ein `dict` mit jedem Scope als Schlüssel und dessen Beschreibung als Wert:
-//// tab | Python 3.10+
-
-```Python hl_lines="62-65"
-{!> ../../../docs_src/security/tutorial005_an_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="62-65"
-{!> ../../../docs_src/security/tutorial005_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="63-66"
-{!> ../../../docs_src/security/tutorial005_an.py!}
-```
-
-////
-
-//// tab | Python 3.10+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="61-64"
-{!> ../../../docs_src/security/tutorial005_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="62-65"
-{!> ../../../docs_src/security/tutorial005_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="62-65"
-{!> ../../../docs_src/security/tutorial005.py!}
-```
-
-////
+{* ../../docs_src/security/tutorial005_an_py310.py hl[63:66] *}
Da wir diese Scopes jetzt deklarieren, werden sie in der API-Dokumentation angezeigt, wenn Sie sich einloggen/autorisieren.
@@ -210,15 +82,15 @@ Das ist derselbe Mechanismus, der verwendet wird, wenn Sie beim Anmelden mit Fac
-## JWT-Token mit Scopes
+## JWT-Token mit Scopes { #jwt-token-with-scopes }
Ändern Sie nun die Token-*Pfadoperation*, um die angeforderten Scopes zurückzugeben.
-Wir verwenden immer noch dasselbe `OAuth2PasswordRequestForm`. Es enthält eine Eigenschaft `scopes` mit einer `list`e von `str`s für jeden Scope, den es im Request erhalten hat.
+Wir verwenden immer noch dasselbe `OAuth2PasswordRequestForm`. Es enthält eine Eigenschaft `scopes` mit einer `list`e von `str`s für jeden Scope, den es im Request erhalten hat.
Und wir geben die Scopes als Teil des JWT-Tokens zurück.
-/// danger | "Gefahr"
+/// danger | Gefahr
Der Einfachheit halber fügen wir hier die empfangenen Scopes direkt zum Token hinzu.
@@ -226,73 +98,9 @@ Aus Sicherheitsgründen sollten Sie jedoch sicherstellen, dass Sie in Ihrer Anwe
///
-//// tab | Python 3.10+
+{* ../../docs_src/security/tutorial005_an_py310.py hl[157] *}
-```Python hl_lines="155"
-{!> ../../../docs_src/security/tutorial005_an_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="155"
-{!> ../../../docs_src/security/tutorial005_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="156"
-{!> ../../../docs_src/security/tutorial005_an.py!}
-```
-
-////
-
-//// tab | Python 3.10+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="154"
-{!> ../../../docs_src/security/tutorial005_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="155"
-{!> ../../../docs_src/security/tutorial005_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="155"
-{!> ../../../docs_src/security/tutorial005.py!}
-```
-
-////
-
-## Scopes in *Pfadoperationen* und Abhängigkeiten deklarieren
+## Scopes in *Pfadoperationen* und Abhängigkeiten deklarieren { #declare-scopes-in-path-operations-and-dependencies }
Jetzt deklarieren wir, dass die *Pfadoperation* für `/users/me/items/` den Scope `items` erfordert.
@@ -308,7 +116,7 @@ Und die Abhängigkeitsfunktion `get_current_active_user` kann auch Unterabhängi
In diesem Fall erfordert sie den Scope `me` (sie könnte mehr als einen Scope erfordern).
-/// note | "Hinweis"
+/// note | Hinweis
Sie müssen nicht unbedingt an verschiedenen Stellen verschiedene Scopes hinzufügen.
@@ -316,73 +124,9 @@ Wir tun dies hier, um zu demonstrieren, wie **FastAPI** auf verschiedenen Ebenen
///
-//// tab | Python 3.10+
+{* ../../docs_src/security/tutorial005_an_py310.py hl[5,141,172] *}
-```Python hl_lines="4 139 170"
-{!> ../../../docs_src/security/tutorial005_an_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="4 139 170"
-{!> ../../../docs_src/security/tutorial005_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="4 140 171"
-{!> ../../../docs_src/security/tutorial005_an.py!}
-```
-
-////
-
-//// tab | Python 3.10+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="3 138 167"
-{!> ../../../docs_src/security/tutorial005_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="4 139 168"
-{!> ../../../docs_src/security/tutorial005_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="4 139 168"
-{!> ../../../docs_src/security/tutorial005.py!}
-```
-
-////
-
-/// info | "Technische Details"
+/// info | Technische Details
`Security` ist tatsächlich eine Unterklasse von `Depends` und hat nur noch einen zusätzlichen Parameter, den wir später kennenlernen werden.
@@ -392,7 +136,7 @@ Wenn Sie jedoch `Query`, `Path`, `Depends`, `Security` und andere von `fastapi`
///
-## `SecurityScopes` verwenden
+## `SecurityScopes` verwenden { #use-securityscopes }
Aktualisieren Sie nun die Abhängigkeit `get_current_user`.
@@ -406,73 +150,9 @@ Wir deklarieren auch einen speziellen Parameter vom Typ `SecurityScopes`, der au
Diese `SecurityScopes`-Klasse ähnelt `Request` (`Request` wurde verwendet, um das Request-Objekt direkt zu erhalten).
-//// tab | Python 3.10+
+{* ../../docs_src/security/tutorial005_an_py310.py hl[9,106] *}
-```Python hl_lines="8 105"
-{!> ../../../docs_src/security/tutorial005_an_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="8 105"
-{!> ../../../docs_src/security/tutorial005_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="8 106"
-{!> ../../../docs_src/security/tutorial005_an.py!}
-```
-
-////
-
-//// tab | Python 3.10+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="7 104"
-{!> ../../../docs_src/security/tutorial005_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="8 105"
-{!> ../../../docs_src/security/tutorial005_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="8 105"
-{!> ../../../docs_src/security/tutorial005.py!}
-```
-
-////
-
-## Die `scopes` verwenden
+## Die `scopes` verwenden { #use-the-scopes }
Der Parameter `security_scopes` wird vom Typ `SecurityScopes` sein.
@@ -484,73 +164,9 @@ Wir erstellen eine `HTTPException`, die wir später an mehreren Stellen wiederve
In diese Exception fügen wir (falls vorhanden) die erforderlichen Scopes als durch Leerzeichen getrennten String ein (unter Verwendung von `scope_str`). Wir fügen diesen String mit den Scopes in den Header `WWW-Authenticate` ein (das ist Teil der Spezifikation).
-//// tab | Python 3.10+
+{* ../../docs_src/security/tutorial005_an_py310.py hl[106,108:116] *}
-```Python hl_lines="105 107-115"
-{!> ../../../docs_src/security/tutorial005_an_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="105 107-115"
-{!> ../../../docs_src/security/tutorial005_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="106 108-116"
-{!> ../../../docs_src/security/tutorial005_an.py!}
-```
-
-////
-
-//// tab | Python 3.10+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="104 106-114"
-{!> ../../../docs_src/security/tutorial005_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="105 107-115"
-{!> ../../../docs_src/security/tutorial005_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="105 107-115"
-{!> ../../../docs_src/security/tutorial005.py!}
-```
-
-////
-
-## Den `username` und das Format der Daten überprüfen
+## Den `username` und das Format der Daten überprüfen { #verify-the-username-and-data-shape }
Wir verifizieren, dass wir einen `username` erhalten, und extrahieren die Scopes.
@@ -564,145 +180,17 @@ Anstelle beispielsweise eines `dict`s oder etwas anderem, was später in der Anw
Wir verifizieren auch, dass wir einen Benutzer mit diesem Benutzernamen haben, und wenn nicht, lösen wir dieselbe Exception aus, die wir zuvor erstellt haben.
-//// tab | Python 3.10+
+{* ../../docs_src/security/tutorial005_an_py310.py hl[47,117:129] *}
-```Python hl_lines="46 116-127"
-{!> ../../../docs_src/security/tutorial005_an_py310.py!}
-```
+## Die `scopes` verifizieren { #verify-the-scopes }
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="46 116-127"
-{!> ../../../docs_src/security/tutorial005_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="47 117-128"
-{!> ../../../docs_src/security/tutorial005_an.py!}
-```
-
-////
-
-//// tab | Python 3.10+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="45 115-126"
-{!> ../../../docs_src/security/tutorial005_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="46 116-127"
-{!> ../../../docs_src/security/tutorial005_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="46 116-127"
-{!> ../../../docs_src/security/tutorial005.py!}
-```
-
-////
-
-## Die `scopes` verifizieren
-
-Wir überprüfen nun, ob das empfangenen Token alle Scopes enthält, die von dieser Abhängigkeit und deren Verwendern (einschließlich *Pfadoperationen*) gefordert werden. Andernfalls lösen wir eine `HTTPException` aus.
+Wir überprüfen nun, ob das empfangene Token alle Scopes enthält, die von dieser Abhängigkeit und deren Verwendern (einschließlich *Pfadoperationen*) gefordert werden. Andernfalls lösen wir eine `HTTPException` aus.
Hierzu verwenden wir `security_scopes.scopes`, das eine `list`e mit allen diesen Scopes als `str` enthält.
-//// tab | Python 3.10+
+{* ../../docs_src/security/tutorial005_an_py310.py hl[130:136] *}
-```Python hl_lines="128-134"
-{!> ../../../docs_src/security/tutorial005_an_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="128-134"
-{!> ../../../docs_src/security/tutorial005_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="129-135"
-{!> ../../../docs_src/security/tutorial005_an.py!}
-```
-
-////
-
-//// tab | Python 3.10+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="127-133"
-{!> ../../../docs_src/security/tutorial005_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="128-134"
-{!> ../../../docs_src/security/tutorial005_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="128-134"
-{!> ../../../docs_src/security/tutorial005.py!}
-```
-
-////
-
-## Abhängigkeitsbaum und Scopes
+## Abhängigkeitsbaum und Scopes { #dependency-tree-and-scopes }
Sehen wir uns diesen Abhängigkeitsbaum und die Scopes noch einmal an.
@@ -727,7 +215,7 @@ So sieht die Hierarchie der Abhängigkeiten und Scopes aus:
* `security_scopes.scopes` enthält `["me"]` für die *Pfadoperation* `read_users_me`, da das in der Abhängigkeit `get_current_active_user` deklariert ist.
* `security_scopes.scopes` wird `[]` (nichts) für die *Pfadoperation* `read_system_status` enthalten, da diese keine `Security` mit `scopes` deklariert hat, und deren Abhängigkeit `get_current_user` ebenfalls keinerlei `scopes` deklariert.
-/// tip | "Tipp"
+/// tip | Tipp
Das Wichtige und „Magische“ hier ist, dass `get_current_user` für jede *Pfadoperation* eine andere Liste von `scopes` hat, die überprüft werden.
@@ -735,7 +223,7 @@ Alles hängt von den „Scopes“ ab, die in jeder *Pfadoperation* und jeder Abh
///
-## Weitere Details zu `SecurityScopes`.
+## Weitere Details zu `SecurityScopes` { #more-details-about-securityscopes }
Sie können `SecurityScopes` an jeder Stelle und an mehreren Stellen verwenden, es muss sich nicht in der „Wurzel“-Abhängigkeit befinden.
@@ -745,7 +233,7 @@ Da die `SecurityScopes` alle von den Verwendern der Abhängigkeiten deklarierten
Diese werden für jede *Pfadoperation* unabhängig überprüft.
-## Testen Sie es
+## Es testen { #check-it }
Wenn Sie die API-Dokumentation öffnen, können Sie sich authentisieren und angeben, welche Scopes Sie autorisieren möchten.
@@ -757,7 +245,7 @@ Und wenn Sie den Scope `me`, aber nicht den Scope `items` auswählen, können Si
Das würde einer Drittanbieteranwendung passieren, die versucht, auf eine dieser *Pfadoperationen* mit einem Token zuzugreifen, das von einem Benutzer bereitgestellt wurde, abhängig davon, wie viele Berechtigungen der Benutzer dieser Anwendung erteilt hat.
-## Über Integrationen von Drittanbietern
+## Über Integrationen von Drittanbietern { #about-third-party-integrations }
In diesem Beispiel verwenden wir den OAuth2-Flow „Password“.
@@ -771,7 +259,7 @@ Am häufigsten ist der „Implicit“-Flow.
Am sichersten ist der „Code“-Flow, die Implementierung ist jedoch komplexer, da mehr Schritte erforderlich sind. Da er komplexer ist, schlagen viele Anbieter letztendlich den „Implicit“-Flow vor.
-/// note | "Hinweis"
+/// note | Hinweis
Es ist üblich, dass jeder Authentifizierungsanbieter seine Flows anders benennt, um sie zu einem Teil seiner Marke zu machen.
@@ -781,6 +269,6 @@ Aber am Ende implementieren sie denselben OAuth2-Standard.
**FastAPI** enthält Werkzeuge für alle diese OAuth2-Authentifizierungs-Flows in `fastapi.security.oauth2`.
-## `Security` in Dekorator-`dependencies`
+## `Security` in Dekorator-`dependencies` { #security-in-decorator-dependencies }
Auf die gleiche Weise können Sie eine `list`e von `Depends` im Parameter `dependencies` des Dekorators definieren (wie in [Abhängigkeiten in Pfadoperation-Dekoratoren](../../tutorial/dependencies/dependencies-in-path-operation-decorators.md){.internal-link target=_blank} erläutert), Sie könnten auch dort `Security` mit `scopes` verwenden.
diff --git a/docs/de/docs/advanced/settings.md b/docs/de/docs/advanced/settings.md
index 3cd4c6c7d4..ccd7f373dd 100644
--- a/docs/de/docs/advanced/settings.md
+++ b/docs/de/docs/advanced/settings.md
@@ -1,4 +1,4 @@
-# Einstellungen und Umgebungsvariablen
+# Einstellungen und Umgebungsvariablen { #settings-and-environment-variables }
In vielen Fällen benötigt Ihre Anwendung möglicherweise einige externe Einstellungen oder Konfigurationen, zum Beispiel geheime Schlüssel, Datenbank-Anmeldeinformationen, Anmeldeinformationen für E-Mail-Dienste, usw.
@@ -6,143 +6,25 @@ Die meisten dieser Einstellungen sind variabel (können sich ändern), wie z. B.
Aus diesem Grund werden diese üblicherweise in Umgebungsvariablen bereitgestellt, die von der Anwendung gelesen werden.
-## Umgebungsvariablen
+/// tip | Tipp
-/// tip | "Tipp"
-
-Wenn Sie bereits wissen, was „Umgebungsvariablen“ sind und wie man sie verwendet, können Sie gerne mit dem nächsten Abschnitt weiter unten fortfahren.
+Um Umgebungsvariablen zu verstehen, können Sie [Umgebungsvariablen](../environment-variables.md){.internal-link target=_blank} lesen.
///
-Eine Umgebungsvariable (auch bekannt als „env var“) ist eine Variable, die sich außerhalb des Python-Codes im Betriebssystem befindet und von Ihrem Python-Code (oder auch von anderen Programmen) gelesen werden kann.
-
-Sie können Umgebungsvariablen in der Shell erstellen und verwenden, ohne Python zu benötigen:
-
-//// tab | Linux, macOS, Windows Bash
-
-
-## Verbindungsabbrüche und mehreren Clients handhaben
+## Verbindungsabbrüche und mehrere Clients handhaben { #handling-disconnections-and-multiple-clients }
Wenn eine WebSocket-Verbindung geschlossen wird, löst `await websocket.receive_text()` eine `WebSocketDisconnect`-Exception aus, die Sie dann wie in folgendem Beispiel abfangen und behandeln können.
-//// tab | Python 3.9+
-
-```Python hl_lines="79-81"
-{!> ../../../docs_src/websockets/tutorial003_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="81-83"
-{!> ../../../docs_src/websockets/tutorial003.py!}
-```
-
-////
+{* ../../docs_src/websockets/tutorial003_py39.py hl[79:81] *}
Zum Ausprobieren:
@@ -238,7 +168,7 @@ Das wird die Ausnahme `WebSocketDisconnect` auslösen und alle anderen Clients e
Client #1596980209979 left the chat
```
-/// tip | "Tipp"
+/// tip | Tipp
Die obige Anwendung ist ein minimales und einfaches Beispiel, das zeigt, wie Nachrichten verarbeitet und an mehrere WebSocket-Verbindungen gesendet werden.
@@ -248,9 +178,9 @@ Wenn Sie etwas benötigen, das sich leicht in FastAPI integrieren lässt, aber r
///
-## Mehr Informationen
+## Mehr Informationen { #more-info }
Weitere Informationen zu Optionen finden Sie in der Dokumentation von Starlette:
-* Die `WebSocket`-Klasse.
-* Klassen-basierte Handhabung von WebSockets.
+* Die `WebSocket`-Klasse.
+* Klassen-basierte Handhabung von WebSockets.
diff --git a/docs/de/docs/advanced/wsgi.md b/docs/de/docs/advanced/wsgi.md
index 19ff90a901..1de9739dde 100644
--- a/docs/de/docs/advanced/wsgi.md
+++ b/docs/de/docs/advanced/wsgi.md
@@ -1,10 +1,10 @@
-# WSGI inkludieren – Flask, Django und andere
+# WSGI inkludieren – Flask, Django und andere { #including-wsgi-flask-django-others }
Sie können WSGI-Anwendungen mounten, wie Sie es in [Unteranwendungen – Mounts](sub-applications.md){.internal-link target=_blank}, [Hinter einem Proxy](behind-a-proxy.md){.internal-link target=_blank} gesehen haben.
Dazu können Sie die `WSGIMiddleware` verwenden und damit Ihre WSGI-Anwendung wrappen, zum Beispiel Flask, Django usw.
-## `WSGIMiddleware` verwenden
+## `WSGIMiddleware` verwenden { #using-wsgimiddleware }
Sie müssen `WSGIMiddleware` importieren.
@@ -12,17 +12,15 @@ Wrappen Sie dann die WSGI-Anwendung (z. B. Flask) mit der Middleware.
Und dann mounten Sie das auf einem Pfad.
-```Python hl_lines="2-3 23"
-{!../../../docs_src/wsgi/tutorial001.py!}
-```
+{* ../../docs_src/wsgi/tutorial001.py hl[2:3,3] *}
-## Es ansehen
+## Es testen { #check-it }
-Jetzt wird jede Anfrage unter dem Pfad `/v1/` von der Flask-Anwendung verarbeitet.
+Jetzt wird jeder Request unter dem Pfad `/v1/` von der Flask-Anwendung verarbeitet.
Und der Rest wird von **FastAPI** gehandhabt.
-Wenn Sie das mit Uvicorn ausführen und auf http://localhost:8000/v1/ gehen, sehen Sie die Response von Flask:
+Wenn Sie das ausführen und auf http://localhost:8000/v1/ gehen, sehen Sie die Response von Flask:
```txt
Hello, World from Flask!
diff --git a/docs/de/docs/alternatives.md b/docs/de/docs/alternatives.md
index 286ef1723b..4dd127dbad 100644
--- a/docs/de/docs/alternatives.md
+++ b/docs/de/docs/alternatives.md
@@ -1,8 +1,8 @@
-# Alternativen, Inspiration und Vergleiche
+# Alternativen, Inspiration und Vergleiche { #alternatives-inspiration-and-comparisons }
-Was hat **FastAPI** inspiriert, ein Vergleich zu Alternativen, und was FastAPI von diesen gelernt hat.
+Was hat **FastAPI** inspiriert, wie es sich im Vergleich zu Alternativen verhält und was es von ihnen gelernt hat.
-## Einführung
+## Einführung { #intro }
**FastAPI** würde ohne die frühere Arbeit anderer nicht existieren.
@@ -12,17 +12,17 @@ Ich habe die Schaffung eines neuen Frameworks viele Jahre lang vermieden. Zuerst
Aber irgendwann gab es keine andere Möglichkeit, als etwas zu schaffen, das all diese Funktionen bereitstellte, die besten Ideen früherer Tools aufnahm und diese auf die bestmögliche Weise kombinierte, wobei Sprachfunktionen verwendet wurden, die vorher noch nicht einmal verfügbar waren (Python 3.6+ Typhinweise).
-## Vorherige Tools
+## Vorherige Tools { #previous-tools }
-### Django
+### Django { #django }
Es ist das beliebteste Python-Framework und genießt großes Vertrauen. Es wird zum Aufbau von Systemen wie Instagram verwendet.
-Ist relativ eng mit relationalen Datenbanken (wie MySQL oder PostgreSQL) gekoppelt, daher ist es nicht sehr einfach, eine NoSQL-Datenbank (wie Couchbase, MongoDB, Cassandra, usw.) als Hauptspeicherengine zu verwenden.
+Es ist relativ eng mit relationalen Datenbanken (wie MySQL oder PostgreSQL) gekoppelt, daher ist es nicht sehr einfach, eine NoSQL-Datenbank (wie Couchbase, MongoDB, Cassandra, usw.) als Hauptspeicherengine zu verwenden.
-Es wurde erstellt, um den HTML-Code im Backend zu generieren, nicht um APIs zu erstellen, die von einem modernen Frontend (wie React, Vue.js und Angular) oder von anderen Systemen (wie IoT-Geräten) verwendet werden, um mit ihm zu kommunizieren.
+Es wurde erstellt, um den HTML-Code im Backend zu generieren, nicht um APIs zu erstellen, die von einem modernen Frontend (wie React, Vue.js und Angular) oder von anderen Systemen (wie IoT-Geräten) verwendet werden, um mit ihm zu kommunizieren.
-### Django REST Framework
+### Django REST Framework { #django-rest-framework }
Das Django REST Framework wurde als flexibles Toolkit zum Erstellen von Web-APIs unter Verwendung von Django entwickelt, um dessen API-Möglichkeiten zu verbessern.
@@ -30,19 +30,19 @@ Es wird von vielen Unternehmen verwendet, darunter Mozilla, Red Hat und Eventbri
Es war eines der ersten Beispiele für **automatische API-Dokumentation**, und dies war insbesondere eine der ersten Ideen, welche „die Suche nach“ **FastAPI** inspirierten.
-/// note | "Hinweis"
+/// note | Hinweis
Das Django REST Framework wurde von Tom Christie erstellt. Derselbe Schöpfer von Starlette und Uvicorn, auf denen **FastAPI** basiert.
///
-/// check | "Inspirierte **FastAPI**"
+/// check | Inspirierte **FastAPI**
Eine automatische API-Dokumentationsoberfläche zu haben.
///
-### Flask
+### Flask { #flask }
Flask ist ein „Mikroframework“, es enthält weder Datenbankintegration noch viele der Dinge, die standardmäßig in Django enthalten sind.
@@ -56,7 +56,7 @@ Diese Entkopplung der Teile und die Tatsache, dass es sich um ein „Mikroframew
Angesichts der Einfachheit von Flask schien es eine gute Ergänzung zum Erstellen von APIs zu sein. Als Nächstes musste ein „Django REST Framework“ für Flask gefunden werden.
-/// check | "Inspirierte **FastAPI**"
+/// check | Inspirierte **FastAPI**
Ein Mikroframework zu sein. Es einfach zu machen, die benötigten Tools und Teile zu kombinieren.
@@ -64,7 +64,7 @@ Ein Mikroframework zu sein. Es einfach zu machen, die benötigten Tools und Teil
///
-### Requests
+### Requests { #requests }
**FastAPI** ist eigentlich keine Alternative zu **Requests**. Der Umfang der beiden ist sehr unterschiedlich.
@@ -82,7 +82,7 @@ Aus diesem Grund heißt es auf der offiziellen Website:
> Requests ist eines der am häufigsten heruntergeladenen Python-Packages aller Zeiten
-Die Art und Weise, wie Sie es verwenden, ist sehr einfach. Um beispielsweise einen `GET`-Request zu machen, würden Sie schreiben:
+Die Art und Weise, wie Sie es verwenden, ist sehr einfach. Um beispielsweise einen `GET`-Request zu machen, würden Sie schreiben:
```Python
response = requests.get("http://example.com/some/url")
@@ -98,7 +98,7 @@ def read_url():
Sehen Sie sich die Ähnlichkeiten in `requests.get(...)` und `@app.get(...)` an.
-/// check | "Inspirierte **FastAPI**"
+/// check | Inspirierte **FastAPI**
* Über eine einfache und intuitive API zu verfügen.
* HTTP-Methodennamen (Operationen) direkt, auf einfache und intuitive Weise zu verwenden.
@@ -106,7 +106,7 @@ Sehen Sie sich die Ähnlichkeiten in `requests.get(...)` und `@app.get(...)` an.
///
-### Swagger / OpenAPI
+### Swagger / OpenAPI { #swagger-openapi }
Die Hauptfunktion, die ich vom Django REST Framework haben wollte, war die automatische API-Dokumentation.
@@ -118,7 +118,7 @@ Irgendwann wurde Swagger an die Linux Foundation übergeben und in OpenAPI umben
Aus diesem Grund spricht man bei Version 2.0 häufig von „Swagger“ und ab Version 3 von „OpenAPI“.
-/// check | "Inspirierte **FastAPI**"
+/// check | Inspirierte **FastAPI**
Einen offenen Standard für API-Spezifikationen zu übernehmen und zu verwenden, anstelle eines benutzerdefinierten Schemas.
@@ -131,13 +131,13 @@ Diese beiden wurden ausgewählt, weil sie ziemlich beliebt und stabil sind, aber
///
-### Flask REST Frameworks
+### Flask REST Frameworks { #flask-rest-frameworks }
Es gibt mehrere Flask REST Frameworks, aber nachdem ich die Zeit und Arbeit investiert habe, sie zu untersuchen, habe ich festgestellt, dass viele nicht mehr unterstützt werden oder abgebrochen wurden und dass mehrere fortbestehende Probleme sie unpassend machten.
-### Marshmallow
+### Marshmallow { #marshmallow }
-Eine der von API-Systemen benötigen Hauptfunktionen ist die Daten-„Serialisierung“, welche Daten aus dem Code (Python) entnimmt und in etwas umwandelt, was durch das Netzwerk gesendet werden kann. Beispielsweise das Konvertieren eines Objekts, welches Daten aus einer Datenbank enthält, in ein JSON-Objekt. Konvertieren von `datetime`-Objekten in Strings, usw.
+Eine der von API-Systemen benötigten Hauptfunktionen ist die Daten-„Serialisierung“, welche Daten aus dem Code (Python) entnimmt und in etwas umwandelt, was durch das Netzwerk gesendet werden kann. Beispielsweise das Konvertieren eines Objekts, welches Daten aus einer Datenbank enthält, in ein JSON-Objekt. Konvertieren von `datetime`-Objekten in Strings, usw.
Eine weitere wichtige Funktion, benötigt von APIs, ist die Datenvalidierung, welche sicherstellt, dass die Daten unter gegebenen Umständen gültig sind. Zum Beispiel, dass ein Feld ein `int` ist und kein zufälliger String. Das ist besonders nützlich für hereinkommende Daten.
@@ -145,15 +145,15 @@ Ohne ein Datenvalidierungssystem müssten Sie alle Prüfungen manuell im Code du
Für diese Funktionen wurde Marshmallow entwickelt. Es ist eine großartige Bibliothek und ich habe sie schon oft genutzt.
-Aber sie wurde erstellt, bevor Typhinweise in Python existierten. Um also ein Schema zu definieren, müssen Sie bestimmte Werkzeuge und Klassen verwenden, die von Marshmallow bereitgestellt werden.
+Aber sie wurde erstellt, bevor Typhinweise in Python existierten. Um also ein Schema zu definieren, müssen Sie bestimmte Werkzeuge und Klassen verwenden, die von Marshmallow bereitgestellt werden.
-/// check | "Inspirierte **FastAPI**"
+/// check | Inspirierte **FastAPI**
Code zu verwenden, um „Schemas“ zu definieren, welche Datentypen und Validierung automatisch bereitstellen.
///
-### Webargs
+### Webargs { #webargs }
Eine weitere wichtige Funktion, die von APIs benötigt wird, ist das Parsen von Daten aus eingehenden Requests.
@@ -163,19 +163,19 @@ Es verwendet unter der Haube Marshmallow, um die Datenvalidierung durchzuführen
Es ist ein großartiges Tool und ich habe es auch oft verwendet, bevor ich **FastAPI** hatte.
-/// info
+/// info | Info
Webargs wurde von denselben Marshmallow-Entwicklern erstellt.
///
-/// check | "Inspirierte **FastAPI**"
+/// check | Inspirierte **FastAPI**
Eingehende Requestdaten automatisch zu validieren.
///
-### APISpec
+### APISpec { #apispec }
Marshmallow und Webargs bieten Validierung, Parsen und Serialisierung als Plugins.
@@ -193,19 +193,19 @@ Aber dann haben wir wieder das Problem einer Mikrosyntax innerhalb eines Python-
Der Texteditor kann dabei nicht viel helfen. Und wenn wir Parameter oder Marshmallow-Schemas ändern und vergessen, auch den YAML-Docstring zu ändern, wäre das generierte Schema veraltet.
-/// info
+/// info | Info
APISpec wurde von denselben Marshmallow-Entwicklern erstellt.
///
-/// check | "Inspirierte **FastAPI**"
+/// check | Inspirierte **FastAPI**
Den offenen Standard für APIs, OpenAPI, zu unterstützen.
///
-### Flask-apispec
+### Flask-apispec { #flask-apispec }
Hierbei handelt es sich um ein Flask-Plugin, welches Webargs, Marshmallow und APISpec miteinander verbindet.
@@ -225,19 +225,19 @@ Die Verwendung führte zur Entwicklung mehrerer Flask-Full-Stack-Generatoren. Di
Und dieselben Full-Stack-Generatoren bildeten die Basis der [**FastAPI**-Projektgeneratoren](project-generation.md){.internal-link target=_blank}.
-/// info
+/// info | Info
Flask-apispec wurde von denselben Marshmallow-Entwicklern erstellt.
///
-/// check | "Inspirierte **FastAPI**"
+/// check | Inspirierte **FastAPI**
Das OpenAPI-Schema automatisch zu generieren, aus demselben Code, welcher die Serialisierung und Validierung definiert.
///
-### NestJS (und Angular)
+### NestJS (und Angular) { #nestjs-and-angular }
Dies ist nicht einmal Python, NestJS ist ein von Angular inspiriertes JavaScript (TypeScript) NodeJS Framework.
@@ -249,9 +249,9 @@ Da die Parameter mit TypeScript-Typen beschrieben werden (ähnlich den Python-Ty
Da TypeScript-Daten jedoch nach der Kompilierung nach JavaScript nicht erhalten bleiben, können die Typen nicht gleichzeitig die Validierung, Serialisierung und Dokumentation definieren. Aus diesem Grund und aufgrund einiger Designentscheidungen ist es für die Validierung, Serialisierung und automatische Schemagenerierung erforderlich, an vielen Stellen Dekoratoren hinzuzufügen. Es wird also ziemlich ausführlich.
-Es kann nicht sehr gut mit verschachtelten Modellen umgehen. Wenn es sich beim JSON-Body in der Anfrage also um ein JSON-Objekt mit inneren Feldern handelt, die wiederum verschachtelte JSON-Objekte sind, kann er nicht richtig dokumentiert und validiert werden.
+Es kann nicht sehr gut mit verschachtelten Modellen umgehen. Wenn es sich beim JSON-Body im Request also um ein JSON-Objekt mit inneren Feldern handelt, die wiederum verschachtelte JSON-Objekte sind, kann er nicht richtig dokumentiert und validiert werden.
-/// check | "Inspirierte **FastAPI**"
+/// check | Inspirierte **FastAPI**
Python-Typen zu verwenden, um eine hervorragende Editorunterstützung zu erhalten.
@@ -259,11 +259,11 @@ Python-Typen zu verwenden, um eine hervorragende Editorunterstützung zu erhalte
///
-### Sanic
+### Sanic { #sanic }
Es war eines der ersten extrem schnellen Python-Frameworks, welches auf `asyncio` basierte. Es wurde so gestaltet, dass es Flask sehr ähnlich ist.
-/// note | "Technische Details"
+/// note | Technische Details
Es verwendete `uvloop` anstelle der standardmäßigen Python-`asyncio`-Schleife. Das hat es so schnell gemacht.
@@ -271,7 +271,7 @@ Hat eindeutig Uvicorn und Starlette inspiriert, welche derzeit in offenen Benchm
///
-/// check | "Inspirierte **FastAPI**"
+/// check | Inspirierte **FastAPI**
Einen Weg zu finden, eine hervorragende Performanz zu haben.
@@ -279,15 +279,15 @@ Aus diesem Grund basiert **FastAPI** auf Starlette, da dieses das schnellste ver
///
-### Falcon
+### Falcon { #falcon }
Falcon ist ein weiteres leistungsstarkes Python-Framework. Es ist minimalistisch konzipiert und dient als Grundlage für andere Frameworks wie Hug.
-Es ist so konzipiert, dass es über Funktionen verfügt, welche zwei Parameter empfangen, einen „Request“ und eine „Response“. Dann „lesen“ Sie Teile des Requests und „schreiben“ Teile der Response. Aufgrund dieses Designs ist es nicht möglich, Request-Parameter und -Bodys mit Standard-Python-Typhinweisen als Funktionsparameter zu deklarieren.
+Es ist so konzipiert, dass es über Funktionen verfügt, welche zwei Parameter empfangen, einen „Request“ und eine „Response“. Dann „lesen“ Sie Teile des Requests und „schreiben“ Teile der Response. Aufgrund dieses Designs ist es nicht möglich, Request-Parameter und -Bodys mit Standard-Python-Typhinweisen als Funktionsparameter zu deklarieren.
Daher müssen Datenvalidierung, Serialisierung und Dokumentation im Code und nicht automatisch erfolgen. Oder sie müssen als Framework oberhalb von Falcon implementiert werden, so wie Hug. Dieselbe Unterscheidung findet auch in anderen Frameworks statt, die vom Design von Falcon inspiriert sind und ein Requestobjekt und ein Responseobjekt als Parameter haben.
-/// check | "Inspirierte **FastAPI**"
+/// check | Inspirierte **FastAPI**
Wege zu finden, eine großartige Performanz zu erzielen.
@@ -297,7 +297,7 @@ Obwohl er in FastAPI optional ist und hauptsächlich zum Festlegen von Headern,
///
-### Molten
+### Molten { #molten }
Ich habe Molten in den ersten Phasen der Entwicklung von **FastAPI** entdeckt. Und es hat ganz ähnliche Ideen:
@@ -313,7 +313,7 @@ Das Dependency Injection System erfordert eine Vorab-Registrierung der Abhängig
Routen werden an einer einzigen Stelle deklariert, indem Funktionen verwendet werden, die an anderen Stellen deklariert wurden (anstatt Dekoratoren zu verwenden, welche direkt über der Funktion platziert werden können, welche den Endpunkt verarbeitet). Dies ähnelt eher der Vorgehensweise von Django als der Vorgehensweise von Flask (und Starlette). Es trennt im Code Dinge, die relativ eng miteinander gekoppelt sind.
-/// check | "Inspirierte **FastAPI**"
+/// check | Inspirierte **FastAPI**
Zusätzliche Validierungen für Datentypen zu definieren, mithilfe des „Default“-Werts von Modellattributen. Dies verbessert die Editorunterstützung und war zuvor in Pydantic nicht verfügbar.
@@ -321,7 +321,7 @@ Das hat tatsächlich dazu geführt, dass Teile von Pydantic aktualisiert wurden,
///
-### Hug
+### Hug { #hug }
Hug war eines der ersten Frameworks, welches die Deklaration von API-Parametertypen mithilfe von Python-Typhinweisen implementierte. Das war eine großartige Idee, die andere Tools dazu inspirierte, dasselbe zu tun.
@@ -335,13 +335,13 @@ Es verfügt über eine interessante, ungewöhnliche Funktion: Mit demselben Fram
Da es auf dem bisherigen Standard für synchrone Python-Webframeworks (WSGI) basiert, kann es nicht mit Websockets und anderen Dingen umgehen, verfügt aber dennoch über eine hohe Performanz.
-/// info
+/// info | Info
-Hug wurde von Timothy Crosley erstellt, dem gleichen Schöpfer von `isort`, einem großartigen Tool zum automatischen Sortieren von Importen in Python-Dateien.
+Hug wurde von Timothy Crosley erstellt, demselben Schöpfer von `isort`, einem großartigen Tool zum automatischen Sortieren von Importen in Python-Dateien.
///
-/// check | "Ideen, die **FastAPI** inspiriert haben"
+/// check | Ideen, die **FastAPI** inspiriert haben
Hug inspirierte Teile von APIStar und war eines der Tools, die ich am vielversprechendsten fand, neben APIStar.
@@ -351,7 +351,7 @@ Hug inspirierte **FastAPI** dazu, einen `response`-Parameter in Funktionen zu de
///
-### APIStar (≦ 0.5)
+### APIStar (≦ 0.5) { #apistar-0-5 }
Kurz bevor ich mich entschied, **FastAPI** zu erstellen, fand ich den **APIStar**-Server. Er hatte fast alles, was ich suchte, und ein tolles Design.
@@ -375,7 +375,7 @@ Es handelte sich nicht länger um ein API-Webframework, da sich der Entwickler a
Jetzt handelt es sich bei APIStar um eine Reihe von Tools zur Validierung von OpenAPI-Spezifikationen, nicht um ein Webframework.
-/// info
+/// info | Info
APIStar wurde von Tom Christie erstellt. Derselbe, welcher Folgendes erstellt hat:
@@ -385,7 +385,7 @@ APIStar wurde von Tom Christie erstellt. Derselbe, welcher Folgendes erstellt ha
///
-/// check | "Inspirierte **FastAPI**"
+/// check | Inspirierte **FastAPI**
Zu existieren.
@@ -399,9 +399,9 @@ Ich betrachte **FastAPI** als einen „spirituellen Nachfolger“ von APIStar, w
///
-## Verwendet von **FastAPI**
+## Verwendet von **FastAPI** { #used-by-fastapi }
-### Pydantic
+### Pydantic { #pydantic }
Pydantic ist eine Bibliothek zum Definieren von Datenvalidierung, Serialisierung und Dokumentation (unter Verwendung von JSON Schema) basierend auf Python-Typhinweisen.
@@ -409,7 +409,7 @@ Das macht es äußerst intuitiv.
Es ist vergleichbar mit Marshmallow. Obwohl es in Benchmarks schneller als Marshmallow ist. Und da es auf den gleichen Python-Typhinweisen basiert, ist die Editorunterstützung großartig.
-/// check | "**FastAPI** verwendet es, um"
+/// check | **FastAPI** verwendet es, um
Die gesamte Datenvalidierung, Datenserialisierung und automatische Modelldokumentation (basierend auf JSON Schema) zu erledigen.
@@ -417,7 +417,7 @@ Die gesamte Datenvalidierung, Datenserialisierung und automatische Modelldokumen
///
-### Starlette
+### Starlette { #starlette }
Starlette ist ein leichtgewichtiges ASGI-Framework/Toolkit, welches sich ideal für die Erstellung hochperformanter asynchroner Dienste eignet.
@@ -428,9 +428,9 @@ Es bietet:
* Eine sehr beeindruckende Leistung.
* WebSocket-Unterstützung.
* Hintergrundtasks im selben Prozess.
-* Events für das Hoch- und Herunterfahren.
+* Startup- und Shutdown-Events.
* Testclient basierend auf HTTPX.
-* CORS, GZip, statische Dateien, Streamende Responses.
+* CORS, GZip, statische Dateien, Responses streamen.
* Session- und Cookie-Unterstützung.
* 100 % Testabdeckung.
* 100 % Typannotierte Codebasis.
@@ -444,7 +444,7 @@ Es bietet jedoch keine automatische Datenvalidierung, Serialisierung oder Dokume
Das ist eines der wichtigsten Dinge, welche **FastAPI** hinzufügt, alles basierend auf Python-Typhinweisen (mit Pydantic). Das, plus, das Dependency Injection System, Sicherheitswerkzeuge, OpenAPI-Schemagenerierung, usw.
-/// note | "Technische Details"
+/// note | Technische Details
ASGI ist ein neuer „Standard“, welcher von Mitgliedern des Django-Kernteams entwickelt wird. Es handelt sich immer noch nicht um einen „Python-Standard“ (ein PEP), obwohl sie gerade dabei sind, das zu tun.
@@ -452,7 +452,7 @@ Dennoch wird es bereits von mehreren Tools als „Standard“ verwendet. Das ver
///
-/// check | "**FastAPI** verwendet es, um"
+/// check | **FastAPI** verwendet es, um
Alle Kern-Webaspekte zu handhaben. Und fügt Funktionen obenauf.
@@ -462,7 +462,7 @@ Alles, was Sie also mit Starlette machen können, können Sie direkt mit **FastA
///
-### Uvicorn
+### Uvicorn { #uvicorn }
Uvicorn ist ein blitzschneller ASGI-Server, der auf uvloop und httptools basiert.
@@ -470,16 +470,16 @@ Es handelt sich nicht um ein Webframework, sondern um einen Server. Beispielswei
Es ist der empfohlene Server für Starlette und **FastAPI**.
-/// check | "**FastAPI** empfiehlt es als"
+/// check | **FastAPI** empfiehlt es als
Hauptwebserver zum Ausführen von **FastAPI**-Anwendungen.
-Sie können ihn mit Gunicorn kombinieren, um einen asynchronen Multiprozess-Server zu erhalten.
+Sie können auch die Kommandozeilenoption `--workers` verwenden, um einen asynchronen Multiprozess-Server zu erhalten.
Weitere Details finden Sie im Abschnitt [Deployment](deployment/index.md){.internal-link target=_blank}.
///
-## Benchmarks und Geschwindigkeit
+## Benchmarks und Geschwindigkeit { #benchmarks-and-speed }
Um den Unterschied zwischen Uvicorn, Starlette und FastAPI zu verstehen, zu vergleichen und zu sehen, lesen Sie den Abschnitt über [Benchmarks](benchmarks.md){.internal-link target=_blank}.
diff --git a/docs/de/docs/async.md b/docs/de/docs/async.md
index 74b6b69687..3dbd442e5f 100644
--- a/docs/de/docs/async.md
+++ b/docs/de/docs/async.md
@@ -1,8 +1,8 @@
-# Nebenläufigkeit und async / await
+# Nebenläufigkeit und async / await { #concurrency-and-async-await }
Details zur `async def`-Syntax für *Pfadoperation-Funktionen* und Hintergrundinformationen zu asynchronem Code, Nebenläufigkeit und Parallelität.
-## In Eile?
+## In Eile? { #in-a-hurry }
TL;DR:
@@ -12,7 +12,7 @@ Wenn Sie Bibliotheken von Dritten verwenden, die mit `await` aufgerufen werden m
results = await some_library()
```
-Dann deklarieren Sie Ihre *Pfadoperation-Funktionen* mit `async def` wie in:
+Dann deklarieren Sie Ihre *Pfadoperation-Funktionen* mit `async def`, wie in:
```Python hl_lines="2"
@app.get('/')
@@ -21,7 +21,7 @@ async def read_results():
return results
```
-/// note
+/// note | Hinweis
Sie können `await` nur innerhalb von Funktionen verwenden, die mit `async def` erstellt wurden.
@@ -29,7 +29,7 @@ Sie können `await` nur innerhalb von Funktionen verwenden, die mit `async def`
---
-Wenn Sie eine Bibliothek eines Dritten verwenden, die mit etwas kommuniziert (einer Datenbank, einer API, dem Dateisystem, usw.) und welche die Verwendung von `await` nicht unterstützt (dies ist derzeit bei den meisten Datenbankbibliotheken der Fall), dann deklarieren Sie Ihre *Pfadoperation-Funktionen* ganz normal nur mit `def`, etwa:
+Wenn Sie eine Bibliothek eines Dritten verwenden, die mit etwas kommuniziert (einer Datenbank, einer API, dem Dateisystem, usw.) und welche die Verwendung von `await` nicht unterstützt (dies ist derzeit bei den meisten Datenbankbibliotheken der Fall), dann deklarieren Sie Ihre *Pfadoperation-Funktionen* ganz normal nur mit `def`, wie in:
```Python hl_lines="2"
@app.get('/')
@@ -40,7 +40,7 @@ def results():
---
-Wenn Ihre Anwendung (irgendwie) mit nichts anderem kommunizieren und auf dessen Antwort warten muss, verwenden Sie `async def`.
+Wenn Ihre Anwendung (irgendwie) nicht mit etwas anderem kommunizieren und auf dessen Antwort warten muss, verwenden Sie `async def`, auch wenn Sie `await` im Inneren nicht verwenden müssen.
---
@@ -52,11 +52,11 @@ Wenn Sie sich unsicher sind, verwenden Sie einfach `def`.
Wie dem auch sei, in jedem der oben genannten Fälle wird FastAPI immer noch asynchron arbeiten und extrem schnell sein.
-Wenn Sie jedoch den oben genannten Schritten folgen, können einige Performance-Optimierungen vorgenommen werden.
+Wenn Sie jedoch den oben genannten Schritten folgen, können einige Performanz-Optimierungen vorgenommen werden.
-## Technische Details
+## Technische Details { #technical-details }
-Moderne Versionen von Python unterstützen **„asynchronen Code“** unter Verwendung sogenannter **„Coroutinen“** mithilfe der Syntax **`async`** und **`await`**.
+Moderne Versionen von Python unterstützen **„asynchronen Code“** unter Verwendung sogenannter **„Coroutinen“** mithilfe der Syntax **`async` und `await`**.
Nehmen wir obigen Satz in den folgenden Abschnitten Schritt für Schritt unter die Lupe:
@@ -64,13 +64,13 @@ Nehmen wir obigen Satz in den folgenden Abschnitten Schritt für Schritt unter d
* **`async` und `await`**
* **Coroutinen**
-## Asynchroner Code
+## Asynchroner Code { #asynchronous-code }
-Asynchroner Code bedeutet lediglich, dass die Sprache 💬 eine Möglichkeit hat, dem Computersystem / Programm 🤖 mitzuteilen, dass es 🤖 an einem bestimmten Punkt im Code darauf warten muss, dass *etwas anderes* irgendwo anders fertig wird. Nehmen wir an, *etwas anderes* ist hier „Langsam-Datei“ 📝.
+Asynchroner Code bedeutet lediglich, dass die Sprache 💬 eine Möglichkeit hat, dem Computer / Programm 🤖 mitzuteilen, dass es 🤖 an einem bestimmten Punkt im Code darauf warten muss, dass *etwas anderes* irgendwo anders fertig wird. Nehmen wir an, *etwas anderes* ist hier „Langsam-Datei“ 📝.
Während der Zeit, die „Langsam-Datei“ 📝 benötigt, kann das System also andere Aufgaben erledigen.
-Dann kommt das System / Programm 🤖 bei jeder Gelegenheit zurück, wenn es entweder wieder wartet, oder wann immer es 🤖 die ganze Arbeit erledigt hat, die zu diesem Zeitpunkt zu tun war. Und es 🤖 wird nachschauen, ob eine der Aufgaben, auf die es gewartet hat, fertig damit ist, zu tun, was sie tun sollte.
+Dann kommt der Computer / das Programm 🤖 bei jeder Gelegenheit zurück, weil es entweder wieder wartet oder wann immer es 🤖 die ganze Arbeit erledigt hat, die zu diesem Zeitpunkt zu tun war. Und es 🤖 wird nachschauen, ob eine der Aufgaben, auf die es gewartet hat, fertig ist.
Dann nimmt es 🤖 die erste erledigte Aufgabe (sagen wir, unsere „Langsam-Datei“ 📝) und bearbeitet sie weiter.
@@ -87,13 +87,13 @@ Das „Warten auf etwas anderes“ bezieht sich normalerweise auf I/O-Operationen verbraucht wird, nennt man dies auch „I/O-lastige“ („I/O bound“) Operationen.
-„Asynchron“, sagt man, weil das Computersystem / Programm nicht mit einer langsamen Aufgabe „synchronisiert“ werden muss und nicht auf den genauen Moment warten muss, in dem die Aufgabe beendet ist, ohne dabei etwas zu tun, um schließlich das Ergebnis der Aufgabe zu übernehmen und die Arbeit fortsetzen zu können.
+„Asynchron“, sagt man, weil der Computer / das Programm nicht mit einer langsamen Aufgabe „synchronisiert“ werden muss und nicht auf den genauen Moment warten muss, in dem die Aufgabe beendet ist, ohne dabei etwas zu tun, um schließlich das Ergebnis der Aufgabe zu übernehmen und die Arbeit fortsetzen zu können.
-Da es sich stattdessen um ein „asynchrones“ System handelt, kann die Aufgabe nach Abschluss ein wenig (einige Mikrosekunden) in der Schlange warten, bis das System / Programm seine anderen Dinge erledigt hat und zurückkommt, um die Ergebnisse entgegenzunehmen und mit ihnen weiterzuarbeiten.
+Da es sich stattdessen um ein „asynchrones“ System handelt, kann die Aufgabe nach Abschluss ein wenig (einige Mikrosekunden) in der Schlange warten, bis der Computer / das Programm seine anderen Dinge erledigt hat und zurückkommt, um die Ergebnisse entgegenzunehmen und mit ihnen weiterzuarbeiten.
-Für „synchron“ (im Gegensatz zu „asynchron“) wird auch oft der Begriff „sequentiell“ verwendet, da das System / Programm alle Schritte in einer Sequenz („der Reihe nach“) ausführt, bevor es zu einer anderen Aufgabe wechselt, auch wenn diese Schritte mit Warten verbunden sind.
+Für „synchron“ (im Gegensatz zu „asynchron“) wird auch oft der Begriff „sequentiell“ verwendet, da der Computer / das Programm alle Schritte in einer Sequenz („der Reihe nach“) ausführt, bevor es zu einer anderen Aufgabe wechselt, auch wenn diese Schritte mit Warten verbunden sind.
-### Nebenläufigkeit und Hamburger
+### Nebenläufigkeit und Hamburger { #concurrency-and-burgers }
Diese oben beschriebene Idee von **asynchronem** Code wird manchmal auch **„Nebenläufigkeit“** genannt. Sie unterscheidet sich von **„Parallelität“**.
@@ -103,7 +103,7 @@ Aber die Details zwischen *Nebenläufigkeit* und *Parallelität* sind ziemlich u
Um den Unterschied zu erkennen, stellen Sie sich die folgende Geschichte über Hamburger vor:
-### Nebenläufige Hamburger
+### Nebenläufige Hamburger { #concurrent-burgers }
Sie gehen mit Ihrem Schwarm Fastfood holen, stehen in der Schlange, während der Kassierer die Bestellungen der Leute vor Ihnen entgegennimmt. 😍
@@ -139,7 +139,7 @@ Sie und Ihr Schwarm essen die Burger und haben eine schöne Zeit. ✨
-/// info
+/// info | Info
Die wunderschönen Illustrationen stammen von Ketrina Thompson. 🎨
@@ -147,7 +147,7 @@ Die wunderschönen Illustrationen stammen von Ketrina Thompson. 🎨
@@ -233,15 +233,15 @@ Und man muss lange in der Schlange warten 🕙 sonst kommt man nicht an die Reih
Sie würden Ihren Schwarm 😍 wahrscheinlich nicht mitnehmen wollen, um Besorgungen bei der Bank zu erledigen 🏦.
-### Hamburger Schlussfolgerung
+### Hamburger Schlussfolgerung { #burger-conclusion }
-In diesem Szenario „Fast Food Burger mit Ihrem Schwarm“ ist es viel sinnvoller, ein nebenläufiges System zu haben ⏸🔀⏯, da viel gewartet wird 🕙.
+In diesem Szenario „Fastfood-Burger mit Ihrem Schwarm“ ist es viel sinnvoller, ein nebenläufiges System zu haben ⏸🔀⏯, da viel gewartet wird 🕙.
Das ist auch bei den meisten Webanwendungen der Fall.
-Viele, viele Benutzer, aber Ihr Server wartet 🕙 darauf, dass deren nicht so gute Internetverbindungen die Requests übermitteln.
+Viele, viele Benutzer, aber Ihr Server wartet 🕙 darauf, dass deren nicht so gute Internetverbindungen die Requests übermitteln.
-Und dann warten 🕙, bis die Responses zurückkommen.
+Und dann wieder warten 🕙, bis die Responses zurückkommen.
Dieses „Warten“ 🕙 wird in Mikrosekunden gemessen, aber zusammenfassend lässt sich sagen, dass am Ende eine Menge gewartet wird.
@@ -253,7 +253,7 @@ Und das ist das gleiche Leistungsniveau, das Sie mit **FastAPI** erhalten.
Und da Sie Parallelität und Asynchronität gleichzeitig haben können, erzielen Sie eine höhere Performanz als die meisten getesteten NodeJS-Frameworks und sind mit Go auf Augenhöhe, einer kompilierten Sprache, die näher an C liegt (alles dank Starlette).
-### Ist Nebenläufigkeit besser als Parallelität?
+### Ist Nebenläufigkeit besser als Parallelität? { #is-concurrency-better-than-parallelism }
Nein! Das ist nicht die Moral der Geschichte.
@@ -290,17 +290,17 @@ Zum Beispiel:
* **Maschinelles Lernen**: Normalerweise sind viele „Matrix“- und „Vektor“-Multiplikationen erforderlich. Stellen Sie sich eine riesige Tabelle mit Zahlen vor, in der Sie alle Zahlen gleichzeitig multiplizieren.
* **Deep Learning**: Dies ist ein Teilgebiet des maschinellen Lernens, daher gilt das Gleiche. Es ist nur so, dass es nicht eine einzige Tabelle mit Zahlen zum Multiplizieren gibt, sondern eine riesige Menge davon, und in vielen Fällen verwendet man einen speziellen Prozessor, um diese Modelle zu erstellen und / oder zu verwenden.
-### Nebenläufigkeit + Parallelität: Web + maschinelles Lernen
+### Nebenläufigkeit + Parallelität: Web + maschinelles Lernen { #concurrency-parallelism-web-machine-learning }
Mit **FastAPI** können Sie die Vorteile der Nebenläufigkeit nutzen, die in der Webentwicklung weit verbreitet ist (derselbe Hauptvorteil von NodeJS).
-Sie können aber auch die Vorteile von Parallelität und Multiprocessing (Mehrere Prozesse werden parallel ausgeführt) für **CPU-lastige** Workloads wie in Systemen für maschinelles Lernen nutzen.
+Sie können aber auch die Vorteile von Parallelität und Multiprocessing (mehrere Prozesse werden parallel ausgeführt) für **CPU-lastige** Workloads wie in Systemen für maschinelles Lernen nutzen.
Dies und die einfache Tatsache, dass Python die Hauptsprache für **Data Science**, maschinelles Lernen und insbesondere Deep Learning ist, machen FastAPI zu einem sehr passenden Werkzeug für Web-APIs und Anwendungen für Data Science / maschinelles Lernen (neben vielen anderen).
Wie Sie diese Parallelität in der Produktion erreichen, erfahren Sie im Abschnitt über [Deployment](deployment/index.md){.internal-link target=_blank}.
-## `async` und `await`.
+## `async` und `await` { #async-and-await }
Moderne Versionen von Python verfügen über eine sehr intuitive Möglichkeit, asynchronen Code zu schreiben. Dadurch sieht es wie normaler „sequentieller“ Code aus und übernimmt im richtigen Moment das „Warten“ für Sie.
@@ -316,16 +316,16 @@ Damit `await` funktioniert, muss es sich in einer Funktion befinden, die diese A
```Python hl_lines="1"
async def get_burgers(number: int):
- # Mach Sie hier etwas Asynchrones, um die Burger zu erstellen
+ # Mache hier etwas Asynchrones, um die Burger zu erstellen
return burgers
```
... statt mit `def`:
```Python hl_lines="2"
-# Die ist nicht asynchron
+# Dies ist nicht asynchron
def get_sequential_burgers(number: int):
- # Mach Sie hier etwas Sequentielles, um die Burger zu erstellen
+ # Mache hier etwas Sequentielles, um die Burger zu erstellen
return burgers
```
@@ -349,7 +349,7 @@ async def read_burgers():
return burgers
```
-### Weitere technische Details
+### Weitere technische Details { #more-technical-details }
Ihnen ist wahrscheinlich aufgefallen, dass `await` nur innerhalb von Funktionen verwendet werden kann, die mit `async def` definiert sind.
@@ -361,15 +361,17 @@ Wenn Sie mit **FastAPI** arbeiten, müssen Sie sich darüber keine Sorgen machen
Wenn Sie jedoch `async` / `await` ohne FastAPI verwenden möchten, können Sie dies auch tun.
-### Schreiben Sie Ihren eigenen asynchronen Code
+### Schreiben Sie Ihren eigenen asynchronen Code { #write-your-own-async-code }
-Starlette (und **FastAPI**) basiert auf AnyIO, was bedeutet, es ist sowohl kompatibel mit der Python-Standardbibliothek asyncio, als auch mit Trio.
+Starlette (und **FastAPI**) basieren auf AnyIO, was bedeutet, dass es sowohl kompatibel mit der Python-Standardbibliothek asyncio als auch mit Trio ist.
-Insbesondere können Sie AnyIO direkt verwenden für Ihre fortgeschritten nebenläufigen und parallelen Anwendungsfälle, die fortgeschrittenere Muster in Ihrem eigenen Code erfordern.
+Insbesondere können Sie AnyIO direkt verwenden für Ihre fortgeschrittenen nebenläufigen Anwendungsfälle, die fortgeschrittenere Muster in Ihrem eigenen Code erfordern.
-Und selbst wenn Sie FastAPI nicht verwenden würden, könnten Sie auch Ihre eigenen asynchronen Anwendungen mit AnyIO so schreiben, dass sie hoch kompatibel sind und Sie dessen Vorteile nutzen können (z. B. *strukturierte Nebenläufigkeit*).
+Und auch wenn Sie FastAPI nicht verwenden würden, könnten Sie Ihre eigenen asynchronen Anwendungen mit AnyIO schreiben, um hochkompatibel zu sein und dessen Vorteile zu nutzen (z. B. *strukturierte Nebenläufigkeit*).
-### Andere Formen von asynchronem Code
+Ich habe eine weitere Bibliothek auf Basis von AnyIO erstellt, als dünne Schicht obendrauf, um die Typannotationen etwas zu verbessern und bessere **Autovervollständigung**, **Inline-Fehler** usw. zu erhalten. Sie hat auch eine freundliche Einführung und ein Tutorial, um Ihnen zu helfen, **Ihren eigenen asynchronen Code zu verstehen** und zu schreiben: Asyncer. Sie ist insbesondere nützlich, wenn Sie **asynchronen Code mit regulärem** (blockierendem/synchronem) Code kombinieren müssen.
+
+### Andere Formen von asynchronem Code { #other-forms-of-asynchronous-code }
Diese Art der Verwendung von `async` und `await` ist in der Sprache relativ neu.
@@ -381,27 +383,27 @@ Davor war der Umgang mit asynchronem Code jedoch deutlich komplexer und schwieri
In früheren Versionen von Python hätten Sie Threads oder Gevent verwenden können. Der Code ist jedoch viel komplexer zu verstehen, zu debuggen und nachzuvollziehen.
-In früheren Versionen von NodeJS / Browser JavaScript hätten Sie „Callbacks“ verwendet. Was zur Callback-Hölle führt.
+In früheren Versionen von NodeJS / Browser JavaScript hätten Sie „Callbacks“ verwendet. Was zur „Callback-Hölle“ führt.
-## Coroutinen
+## Coroutinen { #coroutines }
**Coroutine** ist nur ein schicker Begriff für dasjenige, was von einer `async def`-Funktion zurückgegeben wird. Python weiß, dass es so etwas wie eine Funktion ist, die es starten kann und die irgendwann endet, aber auch dass sie pausiert ⏸ werden kann, wann immer darin ein `await` steht.
Aber all diese Funktionalität der Verwendung von asynchronem Code mit `async` und `await` wird oft als Verwendung von „Coroutinen“ zusammengefasst. Es ist vergleichbar mit dem Hauptmerkmal von Go, den „Goroutinen“.
-## Fazit
+## Fazit { #conclusion }
Sehen wir uns den gleichen Satz von oben noch mal an:
-> Moderne Versionen von Python unterstützen **„asynchronen Code“** unter Verwendung sogenannter **„Coroutinen“** mithilfe der Syntax **`async`** und **`await`**.
+> Moderne Versionen von Python unterstützen **„asynchronen Code“** unter Verwendung sogenannter **„Coroutinen“** mithilfe der Syntax **`async` und `await`**.
Das sollte jetzt mehr Sinn ergeben. ✨
All das ist es, was FastAPI (via Starlette) befeuert und es eine so beeindruckende Performanz haben lässt.
-## Sehr technische Details
+## Sehr technische Details { #very-technical-details }
-/// warning | "Achtung"
+/// warning | Achtung
Das folgende können Sie wahrscheinlich überspringen.
@@ -411,23 +413,23 @@ Wenn Sie über gute technische Kenntnisse verfügen (Coroutinen, Threads, Blocki
///
-### Pfadoperation-Funktionen
+### Pfadoperation-Funktionen { #path-operation-functions }
Wenn Sie eine *Pfadoperation-Funktion* mit normalem `def` anstelle von `async def` deklarieren, wird sie in einem externen Threadpool ausgeführt, der dann `await`et wird, anstatt direkt aufgerufen zu werden (da dies den Server blockieren würde).
Wenn Sie von einem anderen asynchronen Framework kommen, das nicht auf die oben beschriebene Weise funktioniert, und Sie es gewohnt sind, triviale, nur-berechnende *Pfadoperation-Funktionen* mit einfachem `def` zu definieren, um einen geringfügigen Geschwindigkeitsgewinn (etwa 100 Nanosekunden) zu erzielen, beachten Sie bitte, dass der Effekt in **FastAPI** genau gegenteilig wäre. In solchen Fällen ist es besser, `async def` zu verwenden, es sei denn, Ihre *Pfadoperation-Funktionen* verwenden Code, der blockierende I/O-Operationen durchführt.
-Dennoch besteht in beiden Fällen eine gute Chance, dass **FastAPI** [immer noch schneller](index.md#performanz){.internal-link target=_blank} als Ihr bisheriges Framework (oder zumindest damit vergleichbar) ist.
+Dennoch besteht in beiden Fällen eine gute Chance, dass **FastAPI** [immer noch schneller](index.md#performance){.internal-link target=_blank} als Ihr bisheriges Framework (oder zumindest damit vergleichbar) ist.
-### Abhängigkeiten
+### Abhängigkeiten { #dependencies }
-Das Gleiche gilt für [Abhängigkeiten](tutorial/dependencies/index.md){.internal-link target=_blank}. Wenn eine Abhängigkeit eine normale `def`-Funktion ist, anstelle einer `async def`-Funktion, dann wird sie im externen Threadpool ausgeführt.
+Das Gleiche gilt für [Abhängigkeiten](tutorial/dependencies/index.md){.internal-link target=_blank}. Wenn eine Abhängigkeit eine normale `def`-Funktion anstelle einer `async def` ist, wird sie im externen Threadpool ausgeführt.
-### Unterabhängigkeiten
+### Unterabhängigkeiten { #sub-dependencies }
-Sie können mehrere Abhängigkeiten und [Unterabhängigkeiten](tutorial/dependencies/sub-dependencies.md){.internal-link target=_blank} haben, die einander bedingen (als Parameter der Funktionsdefinitionen), einige davon könnten erstellt werden mit `async def` und einige mit normalem `def`. Es würde immer noch funktionieren und diejenigen, die mit normalem `def` erstellt wurden, würden in einem externen Thread (vom Threadpool stammend) aufgerufen werden, anstatt `await`et zu werden.
+Sie können mehrere Abhängigkeiten und [Unterabhängigkeiten](tutorial/dependencies/sub-dependencies.md){.internal-link target=_blank} haben, die einander bedingen (als Parameter der Funktionsdefinitionen), einige davon könnten erstellt werden mit `async def` und einige mit normalem `def`. Es würde immer noch funktionieren, und diejenigen, die mit normalem `def` erstellt wurden, würden in einem externen Thread (vom Threadpool stammend) aufgerufen werden, anstatt `await`et zu werden.
-### Andere Hilfsfunktionen
+### Andere Hilfsfunktionen { #other-utility-functions }
Jede andere Hilfsfunktion, die Sie direkt aufrufen, kann mit normalem `def` oder `async def` erstellt werden, und FastAPI beeinflusst nicht die Art und Weise, wie Sie sie aufrufen.
@@ -439,4 +441,4 @@ Wenn Ihre Hilfsfunktion eine normale Funktion mit `def` ist, wird sie direkt auf
Nochmal, es handelt sich hier um sehr technische Details, die Ihnen helfen, falls Sie danach gesucht haben.
-Andernfalls liegen Sie richtig, wenn Sie sich an die Richtlinien aus dem obigen Abschnitt halten: In Eile?.
+Andernfalls liegen Sie richtig, wenn Sie sich an die Richtlinien aus dem obigen Abschnitt halten: In Eile?.
diff --git a/docs/de/docs/benchmarks.md b/docs/de/docs/benchmarks.md
index 6efd56e830..09126c5d99 100644
--- a/docs/de/docs/benchmarks.md
+++ b/docs/de/docs/benchmarks.md
@@ -1,16 +1,16 @@
-# Benchmarks
+# Benchmarks { #benchmarks }
-Unabhängige TechEmpower-Benchmarks zeigen, **FastAPI**-Anwendungen, die unter Uvicorn ausgeführt werden, gehören zu den schnellsten existierenden Python-Frameworks, nur Starlette und Uvicorn selbst (intern von FastAPI verwendet) sind schneller.
+Unabhängige TechEmpower-Benchmarks zeigen **FastAPI**-Anwendungen, die unter Uvicorn ausgeführt werden, als eines der schnellsten verfügbaren Python-Frameworks, nur unterhalb von Starlette und Uvicorn selbst (die intern von FastAPI verwendet werden).
-Beim Ansehen von Benchmarks und Vergleichen sollten Sie jedoch Folgende Punkte beachten.
+Aber bei der Betrachtung von Benchmarks und Vergleichen sollten Sie Folgendes beachten.
-## Benchmarks und Geschwindigkeit
+## Benchmarks und Geschwindigkeit { #benchmarks-and-speed }
-Wenn Sie sich die Benchmarks ansehen, werden häufig mehrere Tools mit unterschiedlichen Eigenschaften als gleichwertig verglichen.
+Wenn Sie die Benchmarks ansehen, ist es üblich, dass mehrere Tools unterschiedlichen Typs als gleichwertig verglichen werden.
-Konkret geht es darum, Uvicorn, Starlette und FastAPI miteinander zu vergleichen (neben vielen anderen Tools).
+Insbesondere dass Uvicorn, Starlette und FastAPI zusammen verglichen werden (neben vielen anderen Tools).
-Je einfacher das Problem, welches durch das Tool gelöst wird, desto besser ist die Performanz. Und die meisten Benchmarks testen nicht die zusätzlichen Funktionen, welche das Tool bietet.
+Je einfacher das Problem, das durch das Tool gelöst wird, desto besser wird die Performanz sein. Und die meisten Benchmarks testen nicht die zusätzlichen Funktionen, die das Tool bietet.
Die Hierarchie ist wie folgt:
@@ -19,16 +19,16 @@ Die Hierarchie ist wie folgt:
* **FastAPI**: (verwendet Starlette) ein API-Mikroframework mit mehreren zusätzlichen Funktionen zum Erstellen von APIs, mit Datenvalidierung, usw.
* **Uvicorn**:
- * Bietet die beste Leistung, da außer dem Server selbst nicht viel zusätzlicher Code vorhanden ist.
- * Sie würden eine Anwendung nicht direkt in Uvicorn schreiben. Das würde bedeuten, dass Ihr Code zumindest mehr oder weniger den gesamten von Starlette (oder **FastAPI**) bereitgestellten Code enthalten müsste. Und wenn Sie das täten, hätte Ihre endgültige Anwendung den gleichen Overhead wie die Verwendung eines Frameworks nebst Minimierung Ihres Anwendungscodes und der Fehler.
+ * Wird die beste Performanz haben, da außer dem Server selbst nicht viel zusätzlicher Code vorhanden ist.
+ * Sie würden eine Anwendung nicht direkt in Uvicorn schreiben. Das würde bedeuten, dass Ihr Code zumindest mehr oder weniger den gesamten von Starlette (oder **FastAPI**) bereitgestellten Code enthalten müsste. Und wenn Sie das täten, hätte Ihre endgültige Anwendung den gleichen Overhead wie bei der Verwendung eines Frameworks und der Minimierung Ihres Anwendungscodes und der Fehler.
* Wenn Sie Uvicorn vergleichen, vergleichen Sie es mit Anwendungsservern wie Daphne, Hypercorn, uWSGI, usw.
* **Starlette**:
- * Wird nach Uvicorn die nächstbeste Performanz erbringen. Tatsächlich nutzt Starlette intern Uvicorn. Daher kann es wahrscheinlich nur „langsamer“ als Uvicorn sein, weil mehr Code ausgeführt wird.
- * Aber es bietet Ihnen die Tools zum Erstellen einfacher Webanwendungen, mit Routing basierend auf Pfaden, usw.
+ * Wird nach Uvicorn die nächstbeste Performanz erbringen. Tatsächlich verwendet Starlette intern Uvicorn, um zu laufen. Daher kann es wahrscheinlich nur „langsamer“ als Uvicorn werden, weil mehr Code ausgeführt werden muss.
+ * Aber es bietet Ihnen die Werkzeuge, um einfache Webanwendungen zu erstellen, mit Routing basierend auf Pfaden, usw.
* Wenn Sie Starlette vergleichen, vergleichen Sie es mit Webframeworks (oder Mikroframeworks) wie Sanic, Flask, Django, usw.
* **FastAPI**:
* So wie Starlette Uvicorn verwendet und nicht schneller als dieses sein kann, verwendet **FastAPI** Starlette, sodass es nicht schneller als dieses sein kann.
- * FastAPI bietet zusätzlich zu Starlette weitere Funktionen. Funktionen, die Sie beim Erstellen von APIs fast immer benötigen, wie Datenvalidierung und Serialisierung. Und wenn Sie es verwenden, erhalten Sie kostenlos automatische Dokumentation (die automatische Dokumentation verursacht nicht einmal zusätzlichen Aufwand für laufende Anwendungen, sie wird beim Start generiert).
- * Wenn Sie FastAPI nicht, und direkt Starlette (oder ein anderes Tool wie Sanic, Flask, Responder, usw.) verwenden würden, müssten Sie die gesamte Datenvalidierung und Serialisierung selbst implementieren. Ihre finale Anwendung hätte also immer noch den gleichen Overhead, als ob sie mit FastAPI erstellt worden wäre. Und in vielen Fällen ist diese Datenvalidierung und Serialisierung der größte Teil des in Anwendungen geschriebenen Codes.
- * Durch die Verwendung von FastAPI sparen Sie also Entwicklungszeit, Fehler und Codezeilen und würden wahrscheinlich die gleiche Leistung (oder eine bessere) erzielen, die Sie hätten, wenn Sie es nicht verwenden würden (da Sie alles in Ihrem Code implementieren müssten).
- * Wenn Sie FastAPI vergleichen, vergleichen Sie es mit einem Webanwendung-Framework (oder einer Reihe von Tools), welche Datenvalidierung, Serialisierung und Dokumentation bereitstellen, wie Flask-apispec, NestJS, Molten, usw. – Frameworks mit integrierter automatischer Datenvalidierung, Serialisierung und Dokumentation.
+ * FastAPI bietet zusätzliche Funktionen auf Basis von Starlette. Funktionen, die Sie beim Erstellen von APIs fast immer benötigen, wie Datenvalidierung und Serialisierung. Und wenn Sie es verwenden, erhalten Sie kostenlose automatische Dokumentation (die automatische Dokumentation verursacht nicht einmal zusätzlichen Overhead für laufende Anwendungen, sie wird beim Starten generiert).
+ * Wenn Sie FastAPI nicht verwenden und stattdessen Starlette direkt (oder ein anderes Tool wie Sanic, Flask, Responder, usw.) verwenden würden, müssten Sie die gesamte Datenvalidierung und Serialisierung selbst implementieren. Ihre finale Anwendung hätte also immer noch den gleichen Overhead, als ob sie mit FastAPI erstellt worden wäre. Und in vielen Fällen ist diese Datenvalidierung und Serialisierung der größte Teil des in Anwendungen geschriebenen Codes.
+ * Durch die Verwendung von FastAPI sparen Sie also Entwicklungszeit, Fehler und Codezeilen und würden wahrscheinlich die gleiche Performanz (oder eine bessere) erzielen, die Sie hätten, wenn Sie es nicht verwenden würden (da Sie alles in Ihrem Code implementieren müssten).
+ * Wenn Sie FastAPI vergleichen, vergleichen Sie es mit einem Webanwendungs-Framework (oder einer Reihe von Tools), das Datenvalidierung, Serialisierung und Dokumentation bereitstellt, wie Flask-apispec, NestJS, Molten, usw. – Frameworks mit integrierter automatischer Datenvalidierung, Serialisierung und Dokumentation.
diff --git a/docs/de/docs/contributing.md b/docs/de/docs/contributing.md
deleted file mode 100644
index 58567ad7f2..0000000000
--- a/docs/de/docs/contributing.md
+++ /dev/null
@@ -1,484 +0,0 @@
-# Entwicklung – Mitwirken
-
-Vielleicht möchten Sie sich zuerst die grundlegenden Möglichkeiten anschauen, [FastAPI zu helfen und Hilfe zu erhalten](help-fastapi.md){.internal-link target=_blank}.
-
-## Entwicklung
-
-Wenn Sie das fastapi Repository bereits geklont haben und tief in den Code eintauchen möchten, hier einen Leitfaden zum Einrichten Ihrer Umgebung.
-
-### Virtuelle Umgebung mit `venv`
-
-Sie können mit dem Python-Modul `venv` in einem Verzeichnis eine isolierte virtuelle lokale Umgebung erstellen. Machen wir das im geklonten Repository (da wo sich die `requirements.txt` befindet):
-
-
-## Das Theme ändern
+## Das Theme ändern { #change-the-theme }
-Auf die gleiche Weise könnten Sie das Theme der Syntaxhervorhebung mit dem Schlüssel `syntaxHighlight.theme` festlegen (beachten Sie, dass er einen Punkt in der Mitte hat):
+Auf die gleiche Weise könnten Sie das Theme der Syntaxhervorhebung mit dem Schlüssel `"syntaxHighlight.theme"` festlegen (beachten Sie, dass er einen Punkt in der Mitte hat):
-```Python hl_lines="3"
-{!../../../docs_src/configure_swagger_ui/tutorial002.py!}
-```
+{* ../../docs_src/configure_swagger_ui/tutorial002.py hl[3] *}
Obige Konfiguration würde das Theme für die Farbe der Syntaxhervorhebung ändern:
-## Defaultparameter der Swagger-Oberfläche ändern
+## Defaultparameter der Swagger-Oberfläche ändern { #change-default-swagger-ui-parameters }
FastAPI enthält einige Defaultkonfigurationsparameter, die für die meisten Anwendungsfälle geeignet sind.
Es umfasst die folgenden Defaultkonfigurationen:
-```Python
-{!../../../fastapi/openapi/docs.py[ln:7-23]!}
-```
+{* ../../fastapi/openapi/docs.py ln[8:23] hl[17:23] *}
Sie können jede davon überschreiben, indem Sie im Argument `swagger_ui_parameters` einen anderen Wert festlegen.
Um beispielsweise `deepLinking` zu deaktivieren, könnten Sie folgende Einstellungen an `swagger_ui_parameters` übergeben:
-```Python hl_lines="3"
-{!../../../docs_src/configure_swagger_ui/tutorial003.py!}
-```
+{* ../../docs_src/configure_swagger_ui/tutorial003.py hl[3] *}
-## Andere Parameter der Swagger-Oberfläche
+## Andere Parameter der Swagger-Oberfläche { #other-swagger-ui-parameters }
-Um alle anderen möglichen Konfigurationen zu sehen, die Sie verwenden können, lesen Sie die offizielle Dokumentation für die Parameter der Swagger-Oberfläche.
+Um alle anderen möglichen Konfigurationen zu sehen, die Sie verwenden können, lesen Sie die offizielle Dokumentation für die Parameter der Swagger-Oberfläche.
-## JavaScript-basierte Einstellungen
+## Nur-JavaScript-Einstellungen { #javascript-only-settings }
-Die Swagger-Oberfläche erlaubt, dass andere Konfigurationen auch **JavaScript**-Objekte sein können (z. B. JavaScript-Funktionen).
+Die Swagger-Oberfläche erlaubt, dass andere Konfigurationen auch **Nur-JavaScript**-Objekte sein können (z. B. JavaScript-Funktionen).
FastAPI umfasst auch diese Nur-JavaScript-`presets`-Einstellungen:
diff --git a/docs/de/docs/how-to/custom-docs-ui-assets.md b/docs/de/docs/how-to/custom-docs-ui-assets.md
index e8750f7c22..6b8b1a1767 100644
--- a/docs/de/docs/how-to/custom-docs-ui-assets.md
+++ b/docs/de/docs/how-to/custom-docs-ui-assets.md
@@ -1,28 +1,26 @@
-# Statische Assets der Dokumentationsoberfläche (selbst hosten)
+# Statische Assets der Dokumentationsoberfläche (Selbst-Hosting) { #custom-docs-ui-static-assets-self-hosting }
Die API-Dokumentation verwendet **Swagger UI** und **ReDoc**, und jede dieser Dokumentationen benötigt einige JavaScript- und CSS-Dateien.
-Standardmäßig werden diese Dateien von einem CDN bereitgestellt.
+Standardmäßig werden diese Dateien von einem CDN bereitgestellt.
Es ist jedoch möglich, das anzupassen, ein bestimmtes CDN festzulegen oder die Dateien selbst bereitzustellen.
-## Benutzerdefiniertes CDN für JavaScript und CSS
+## Benutzerdefiniertes CDN für JavaScript und CSS { #custom-cdn-for-javascript-and-css }
-Nehmen wir an, Sie möchten ein anderes CDN verwenden, zum Beispiel möchten Sie `https://unpkg.com/` verwenden.
+Nehmen wir an, Sie möchten ein anderes CDN verwenden, zum Beispiel möchten Sie `https://unpkg.com/` verwenden.
Das kann nützlich sein, wenn Sie beispielsweise in einem Land leben, in dem bestimmte URLs eingeschränkt sind.
-### Die automatischen Dokumentationen deaktivieren
+### Die automatischen Dokumentationen deaktivieren { #disable-the-automatic-docs }
Der erste Schritt besteht darin, die automatischen Dokumentationen zu deaktivieren, da diese standardmäßig das Standard-CDN verwenden.
Um diese zu deaktivieren, setzen Sie deren URLs beim Erstellen Ihrer `FastAPI`-App auf `None`:
-```Python hl_lines="8"
-{!../../../docs_src/custom_docs_ui/tutorial001.py!}
-```
+{* ../../docs_src/custom_docs_ui/tutorial001.py hl[8] *}
-### Die benutzerdefinierten Dokumentationen hinzufügen
+### Die benutzerdefinierten Dokumentationen hinzufügen { #include-the-custom-docs }
Jetzt können Sie die *Pfadoperationen* für die benutzerdefinierten Dokumentationen erstellen.
@@ -34,13 +32,11 @@ Sie können die internen Funktionen von FastAPI wiederverwenden, um die HTML-Sei
* `swagger_js_url`: die URL, unter welcher der HTML-Code für Ihre Swagger-UI-Dokumentation die **JavaScript**-Datei abrufen kann. Dies ist die benutzerdefinierte CDN-URL.
* `swagger_css_url`: die URL, unter welcher der HTML-Code für Ihre Swagger-UI-Dokumentation die **CSS**-Datei abrufen kann. Dies ist die benutzerdefinierte CDN-URL.
-Und genau so für ReDoc ...
+Und ähnlich für ReDoc ...
-```Python hl_lines="2-6 11-19 22-24 27-33"
-{!../../../docs_src/custom_docs_ui/tutorial001.py!}
-```
+{* ../../docs_src/custom_docs_ui/tutorial001.py hl[2:6,11:19,22:24,27:33] *}
-/// tip | "Tipp"
+/// tip | Tipp
Die *Pfadoperation* für `swagger_ui_redirect` ist ein Hilfsmittel bei der Verwendung von OAuth2.
@@ -50,25 +46,23 @@ Swagger UI erledigt das hinter den Kulissen für Sie, benötigt aber diesen „U
///
-### Eine *Pfadoperation* erstellen, um es zu testen
+### Eine *Pfadoperation* erstellen, um es zu testen { #create-a-path-operation-to-test-it }
Um nun testen zu können, ob alles funktioniert, erstellen Sie eine *Pfadoperation*:
-```Python hl_lines="36-38"
-{!../../../docs_src/custom_docs_ui/tutorial001.py!}
-```
+{* ../../docs_src/custom_docs_ui/tutorial001.py hl[36:38] *}
-### Es ausprobieren
+### Es testen { #test-it }
-Jetzt sollten Sie in der Lage sein, zu Ihrer Dokumentation auf http://127.0.0.1:8000/docs zu gehen und die Seite neu zuladen, die Assets werden nun vom neuen CDN geladen.
+Jetzt sollten Sie in der Lage sein, zu Ihrer Dokumentation auf http://127.0.0.1:8000/docs zu gehen und die Seite neu zu laden, die Assets werden nun vom neuen CDN geladen.
-## JavaScript und CSS für die Dokumentation selbst hosten
+## JavaScript und CSS für die Dokumentation selbst hosten { #self-hosting-javascript-and-css-for-docs }
-Das Selbst Hosten von JavaScript und CSS kann nützlich sein, wenn Sie beispielsweise möchten, dass Ihre Anwendung auch offline, ohne bestehenden Internetzugang oder in einem lokalen Netzwerk weiter funktioniert.
+Das Selbst-Hosting von JavaScript und CSS kann nützlich sein, wenn Sie beispielsweise möchten, dass Ihre Anwendung auch offline, ohne bestehenden Internetzugang oder in einem lokalen Netzwerk weiter funktioniert.
Hier erfahren Sie, wie Sie diese Dateien selbst in derselben FastAPI-App bereitstellen und die Dokumentation für deren Verwendung konfigurieren.
-### Projektdateistruktur
+### Projektdateistruktur { #project-file-structure }
Nehmen wir an, die Dateistruktur Ihres Projekts sieht folgendermaßen aus:
@@ -91,11 +85,11 @@ Ihre neue Dateistruktur könnte so aussehen:
└── static/
```
-### Die Dateien herunterladen
+### Die Dateien herunterladen { #download-the-files }
Laden Sie die für die Dokumentation benötigten statischen Dateien herunter und legen Sie diese im Verzeichnis `static/` ab.
-Sie können wahrscheinlich mit der rechten Maustaste auf jeden Link klicken und eine Option wie etwa `Link speichern unter...` auswählen.
+Sie können wahrscheinlich mit der rechten Maustaste auf jeden Link klicken und eine Option wie etwa „Link speichern unter ...“ auswählen.
**Swagger UI** verwendet folgende Dateien:
@@ -104,7 +98,7 @@ Sie können wahrscheinlich mit der rechten Maustaste auf jeden Link klicken und
Und **ReDoc** verwendet diese Datei:
-* `redoc.standalone.js`
+* `redoc.standalone.js`
Danach könnte Ihre Dateistruktur wie folgt aussehen:
@@ -119,16 +113,14 @@ Danach könnte Ihre Dateistruktur wie folgt aussehen:
└── swagger-ui.css
```
-### Die statischen Dateien bereitstellen
+### Die statischen Dateien bereitstellen { #serve-the-static-files }
* Importieren Sie `StaticFiles`.
* „Mounten“ Sie eine `StaticFiles()`-Instanz in einem bestimmten Pfad.
-```Python hl_lines="7 11"
-{!../../../docs_src/custom_docs_ui/tutorial002.py!}
-```
+{* ../../docs_src/custom_docs_ui/tutorial002.py hl[7,11] *}
-### Die statischen Dateien testen
+### Die statischen Dateien testen { #test-the-static-files }
Starten Sie Ihre Anwendung und gehen Sie auf http://127.0.0.1:8000/static/redoc.standalone.js.
@@ -137,14 +129,8 @@ Sie sollten eine sehr lange JavaScript-Datei für **ReDoc** sehen.
Sie könnte beginnen mit etwas wie:
```JavaScript
-/*!
- * ReDoc - OpenAPI/Swagger-generated API Reference Documentation
- * -------------------------------------------------------------
- * Version: "2.0.0-rc.18"
- * Repo: https://github.com/Redocly/redoc
- */
-!function(e,t){"object"==typeof exports&&"object"==typeof m
-
+/*! For license information please see redoc.standalone.js.LICENSE.txt */
+!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("null")):
...
```
@@ -152,21 +138,19 @@ Das zeigt, dass Sie statische Dateien aus Ihrer Anwendung bereitstellen können
Jetzt können wir die Anwendung so konfigurieren, dass sie diese statischen Dateien für die Dokumentation verwendet.
-### Die automatischen Dokumentationen deaktivieren, für statische Dateien
+### Die automatischen Dokumentationen für statische Dateien deaktivieren { #disable-the-automatic-docs-for-static-files }
Wie bei der Verwendung eines benutzerdefinierten CDN besteht der erste Schritt darin, die automatischen Dokumentationen zu deaktivieren, da diese standardmäßig das CDN verwenden.
-Um diese zu deaktivieren, setzen Sie deren URLs beim Erstellen Ihrer `FastAPI`-App auf `None`:
+Um sie zu deaktivieren, setzen Sie deren URLs beim Erstellen Ihrer `FastAPI`-App auf `None`:
-```Python hl_lines="9"
-{!../../../docs_src/custom_docs_ui/tutorial002.py!}
-```
+{* ../../docs_src/custom_docs_ui/tutorial002.py hl[9] *}
-### Die benutzerdefinierten Dokumentationen, mit statischen Dateien, hinzufügen
+### Die benutzerdefinierten Dokumentationen für statische Dateien hinzufügen { #include-the-custom-docs-for-static-files }
Und genau wie bei einem benutzerdefinierten CDN können Sie jetzt die *Pfadoperationen* für die benutzerdefinierten Dokumentationen erstellen.
-Auch hier können Sie die internen Funktionen von FastAPI wiederverwenden, um die HTML-Seiten für die Dokumentationen zu erstellen, und diesen die erforderlichen Argumente übergeben:
+Auch hier können Sie die internen Funktionen von FastAPI wiederverwenden, um die HTML-Seiten für die Dokumentationen zu erstellen und ihnen die erforderlichen Argumente zu übergeben:
* `openapi_url`: die URL, unter der die HTML-Seite für die Dokumentation das OpenAPI-Schema für Ihre API abrufen kann. Sie können hier das Attribut `app.openapi_url` verwenden.
* `title`: der Titel Ihrer API.
@@ -174,13 +158,11 @@ Auch hier können Sie die internen Funktionen von FastAPI wiederverwenden, um di
* `swagger_js_url`: die URL, unter welcher der HTML-Code für Ihre Swagger-UI-Dokumentation die **JavaScript**-Datei abrufen kann. **Das ist die, welche jetzt von Ihrer eigenen Anwendung bereitgestellt wird**.
* `swagger_css_url`: die URL, unter welcher der HTML-Code für Ihre Swagger-UI-Dokumentation die **CSS**-Datei abrufen kann. **Das ist die, welche jetzt von Ihrer eigenen Anwendung bereitgestellt wird**.
-Und genau so für ReDoc ...
+Und ähnlich für ReDoc ...
-```Python hl_lines="2-6 14-22 25-27 30-36"
-{!../../../docs_src/custom_docs_ui/tutorial002.py!}
-```
+{* ../../docs_src/custom_docs_ui/tutorial002.py hl[2:6,14:22,25:27,30:36] *}
-/// tip | "Tipp"
+/// tip | Tipp
Die *Pfadoperation* für `swagger_ui_redirect` ist ein Hilfsmittel bei der Verwendung von OAuth2.
@@ -190,16 +172,14 @@ Swagger UI erledigt das hinter den Kulissen für Sie, benötigt aber diesen „U
///
-### Eine *Pfadoperation* erstellen, um statische Dateien zu testen
+### Eine *Pfadoperation* erstellen, um statische Dateien zu testen { #create-a-path-operation-to-test-static-files }
Um nun testen zu können, ob alles funktioniert, erstellen Sie eine *Pfadoperation*:
-```Python hl_lines="39-41"
-{!../../../docs_src/custom_docs_ui/tutorial002.py!}
-```
+{* ../../docs_src/custom_docs_ui/tutorial002.py hl[39:41] *}
-### Benutzeroberfläche, mit statischen Dateien, testen
+### Benutzeroberfläche mit statischen Dateien testen { #test-static-files-ui }
Jetzt sollten Sie in der Lage sein, Ihr WLAN zu trennen, gehen Sie zu Ihrer Dokumentation unter http://127.0.0.1:8000/docs und laden Sie die Seite neu.
-Und selbst ohne Internet könnten Sie die Dokumentation für Ihre API sehen und damit interagieren.
+Und selbst ohne Internet können Sie die Dokumentation für Ihre API sehen und mit ihr interagieren.
diff --git a/docs/de/docs/how-to/custom-request-and-route.md b/docs/de/docs/how-to/custom-request-and-route.md
index a0c4a0e0cf..246717c04d 100644
--- a/docs/de/docs/how-to/custom-request-and-route.md
+++ b/docs/de/docs/how-to/custom-request-and-route.md
@@ -1,12 +1,12 @@
-# Benutzerdefinierte Request- und APIRoute-Klasse
+# Benutzerdefinierte Request- und APIRoute-Klasse { #custom-request-and-apiroute-class }
In einigen Fällen möchten Sie möglicherweise die von den Klassen `Request` und `APIRoute` verwendete Logik überschreiben.
Das kann insbesondere eine gute Alternative zur Logik in einer Middleware sein.
-Wenn Sie beispielsweise den Requestbody lesen oder manipulieren möchten, bevor er von Ihrer Anwendung verarbeitet wird.
+Wenn Sie beispielsweise den Requestbody lesen oder manipulieren möchten, bevor er von Ihrer Anwendung verarbeitet wird.
-/// danger | "Gefahr"
+/// danger | Gefahr
Dies ist eine „fortgeschrittene“ Funktion.
@@ -14,7 +14,7 @@ Wenn Sie gerade erst mit **FastAPI** beginnen, möchten Sie diesen Abschnitt vie
///
-## Anwendungsfälle
+## Anwendungsfälle { #use-cases }
Einige Anwendungsfälle sind:
@@ -22,15 +22,15 @@ Einige Anwendungsfälle sind:
* Dekomprimierung gzip-komprimierter Requestbodys.
* Automatisches Loggen aller Requestbodys.
-## Handhaben von benutzerdefinierten Requestbody-Kodierungen
+## Handhaben von benutzerdefinierten Requestbody-Kodierungen { #handling-custom-request-body-encodings }
Sehen wir uns an, wie Sie eine benutzerdefinierte `Request`-Unterklasse verwenden, um gzip-Requests zu dekomprimieren.
Und eine `APIRoute`-Unterklasse zur Verwendung dieser benutzerdefinierten Requestklasse.
-### Eine benutzerdefinierte `GzipRequest`-Klasse erstellen
+### Eine benutzerdefinierte `GzipRequest`-Klasse erstellen { #create-a-custom-gziprequest-class }
-/// tip | "Tipp"
+/// tip | Tipp
Dies ist nur ein einfaches Beispiel, um zu demonstrieren, wie es funktioniert. Wenn Sie Gzip-Unterstützung benötigen, können Sie die bereitgestellte [`GzipMiddleware`](../advanced/middleware.md#gzipmiddleware){.internal-link target=_blank} verwenden.
@@ -42,35 +42,31 @@ Wenn der Header kein `gzip` enthält, wird nicht versucht, den Body zu dekomprim
Auf diese Weise kann dieselbe Routenklasse gzip-komprimierte oder unkomprimierte Requests verarbeiten.
-```Python hl_lines="8-15"
-{!../../../docs_src/custom_request_and_route/tutorial001.py!}
-```
+{* ../../docs_src/custom_request_and_route/tutorial001.py hl[8:15] *}
-### Eine benutzerdefinierte `GzipRoute`-Klasse erstellen
+### Eine benutzerdefinierte `GzipRoute`-Klasse erstellen { #create-a-custom-gziproute-class }
Als Nächstes erstellen wir eine benutzerdefinierte Unterklasse von `fastapi.routing.APIRoute`, welche `GzipRequest` nutzt.
Dieses Mal wird die Methode `APIRoute.get_route_handler()` überschrieben.
-Diese Methode gibt eine Funktion zurück. Und diese Funktion empfängt einen Request und gibt eine Response zurück.
+Diese Methode gibt eine Funktion zurück. Und diese Funktion empfängt einen Request und gibt eine Response zurück.
Hier verwenden wir sie, um aus dem ursprünglichen Request einen `GzipRequest` zu erstellen.
-```Python hl_lines="18-26"
-{!../../../docs_src/custom_request_and_route/tutorial001.py!}
-```
+{* ../../docs_src/custom_request_and_route/tutorial001.py hl[18:26] *}
-/// note | "Technische Details"
+/// note | Technische Details
-Ein `Request` hat ein `request.scope`-Attribut, welches einfach ein Python-`dict` ist, welches die mit dem Request verbundenen Metadaten enthält.
+Ein `Request` hat ein `request.scope`-Attribut, welches einfach ein Python-`dict` ist, welches die mit dem Request verbundenen Metadaten enthält.
-Ein `Request` hat auch ein `request.receive`, welches eine Funktion ist, die den Hauptteil des Requests empfängt.
+Ein `Request` hat auch ein `request.receive`, welches eine Funktion ist, die den Body des Requests empfängt.
Das `scope`-`dict` und die `receive`-Funktion sind beide Teil der ASGI-Spezifikation.
Und diese beiden Dinge, `scope` und `receive`, werden benötigt, um eine neue `Request`-Instanz zu erstellen.
-Um mehr über den `Request` zu erfahren, schauen Sie sich Starlettes Dokumentation zu Requests an.
+Um mehr über den `Request` zu erfahren, schauen Sie sich Starlettes Dokumentation zu Requests an.
///
@@ -82,11 +78,11 @@ Danach ist die gesamte Verarbeitungslogik dieselbe.
Aufgrund unserer Änderungen in `GzipRequest.body` wird der Requestbody jedoch bei Bedarf automatisch dekomprimiert, wenn er von **FastAPI** geladen wird.
-## Zugriff auf den Requestbody in einem Exceptionhandler
+## Zugriff auf den Requestbody in einem Exceptionhandler { #accessing-the-request-body-in-an-exception-handler }
-/// tip | "Tipp"
+/// tip | Tipp
-Um dasselbe Problem zu lösen, ist es wahrscheinlich viel einfacher, den `body` in einem benutzerdefinierten Handler für `RequestValidationError` zu verwenden ([Fehlerbehandlung](../tutorial/handling-errors.md#den-requestvalidationerror-body-verwenden){.internal-link target=_blank}).
+Um dasselbe Problem zu lösen, ist es wahrscheinlich viel einfacher, den `body` in einem benutzerdefinierten Handler für `RequestValidationError` zu verwenden ([Fehlerbehandlung](../tutorial/handling-errors.md#use-the-requestvalidationerror-body){.internal-link target=_blank}).
Dieses Beispiel ist jedoch immer noch gültig und zeigt, wie mit den internen Komponenten interagiert wird.
@@ -96,26 +92,18 @@ Wir können denselben Ansatz auch verwenden, um in einem Exceptionhandler auf de
Alles, was wir tun müssen, ist, den Request innerhalb eines `try`/`except`-Blocks zu handhaben:
-```Python hl_lines="13 15"
-{!../../../docs_src/custom_request_and_route/tutorial002.py!}
-```
+{* ../../docs_src/custom_request_and_route/tutorial002.py hl[13,15] *}
Wenn eine Exception auftritt, befindet sich die `Request`-Instanz weiterhin im Gültigkeitsbereich, sodass wir den Requestbody lesen und bei der Fehlerbehandlung verwenden können:
-```Python hl_lines="16-18"
-{!../../../docs_src/custom_request_and_route/tutorial002.py!}
-```
+{* ../../docs_src/custom_request_and_route/tutorial002.py hl[16:18] *}
-## Benutzerdefinierte `APIRoute`-Klasse in einem Router
+## Benutzerdefinierte `APIRoute`-Klasse in einem Router { #custom-apiroute-class-in-a-router }
Sie können auch den Parameter `route_class` eines `APIRouter` festlegen:
-```Python hl_lines="26"
-{!../../../docs_src/custom_request_and_route/tutorial003.py!}
-```
+{* ../../docs_src/custom_request_and_route/tutorial003.py hl[26] *}
In diesem Beispiel verwenden die *Pfadoperationen* unter dem `router` die benutzerdefinierte `TimedRoute`-Klasse und haben in der Response einen zusätzlichen `X-Response-Time`-Header mit der Zeit, die zum Generieren der Response benötigt wurde:
-```Python hl_lines="13-20"
-{!../../../docs_src/custom_request_and_route/tutorial003.py!}
-```
+{* ../../docs_src/custom_request_and_route/tutorial003.py hl[13:20] *}
diff --git a/docs/de/docs/how-to/extending-openapi.md b/docs/de/docs/how-to/extending-openapi.md
index 347c5bed3f..146ee098bc 100644
--- a/docs/de/docs/how-to/extending-openapi.md
+++ b/docs/de/docs/how-to/extending-openapi.md
@@ -1,24 +1,24 @@
-# OpenAPI erweitern
+# OpenAPI erweitern { #extending-openapi }
-In einigen Fällen müssen Sie möglicherweise das generierte OpenAPI-Schema ändern.
+Es gibt einige Fälle, in denen Sie das generierte OpenAPI-Schema ändern müssen.
In diesem Abschnitt erfahren Sie, wie.
-## Der normale Vorgang
+## Der normale Vorgang { #the-normal-process }
Der normale (Standard-)Prozess ist wie folgt.
-Eine `FastAPI`-Anwendung (-Instanz) verfügt über eine `.openapi()`-Methode, von der erwartet wird, dass sie das OpenAPI-Schema zurückgibt.
+Eine `FastAPI`-Anwendung (Instanz) verfügt über eine `.openapi()`-Methode, von der erwartet wird, dass sie das OpenAPI-Schema zurückgibt.
Als Teil der Erstellung des Anwendungsobjekts wird eine *Pfadoperation* für `/openapi.json` (oder welcher Wert für den Parameter `openapi_url` gesetzt wurde) registriert.
-Diese gibt lediglich eine JSON-Response zurück, mit dem Ergebnis der Methode `.openapi()` der Anwendung.
+Diese gibt lediglich eine JSON-Response zurück, mit dem Ergebnis der Methode `.openapi()` der Anwendung.
Standardmäßig überprüft die Methode `.openapi()` die Eigenschaft `.openapi_schema`, um zu sehen, ob diese Inhalt hat, und gibt diesen zurück.
Ist das nicht der Fall, wird der Inhalt mithilfe der Hilfsfunktion unter `fastapi.openapi.utils.get_openapi` generiert.
-Und diese Funktion `get_openapi()` erhält als Parameter:
+Diese Funktion `get_openapi()` erhält als Parameter:
* `title`: Der OpenAPI-Titel, der in der Dokumentation angezeigt wird.
* `version`: Die Version Ihrer API, z. B. `2.5.0`.
@@ -27,63 +27,53 @@ Und diese Funktion `get_openapi()` erhält als Parameter:
* `description`: Die Beschreibung Ihrer API. Dies kann Markdown enthalten und wird in der Dokumentation angezeigt.
* `routes`: Eine Liste von Routen, dies sind alle registrierten *Pfadoperationen*. Sie stammen von `app.routes`.
-/// info
+/// info | Info
Der Parameter `summary` ist in OpenAPI 3.1.0 und höher verfügbar und wird von FastAPI 0.99.0 und höher unterstützt.
///
-## Überschreiben der Standardeinstellungen
+## Überschreiben der Standardeinstellungen { #overriding-the-defaults }
Mithilfe der oben genannten Informationen können Sie dieselbe Hilfsfunktion verwenden, um das OpenAPI-Schema zu generieren und jeden benötigten Teil zu überschreiben.
-Fügen wir beispielsweise ReDocs OpenAPI-Erweiterung zum Einbinden eines benutzerdefinierten Logos hinzu.
+Fügen wir beispielsweise ReDocs OpenAPI-Erweiterung zum Einbinden eines benutzerdefinierten Logos hinzu.
-### Normales **FastAPI**
+### Normales **FastAPI** { #normal-fastapi }
Schreiben Sie zunächst wie gewohnt Ihre ganze **FastAPI**-Anwendung:
-```Python hl_lines="1 4 7-9"
-{!../../../docs_src/extending_openapi/tutorial001.py!}
-```
+{* ../../docs_src/extending_openapi/tutorial001.py hl[1,4,7:9] *}
-### Das OpenAPI-Schema generieren
+### Das OpenAPI-Schema generieren { #generate-the-openapi-schema }
Verwenden Sie dann dieselbe Hilfsfunktion, um das OpenAPI-Schema innerhalb einer `custom_openapi()`-Funktion zu generieren:
-```Python hl_lines="2 15-21"
-{!../../../docs_src/extending_openapi/tutorial001.py!}
-```
+{* ../../docs_src/extending_openapi/tutorial001.py hl[2,15:21] *}
-### Das OpenAPI-Schema ändern
+### Das OpenAPI-Schema ändern { #modify-the-openapi-schema }
Jetzt können Sie die ReDoc-Erweiterung hinzufügen und dem `info`-„Objekt“ im OpenAPI-Schema ein benutzerdefiniertes `x-logo` hinzufügen:
-```Python hl_lines="22-24"
-{!../../../docs_src/extending_openapi/tutorial001.py!}
-```
+{* ../../docs_src/extending_openapi/tutorial001.py hl[22:24] *}
-### Zwischenspeichern des OpenAPI-Schemas
+### Zwischenspeichern des OpenAPI-Schemas { #cache-the-openapi-schema }
Sie können die Eigenschaft `.openapi_schema` als „Cache“ verwenden, um Ihr generiertes Schema zu speichern.
Auf diese Weise muss Ihre Anwendung das Schema nicht jedes Mal generieren, wenn ein Benutzer Ihre API-Dokumentation öffnet.
-Es wird nur einmal generiert und dann wird dasselbe zwischengespeicherte Schema für die nächsten Requests verwendet.
+Es wird nur einmal generiert und dann wird dasselbe zwischengespeicherte Schema für die nächsten Requests verwendet.
-```Python hl_lines="13-14 25-26"
-{!../../../docs_src/extending_openapi/tutorial001.py!}
-```
+{* ../../docs_src/extending_openapi/tutorial001.py hl[13:14,25:26] *}
-### Die Methode überschreiben
+### Die Methode überschreiben { #override-the-method }
Jetzt können Sie die Methode `.openapi()` durch Ihre neue Funktion ersetzen.
-```Python hl_lines="29"
-{!../../../docs_src/extending_openapi/tutorial001.py!}
-```
+{* ../../docs_src/extending_openapi/tutorial001.py hl[29] *}
-### Testen
+### Es testen { #check-it }
Sobald Sie auf http://127.0.0.1:8000/redoc gehen, werden Sie sehen, dass Ihr benutzerdefiniertes Logo verwendet wird (in diesem Beispiel das Logo von **FastAPI**):
diff --git a/docs/de/docs/how-to/general.md b/docs/de/docs/how-to/general.md
index b38b5fabfb..0045eab749 100644
--- a/docs/de/docs/how-to/general.md
+++ b/docs/de/docs/how-to/general.md
@@ -1,39 +1,39 @@
-# Allgemeines – How-To – Rezepte
+# Allgemeines – How-To – Rezepte { #general-how-to-recipes }
Hier finden Sie mehrere Verweise auf andere Stellen in der Dokumentation, für allgemeine oder häufige Fragen.
-## Daten filtern – Sicherheit
+## Daten filtern – Sicherheit { #filter-data-security }
Um sicherzustellen, dass Sie nicht mehr Daten zurückgeben, als Sie sollten, lesen Sie die Dokumentation unter [Tutorial – Responsemodell – Rückgabetyp](../tutorial/response-model.md){.internal-link target=_blank}.
-## Dokumentations-Tags – OpenAPI
+## Dokumentations-Tags – OpenAPI { #documentation-tags-openapi }
Um Tags zu Ihren *Pfadoperationen* hinzuzufügen und diese in der Oberfläche der Dokumentation zu gruppieren, lesen Sie die Dokumentation unter [Tutorial – Pfadoperation-Konfiguration – Tags](../tutorial/path-operation-configuration.md#tags){.internal-link target=_blank}.
-## Zusammenfassung und Beschreibung in der Dokumentation – OpenAPI
+## Zusammenfassung und Beschreibung in der Dokumentation – OpenAPI { #documentation-summary-and-description-openapi }
-Um Ihren *Pfadoperationen* eine Zusammenfassung und Beschreibung hinzuzufügen und diese in der Oberfläche der Dokumentation anzuzeigen, lesen Sie die Dokumentation unter [Tutorial – Pfadoperation-Konfiguration – Zusammenfassung und Beschreibung](../tutorial/path-operation-configuration.md#zusammenfassung-und-beschreibung){.internal-link target=_blank}.
+Um Ihren *Pfadoperationen* eine Zusammenfassung und Beschreibung hinzuzufügen und diese in der Oberfläche der Dokumentation anzuzeigen, lesen Sie die Dokumentation unter [Tutorial – Pfadoperation-Konfiguration – Zusammenfassung und Beschreibung](../tutorial/path-operation-configuration.md#summary-and-description){.internal-link target=_blank}.
-## Beschreibung der Response in der Dokumentation – OpenAPI
+## Beschreibung der Response in der Dokumentation – OpenAPI { #documentation-response-description-openapi }
-Um die Beschreibung der Response zu definieren, welche in der Oberfläche der Dokumentation angezeigt wird, lesen Sie die Dokumentation unter [Tutorial – Pfadoperation-Konfiguration – Beschreibung der Response](../tutorial/path-operation-configuration.md#beschreibung-der-response){.internal-link target=_blank}.
+Um die Beschreibung der Response zu definieren, welche in der Oberfläche der Dokumentation angezeigt wird, lesen Sie die Dokumentation unter [Tutorial – Pfadoperation-Konfiguration – Beschreibung der Response](../tutorial/path-operation-configuration.md#response-description){.internal-link target=_blank}.
-## *Pfadoperation* in der Dokumentation deprecaten – OpenAPI
+## *Pfadoperation* in der Dokumentation deprecaten – OpenAPI { #documentation-deprecate-a-path-operation-openapi }
-Um eine *Pfadoperation* zu deprecaten – sie als veraltet zu markieren – und das in der Oberfläche der Dokumentation anzuzeigen, lesen Sie die Dokumentation unter [Tutorial – Pfadoperation-Konfiguration – Deprecaten](../tutorial/path-operation-configuration.md#eine-pfadoperation-deprecaten){.internal-link target=_blank}.
+Um eine *Pfadoperation* zu deprecaten und das in der Oberfläche der Dokumentation anzuzeigen, lesen Sie die Dokumentation unter [Tutorial – Pfadoperation-Konfiguration – Deprecaten](../tutorial/path-operation-configuration.md#deprecate-a-path-operation){.internal-link target=_blank}.
-## Daten in etwas JSON-kompatibles konvertieren
+## Daten in etwas JSON-kompatibles konvertieren { #convert-any-data-to-json-compatible }
Um Daten in etwas JSON-kompatibles zu konvertieren, lesen Sie die Dokumentation unter [Tutorial – JSON-kompatibler Encoder](../tutorial/encoder.md){.internal-link target=_blank}.
-## OpenAPI-Metadaten – Dokumentation
+## OpenAPI-Metadaten – Dokumentation { #openapi-metadata-docs }
-Um Metadaten zu Ihrem OpenAPI-Schema hinzuzufügen, einschließlich einer Lizenz, Version, Kontakt, usw., lesen Sie die Dokumentation unter [Tutorial – Metadaten und URLs der Dokumentationen](../tutorial/metadata.md){.internal-link target=_blank}.
+Um Metadaten zu Ihrem OpenAPI-Schema hinzuzufügen, einschließlich einer Lizenz, Version, Kontakt, usw., lesen Sie die Dokumentation unter [Tutorial – Metadaten und URLs der Dokumentation](../tutorial/metadata.md){.internal-link target=_blank}.
-## Benutzerdefinierte OpenAPI-URL
+## Benutzerdefinierte OpenAPI-URL { #openapi-custom-url }
-Um die OpenAPI-URL anzupassen (oder zu entfernen), lesen Sie die Dokumentation unter [Tutorial – Metadaten und URLs der Dokumentationen](../tutorial/metadata.md#openapi-url){.internal-link target=_blank}.
+Um die OpenAPI-URL anzupassen (oder zu entfernen), lesen Sie die Dokumentation unter [Tutorial – Metadaten und URLs der Dokumentation](../tutorial/metadata.md#openapi-url){.internal-link target=_blank}.
-## URLs der OpenAPI-Dokumentationen
+## URLs der OpenAPI-Dokumentationen { #openapi-docs-urls }
-Um die URLs zu aktualisieren, die für die automatisch generierten Dokumentations-Oberflächen verwendet werden, lesen Sie die Dokumentation unter [Tutorial – Metadaten und URLs der Dokumentationen](../tutorial/metadata.md#urls-der-dokumentationen){.internal-link target=_blank}.
+Um die URLs zu aktualisieren, die für die automatisch generierten Dokumentations-Oberflächen verwendet werden, lesen Sie die Dokumentation unter [Tutorial – Metadaten und URLs der Dokumentation](../tutorial/metadata.md#docs-urls){.internal-link target=_blank}.
diff --git a/docs/de/docs/how-to/graphql.md b/docs/de/docs/how-to/graphql.md
index 19af25bb38..d2958dcd9e 100644
--- a/docs/de/docs/how-to/graphql.md
+++ b/docs/de/docs/how-to/graphql.md
@@ -1,61 +1,59 @@
-# GraphQL
+# GraphQL { #graphql }
Da **FastAPI** auf dem **ASGI**-Standard basiert, ist es sehr einfach, jede **GraphQL**-Bibliothek zu integrieren, die auch mit ASGI kompatibel ist.
Sie können normale FastAPI-*Pfadoperationen* mit GraphQL in derselben Anwendung kombinieren.
-/// tip | "Tipp"
+/// tip | Tipp
**GraphQL** löst einige sehr spezifische Anwendungsfälle.
Es hat **Vorteile** und **Nachteile** im Vergleich zu gängigen **Web-APIs**.
-Wiegen Sie ab, ob die **Vorteile** für Ihren Anwendungsfall die **Nachteile** ausgleichen. 🤓
+Stellen Sie sicher, dass Sie prüfen, ob die **Vorteile** für Ihren Anwendungsfall die **Nachteile** ausgleichen. 🤓
///
-## GraphQL-Bibliotheken
+## GraphQL-Bibliotheken { #graphql-libraries }
-Hier sind einige der **GraphQL**-Bibliotheken, welche **ASGI** unterstützen. Diese könnten Sie mit **FastAPI** verwenden:
+Hier sind einige der **GraphQL**-Bibliotheken, die **ASGI**-Unterstützung haben. Sie könnten sie mit **FastAPI** verwenden:
* Strawberry 🍓
* Mit Dokumentation für FastAPI
* Ariadne
* Mit Dokumentation für FastAPI
* Tartiflette
- * Mit Tartiflette ASGI, für ASGI-Integration
+ * Mit Tartiflette ASGI für ASGI-Integration
* Graphene
* Mit starlette-graphene3
-## GraphQL mit Strawberry
+## GraphQL mit Strawberry { #graphql-with-strawberry }
-Wenn Sie mit **GraphQL** arbeiten möchten oder müssen, ist **Strawberry** die **empfohlene** Bibliothek, da deren Design dem Design von **FastAPI** am nächsten kommt und alles auf **Typannotationen** basiert.
+Wenn Sie mit **GraphQL** arbeiten möchten oder müssen, ist **Strawberry** die **empfohlene** Bibliothek, da deren Design **FastAPIs** Design am nächsten kommt und alles auf **Typannotationen** basiert.
-Abhängig von Ihrem Anwendungsfall bevorzugen Sie vielleicht eine andere Bibliothek, aber wenn Sie mich fragen würden, würde ich Ihnen wahrscheinlich empfehlen, **Strawberry** auszuprobieren.
+Abhängig von Ihrem Anwendungsfall könnten Sie eine andere Bibliothek vorziehen, aber wenn Sie mich fragen würden, würde ich Ihnen wahrscheinlich empfehlen, **Strawberry** auszuprobieren.
Hier ist eine kleine Vorschau, wie Sie Strawberry mit FastAPI integrieren können:
-```Python hl_lines="3 22 25-26"
-{!../../../docs_src/graphql/tutorial001.py!}
-```
+{* ../../docs_src/graphql/tutorial001.py hl[3,22,25] *}
Weitere Informationen zu Strawberry finden Sie in der Strawberry-Dokumentation.
-Und auch die Dokumentation zu Strawberry mit FastAPI.
+Und auch in der Dokumentation zu Strawberry mit FastAPI.
-## Ältere `GraphQLApp` von Starlette
+## Ältere `GraphQLApp` von Starlette { #older-graphqlapp-from-starlette }
Frühere Versionen von Starlette enthielten eine `GraphQLApp`-Klasse zur Integration mit Graphene.
-Das wurde von Starlette deprecated, aber wenn Sie Code haben, der das verwendet, können Sie einfach zu starlette-graphene3 **migrieren**, welches denselben Anwendungsfall abdeckt und über eine **fast identische Schnittstelle** verfügt.
+Das wurde von Starlette deprecatet, aber wenn Sie Code haben, der das verwendet, können Sie einfach zu starlette-graphene3 **migrieren**, das denselben Anwendungsfall abdeckt und eine **fast identische Schnittstelle** hat.
-/// tip | "Tipp"
+/// tip | Tipp
Wenn Sie GraphQL benötigen, würde ich Ihnen trotzdem empfehlen, sich Strawberry anzuschauen, da es auf Typannotationen basiert, statt auf benutzerdefinierten Klassen und Typen.
///
-## Mehr darüber lernen
+## Mehr darüber lernen { #learn-more }
Weitere Informationen zu **GraphQL** finden Sie in der offiziellen GraphQL-Dokumentation.
diff --git a/docs/de/docs/how-to/index.md b/docs/de/docs/how-to/index.md
index 75779a01ce..36229dcd79 100644
--- a/docs/de/docs/how-to/index.md
+++ b/docs/de/docs/how-to/index.md
@@ -1,4 +1,4 @@
-# How-To – Rezepte
+# How-To – Rezepte { #how-to-recipes }
Hier finden Sie verschiedene Rezepte und „How-To“-Anleitungen zu **verschiedenen Themen**.
@@ -6,7 +6,7 @@ Die meisten dieser Ideen sind mehr oder weniger **unabhängig**, und in den meis
Wenn etwas für Ihr Projekt interessant und nützlich erscheint, lesen Sie es, andernfalls überspringen Sie es einfach.
-/// tip | "Tipp"
+/// tip | Tipp
Wenn Sie strukturiert **FastAPI lernen** möchten (empfohlen), lesen Sie stattdessen Kapitel für Kapitel das [Tutorial – Benutzerhandbuch](../tutorial/index.md){.internal-link target=_blank}.
diff --git a/docs/de/docs/how-to/migrate-from-pydantic-v1-to-pydantic-v2.md b/docs/de/docs/how-to/migrate-from-pydantic-v1-to-pydantic-v2.md
new file mode 100644
index 0000000000..7f60492ee9
--- /dev/null
+++ b/docs/de/docs/how-to/migrate-from-pydantic-v1-to-pydantic-v2.md
@@ -0,0 +1,133 @@
+# Von Pydantic v1 zu Pydantic v2 migrieren { #migrate-from-pydantic-v1-to-pydantic-v2 }
+
+Wenn Sie eine ältere FastAPI-App haben, nutzen Sie möglicherweise Pydantic Version 1.
+
+FastAPI unterstützt seit Version 0.100.0 sowohl Pydantic v1 als auch v2.
+
+Wenn Sie Pydantic v2 installiert hatten, wurde dieses verwendet. Wenn stattdessen Pydantic v1 installiert war, wurde jenes verwendet.
+
+Pydantic v1 ist jetzt deprecatet und die Unterstützung dafür wird in den nächsten Versionen von FastAPI entfernt, Sie sollten also zu **Pydantic v2 migrieren**. Auf diese Weise erhalten Sie die neuesten Features, Verbesserungen und Fixes.
+
+/// warning | Achtung
+
+Außerdem hat das Pydantic-Team die Unterstützung für Pydantic v1 in den neuesten Python-Versionen eingestellt, beginnend mit **Python 3.14**.
+
+Wenn Sie die neuesten Features von Python nutzen möchten, müssen Sie sicherstellen, dass Sie Pydantic v2 verwenden.
+
+///
+
+Wenn Sie eine ältere FastAPI-App mit Pydantic v1 haben, zeige ich Ihnen hier, wie Sie sie zu Pydantic v2 migrieren, und die **neuen Features in FastAPI 0.119.0**, die Ihnen bei einer schrittweisen Migration helfen.
+
+## Offizieller Leitfaden { #official-guide }
+
+Pydantic hat einen offiziellen Migrationsleitfaden von v1 zu v2.
+
+Er enthält auch, was sich geändert hat, wie Validierungen nun korrekter und strikter sind, mögliche Stolpersteine, usw.
+
+Sie können ihn lesen, um besser zu verstehen, was sich geändert hat.
+
+## Tests { #tests }
+
+Stellen Sie sicher, dass Sie [Tests](../tutorial/testing.md){.internal-link target=_blank} für Ihre App haben und diese in Continuous Integration (CI) ausführen.
+
+Auf diese Weise können Sie das Update durchführen und sicherstellen, dass weiterhin alles wie erwartet funktioniert.
+
+## `bump-pydantic` { #bump-pydantic }
+
+In vielen Fällen, wenn Sie reguläre Pydantic-Modelle ohne Anpassungen verwenden, können Sie den Großteil des Prozesses der Migration von Pydantic v1 auf Pydantic v2 automatisieren.
+
+Sie können `bump-pydantic` vom selben Pydantic-Team verwenden.
+
+Dieses Tool hilft Ihnen, den Großteil des zu ändernden Codes automatisch anzupassen.
+
+Danach können Sie die Tests ausführen und prüfen, ob alles funktioniert. Falls ja, sind Sie fertig. 😎
+
+## Pydantic v1 in v2 { #pydantic-v1-in-v2 }
+
+Pydantic v2 enthält alles aus Pydantic v1 als Untermodul `pydantic.v1`.
+
+Das bedeutet, Sie können die neueste Version von Pydantic v2 installieren und die alten Pydantic‑v1‑Komponenten aus diesem Untermodul importieren und verwenden, als hätten Sie das alte Pydantic v1 installiert.
+
+{* ../../docs_src/pydantic_v1_in_v2/tutorial001_an_py310.py hl[1,4] *}
+
+### FastAPI-Unterstützung für Pydantic v1 in v2 { #fastapi-support-for-pydantic-v1-in-v2 }
+
+Seit FastAPI 0.119.0 gibt es außerdem eine teilweise Unterstützung für Pydantic v1 innerhalb von Pydantic v2, um die Migration auf v2 zu erleichtern.
+
+Sie könnten also Pydantic auf die neueste Version 2 aktualisieren und die Importe so ändern, dass das Untermodul `pydantic.v1` verwendet wird, und in vielen Fällen würde es einfach funktionieren.
+
+{* ../../docs_src/pydantic_v1_in_v2/tutorial002_an_py310.py hl[2,5,15] *}
+
+/// warning | Achtung
+
+Beachten Sie, dass, da das Pydantic‑Team Pydantic v1 in neueren Python‑Versionen nicht mehr unterstützt, beginnend mit Python 3.14, auch die Verwendung von `pydantic.v1` unter Python 3.14 und höher nicht unterstützt wird.
+
+///
+
+### Pydantic v1 und v2 in derselben App { #pydantic-v1-and-v2-on-the-same-app }
+
+Es wird von Pydantic **nicht unterstützt**, dass ein Pydantic‑v2‑Modell Felder hat, die als Pydantic‑v1‑Modelle definiert sind, und umgekehrt.
+
+```mermaid
+graph TB
+ subgraph "❌ Nicht unterstützt"
+ direction TB
+ subgraph V2["Pydantic-v2-Modell"]
+ V1Field["Pydantic-v1-Modell"]
+ end
+ subgraph V1["Pydantic-v1-Modell"]
+ V2Field["Pydantic-v2-Modell"]
+ end
+ end
+
+ style V2 fill:#f9fff3
+ style V1 fill:#fff6f0
+ style V1Field fill:#fff6f0
+ style V2Field fill:#f9fff3
+```
+
+... aber Sie können getrennte Modelle, die Pydantic v1 bzw. v2 nutzen, in derselben App verwenden.
+
+```mermaid
+graph TB
+ subgraph "✅ Unterstützt"
+ direction TB
+ subgraph V2["Pydantic-v2-Modell"]
+ V2Field["Pydantic-v2-Modell"]
+ end
+ subgraph V1["Pydantic-v1-Modell"]
+ V1Field["Pydantic-v1-Modell"]
+ end
+ end
+
+ style V2 fill:#f9fff3
+ style V1 fill:#fff6f0
+ style V1Field fill:#fff6f0
+ style V2Field fill:#f9fff3
+```
+
+In einigen Fällen ist es sogar möglich, sowohl Pydantic‑v1‑ als auch Pydantic‑v2‑Modelle in derselben **Pfadoperation** Ihrer FastAPI‑App zu verwenden:
+
+{* ../../docs_src/pydantic_v1_in_v2/tutorial003_an_py310.py hl[2:3,6,12,21:22] *}
+
+Im obigen Beispiel ist das Eingabemodell ein Pydantic‑v1‑Modell, und das Ausgabemodell (definiert in `response_model=ItemV2`) ist ein Pydantic‑v2‑Modell.
+
+### Pydantic v1 Parameter { #pydantic-v1-parameters }
+
+Wenn Sie einige der FastAPI-spezifischen Tools für Parameter wie `Body`, `Query`, `Form`, usw. zusammen mit Pydantic‑v1‑Modellen verwenden müssen, können Sie die aus `fastapi.temp_pydantic_v1_params` importieren, während Sie die Migration zu Pydantic v2 abschließen:
+
+{* ../../docs_src/pydantic_v1_in_v2/tutorial004_an_py310.py hl[4,18] *}
+
+### In Schritten migrieren { #migrate-in-steps }
+
+/// tip | Tipp
+
+Probieren Sie zuerst `bump-pydantic` aus. Wenn Ihre Tests erfolgreich sind und das funktioniert, sind Sie mit einem einzigen Befehl fertig. ✨
+
+///
+
+Wenn `bump-pydantic` für Ihren Anwendungsfall nicht funktioniert, können Sie die Unterstützung für Pydantic‑v1‑ und Pydantic‑v2‑Modelle in derselben App nutzen, um die Migration zu Pydantic v2 schrittweise durchzuführen.
+
+Sie könnten zuerst Pydantic auf die neueste Version 2 aktualisieren und die Importe so ändern, dass für all Ihre Modelle `pydantic.v1` verwendet wird.
+
+Anschließend können Sie beginnen, Ihre Modelle gruppenweise von Pydantic v1 auf v2 zu migrieren – in kleinen, schrittweisen Etappen. 🚶
diff --git a/docs/de/docs/how-to/separate-openapi-schemas.md b/docs/de/docs/how-to/separate-openapi-schemas.md
index eaecb27de4..31653590b5 100644
--- a/docs/de/docs/how-to/separate-openapi-schemas.md
+++ b/docs/de/docs/how-to/separate-openapi-schemas.md
@@ -1,136 +1,26 @@
-# Separate OpenAPI-Schemas für Eingabe und Ausgabe oder nicht
+# Separate OpenAPI-Schemas für Eingabe und Ausgabe oder nicht { #separate-openapi-schemas-for-input-and-output-or-not }
Bei Verwendung von **Pydantic v2** ist die generierte OpenAPI etwas genauer und **korrekter** als zuvor. 😎
-Tatsächlich gibt es in einigen Fällen sogar **zwei JSON-Schemas** in OpenAPI für dasselbe Pydantic-Modell für Eingabe und Ausgabe, je nachdem, ob sie **Defaultwerte** haben.
+Tatsächlich gibt es in einigen Fällen sogar **zwei JSON-Schemas** in OpenAPI für dasselbe Pydantic-Modell, für Eingabe und Ausgabe, je nachdem, ob sie **Defaultwerte** haben.
Sehen wir uns an, wie das funktioniert und wie Sie es bei Bedarf ändern können.
-## Pydantic-Modelle für Eingabe und Ausgabe
+## Pydantic-Modelle für Eingabe und Ausgabe { #pydantic-models-for-input-and-output }
Nehmen wir an, Sie haben ein Pydantic-Modell mit Defaultwerten wie dieses:
-//// tab | Python 3.10+
+{* ../../docs_src/separate_openapi_schemas/tutorial001_py310.py ln[1:7] hl[7] *}
-```Python hl_lines="7"
-{!> ../../../docs_src/separate_openapi_schemas/tutorial001_py310.py[ln:1-7]!}
-
-# Code unterhalb weggelassen 👇
-```
-
-
-### Modell für die Ausgabe
+### Modell für die Ausgabe { #model-for-output }
Wenn Sie jedoch dasselbe Modell als Ausgabe verwenden, wie hier:
-//// tab | Python 3.10+
+{* ../../docs_src/separate_openapi_schemas/tutorial001_py310.py hl[19] *}
-```Python hl_lines="19"
-{!> ../../../docs_src/separate_openapi_schemas/tutorial001_py310.py!}
-```
+... dann, weil `description` einen Defaultwert hat, wird es, wenn Sie für dieses Feld **nichts zurückgeben**, immer noch diesen **Defaultwert** haben.
-////
+### Modell für Ausgabe-Responsedaten { #model-for-output-response-data }
-//// tab | Python 3.9+
-
-```Python hl_lines="21"
-{!> ../../../docs_src/separate_openapi_schemas/tutorial001_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="21"
-{!> ../../../docs_src/separate_openapi_schemas/tutorial001.py!}
-```
-
-////
-
-... dann, weil `description` einen Defaultwert hat, wird es, wenn Sie für dieses Feld **nichts zurückgeben**, immer noch diesen **Defaultwert** haben.
-
-### Modell für Ausgabe-Responsedaten
-
-Wenn Sie mit der Dokumentation interagieren und die Response überprüfen, enthält die JSON-Response den Defaultwert (`null`), obwohl der Code nichts in eines der `description`-Felder geschrieben hat:
+Wenn Sie mit der Dokumentation interagieren und die Response überprüfen, enthält die JSON-Response den Defaultwert (`null`), obwohl der Code nichts in eines der `description`-Felder geschrieben hat:
@@ -187,7 +55,7 @@ Aus diesem Grund kann das JSON-Schema für ein Modell unterschiedlich sein, je n
* für die **Eingabe** ist `description` **nicht erforderlich**
* für die **Ausgabe** ist es **erforderlich** (und möglicherweise `None` oder, in JSON-Begriffen, `null`)
-### Ausgabemodell in der Dokumentation
+### Ausgabemodell in der Dokumentation { #model-for-output-in-docs }
Sie können das Ausgabemodell auch in der Dokumentation überprüfen. **Sowohl** `name` **als auch** `description` sind mit einem **roten Sternchen** als **erforderlich** markiert:
@@ -195,7 +63,7 @@ Sie können das Ausgabemodell auch in der Dokumentation überprüfen. **Sowohl**
- FastAPI Framework, hochperformant, leicht zu erlernen, schnell zu programmieren, einsatzbereit + FastAPI-Framework, hohe Performanz, leicht zu lernen, schnell zu entwickeln, produktionsreif
-
+
-
+
@@ -27,7 +27,7 @@
---
-**Dokumentation**: https://fastapi.tiangolo.com
+**Dokumentation**: https://fastapi.tiangolo.com/de
**Quellcode**: https://github.com/fastapi/fastapi
@@ -37,19 +37,18 @@ FastAPI ist ein modernes, schnelles (hoch performantes) Webframework zur Erstell
Seine Schlüssel-Merkmale sind:
-* **Schnell**: Sehr hohe Leistung, auf Augenhöhe mit **NodeJS** und **Go** (Dank Starlette und Pydantic). [Eines der schnellsten verfügbaren Python-Frameworks](#performanz).
-
-* **Schnell zu programmieren**: Erhöhen Sie die Geschwindigkeit bei der Entwicklung von Funktionen um etwa 200 % bis 300 %. *
+* **Schnell**: Sehr hohe Performanz, auf Augenhöhe mit **NodeJS** und **Go** (dank Starlette und Pydantic). [Eines der schnellsten verfügbaren Python-Frameworks](#performance).
+* **Schnell zu entwickeln**: Erhöhen Sie die Geschwindigkeit bei der Entwicklung von Features um etwa 200 % bis 300 %. *
* **Weniger Bugs**: Verringern Sie die von Menschen (Entwicklern) verursachten Fehler um etwa 40 %. *
-* **Intuitiv**: Exzellente Editor-Unterstützung. Code-Vervollständigung überall. Weniger Debuggen.
-* **Einfach**: So konzipiert, dass es einfach zu benutzen und zu erlernen ist. Weniger Zeit für das Lesen der Dokumentation.
-* **Kurz**: Minimieren Sie die Verdoppelung von Code. Mehrere Funktionen aus jeder Parameterdeklaration. Weniger Bugs.
+* **Intuitiv**: Hervorragende Editor-Unterstützung. Code-Vervollständigung überall. Weniger Zeit mit Debuggen verbringen.
+* **Einfach**: So konzipiert, dass es einfach zu benutzen und zu erlernen ist. Weniger Zeit mit dem Lesen von Dokumentation verbringen.
+* **Kurz**: Minimieren Sie die Verdoppelung von Code. Mehrere Features aus jeder Parameterdeklaration. Weniger Bugs.
* **Robust**: Erhalten Sie produktionsreifen Code. Mit automatischer, interaktiver Dokumentation.
* **Standards-basiert**: Basierend auf (und vollständig kompatibel mit) den offenen Standards für APIs: OpenAPI (früher bekannt als Swagger) und JSON Schema.
-* Schätzung auf Basis von Tests in einem internen Entwicklungsteam, das Produktionsanwendungen erstellt.
+* Schätzung basierend auf Tests in einem internen Entwicklungsteam, das Produktionsanwendungen erstellt.
-## Sponsoren
+## Sponsoren { #sponsors }
@@ -64,55 +63,55 @@ Seine Schlüssel-Merkmale sind:
-Andere Sponsoren
+Andere Sponsoren
-## Meinungen
+## Meinungen { #opinions }
-„_[...] Ich verwende **FastAPI** heutzutage sehr oft. [...] Ich habe tatsächlich vor, es für alle **ML-Dienste meines Teams bei Microsoft** zu verwenden. Einige davon werden in das Kernprodukt **Windows** und einige **Office**-Produkte integriert._“
+„_[...] Ich verwende **FastAPI** heutzutage sehr oft. [...] Ich habe tatsächlich vor, es für alle **ML-Services meines Teams bei Microsoft** zu verwenden. Einige davon werden in das Kernprodukt **Windows** und einige **Office**-Produkte integriert._“
-
uvicorn main:app --reload ...fastapi dev main.py macht ...email-validator - für E-Mail-Validierung.
-* pydantic-settings - für die Verwaltung von Einstellungen.
-* pydantic-extra-types - für zusätzliche Typen, mit Pydantic zu verwenden.
+### `standard`-Abhängigkeiten { #standard-dependencies }
-Wird von Starlette verwendet:
+Wenn Sie FastAPI mit `pip install "fastapi[standard]"` installieren, kommt es mit der `standard`-Gruppe optionaler Abhängigkeiten:
-* httpx - erforderlich, wenn Sie den `TestClient` verwenden möchten.
-* jinja2 - erforderlich, wenn Sie die Standardkonfiguration für Templates verwenden möchten.
-* python-multipart - erforderlich, wenn Sie Formulare mittels `request.form()` „parsen“ möchten.
-* itsdangerous - erforderlich für `SessionMiddleware` Unterstützung.
-* pyyaml - erforderlich für Starlette's `SchemaGenerator` Unterstützung (Sie brauchen das wahrscheinlich nicht mit FastAPI).
-* ujson - erforderlich, wenn Sie `UJSONResponse` verwenden möchten.
+Verwendet von Pydantic:
-Wird von FastAPI / Starlette verwendet:
+* email-validator – für E-Mail-Validierung.
-* uvicorn - für den Server, der Ihre Anwendung lädt und serviert.
-* orjson - erforderlich, wenn Sie `ORJSONResponse` verwenden möchten.
+Verwendet von Starlette:
-Sie können diese alle mit `pip install "fastapi[all]"` installieren.
+* httpx – erforderlich, wenn Sie den `TestClient` verwenden möchten.
+* jinja2 – erforderlich, wenn Sie die Default-Template-Konfiguration verwenden möchten.
+* python-multipart – erforderlich, wenn Sie Formulare mittels `request.form()` „parsen“ möchten.
-## Lizenz
+Verwendet von FastAPI:
+
+* uvicorn – für den Server, der Ihre Anwendung lädt und bereitstellt. Dies umfasst `uvicorn[standard]`, das einige Abhängigkeiten (z. B. `uvloop`) beinhaltet, die für eine Bereitstellung mit hoher Performanz benötigt werden.
+* `fastapi-cli[standard]` – um den `fastapi`-Befehl bereitzustellen.
+ * Dies beinhaltet `fastapi-cloud-cli`, das es Ihnen ermöglicht, Ihre FastAPI-Anwendung auf FastAPI Cloud bereitzustellen.
+
+### Ohne `standard`-Abhängigkeiten { #without-standard-dependencies }
+
+Wenn Sie die `standard` optionalen Abhängigkeiten nicht einschließen möchten, können Sie mit `pip install fastapi` statt `pip install "fastapi[standard]"` installieren.
+
+### Ohne `fastapi-cloud-cli` { #without-fastapi-cloud-cli }
+
+Wenn Sie FastAPI mit den Standardabhängigkeiten, aber ohne das `fastapi-cloud-cli` installieren möchten, können Sie mit `pip install "fastapi[standard-no-fastapi-cloud-cli]"` installieren.
+
+### Zusätzliche optionale Abhängigkeiten { #additional-optional-dependencies }
+
+Es gibt einige zusätzliche Abhängigkeiten, die Sie installieren möchten.
+
+Zusätzliche optionale Pydantic-Abhängigkeiten:
+
+* pydantic-settings – für die Verwaltung von Einstellungen.
+* pydantic-extra-types – für zusätzliche Typen zur Verwendung mit Pydantic.
+
+Zusätzliche optionale FastAPI-Abhängigkeiten:
+
+* orjson – erforderlich, wenn Sie `ORJSONResponse` verwenden möchten.
+* ujson – erforderlich, wenn Sie `UJSONResponse` verwenden möchten.
+
+## Lizenz { #license }
Dieses Projekt ist unter den Bedingungen der MIT-Lizenz lizenziert.
diff --git a/docs/de/docs/learn/index.md b/docs/de/docs/learn/index.md
index b5582f55b6..e1f583fb38 100644
--- a/docs/de/docs/learn/index.md
+++ b/docs/de/docs/learn/index.md
@@ -1,5 +1,5 @@
-# Lernen
+# Lernen { #learn }
-Hier finden Sie die einführenden Kapitel und Tutorials zum Erlernen von **FastAPI**.
+Hier sind die einführenden Abschnitte und Tutorials, um **FastAPI** zu lernen.
Sie könnten dies als **Buch**, als **Kurs**, als **offizielle** und empfohlene Methode zum Erlernen von FastAPI betrachten. 😎
diff --git a/docs/de/docs/project-generation.md b/docs/de/docs/project-generation.md
index c47bcb6d30..e6da4949c1 100644
--- a/docs/de/docs/project-generation.md
+++ b/docs/de/docs/project-generation.md
@@ -1,84 +1,28 @@
-# Projektgenerierung – Vorlage
+# Full Stack FastAPI Template { #full-stack-fastapi-template }
-Sie können einen Projektgenerator für den Einstieg verwenden, welcher einen Großteil der Ersteinrichtung, Sicherheit, Datenbank und einige API-Endpunkte bereits für Sie erstellt.
+Vorlagen, die normalerweise mit einem bestimmten Setup geliefert werden, sind so konzipiert, dass sie flexibel und anpassbar sind. Dies ermöglicht es Ihnen, sie zu ändern und an die Anforderungen Ihres Projekts anzupassen und sie somit zu einem hervorragenden Ausgangspunkt zu machen. 🏁
-Ein Projektgenerator verfügt immer über ein sehr spezifisches Setup, das Sie aktualisieren und an Ihre eigenen Bedürfnisse anpassen sollten, aber es könnte ein guter Ausgangspunkt für Ihr Projekt sein.
+Sie können diese Vorlage verwenden, um loszulegen, da sie bereits vieles der anfänglichen Einrichtung, Sicherheit, Datenbank und einige API-Endpunkte für Sie eingerichtet hat.
-## Full Stack FastAPI PostgreSQL
+GitHub-Repository: Full Stack FastAPI Template
-GitHub: https://github.com/tiangolo/full-stack-fastapi-postgresql
+## Full Stack FastAPI Template – Technologiestack und Funktionen { #full-stack-fastapi-template-technology-stack-and-features }
-### Full Stack FastAPI PostgreSQL – Funktionen
-
-* Vollständige **Docker**-Integration (Docker-basiert).
-* Docker-Schwarmmodus-Deployment.
-* **Docker Compose**-Integration und Optimierung für die lokale Entwicklung.
-* **Produktionsbereit** Python-Webserver, verwendet Uvicorn und Gunicorn.
-* Python **FastAPI**-Backend:
- * **Schnell**: Sehr hohe Leistung, auf Augenhöhe mit **NodeJS** und **Go** (dank Starlette und Pydantic).
- * **Intuitiv**: Hervorragende Editor-Unterstützung. Codevervollständigung überall. Weniger Zeitaufwand für das Debuggen.
- * **Einfach**: Einfach zu bedienen und zu erlernen. Weniger Zeit für das Lesen von Dokumentationen.
- * **Kurz**: Codeverdoppelung minimieren. Mehrere Funktionalitäten aus jeder Parameterdeklaration.
- * **Robust**: Erhalten Sie produktionsbereiten Code. Mit automatischer, interaktiver Dokumentation.
- * **Standards-basiert**: Basierend auf (und vollständig kompatibel mit) den offenen Standards für APIs: OpenAPI und JSON Schema.
- * **Viele weitere Funktionen**, einschließlich automatischer Validierung, Serialisierung, interaktiver Dokumentation, Authentifizierung mit OAuth2-JWT-Tokens, usw.
-* **Sicheres Passwort**-Hashing standardmäßig.
-* **JWT-Token**-Authentifizierung.
-* **SQLAlchemy**-Modelle (unabhängig von Flask-Erweiterungen, sodass sie direkt mit Celery-Workern verwendet werden können).
-* Grundlegende Startmodelle für Benutzer (ändern und entfernen Sie nach Bedarf).
-* **Alembic**-Migrationen.
-* **CORS** (Cross Origin Resource Sharing).
-* **Celery**-Worker, welche Modelle und Code aus dem Rest des Backends selektiv importieren und verwenden können.
-* REST-Backend-Tests basierend auf **Pytest**, integriert in Docker, sodass Sie die vollständige API-Interaktion unabhängig von der Datenbank testen können. Da es in Docker ausgeführt wird, kann jedes Mal ein neuer Datenspeicher von Grund auf erstellt werden (Sie können also ElasticSearch, MongoDB, CouchDB oder was auch immer Sie möchten verwenden und einfach testen, ob die API funktioniert).
-* Einfache Python-Integration mit **Jupyter-Kerneln** für Remote- oder In-Docker-Entwicklung mit Erweiterungen wie Atom Hydrogen oder Visual Studio Code Jupyter.
-* **Vue**-Frontend:
- * Mit Vue CLI generiert.
- * Handhabung der **JWT-Authentifizierung**.
- * Login-View.
- * Nach der Anmeldung Hauptansicht des Dashboards.
- * Haupt-Dashboard mit Benutzererstellung und -bearbeitung.
- * Bearbeitung des eigenen Benutzers.
- * **Vuex**.
- * **Vue-Router**.
- * **Vuetify** für schöne Material-Designkomponenten.
- * **TypeScript**.
- * Docker-Server basierend auf **Nginx** (konfiguriert, um gut mit Vue-Router zu funktionieren).
- * Mehrstufigen Docker-Erstellung, sodass Sie kompilierten Code nicht speichern oder committen müssen.
- * Frontend-Tests, welche zur Erstellungszeit ausgeführt werden (können auch deaktiviert werden).
- * So modular wie möglich gestaltet, sodass es sofort einsatzbereit ist. Sie können es aber mit Vue CLI neu generieren oder es so wie Sie möchten erstellen und wiederverwenden, was Sie möchten.
-* **PGAdmin** für die PostgreSQL-Datenbank, können Sie problemlos ändern, sodass PHPMyAdmin und MySQL verwendet wird.
-* **Flower** für die Überwachung von Celery-Jobs.
-* Load Balancing zwischen Frontend und Backend mit **Traefik**, sodass Sie beide unter derselben Domain haben können, getrennt durch den Pfad, aber von unterschiedlichen Containern ausgeliefert.
-* Traefik-Integration, einschließlich automatischer Generierung von Let's Encrypt-**HTTPS**-Zertifikaten.
-* GitLab **CI** (kontinuierliche Integration), einschließlich Frontend- und Backend-Testen.
-
-## Full Stack FastAPI Couchbase
-
-GitHub: https://github.com/tiangolo/full-stack-fastapi-couchbase
-
-⚠️ **WARNUNG** ⚠️
-
-Wenn Sie ein neues Projekt von Grund auf starten, prüfen Sie die Alternativen hier.
-
-Zum Beispiel könnte der Projektgenerator Full Stack FastAPI PostgreSQL eine bessere Alternative sein, da er aktiv gepflegt und genutzt wird. Und er enthält alle neuen Funktionen und Verbesserungen.
-
-Es steht Ihnen weiterhin frei, den Couchbase-basierten Generator zu verwenden, wenn Sie möchten. Er sollte wahrscheinlich immer noch gut funktionieren, und wenn Sie bereits ein Projekt damit erstellt haben, ist das auch in Ordnung (und Sie haben es wahrscheinlich bereits an Ihre Bedürfnisse angepasst).
-
-Weitere Informationen hierzu finden Sie in der Dokumentation des Repos.
-
-## Full Stack FastAPI MongoDB
-
-... könnte später kommen, abhängig von meiner verfügbaren Zeit und anderen Faktoren. 😅 🎉
-
-## Modelle für maschinelles Lernen mit spaCy und FastAPI
-
-GitHub: https://github.com/microsoft/cookiecutter-spacy-fastapi
-
-### Modelle für maschinelles Lernen mit spaCy und FastAPI – Funktionen
-
-* **spaCy** NER-Modellintegration.
-* **Azure Cognitive Search**-Anforderungsformat integriert.
-* **Produktionsbereit** Python-Webserver, verwendet Uvicorn und Gunicorn.
-* **Azure DevOps** Kubernetes (AKS) CI/CD-Deployment integriert.
-* **Mehrsprachig** Wählen Sie bei der Projekteinrichtung ganz einfach eine der integrierten Sprachen von spaCy aus.
-* **Einfach erweiterbar** auf andere Modellframeworks (Pytorch, Tensorflow), nicht nur auf SpaCy.
+- ⚡ [**FastAPI**](https://fastapi.tiangolo.com/de) für die Python-Backend-API.
+ - 🧰 [SQLModel](https://sqlmodel.tiangolo.com) für die Interaktion mit der Python-SQL-Datenbank (ORM).
+ - 🔍 [Pydantic](https://docs.pydantic.dev), verwendet von FastAPI, für die Datenvalidierung und das Einstellungsmanagement.
+ - 💾 [PostgreSQL](https://www.postgresql.org) als SQL-Datenbank.
+- 🚀 [React](https://react.dev) für das Frontend.
+ - 💃 Verwendung von TypeScript, Hooks, [Vite](https://vitejs.dev) und anderen Teilen eines modernen Frontend-Stacks.
+ - 🎨 [Chakra UI](https://chakra-ui.com) für die Frontend-Komponenten.
+ - 🤖 Ein automatisch generierter Frontend-Client.
+ - 🧪 [Playwright](https://playwright.dev) für End-to-End-Tests.
+ - 🦇 Unterstützung des Dunkelmodus.
+- 🐋 [Docker Compose](https://www.docker.com) für Entwicklung und Produktion.
+- 🔒 Sicheres Passwort-Hashing standardmäßig.
+- 🔑 JWT-Token-Authentifizierung.
+- 📫 E-Mail-basierte Passwortwiederherstellung.
+- ✅ Tests mit [Pytest](https://pytest.org).
+- 📞 [Traefik](https://traefik.io) als Reverse-Proxy / Load Balancer.
+- 🚢 Deployment-Anleitungen unter Verwendung von Docker Compose, einschließlich der Einrichtung eines Frontend-Traefik-Proxys zur Handhabung automatischer HTTPS-Zertifikate.
+- 🏭 CI (kontinuierliche Integration) und CD (kontinuierliche Bereitstellung) basierend auf GitHub Actions.
diff --git a/docs/de/docs/python-types.md b/docs/de/docs/python-types.md
index 9bbff83d33..317ee4e629 100644
--- a/docs/de/docs/python-types.md
+++ b/docs/de/docs/python-types.md
@@ -1,8 +1,8 @@
-# Einführung in Python-Typen
+# Einführung in Python-Typen { #python-types-intro }
-Python hat Unterstützung für optionale „Typhinweise“ (Englisch: „Type Hints“). Auch „Typ Annotationen“ genannt.
+Python hat Unterstützung für optionale „Typhinweise“ (auch „Typannotationen“ genannt).
-Diese **„Typhinweise“** oder -Annotationen sind eine spezielle Syntax, die es erlaubt, den Typ einer Variablen zu deklarieren.
+Diese **„Typhinweise“** oder -Annotationen sind eine spezielle Syntax, die es erlaubt, den Typ einer Variablen zu deklarieren.
Durch das Deklarieren von Typen für Ihre Variablen können Editoren und Tools bessere Unterstützung bieten.
@@ -12,19 +12,17 @@ Dies ist lediglich eine **schnelle Anleitung / Auffrischung** über Pythons Typh
Aber selbst wenn Sie **FastAPI** nie verwenden, wird es für Sie nützlich sein, ein wenig darüber zu lernen.
-/// note | "Hinweis"
+/// note | Hinweis
Wenn Sie ein Python-Experte sind und bereits alles über Typhinweise wissen, überspringen Sie dieses Kapitel und fahren Sie mit dem nächsten fort.
///
-## Motivation
+## Motivation { #motivation }
Fangen wir mit einem einfachen Beispiel an:
-```Python
-{!../../../docs_src/python_types/tutorial001.py!}
-```
+{* ../../docs_src/python_types/tutorial001.py *}
Dieses Programm gibt aus:
@@ -38,11 +36,9 @@ Die Funktion macht Folgendes:
* Schreibt den ersten Buchstaben eines jeden Wortes groß, mithilfe von `title()`.
* Verkettet sie mit einem Leerzeichen in der Mitte.
-```Python hl_lines="2"
-{!../../../docs_src/python_types/tutorial001.py!}
-```
+{* ../../docs_src/python_types/tutorial001.py hl[2] *}
-### Bearbeiten Sie es
+### Es bearbeiten { #edit-it }
Es ist ein sehr einfaches Programm.
@@ -62,7 +58,7 @@ Aber leider erhalten Sie nichts Nützliches:
-### Typen hinzufügen
+### Typen hinzufügen { #add-types }
Lassen Sie uns eine einzelne Zeile aus der vorherigen Version ändern.
@@ -82,9 +78,7 @@ Das war's.
Das sind die „Typhinweise“:
-```Python hl_lines="1"
-{!../../../docs_src/python_types/tutorial002.py!}
-```
+{* ../../docs_src/python_types/tutorial002.py hl[1] *}
Das ist nicht das gleiche wie das Deklarieren von Defaultwerten, wie es hier der Fall ist:
@@ -108,13 +102,11 @@ Hier können Sie durch die Optionen blättern, bis Sie diejenige finden, bei der
-## Mehr Motivation
+## Mehr Motivation { #more-motivation }
Sehen Sie sich diese Funktion an, sie hat bereits Typhinweise:
-```Python hl_lines="1"
-{!../../../docs_src/python_types/tutorial003.py!}
-```
+{* ../../docs_src/python_types/tutorial003.py hl[1] *}
Da der Editor die Typen der Variablen kennt, erhalten Sie nicht nur Code-Vervollständigung, sondern auch eine Fehlerprüfung:
@@ -122,17 +114,15 @@ Da der Editor die Typen der Variablen kennt, erhalten Sie nicht nur Code-Vervoll
Jetzt, da Sie wissen, dass Sie das reparieren müssen, konvertieren Sie `age` mittels `str(age)` in einen String:
-```Python hl_lines="2"
-{!../../../docs_src/python_types/tutorial004.py!}
-```
+{* ../../docs_src/python_types/tutorial004.py hl[2] *}
-## Deklarieren von Typen
+## Deklarieren von Typen { #declaring-types }
Sie haben gerade den Haupt-Einsatzort für die Deklaration von Typhinweisen gesehen. Als Funktionsparameter.
Das ist auch meistens, wie sie in **FastAPI** verwendet werden.
-### Einfache Typen
+### Einfache Typen { #simple-types }
Sie können alle Standard-Python-Typen deklarieren, nicht nur `str`.
@@ -143,11 +133,9 @@ Zum Beispiel diese:
* `bool`
* `bytes`
-```Python hl_lines="1"
-{!../../../docs_src/python_types/tutorial005.py!}
-```
+{* ../../docs_src/python_types/tutorial005.py hl[1] *}
-### Generische Typen mit Typ-Parametern
+### Generische Typen mit Typ-Parametern { #generic-types-with-type-parameters }
Es gibt Datenstrukturen, die andere Werte enthalten können, wie etwa `dict`, `list`, `set` und `tuple`. Die inneren Werte können auch ihren eigenen Typ haben.
@@ -155,7 +143,7 @@ Diese Typen mit inneren Typen werden „**generische**“ Typen genannt. Es ist
Um diese Typen und die inneren Typen zu deklarieren, können Sie Pythons Standardmodul `typing` verwenden. Es existiert speziell für die Unterstützung dieser Typhinweise.
-#### Neuere Python-Versionen
+#### Neuere Python-Versionen { #newer-versions-of-python }
Die Syntax, welche `typing` verwendet, ist **kompatibel** mit allen Versionen, von Python 3.6 aufwärts zu den neuesten, inklusive Python 3.9, Python 3.10, usw.
@@ -169,7 +157,7 @@ Zum Beispiel bedeutet „**Python 3.6+**“, dass das Beispiel kompatibel mit Py
Wenn Sie über die **neueste Version von Python** verfügen, verwenden Sie die Beispiele für die neueste Version, diese werden die **beste und einfachste Syntax** haben, zum Beispiel, „**Python 3.10+**“.
-#### Liste
+#### Liste { #list }
Definieren wir zum Beispiel eine Variable, die eine `list` von `str` – eine Liste von Strings – sein soll.
@@ -182,7 +170,7 @@ Als Typ nehmen Sie `list`.
Da die Liste ein Typ ist, welcher innere Typen enthält, werden diese von eckigen Klammern umfasst:
```Python hl_lines="1"
-{!> ../../../docs_src/python_types/tutorial006_py39.py!}
+{!> ../../docs_src/python_types/tutorial006_py39.py!}
```
////
@@ -192,7 +180,7 @@ Da die Liste ein Typ ist, welcher innere Typen enthält, werden diese von eckige
Von `typing` importieren Sie `List` (mit Großbuchstaben `L`):
```Python hl_lines="1"
-{!> ../../../docs_src/python_types/tutorial006.py!}
+{!> ../../docs_src/python_types/tutorial006.py!}
```
Deklarieren Sie die Variable mit der gleichen Doppelpunkt-Syntax (`:`).
@@ -202,12 +190,12 @@ Als Typ nehmen Sie das `List`, das Sie von `typing` importiert haben.
Da die Liste ein Typ ist, welcher innere Typen enthält, werden diese von eckigen Klammern umfasst:
```Python hl_lines="4"
-{!> ../../../docs_src/python_types/tutorial006.py!}
+{!> ../../docs_src/python_types/tutorial006.py!}
```
////
-/// tip | "Tipp"
+/// info | Info
Die inneren Typen in den eckigen Klammern werden als „Typ-Parameter“ bezeichnet.
@@ -217,7 +205,7 @@ In diesem Fall ist `str` der Typ-Parameter, der an `List` übergeben wird (oder
Das bedeutet: Die Variable `items` ist eine Liste – `list` – und jedes der Elemente in dieser Liste ist ein String – `str`.
-/// tip | "Tipp"
+/// tip | Tipp
Wenn Sie Python 3.9 oder höher verwenden, müssen Sie `List` nicht von `typing` importieren, Sie können stattdessen den regulären `list`-Typ verwenden.
@@ -233,14 +221,14 @@ Beachten Sie, dass die Variable `item` eines der Elemente in der Liste `items` i
Und trotzdem weiß der Editor, dass es sich um ein `str` handelt, und bietet entsprechende Unterstützung.
-#### Tupel und Menge
+#### Tupel und Menge { #tuple-and-set }
Das Gleiche gilt für die Deklaration eines Tupels – `tuple` – und einer Menge – `set`:
//// tab | Python 3.9+
```Python hl_lines="1"
-{!> ../../../docs_src/python_types/tutorial007_py39.py!}
+{!> ../../docs_src/python_types/tutorial007_py39.py!}
```
////
@@ -248,7 +236,7 @@ Das Gleiche gilt für die Deklaration eines Tupels – `tuple` – und einer Men
//// tab | Python 3.8+
```Python hl_lines="1 4"
-{!> ../../../docs_src/python_types/tutorial007.py!}
+{!> ../../docs_src/python_types/tutorial007.py!}
```
////
@@ -258,7 +246,7 @@ Das bedeutet:
* Die Variable `items_t` ist ein `tuple` mit 3 Elementen, einem `int`, einem weiteren `int` und einem `str`.
* Die Variable `items_s` ist ein `set`, und jedes seiner Elemente ist vom Typ `bytes`.
-#### Dict
+#### Dict { #dict }
Um ein `dict` zu definieren, übergeben Sie zwei Typ-Parameter, getrennt durch Kommas.
@@ -269,7 +257,7 @@ Der zweite Typ-Parameter ist für die Werte des `dict`:
//// tab | Python 3.9+
```Python hl_lines="1"
-{!> ../../../docs_src/python_types/tutorial008_py39.py!}
+{!> ../../docs_src/python_types/tutorial008_py39.py!}
```
////
@@ -277,7 +265,7 @@ Der zweite Typ-Parameter ist für die Werte des `dict`:
//// tab | Python 3.8+
```Python hl_lines="1 4"
-{!> ../../../docs_src/python_types/tutorial008.py!}
+{!> ../../docs_src/python_types/tutorial008.py!}
```
////
@@ -288,7 +276,7 @@ Das bedeutet:
* Die Schlüssel dieses `dict` sind vom Typ `str` (z. B. die Namen der einzelnen Artikel).
* Die Werte dieses `dict` sind vom Typ `float` (z. B. der Preis jedes Artikels).
-#### Union
+#### Union { #union }
Sie können deklarieren, dass eine Variable einer von **verschiedenen Typen** sein kann, zum Beispiel ein `int` oder ein `str`.
@@ -299,7 +287,7 @@ In Python 3.10 gibt es zusätzlich eine **neue Syntax**, die es erlaubt, die mö
//// tab | Python 3.10+
```Python hl_lines="1"
-{!> ../../../docs_src/python_types/tutorial008b_py310.py!}
+{!> ../../docs_src/python_types/tutorial008b_py310.py!}
```
////
@@ -307,21 +295,21 @@ In Python 3.10 gibt es zusätzlich eine **neue Syntax**, die es erlaubt, die mö
//// tab | Python 3.8+
```Python hl_lines="1 4"
-{!> ../../../docs_src/python_types/tutorial008b.py!}
+{!> ../../docs_src/python_types/tutorial008b.py!}
```
////
In beiden Fällen bedeutet das, dass `item` ein `int` oder ein `str` sein kann.
-#### Vielleicht `None`
+#### Vielleicht `None` { #possibly-none }
Sie können deklarieren, dass ein Wert ein `str`, aber vielleicht auch `None` sein kann.
In Python 3.6 und darüber (inklusive Python 3.10) können Sie das deklarieren, indem Sie `Optional` vom `typing` Modul importieren und verwenden.
```Python hl_lines="1 4"
-{!../../../docs_src/python_types/tutorial009.py!}
+{!../../docs_src/python_types/tutorial009.py!}
```
Wenn Sie `Optional[str]` anstelle von nur `str` verwenden, wird Ihr Editor Ihnen dabei helfen, Fehler zu erkennen, bei denen Sie annehmen könnten, dass ein Wert immer eine String (`str`) ist, obwohl er auch `None` sein könnte.
@@ -333,7 +321,7 @@ Das bedeutet auch, dass Sie in Python 3.10 `Something | None` verwenden können:
//// tab | Python 3.10+
```Python hl_lines="1"
-{!> ../../../docs_src/python_types/tutorial009_py310.py!}
+{!> ../../docs_src/python_types/tutorial009_py310.py!}
```
////
@@ -341,7 +329,7 @@ Das bedeutet auch, dass Sie in Python 3.10 `Something | None` verwenden können:
//// tab | Python 3.8+
```Python hl_lines="1 4"
-{!> ../../../docs_src/python_types/tutorial009.py!}
+{!> ../../docs_src/python_types/tutorial009.py!}
```
////
@@ -349,12 +337,12 @@ Das bedeutet auch, dass Sie in Python 3.10 `Something | None` verwenden können:
//// tab | Python 3.8+ Alternative
```Python hl_lines="1 4"
-{!> ../../../docs_src/python_types/tutorial009b.py!}
+{!> ../../docs_src/python_types/tutorial009b.py!}
```
////
-#### `Union` oder `Optional` verwenden?
+#### `Union` oder `Optional` verwenden? { #using-union-or-optional }
Wenn Sie eine Python-Version unterhalb 3.10 verwenden, hier ist mein sehr **subjektiver** Standpunkt dazu:
@@ -369,9 +357,7 @@ Es geht nur um Wörter und Namen. Aber diese Worte können beeinflussen, wie Sie
Nehmen wir zum Beispiel diese Funktion:
-```Python hl_lines="1 4"
-{!../../../docs_src/python_types/tutorial009c.py!}
-```
+{* ../../docs_src/python_types/tutorial009c.py hl[1,4] *}
Der Parameter `name` ist definiert als `Optional[str]`, aber er ist **nicht optional**, Sie können die Funktion nicht ohne diesen Parameter aufrufen:
@@ -382,18 +368,16 @@ say_hi() # Oh, nein, das löst einen Fehler aus! 😱
Der `name` Parameter wird **immer noch benötigt** (nicht *optional*), weil er keinen Default-Wert hat. `name` akzeptiert aber dennoch `None` als Wert:
```Python
-say_hi(name=None) # Das funktioniert, None is gültig 🎉
+say_hi(name=None) # Das funktioniert, None ist gültig 🎉
```
Die gute Nachricht ist, dass Sie sich darüber keine Sorgen mehr machen müssen, wenn Sie Python 3.10 verwenden, da Sie einfach `|` verwenden können, um Vereinigungen von Typen zu definieren:
-```Python hl_lines="1 4"
-{!../../../docs_src/python_types/tutorial009c_py310.py!}
-```
+{* ../../docs_src/python_types/tutorial009c_py310.py hl[1,4] *}
Und dann müssen Sie sich nicht mehr um Namen wie `Optional` und `Union` kümmern. 😎
-#### Generische Typen
+#### Generische Typen { #generic-types }
Diese Typen, die Typ-Parameter in eckigen Klammern akzeptieren, werden **generische Typen** oder **Generics** genannt.
@@ -445,21 +429,17 @@ Verwenden Sie für den Rest, wie unter Python 3.8, das `typing`-Modul:
////
-### Klassen als Typen
+### Klassen als Typen { #classes-as-types }
Sie können auch eine Klasse als Typ einer Variablen deklarieren.
Nehmen wir an, Sie haben eine Klasse `Person`, mit einem Namen:
-```Python hl_lines="1-3"
-{!../../../docs_src/python_types/tutorial010.py!}
-```
+{* ../../docs_src/python_types/tutorial010.py hl[1:3] *}
Dann können Sie eine Variable vom Typ `Person` deklarieren:
-```Python hl_lines="6"
-{!../../../docs_src/python_types/tutorial010.py!}
-```
+{* ../../docs_src/python_types/tutorial010.py hl[6] *}
Und wiederum bekommen Sie die volle Editor-Unterstützung:
@@ -469,9 +449,9 @@ Beachten Sie, das bedeutet: „`one_person` ist eine **Instanz** der Klasse `Per
Es bedeutet nicht: „`one_person` ist die **Klasse** genannt `Person`“.
-## Pydantic Modelle
+## Pydantic-Modelle { #pydantic-models }
-Pydantic ist eine Python-Bibliothek für die Validierung von Daten.
+Pydantic ist eine Python-Bibliothek für die Validierung von Daten.
Sie deklarieren die „Form“ der Daten als Klassen mit Attributen.
@@ -486,7 +466,7 @@ Ein Beispiel aus der offiziellen Pydantic Dokumentation:
//// tab | Python 3.10+
```Python
-{!> ../../../docs_src/python_types/tutorial011_py310.py!}
+{!> ../../docs_src/python_types/tutorial011_py310.py!}
```
////
@@ -494,7 +474,7 @@ Ein Beispiel aus der offiziellen Pydantic Dokumentation:
//// tab | Python 3.9+
```Python
-{!> ../../../docs_src/python_types/tutorial011_py39.py!}
+{!> ../../docs_src/python_types/tutorial011_py39.py!}
```
////
@@ -502,37 +482,37 @@ Ein Beispiel aus der offiziellen Pydantic Dokumentation:
//// tab | Python 3.8+
```Python
-{!> ../../../docs_src/python_types/tutorial011.py!}
+{!> ../../docs_src/python_types/tutorial011.py!}
```
////
-/// info
+/// info | Info
-Um mehr über Pydantic zu erfahren, schauen Sie sich dessen Dokumentation an.
+Um mehr über Pydantic zu erfahren, schauen Sie sich dessen Dokumentation an.
///
**FastAPI** basiert vollständig auf Pydantic.
-Viel mehr von all dem werden Sie in praktischer Anwendung im [Tutorial - Benutzerhandbuch](tutorial/index.md){.internal-link target=_blank} sehen.
+Viel mehr von all dem werden Sie in praktischer Anwendung im [Tutorial – Benutzerhandbuch](tutorial/index.md){.internal-link target=_blank} sehen.
-/// tip | "Tipp"
+/// tip | Tipp
-Pydantic verhält sich speziell, wenn Sie `Optional` oder `Union[Etwas, None]` ohne einen Default-Wert verwenden. Sie können darüber in der Pydantic Dokumentation unter Required fields mehr erfahren.
+Pydantic verhält sich speziell, wenn Sie `Optional` oder `Union[Something, None]` ohne einen Defaultwert verwenden. Sie können darüber in der Pydantic Dokumentation unter Erforderliche optionale Felder mehr erfahren.
///
-## Typhinweise mit Metadaten-Annotationen
+## Typhinweise mit Metadaten-Annotationen { #type-hints-with-metadata-annotations }
-Python bietet auch die Möglichkeit, **zusätzliche Metadaten** in Typhinweisen unterzubringen, mittels `Annotated`.
+Python bietet auch die Möglichkeit, **zusätzliche Metadaten** in Typhinweisen unterzubringen, mittels `Annotated`.
//// tab | Python 3.9+
In Python 3.9 ist `Annotated` ein Teil der Standardbibliothek, Sie können es von `typing` importieren.
```Python hl_lines="1 4"
-{!> ../../../docs_src/python_types/tutorial013_py39.py!}
+{!> ../../docs_src/python_types/tutorial013_py39.py!}
```
////
@@ -544,14 +524,14 @@ In Versionen niedriger als Python 3.9 importieren Sie `Annotated` von `typing_ex
Es wird bereits mit **FastAPI** installiert sein.
```Python hl_lines="1 4"
-{!> ../../../docs_src/python_types/tutorial013.py!}
+{!> ../../docs_src/python_types/tutorial013.py!}
```
////
Python selbst macht nichts mit `Annotated`. Für Editoren und andere Tools ist der Typ immer noch `str`.
-Aber Sie können `Annotated` nutzen, um **FastAPI** mit Metadaten zu versorgen, die ihm sagen, wie sich ihre Anwendung verhalten soll.
+Aber Sie können `Annotated` nutzen, um **FastAPI** mit Metadaten zu versorgen, die ihm sagen, wie sich Ihre Anwendung verhalten soll.
Wichtig ist, dass **der erste *Typ-Parameter***, den Sie `Annotated` übergeben, der **tatsächliche Typ** ist. Der Rest sind Metadaten für andere Tools.
@@ -559,15 +539,15 @@ Im Moment müssen Sie nur wissen, dass `Annotated` existiert, und dass es Standa
Später werden Sie sehen, wie **mächtig** es sein kann.
-/// tip | "Tipp"
+/// tip | Tipp
-Der Umstand, dass es **Standard-Python** ist, bedeutet, dass Sie immer noch die **bestmögliche Entwickler-Erfahrung** in ihrem Editor haben, sowie mit den Tools, die Sie nutzen, um ihren Code zu analysieren, zu refaktorisieren, usw. ✨
+Der Umstand, dass es **Standard-Python** ist, bedeutet, dass Sie immer noch die **bestmögliche Entwickler-Erfahrung** in Ihrem Editor haben, sowie mit den Tools, die Sie nutzen, um Ihren Code zu analysieren, zu refaktorisieren, usw. ✨
Und ebenfalls, dass Ihr Code sehr kompatibel mit vielen anderen Python-Tools und -Bibliotheken sein wird. 🚀
///
-## Typhinweise in **FastAPI**
+## Typhinweise in **FastAPI** { #type-hints-in-fastapi }
**FastAPI** macht sich diese Typhinweise zunutze, um mehrere Dinge zu tun.
@@ -578,18 +558,18 @@ Mit **FastAPI** deklarieren Sie Parameter mit Typhinweisen, und Sie erhalten:
... und **FastAPI** verwendet dieselben Deklarationen, um:
-* **Anforderungen** zu definieren: aus Anfrage-Pfadparametern, Abfrageparametern, Header-Feldern, Bodys, Abhängigkeiten, usw.
-* **Daten umzuwandeln**: aus der Anfrage in den erforderlichen Typ.
-* **Daten zu validieren**: aus jeder Anfrage:
+* **Anforderungen** zu definieren: aus Request-Pfadparametern, Query-Parametern, Header-Feldern, Bodys, Abhängigkeiten, usw.
+* **Daten umzuwandeln**: aus dem Request in den erforderlichen Typ.
+* **Daten zu validieren**: aus jedem Request:
* **Automatische Fehler** generieren, die an den Client zurückgegeben werden, wenn die Daten ungültig sind.
* Die API mit OpenAPI zu **dokumentieren**:
* Die dann von den Benutzeroberflächen der automatisch generierten interaktiven Dokumentation verwendet wird.
-Das mag alles abstrakt klingen. Machen Sie sich keine Sorgen. Sie werden all das in Aktion sehen im [Tutorial - Benutzerhandbuch](tutorial/index.md){.internal-link target=_blank}.
+Das mag alles abstrakt klingen. Machen Sie sich keine Sorgen. Sie werden all das in Aktion sehen im [Tutorial – Benutzerhandbuch](tutorial/index.md){.internal-link target=_blank}.
Das Wichtigste ist, dass **FastAPI** durch die Verwendung von Standard-Python-Typen an einer einzigen Stelle (anstatt weitere Klassen, Dekoratoren usw. hinzuzufügen) einen Großteil der Arbeit für Sie erledigt.
-/// info
+/// info | Info
Wenn Sie bereits das ganze Tutorial durchgearbeitet haben und mehr über Typen erfahren wollen, dann ist eine gute Ressource der „Cheat Sheet“ von `mypy`.
diff --git a/docs/de/docs/resources/index.md b/docs/de/docs/resources/index.md
index abf270d9fd..2c5046c731 100644
--- a/docs/de/docs/resources/index.md
+++ b/docs/de/docs/resources/index.md
@@ -1,3 +1,3 @@
-# Ressourcen
+# Ressourcen { #resources }
Zusätzliche Ressourcen, externe Links, Artikel und mehr. ✈️
diff --git a/docs/de/docs/tutorial/background-tasks.md b/docs/de/docs/tutorial/background-tasks.md
index 0852288d59..2c381ccfac 100644
--- a/docs/de/docs/tutorial/background-tasks.md
+++ b/docs/de/docs/tutorial/background-tasks.md
@@ -1,8 +1,8 @@
-# Hintergrundtasks
+# Hintergrundtasks { #background-tasks }
-Sie können Hintergrundtasks (Hintergrund-Aufgaben) definieren, die *nach* der Rückgabe einer Response ausgeführt werden sollen.
+Sie können Hintergrundtasks definieren, die *nach* der Rückgabe einer Response ausgeführt werden sollen.
-Das ist nützlich für Vorgänge, die nach einem Request ausgeführt werden müssen, bei denen der Client jedoch nicht unbedingt auf den Abschluss des Vorgangs warten muss, bevor er die Response erhält.
+Das ist nützlich für Vorgänge, die nach einem Request ausgeführt werden müssen, bei denen der Client jedoch nicht unbedingt auf den Abschluss des Vorgangs warten muss, bevor er die Response erhält.
Hierzu zählen beispielsweise:
@@ -11,17 +11,15 @@ Hierzu zählen beispielsweise:
* Daten verarbeiten:
* Angenommen, Sie erhalten eine Datei, die einen langsamen Prozess durchlaufen muss. Sie können als Response „Accepted“ (HTTP 202) zurückgeben und die Datei im Hintergrund verarbeiten.
-## `BackgroundTasks` verwenden
+## `BackgroundTasks` verwenden { #using-backgroundtasks }
Importieren Sie zunächst `BackgroundTasks` und definieren Sie einen Parameter in Ihrer *Pfadoperation-Funktion* mit der Typdeklaration `BackgroundTasks`:
-```Python hl_lines="1 13"
-{!../../../docs_src/background_tasks/tutorial001.py!}
-```
+{* ../../docs_src/background_tasks/tutorial001.py hl[1,13] *}
**FastAPI** erstellt für Sie das Objekt vom Typ `BackgroundTasks` und übergibt es als diesen Parameter.
-## Eine Taskfunktion erstellen
+## Eine Taskfunktion erstellen { #create-a-task-function }
Erstellen Sie eine Funktion, die als Hintergrundtask ausgeführt werden soll.
@@ -33,17 +31,13 @@ In diesem Fall schreibt die Taskfunktion in eine Datei (den Versand einer E-Mail
Und da der Schreibvorgang nicht `async` und `await` verwendet, definieren wir die Funktion mit normalem `def`:
-```Python hl_lines="6-9"
-{!../../../docs_src/background_tasks/tutorial001.py!}
-```
+{* ../../docs_src/background_tasks/tutorial001.py hl[6:9] *}
-## Den Hintergrundtask hinzufügen
+## Den Hintergrundtask hinzufügen { #add-the-background-task }
Übergeben Sie innerhalb Ihrer *Pfadoperation-Funktion* Ihre Taskfunktion mit der Methode `.add_task()` an das *Hintergrundtasks*-Objekt:
-```Python hl_lines="14"
-{!../../../docs_src/background_tasks/tutorial001.py!}
-```
+{* ../../docs_src/background_tasks/tutorial001.py hl[14] *}
`.add_task()` erhält als Argumente:
@@ -51,63 +45,15 @@ Und da der Schreibvorgang nicht `async` und `await` verwendet, definieren wir di
* Eine beliebige Folge von Argumenten, die der Reihe nach an die Taskfunktion übergeben werden sollen (`email`).
* Alle Schlüsselwort-Argumente, die an die Taskfunktion übergeben werden sollen (`message="some notification"`).
-## Dependency Injection
+## Dependency Injection { #dependency-injection }
Die Verwendung von `BackgroundTasks` funktioniert auch mit dem Dependency Injection System. Sie können einen Parameter vom Typ `BackgroundTasks` auf mehreren Ebenen deklarieren: in einer *Pfadoperation-Funktion*, in einer Abhängigkeit (Dependable), in einer Unterabhängigkeit usw.
**FastAPI** weiß, was jeweils zu tun ist und wie dasselbe Objekt wiederverwendet werden kann, sodass alle Hintergrundtasks zusammengeführt und anschließend im Hintergrund ausgeführt werden:
-//// tab | Python 3.10+
-```Python hl_lines="13 15 22 25"
-{!> ../../../docs_src/background_tasks/tutorial002_an_py310.py!}
-```
+{* ../../docs_src/background_tasks/tutorial002_an_py310.py hl[13,15,22,25] *}
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="13 15 22 25"
-{!> ../../../docs_src/background_tasks/tutorial002_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="14 16 23 26"
-{!> ../../../docs_src/background_tasks/tutorial002_an.py!}
-```
-
-////
-
-//// tab | Python 3.10+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="11 13 20 23"
-{!> ../../../docs_src/background_tasks/tutorial002_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="13 15 22 25"
-{!> ../../../docs_src/background_tasks/tutorial002.py!}
-```
-
-////
In obigem Beispiel werden die Nachrichten, *nachdem* die Response gesendet wurde, in die Datei `log.txt` geschrieben.
@@ -115,9 +61,9 @@ Wenn im Request ein Query-Parameter enthalten war, wird dieser in einem Hintergr
Und dann schreibt ein weiterer Hintergrundtask, der in der *Pfadoperation-Funktion* erstellt wird, eine Nachricht unter Verwendung des Pfad-Parameters `email`.
-## Technische Details
+## Technische Details { #technical-details }
-Die Klasse `BackgroundTasks` stammt direkt von `starlette.background`.
+Die Klasse `BackgroundTasks` stammt direkt von `starlette.background`.
Sie wird direkt in FastAPI importiert/inkludiert, sodass Sie sie von `fastapi` importieren können und vermeiden, versehentlich das alternative `BackgroundTask` (ohne das `s` am Ende) von `starlette.background` zu importieren.
@@ -125,18 +71,16 @@ Indem Sie nur `BackgroundTasks` (und nicht `BackgroundTask`) verwenden, ist es d
Es ist immer noch möglich, `BackgroundTask` allein in FastAPI zu verwenden, aber Sie müssen das Objekt in Ihrem Code erstellen und eine Starlette-`Response` zurückgeben, die es enthält.
-Weitere Details finden Sie in der offiziellen Starlette-Dokumentation für Hintergrundtasks.
+Weitere Details finden Sie in Starlettes offizieller Dokumentation für Hintergrundtasks.
-## Vorbehalt
+## Vorbehalt { #caveat }
Wenn Sie umfangreiche Hintergrundberechnungen durchführen müssen und diese nicht unbedingt vom selben Prozess ausgeführt werden müssen (z. B. müssen Sie Speicher, Variablen, usw. nicht gemeinsam nutzen), könnte die Verwendung anderer größerer Tools wie z. B. Celery von Vorteil sein.
Sie erfordern in der Regel komplexere Konfigurationen und einen Nachrichten-/Job-Queue-Manager wie RabbitMQ oder Redis, ermöglichen Ihnen jedoch die Ausführung von Hintergrundtasks in mehreren Prozessen und insbesondere auf mehreren Servern.
-Um ein Beispiel zu sehen, sehen Sie sich die [Projektgeneratoren](../project-generation.md){.internal-link target=_blank} an. Sie alle enthalten Celery, bereits konfiguriert.
+Wenn Sie jedoch über dieselbe **FastAPI**-App auf Variablen und Objekte zugreifen oder kleine Hintergrundtasks ausführen müssen (z. B. das Senden einer E-Mail-Benachrichtigung), können Sie einfach `BackgroundTasks` verwenden.
-Wenn Sie jedoch über dieselbe **FastAPI**-Anwendung auf Variablen und Objekte zugreifen oder kleine Hintergrundtasks ausführen müssen (z. B. das Senden einer E-Mail-Benachrichtigung), können Sie einfach `BackgroundTasks` verwenden.
-
-## Zusammenfassung
+## Zusammenfassung { #recap }
Importieren und verwenden Sie `BackgroundTasks` mit Parametern in *Pfadoperation-Funktionen* und Abhängigkeiten, um Hintergrundtasks hinzuzufügen.
diff --git a/docs/de/docs/tutorial/bigger-applications.md b/docs/de/docs/tutorial/bigger-applications.md
index 986a99a38c..928d50adff 100644
--- a/docs/de/docs/tutorial/bigger-applications.md
+++ b/docs/de/docs/tutorial/bigger-applications.md
@@ -1,16 +1,16 @@
-# Größere Anwendungen – mehrere Dateien
+# Größere Anwendungen – mehrere Dateien { #bigger-applications-multiple-files }
Wenn Sie eine Anwendung oder eine Web-API erstellen, ist es selten der Fall, dass Sie alles in einer einzigen Datei unterbringen können.
**FastAPI** bietet ein praktisches Werkzeug zur Strukturierung Ihrer Anwendung bei gleichzeitiger Wahrung der Flexibilität.
-/// info
+/// info | Info
Wenn Sie von Flask kommen, wäre dies das Äquivalent zu Flasks Blueprints.
///
-## Eine Beispiel-Dateistruktur
+## Eine Beispiel-Dateistruktur { #an-example-file-structure }
Nehmen wir an, Sie haben eine Dateistruktur wie diese:
@@ -29,7 +29,7 @@ Nehmen wir an, Sie haben eine Dateistruktur wie diese:
│ └── admin.py
```
-/// tip | "Tipp"
+/// tip | Tipp
Es gibt mehrere `__init__.py`-Dateien: eine in jedem Verzeichnis oder Unterverzeichnis.
@@ -52,7 +52,7 @@ from app.routers import items
* Es gibt auch ein Unterverzeichnis `app/internal/` mit einer weiteren Datei `__init__.py`, es handelt sich also um ein weiteres „Python-Subpackage“: `app.internal`.
* Und die Datei `app/internal/admin.py` ist ein weiteres Submodul: `app.internal.admin`.
-
-## Den gleichen Router mehrmals mit unterschiedlichem `prefix` inkludieren
+## Den gleichen Router mehrmals mit unterschiedlichem `prefix` inkludieren { #include-the-same-router-multiple-times-with-different-prefix }
Sie können `.include_router()` auch mehrmals mit *demselben* Router und unterschiedlichen Präfixen verwenden.
@@ -546,7 +545,7 @@ Dies könnte beispielsweise nützlich sein, um dieselbe API unter verschiedenen
Dies ist eine fortgeschrittene Verwendung, die Sie möglicherweise nicht wirklich benötigen, aber für den Fall, dass Sie sie benötigen, ist sie vorhanden.
-## Einen `APIRouter` in einen anderen einfügen
+## Einen `APIRouter` in einen anderen einfügen { #include-an-apirouter-in-another }
Auf die gleiche Weise, wie Sie einen `APIRouter` in eine `FastAPI`-Anwendung einbinden können, können Sie einen `APIRouter` in einen anderen `APIRouter` einbinden, indem Sie Folgendes verwenden:
diff --git a/docs/de/docs/tutorial/body-fields.md b/docs/de/docs/tutorial/body-fields.md
index 33f7713ee4..b73d57d2d8 100644
--- a/docs/de/docs/tutorial/body-fields.md
+++ b/docs/de/docs/tutorial/body-fields.md
@@ -1,159 +1,59 @@
-# Body – Felder
+# Body – Felder { #body-fields }
-So wie Sie zusätzliche Validation und Metadaten in Parametern der **Pfadoperation-Funktion** mittels `Query`, `Path` und `Body` deklarieren, können Sie auch innerhalb von Pydantic-Modellen zusätzliche Validation und Metadaten deklarieren, mittels Pydantics `Field`.
+So wie Sie zusätzliche Validierung und Metadaten in Parametern der *Pfadoperation-Funktion* mittels `Query`, `Path` und `Body` deklarieren, können Sie auch innerhalb von Pydantic-Modellen zusätzliche Validierung und Metadaten deklarieren, mittels Pydantics `Field`.
-## `Field` importieren
+## `Field` importieren { #import-field }
Importieren Sie es zuerst:
-//// tab | Python 3.10+
+{* ../../docs_src/body_fields/tutorial001_an_py310.py hl[4] *}
-```Python hl_lines="4"
-{!> ../../../docs_src/body_fields/tutorial001_an_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="4"
-{!> ../../../docs_src/body_fields/tutorial001_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="4"
-{!> ../../../docs_src/body_fields/tutorial001_an.py!}
-```
-
-////
-
-//// tab | Python 3.10+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="2"
-{!> ../../../docs_src/body_fields/tutorial001_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="4"
-{!> ../../../docs_src/body_fields/tutorial001.py!}
-```
-
-////
-
-/// warning | "Achtung"
+/// warning | Achtung
Beachten Sie, dass `Field` direkt von `pydantic` importiert wird, nicht von `fastapi`, wie die anderen (`Query`, `Path`, `Body`, usw.)
///
-## Modellattribute deklarieren
+## Modellattribute deklarieren { #declare-model-attributes }
Dann können Sie `Field` mit Modellattributen deklarieren:
-//// tab | Python 3.10+
-
-```Python hl_lines="11-14"
-{!> ../../../docs_src/body_fields/tutorial001_an_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="11-14"
-{!> ../../../docs_src/body_fields/tutorial001_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="12-15"
-{!> ../../../docs_src/body_fields/tutorial001_an.py!}
-```
-
-////
-
-//// tab | Python 3.10+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="9-12"
-{!> ../../../docs_src/body_fields/tutorial001_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="11-14"
-{!> ../../../docs_src/body_fields/tutorial001.py!}
-```
-
-////
+{* ../../docs_src/body_fields/tutorial001_an_py310.py hl[11:14] *}
`Field` funktioniert genauso wie `Query`, `Path` und `Body`, es hat die gleichen Parameter, usw.
-/// note | "Technische Details"
+/// note | Technische Details
-Tatsächlich erstellen `Query`, `Path` und andere, die sie kennenlernen werden, Instanzen von Unterklassen einer allgemeinen Klasse `Param`, die ihrerseits eine Unterklasse von Pydantics `FieldInfo`-Klasse ist.
+Tatsächlich erstellen `Query`, `Path` und andere, die Sie als nächstes sehen werden, Instanzen von Unterklassen einer allgemeinen Klasse `Param`, welche selbst eine Unterklasse von Pydantics `FieldInfo`-Klasse ist.
Und Pydantics `Field` gibt ebenfalls eine Instanz von `FieldInfo` zurück.
-`Body` gibt auch Instanzen einer Unterklasse von `FieldInfo` zurück. Und später werden Sie andere sehen, die Unterklassen der `Body`-Klasse sind.
+`Body` gibt auch direkt Instanzen einer Unterklasse von `FieldInfo` zurück. Später werden Sie andere sehen, die Unterklassen der `Body`-Klasse sind.
-Denken Sie daran, dass `Query`, `Path` und andere von `fastapi` tatsächlich Funktionen sind, die spezielle Klassen zurückgeben.
+Denken Sie daran, dass `Query`, `Path` und andere, wenn Sie sie von `fastapi` importieren, tatsächlich Funktionen sind, die spezielle Klassen zurückgeben.
///
-/// tip | "Tipp"
+/// tip | Tipp
-Beachten Sie, dass jedes Modellattribut mit einem Typ, Defaultwert und `Field` die gleiche Struktur hat wie ein Parameter einer Pfadoperation-Funktion, nur mit `Field` statt `Path`, `Query`, `Body`.
+Beachten Sie, wie jedes Attribut eines Modells mit einem Typ, Defaultwert und `Field` die gleiche Struktur hat wie ein Parameter einer *Pfadoperation-Funktion*, nur mit `Field` statt `Path`, `Query`, `Body`.
///
-## Zusätzliche Information hinzufügen
+## Zusätzliche Information hinzufügen { #add-extra-information }
Sie können zusätzliche Information in `Field`, `Query`, `Body`, usw. deklarieren. Und es wird im generierten JSON-Schema untergebracht.
Sie werden später mehr darüber lernen, wie man zusätzliche Information unterbringt, wenn Sie lernen, Beispiele zu deklarieren.
-/// warning | "Achtung"
+/// warning | Achtung
-Extra-Schlüssel, die `Field` überreicht werden, werden auch im resultierenden OpenAPI-Schema Ihrer Anwendung gelistet. Da diese Schlüssel nicht notwendigerweise Teil der OpenAPI-Spezifikation sind, könnten einige OpenAPI-Tools, wie etwa [der OpenAPI-Validator](https://validator.swagger.io/), nicht mit Ihrem generierten Schema funktionieren.
+Extra-Schlüssel, die `Field` überreicht werden, werden auch im resultierenden OpenAPI-Schema Ihrer Anwendung gelistet. Da diese Schlüssel möglicherweise nicht Teil der OpenAPI-Spezifikation sind, könnten einige OpenAPI-Tools, wie etwa [der OpenAPI-Validator](https://validator.swagger.io/), nicht mit Ihrem generierten Schema funktionieren.
///
-## Zusammenfassung
+## Zusammenfassung { #recap }
Sie können Pydantics `Field` verwenden, um zusätzliche Validierungen und Metadaten für Modellattribute zu deklarieren.
-Sie können auch Extra-Schlüssel verwenden, um zusätzliche JSON-Schema-Metadaten zu überreichen.
+Sie können auch die zusätzlichen Schlüsselwortargumente verwenden, um zusätzliche JSON-Schema-Metadaten zu übergeben.
diff --git a/docs/de/docs/tutorial/body-multiple-params.md b/docs/de/docs/tutorial/body-multiple-params.md
index 977e17671b..3b5fa52dde 100644
--- a/docs/de/docs/tutorial/body-multiple-params.md
+++ b/docs/de/docs/tutorial/body-multiple-params.md
@@ -1,74 +1,24 @@
-# Body – Mehrere Parameter
+# Body – Mehrere Parameter { #body-multiple-parameters }
-Jetzt, da wir gesehen haben, wie `Path` und `Query` verwendet werden, schauen wir uns fortgeschrittenere Verwendungsmöglichkeiten von Requestbody-Deklarationen an.
+Nun, da wir gesehen haben, wie `Path` und `Query` verwendet werden, schauen wir uns fortgeschrittenere Verwendungsmöglichkeiten von Requestbody-Deklarationen an.
-## `Path`-, `Query`- und Body-Parameter vermischen
+## `Path`-, `Query`- und Body-Parameter vermischen { #mix-path-query-and-body-parameters }
Zuerst einmal, Sie können `Path`-, `Query`- und Requestbody-Parameter-Deklarationen frei mischen und **FastAPI** wird wissen, was zu tun ist.
Und Sie können auch Body-Parameter als optional kennzeichnen, indem Sie den Defaultwert auf `None` setzen:
-//// tab | Python 3.10+
+{* ../../docs_src/body_multiple_params/tutorial001_an_py310.py hl[18:20] *}
-```Python hl_lines="18-20"
-{!> ../../../docs_src/body_multiple_params/tutorial001_an_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="18-20"
-{!> ../../../docs_src/body_multiple_params/tutorial001_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="19-21"
-{!> ../../../docs_src/body_multiple_params/tutorial001_an.py!}
-```
-
-////
-
-//// tab | Python 3.10+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="17-19"
-{!> ../../../docs_src/body_multiple_params/tutorial001_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="19-21"
-{!> ../../../docs_src/body_multiple_params/tutorial001.py!}
-```
-
-////
-
-/// note | "Hinweis"
+/// note | Hinweis
Beachten Sie, dass in diesem Fall das `item`, welches vom Body genommen wird, optional ist. Da es `None` als Defaultwert hat.
///
-## Mehrere Body-Parameter
+## Mehrere Body-Parameter { #multiple-body-parameters }
-Im vorherigen Beispiel erwartete die *Pfadoperation* einen JSON-Body mit den Attributen eines `Item`s, etwa:
+Im vorherigen Beispiel erwarteten die *Pfadoperationen* einen JSON-Body mit den Attributen eines `Item`s, etwa:
```JSON
{
@@ -81,25 +31,11 @@ Im vorherigen Beispiel erwartete die *Pfadoperation* einen JSON-Body mit den Att
Aber Sie können auch mehrere Body-Parameter deklarieren, z. B. `item` und `user`:
-//// tab | Python 3.10+
-
-```Python hl_lines="20"
-{!> ../../../docs_src/body_multiple_params/tutorial002_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="22"
-{!> ../../../docs_src/body_multiple_params/tutorial002.py!}
-```
-
-////
+{* ../../docs_src/body_multiple_params/tutorial002_py310.py hl[20] *}
In diesem Fall wird **FastAPI** bemerken, dass es mehr als einen Body-Parameter in der Funktion gibt (zwei Parameter, die Pydantic-Modelle sind).
-Es wird deshalb die Parameternamen als Schlüssel (Feldnamen) im Body verwenden, und erwartet einen Body wie folgt:
+Es wird deshalb die Parameternamen als Schlüssel (Feldnamen) im Body verwenden und erwartet einen Body wie folgt:
```JSON
{
@@ -116,77 +52,27 @@ Es wird deshalb die Parameternamen als Schlüssel (Feldnamen) im Body verwenden,
}
```
-/// note | "Hinweis"
+/// note | Hinweis
Beachten Sie, dass, obwohl `item` wie zuvor deklariert wurde, es nun unter einem Schlüssel `item` im Body erwartet wird.
///
-**FastAPI** wird die automatische Konvertierung des Requests übernehmen, sodass der Parameter `item` seinen spezifischen Inhalt bekommt, genau so wie der Parameter `user`.
+**FastAPI** wird die automatische Konvertierung des Requests übernehmen, sodass der Parameter `item` seinen spezifischen Inhalt bekommt, und das Gleiche gilt für den Parameter `user`.
-Es wird die Validierung dieser zusammengesetzten Daten übernehmen, und sie im OpenAPI-Schema und der automatischen Dokumentation dokumentieren.
+Es wird die Validierung dieser zusammengesetzten Daten übernehmen, und diese im OpenAPI-Schema und der automatischen Dokumentation dokumentieren.
-## Einzelne Werte im Body
+## Einzelne Werte im Body { #singular-values-in-body }
-So wie `Query` und `Path` für Query- und Pfad-Parameter, hat **FastAPI** auch das Äquivalent `Body`, um Extra-Daten für Body-Parameter zu definieren.
+So wie `Query` und `Path` für Query- und Pfad-Parameter, stellt **FastAPI** das Äquivalent `Body` zur Verfügung, um Extra-Daten für Body-Parameter zu definieren.
-Zum Beispiel, das vorherige Modell erweiternd, könnten Sie entscheiden, dass Sie einen weiteren Schlüssel `importance` haben möchten, im selben Body, Seite an Seite mit `item` und `user`.
+Zum Beispiel, das vorherige Modell erweiternd, könnten Sie entscheiden, dass Sie einen weiteren Schlüssel `importance` im selben Body haben möchten, neben `item` und `user`.
-Wenn Sie diesen Parameter einfach so hinzufügen, wird **FastAPI** annehmen, dass es ein Query-Parameter ist.
+Wenn Sie diesen Parameter einfach so hinzufügen, wird **FastAPI** annehmen, dass es ein Query-Parameter ist, da er ein einzelner Wert ist.
Aber Sie können **FastAPI** instruieren, ihn als weiteren Body-Schlüssel zu erkennen, indem Sie `Body` verwenden:
-//// tab | Python 3.10+
-
-```Python hl_lines="23"
-{!> ../../../docs_src/body_multiple_params/tutorial003_an_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="23"
-{!> ../../../docs_src/body_multiple_params/tutorial003_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="24"
-{!> ../../../docs_src/body_multiple_params/tutorial003_an.py!}
-```
-
-////
-
-//// tab | Python 3.10+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="20"
-{!> ../../../docs_src/body_multiple_params/tutorial003_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="22"
-{!> ../../../docs_src/body_multiple_params/tutorial003.py!}
-```
-
-////
+{* ../../docs_src/body_multiple_params/tutorial003_an_py310.py hl[23] *}
In diesem Fall erwartet **FastAPI** einen Body wie:
@@ -206,9 +92,9 @@ In diesem Fall erwartet **FastAPI** einen Body wie:
}
```
-Wiederum wird es die Daten konvertieren, validieren, dokumentieren, usw.
+Wiederum wird es die Datentypen konvertieren, validieren, dokumentieren, usw.
-## Mehrere Body-Parameter und Query-Parameter
+## Mehrere Body-Parameter und Query-Parameter { #multiple-body-params-and-query }
Natürlich können Sie auch, wann immer Sie das brauchen, weitere Query-Parameter hinzufügen, zusätzlich zu den Body-Parametern.
@@ -226,71 +112,21 @@ q: str | None = None
Zum Beispiel:
-//// tab | Python 3.10+
+{* ../../docs_src/body_multiple_params/tutorial004_an_py310.py hl[28] *}
-```Python hl_lines="27"
-{!> ../../../docs_src/body_multiple_params/tutorial004_an_py310.py!}
-```
+/// info | Info
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="27"
-{!> ../../../docs_src/body_multiple_params/tutorial004_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="28"
-{!> ../../../docs_src/body_multiple_params/tutorial004_an.py!}
-```
-
-////
-
-//// tab | Python 3.10+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
+`Body` hat die gleichen zusätzlichen Validierungs- und Metadaten-Parameter wie `Query`, `Path` und andere, die Sie später kennenlernen werden.
///
-```Python hl_lines="25"
-{!> ../../../docs_src/body_multiple_params/tutorial004_py310.py!}
-```
+## Einen einzelnen Body-Parameter einbetten { #embed-a-single-body-parameter }
-////
+Nehmen wir an, Sie haben nur einen einzelnen `item`-Body-Parameter von einem Pydantic-Modell `Item`.
-//// tab | Python 3.8+ nicht annotiert
+Standardmäßig wird **FastAPI** dann seinen Body direkt erwarten.
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="27"
-{!> ../../../docs_src/body_multiple_params/tutorial004.py!}
-```
-
-////
-
-/// info
-
-`Body` hat die gleichen zusätzlichen Validierungs- und Metadaten-Parameter wie `Query` und `Path` und andere, die Sie später kennenlernen.
-
-///
-
-## Einen einzelnen Body-Parameter einbetten
-
-Nehmen wir an, Sie haben nur einen einzelnen `item`-Body-Parameter, ein Pydantic-Modell `Item`.
-
-Normalerweise wird **FastAPI** dann seinen JSON-Body direkt erwarten.
-
-Aber wenn Sie möchten, dass es einen JSON-Body erwartet, mit einem Schlüssel `item` und darin den Inhalt des Modells, so wie es das tut, wenn Sie mehrere Body-Parameter deklarieren, dann können Sie den speziellen `Body`-Parameter `embed` setzen:
+Aber wenn Sie möchten, dass es einen JSON-Body mit einem Schlüssel `item` erwartet, und darin den Inhalt des Modells, so wie es das tut, wenn Sie mehrere Body-Parameter deklarieren, dann können Sie den speziellen `Body`-Parameter `embed` setzen:
```Python
item: Item = Body(embed=True)
@@ -298,57 +134,7 @@ item: Item = Body(embed=True)
so wie in:
-//// tab | Python 3.10+
-
-```Python hl_lines="17"
-{!> ../../../docs_src/body_multiple_params/tutorial005_an_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="17"
-{!> ../../../docs_src/body_multiple_params/tutorial005_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="18"
-{!> ../../../docs_src/body_multiple_params/tutorial005_an.py!}
-```
-
-////
-
-//// tab | Python 3.10+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="15"
-{!> ../../../docs_src/body_multiple_params/tutorial005_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="17"
-{!> ../../../docs_src/body_multiple_params/tutorial005.py!}
-```
-
-////
+{* ../../docs_src/body_multiple_params/tutorial005_an_py310.py hl[17] *}
In diesem Fall erwartet **FastAPI** einen Body wie:
@@ -374,11 +160,11 @@ statt:
}
```
-## Zusammenfassung
+## Zusammenfassung { #recap }
-Sie können mehrere Body-Parameter zu ihrer *Pfadoperation-Funktion* hinzufügen, obwohl ein Request nur einen einzigen Body enthalten kann.
+Sie können mehrere Body-Parameter zu Ihrer *Pfadoperation-Funktion* hinzufügen, obwohl ein Request nur einen einzigen Body enthalten kann.
-**FastAPI** wird sich darum kümmern, Ihnen korrekte Daten in Ihrer Funktion zu überreichen, und das korrekte Schema in der *Pfadoperation* zu validieren und zu dokumentieren.
+Aber **FastAPI** wird sich darum kümmern, Ihnen korrekte Daten in Ihrer Funktion zu überreichen, und das korrekte Schema in der *Pfadoperation* zu validieren und zu dokumentieren.
Sie können auch einzelne Werte deklarieren, die als Teil des Bodys empfangen werden.
diff --git a/docs/de/docs/tutorial/body-nested-models.md b/docs/de/docs/tutorial/body-nested-models.md
index 8aef965f4c..324d31928d 100644
--- a/docs/de/docs/tutorial/body-nested-models.md
+++ b/docs/de/docs/tutorial/body-nested-models.md
@@ -1,44 +1,28 @@
-# Body – Verschachtelte Modelle
+# Body – Verschachtelte Modelle { #body-nested-models }
-Mit **FastAPI** können Sie (dank Pydantic) beliebig tief verschachtelte Modelle definieren, validieren und dokumentieren.
+Mit **FastAPI** können Sie (dank Pydantic) beliebig tief verschachtelte Modelle definieren, validieren, dokumentieren und verwenden.
-## Listen als Felder
+## Listen als Felder { #list-fields }
-Sie können ein Attribut als Kindtyp definieren, zum Beispiel eine Python-`list`e.
+Sie können ein Attribut als Kindtyp definieren, zum Beispiel eine Python-`list`.
-//// tab | Python 3.10+
-
-```Python hl_lines="12"
-{!> ../../../docs_src/body_nested_models/tutorial001_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="14"
-{!> ../../../docs_src/body_nested_models/tutorial001.py!}
-```
-
-////
+{* ../../docs_src/body_nested_models/tutorial001_py310.py hl[12] *}
Das bewirkt, dass `tags` eine Liste ist, wenngleich es nichts über den Typ der Elemente der Liste aussagt.
-## Listen mit Typ-Parametern als Felder
+## Listen mit Typ-Parametern als Felder { #list-fields-with-type-parameter }
Aber Python erlaubt es, Listen mit inneren Typen, auch „Typ-Parameter“ genannt, zu deklarieren.
-### `List` von `typing` importieren
+### `List` von `typing` importieren { #import-typings-list }
In Python 3.9 oder darüber können Sie einfach `list` verwenden, um diese Typannotationen zu deklarieren, wie wir unten sehen werden. 💡
In Python-Versionen vor 3.9 (3.6 und darüber), müssen Sie zuerst `List` von Pythons Standardmodul `typing` importieren.
-```Python hl_lines="1"
-{!> ../../../docs_src/body_nested_models/tutorial002.py!}
-```
+{* ../../docs_src/body_nested_models/tutorial002.py hl[1] *}
-### Eine `list`e mit einem Typ-Parameter deklarieren
+### Eine `list` mit einem Typ-Parameter deklarieren { #declare-a-list-with-a-type-parameter }
Um Typen wie `list`, `dict`, `tuple` mit inneren Typ-Parametern (inneren Typen) zu deklarieren:
@@ -65,31 +49,9 @@ Verwenden Sie dieselbe Standardsyntax für Modellattribute mit inneren Typen.
In unserem Beispiel können wir also bewirken, dass `tags` spezifisch eine „Liste von Strings“ ist:
-//// tab | Python 3.10+
+{* ../../docs_src/body_nested_models/tutorial002_py310.py hl[12] *}
-```Python hl_lines="12"
-{!> ../../../docs_src/body_nested_models/tutorial002_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="14"
-{!> ../../../docs_src/body_nested_models/tutorial002_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="14"
-{!> ../../../docs_src/body_nested_models/tutorial002.py!}
-```
-
-////
-
-## Set-Typen
+## Set-Typen { #set-types }
Aber dann denken wir darüber nach und stellen fest, dass sich die Tags nicht wiederholen sollen, es sollen eindeutige Strings sein.
@@ -97,37 +59,15 @@ Python hat einen Datentyp speziell für Mengen eindeutiger Dinge: das ../../../docs_src/body_nested_models/tutorial003_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="14"
-{!> ../../../docs_src/body_nested_models/tutorial003_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="1 14"
-{!> ../../../docs_src/body_nested_models/tutorial003.py!}
-```
-
-////
-
-Jetzt, selbst wenn Sie einen Request mit duplizierten Daten erhalten, werden diese zu einem Set eindeutiger Dinge konvertiert.
+Jetzt, selbst wenn Sie einen Request mit duplizierten Daten erhalten, werden diese zu einem Set eindeutiger Dinge konvertiert.
Und wann immer Sie diese Daten ausgeben, selbst wenn die Quelle Duplikate hatte, wird es als Set von eindeutigen Dingen ausgegeben.
Und es wird entsprechend annotiert/dokumentiert.
-## Verschachtelte Modelle
+## Verschachtelte Modelle { #nested-models }
Jedes Attribut eines Pydantic-Modells hat einen Typ.
@@ -137,63 +77,19 @@ Sie können also tief verschachtelte JSON-„Objekte“ deklarieren, mit spezifi
Alles das beliebig tief verschachtelt.
-### Ein Kindmodell definieren
+### Ein Kindmodell definieren { #define-a-submodel }
-Wir können zum Beispiel ein `Image`-Modell definieren.
+Für ein Beispiel können wir ein `Image`-Modell definieren.
-//// tab | Python 3.10+
+{* ../../docs_src/body_nested_models/tutorial004_py310.py hl[7:9] *}
-```Python hl_lines="7-9"
-{!> ../../../docs_src/body_nested_models/tutorial004_py310.py!}
-```
+### Das Kindmodell als Typ verwenden { #use-the-submodel-as-a-type }
-////
+Und dann können wir es als Typ eines Attributes verwenden:
-//// tab | Python 3.9+
+{* ../../docs_src/body_nested_models/tutorial004_py310.py hl[18] *}
-```Python hl_lines="9-11"
-{!> ../../../docs_src/body_nested_models/tutorial004_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="9-11"
-{!> ../../../docs_src/body_nested_models/tutorial004.py!}
-```
-
-////
-
-### Das Kindmodell als Typ verwenden
-
-Und dann können wir es als Typ eines Attributes verwenden.
-
-//// tab | Python 3.10+
-
-```Python hl_lines="18"
-{!> ../../../docs_src/body_nested_models/tutorial004_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="20"
-{!> ../../../docs_src/body_nested_models/tutorial004_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="20"
-{!> ../../../docs_src/body_nested_models/tutorial004.py!}
-```
-
-////
-
-Das würde bedeuten, dass **FastAPI** einen Body erwartet wie:
+Das würde bedeuten, dass **FastAPI** einen Body wie folgt erwartet:
```JSON
{
@@ -216,69 +112,25 @@ Wiederum, nur mit dieser Deklaration erhalten Sie von **FastAPI**:
* Datenvalidierung
* Automatische Dokumentation
-## Spezielle Typen und Validierungen
+## Spezielle Typen und Validierungen { #special-types-and-validation }
-Abgesehen von normalen einfachen Typen, wie `str`, `int`, `float`, usw. können Sie komplexere einfache Typen verwenden, die von `str` erben.
+Abgesehen von normalen einfachen Typen wie `str`, `int`, `float`, usw. können Sie komplexere einfache Typen verwenden, die von `str` erben.
-Um alle Optionen kennenzulernen, die Sie haben, schauen Sie sich Pydantics Typübersicht an. Sie werden im nächsten Kapitel ein paar Beispiele kennenlernen.
+Um alle Optionen kennenzulernen, die Sie haben, schauen Sie sich Pydantics Typübersicht an. Sie werden einige Beispiele im nächsten Kapitel kennenlernen.
-Da wir zum Beispiel im `Image`-Modell ein Feld `url` haben, können wir deklarieren, dass das eine Instanz von Pydantics `HttpUrl` sein soll, anstelle eines `str`:
+Zum Beispiel, da wir im `Image`-Modell ein Feld `url` haben, können wir deklarieren, dass das eine Instanz von Pydantics `HttpUrl` sein soll, anstelle eines `str`:
-//// tab | Python 3.10+
-
-```Python hl_lines="2 8"
-{!> ../../../docs_src/body_nested_models/tutorial005_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="4 10"
-{!> ../../../docs_src/body_nested_models/tutorial005_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="4 10"
-{!> ../../../docs_src/body_nested_models/tutorial005.py!}
-```
-
-////
+{* ../../docs_src/body_nested_models/tutorial005_py310.py hl[2,8] *}
Es wird getestet, ob der String eine gültige URL ist, und als solche wird er in JSON Schema / OpenAPI dokumentiert.
-## Attribute mit Listen von Kindmodellen
+## Attribute mit Listen von Kindmodellen { #attributes-with-lists-of-submodels }
Sie können Pydantic-Modelle auch als Typen innerhalb von `list`, `set`, usw. verwenden:
-//// tab | Python 3.10+
+{* ../../docs_src/body_nested_models/tutorial006_py310.py hl[18] *}
-```Python hl_lines="18"
-{!> ../../../docs_src/body_nested_models/tutorial006_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="20"
-{!> ../../../docs_src/body_nested_models/tutorial006_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="20"
-{!> ../../../docs_src/body_nested_models/tutorial006.py!}
-```
-
-////
-
-Das wird einen JSON-Body erwarten (konvertieren, validieren, dokumentieren), wie:
+Das wird einen JSON-Body erwarten (konvertieren, validieren, dokumentieren, usw.) wie:
```JSON hl_lines="11"
{
@@ -304,49 +156,27 @@ Das wird einen JSON-Body erwarten (konvertieren, validieren, dokumentieren), wie
}
```
-/// info
+/// info | Info
Beachten Sie, dass der `images`-Schlüssel jetzt eine Liste von Bild-Objekten hat.
///
-## Tief verschachtelte Modelle
+## Tief verschachtelte Modelle { #deeply-nested-models }
Sie können beliebig tief verschachtelte Modelle definieren:
-//// tab | Python 3.10+
+{* ../../docs_src/body_nested_models/tutorial007_py310.py hl[7,12,18,21,25] *}
-```Python hl_lines="7 12 18 21 25"
-{!> ../../../docs_src/body_nested_models/tutorial007_py310.py!}
-```
+/// info | Info
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="9 14 20 23 27"
-{!> ../../../docs_src/body_nested_models/tutorial007_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="9 14 20 23 27"
-{!> ../../../docs_src/body_nested_models/tutorial007.py!}
-```
-
-////
-
-/// info
-
-Beachten Sie, wie `Offer` eine Liste von `Item`s hat, von denen jedes seinerseits eine optionale Liste von `Image`s hat.
+Beachten Sie, wie `Offer` eine Liste von `Item`s hat, die ihrerseits eine optionale Liste von `Image`s haben.
///
-## Bodys aus reinen Listen
+## Bodys aus reinen Listen { #bodies-of-pure-lists }
-Wenn Sie möchten, dass das äußerste Element des JSON-Bodys ein JSON-`array` (eine Python-`list`e) ist, können Sie den Typ im Funktionsparameter deklarieren, mit der gleichen Syntax wie in Pydantic-Modellen:
+Wenn das äußerste Element des JSON-Bodys, das Sie erwarten, ein JSON-`array` (eine Python-`list`) ist, können Sie den Typ im Funktionsparameter deklarieren, mit der gleichen Syntax wie in Pydantic-Modellen:
```Python
images: List[Image]
@@ -360,23 +190,9 @@ images: list[Image]
so wie in:
-//// tab | Python 3.9+
+{* ../../docs_src/body_nested_models/tutorial008_py39.py hl[13] *}
-```Python hl_lines="13"
-{!> ../../../docs_src/body_nested_models/tutorial008_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="15"
-{!> ../../../docs_src/body_nested_models/tutorial008.py!}
-```
-
-////
-
-## Editor-Unterstützung überall
+## Editor-Unterstützung überall { #editor-support-everywhere }
Und Sie erhalten Editor-Unterstützung überall.
@@ -388,11 +204,11 @@ Sie würden diese Editor-Unterstützung nicht erhalten, wenn Sie direkt mit `dic
Aber Sie müssen sich auch nicht weiter um die Modelle kümmern, hereinkommende Dicts werden automatisch in sie konvertiert. Und was Sie zurückgeben, wird automatisch nach JSON konvertiert.
-## Bodys mit beliebigen `dict`s
+## Bodys mit beliebigen `dict`s { #bodies-of-arbitrary-dicts }
Sie können einen Body auch als `dict` deklarieren, mit Schlüsseln eines Typs und Werten eines anderen Typs.
-So brauchen Sie vorher nicht zu wissen, wie die Feld-/Attribut-Namen lauten (wie es bei Pydantic-Modellen der Fall wäre).
+So brauchen Sie vorher nicht zu wissen, wie die Feld-/Attributnamen lauten (wie es bei Pydantic-Modellen der Fall wäre).
Das ist nützlich, wenn Sie Schlüssel empfangen, deren Namen Sie nicht bereits kennen.
@@ -402,25 +218,11 @@ Ein anderer nützlicher Anwendungsfall ist, wenn Sie Schlüssel eines anderen Ty
Das schauen wir uns mal an.
-Im folgenden Beispiel akzeptieren Sie irgendein `dict`, solange es `int`-Schlüssel und `float`-Werte hat.
+Im folgenden Beispiel akzeptieren Sie irgendein `dict`, solange es `int`-Schlüssel und `float`-Werte hat:
-//// tab | Python 3.9+
+{* ../../docs_src/body_nested_models/tutorial009_py39.py hl[7] *}
-```Python hl_lines="7"
-{!> ../../../docs_src/body_nested_models/tutorial009_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="9"
-{!> ../../../docs_src/body_nested_models/tutorial009.py!}
-```
-
-////
-
-/// tip | "Tipp"
+/// tip | Tipp
Bedenken Sie, dass JSON nur `str` als Schlüssel unterstützt.
@@ -428,11 +230,11 @@ Aber Pydantic hat automatische Datenkonvertierung.
Das bedeutet, dass Ihre API-Clients nur Strings senden können, aber solange diese Strings nur Zahlen enthalten, wird Pydantic sie konvertieren und validieren.
-Und das `dict` welches Sie als `weights` erhalten, wird `int`-Schlüssel und `float`-Werte haben.
+Und das `dict`, welches Sie als `weights` erhalten, wird `int`-Schlüssel und `float`-Werte haben.
///
-## Zusammenfassung
+## Zusammenfassung { #recap }
Mit **FastAPI** haben Sie die maximale Flexibilität von Pydantic-Modellen, während Ihr Code einfach, kurz und elegant bleibt.
diff --git a/docs/de/docs/tutorial/body-updates.md b/docs/de/docs/tutorial/body-updates.md
index b835549146..aa62199feb 100644
--- a/docs/de/docs/tutorial/body-updates.md
+++ b/docs/de/docs/tutorial/body-updates.md
@@ -1,38 +1,16 @@
-# Body – Aktualisierungen
+# Body – Aktualisierungen { #body-updates }
-## Ersetzendes Aktualisieren mit `PUT`
+## Ersetzendes Aktualisieren mit `PUT` { #update-replacing-with-put }
Um einen Artikel zu aktualisieren, können Sie die HTTP `PUT` Operation verwenden.
-Sie können den `jsonable_encoder` verwenden, um die empfangenen Daten in etwas zu konvertieren, das als JSON gespeichert werden kann (in z. B. einer NoSQL-Datenbank). Zum Beispiel, um ein `datetime` in einen `str` zu konvertieren.
+Sie können den `jsonable_encoder` verwenden, um die empfangenen Daten in etwas zu konvertieren, das als JSON gespeichert werden kann (z. B. in einer NoSQL-Datenbank). Zum Beispiel, um ein `datetime` in einen `str` zu konvertieren.
-//// tab | Python 3.10+
-
-```Python hl_lines="28-33"
-{!> ../../../docs_src/body_updates/tutorial001_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="30-35"
-{!> ../../../docs_src/body_updates/tutorial001_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="30-35"
-{!> ../../../docs_src/body_updates/tutorial001.py!}
-```
-
-////
+{* ../../docs_src/body_updates/tutorial001_py310.py hl[28:33] *}
`PUT` wird verwendet, um Daten zu empfangen, die die existierenden Daten ersetzen sollen.
-### Warnung bezüglich des Ersetzens
+### Warnung bezüglich des Ersetzens { #warning-about-replacing }
Das bedeutet, dass, wenn Sie den Artikel `bar` aktualisieren wollen, mittels `PUT` und folgendem Body:
@@ -44,17 +22,17 @@ Das bedeutet, dass, wenn Sie den Artikel `bar` aktualisieren wollen, mittels `PU
}
```
-das Eingabemodell nun den Defaultwert `"tax": 10.5` hat, weil Sie das bereits gespeicherte Attribut `"tax": 20.2` nicht mit übergeben haben.
+weil das bereits gespeicherte Attribut `"tax": 20.2` nicht enthalten ist, das Eingabemodell den Defaultwert `"tax": 10.5` erhalten würde.
-Die Daten werden darum mit einem „neuen“ `tax`-Wert von `10.5` abgespeichert.
+Und die Daten würden mit diesem „neuen“ `tax` von `10.5` gespeichert werden.
-## Teilweises Ersetzen mit `PATCH`
+## Teil-Aktualisierungen mit `PATCH` { #partial-updates-with-patch }
Sie können auch die HTTP `PATCH` Operation verwenden, um Daten *teilweise* zu ersetzen.
-Das bedeutet, sie senden nur die Daten, die Sie aktualisieren wollen, der Rest bleibt unverändert.
+Das bedeutet, Sie senden nur die Daten, die Sie aktualisieren wollen, der Rest bleibt unverändert.
-/// note | "Hinweis"
+/// note | Hinweis
`PATCH` wird seltener verwendet und ist weniger bekannt als `PUT`.
@@ -66,55 +44,33 @@ Aber dieser Leitfaden zeigt Ihnen mehr oder weniger, wie die beiden normalerweis
///
-### Pydantics `exclude_unset`-Parameter verwenden
+### Pydantics `exclude_unset`-Parameter verwenden { #using-pydantics-exclude-unset-parameter }
Wenn Sie Teil-Aktualisierungen entgegennehmen, ist der `exclude_unset`-Parameter in der `.model_dump()`-Methode von Pydantic-Modellen sehr nützlich.
Wie in `item.model_dump(exclude_unset=True)`.
-/// info
+/// info | Info
-In Pydantic v1 hieß diese Methode `.dict()`, in Pydantic v2 wurde sie deprecated (aber immer noch unterstützt) und in `.model_dump()` umbenannt.
+In Pydantic v1 hieß diese Methode `.dict()`, in Pydantic v2 wurde sie deprecatet (aber immer noch unterstützt) und in `.model_dump()` umbenannt.
Die Beispiele hier verwenden `.dict()` für die Kompatibilität mit Pydantic v1, Sie sollten jedoch stattdessen `.model_dump()` verwenden, wenn Sie Pydantic v2 verwenden können.
///
-Das wird ein `dict` erstellen, mit nur den Daten, die gesetzt wurden als das `item`-Modell erstellt wurde, Defaultwerte ausgeschlossen.
+Das wird ein `dict` erstellen, mit nur den Daten, die gesetzt wurden, als das `item`-Modell erstellt wurde, Defaultwerte ausgeschlossen.
-Sie können das verwenden, um ein `dict` zu erstellen, das nur die (im Request) gesendeten Daten enthält, ohne Defaultwerte:
+Sie können das verwenden, um ein `dict` zu erstellen, das nur die (im Request) gesendeten Daten enthält, ohne Defaultwerte:
-//// tab | Python 3.10+
+{* ../../docs_src/body_updates/tutorial002_py310.py hl[32] *}
-```Python hl_lines="32"
-{!> ../../../docs_src/body_updates/tutorial002_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="34"
-{!> ../../../docs_src/body_updates/tutorial002_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="34"
-{!> ../../../docs_src/body_updates/tutorial002.py!}
-```
-
-////
-
-### Pydantics `update`-Parameter verwenden
+### Pydantics `update`-Parameter verwenden { #using-pydantics-update-parameter }
Jetzt können Sie eine Kopie des existierenden Modells mittels `.model_copy()` erstellen, wobei Sie dem `update`-Parameter ein `dict` mit den zu ändernden Daten übergeben.
-/// info
+/// info | Info
-In Pydantic v1 hieß diese Methode `.copy()`, in Pydantic v2 wurde sie deprecated (aber immer noch unterstützt) und in `.model_copy()` umbenannt.
+In Pydantic v1 hieß diese Methode `.copy()`, in Pydantic v2 wurde sie deprecatet (aber immer noch unterstützt) und in `.model_copy()` umbenannt.
Die Beispiele hier verwenden `.copy()` für die Kompatibilität mit Pydantic v1, Sie sollten jedoch stattdessen `.model_copy()` verwenden, wenn Sie Pydantic v2 verwenden können.
@@ -122,33 +78,11 @@ Die Beispiele hier verwenden `.copy()` für die Kompatibilität mit Pydantic v1,
Wie in `stored_item_model.model_copy(update=update_data)`:
-//// tab | Python 3.10+
+{* ../../docs_src/body_updates/tutorial002_py310.py hl[33] *}
-```Python hl_lines="33"
-{!> ../../../docs_src/body_updates/tutorial002_py310.py!}
-```
+### Rekapitulation zu Teil-Aktualisierungen { #partial-updates-recap }
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="35"
-{!> ../../../docs_src/body_updates/tutorial002_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="35"
-{!> ../../../docs_src/body_updates/tutorial002.py!}
-```
-
-////
-
-### Rekapitulation zum teilweisen Ersetzen
-
-Zusammengefasst, um Teil-Ersetzungen vorzunehmen:
+Zusammengefasst, um Teil-Aktualisierungen vorzunehmen:
* (Optional) verwenden Sie `PATCH` statt `PUT`.
* Lesen Sie die bereits gespeicherten Daten aus.
@@ -156,36 +90,14 @@ Zusammengefasst, um Teil-Ersetzungen vorzunehmen:
* Erzeugen Sie aus dem empfangenen Modell ein `dict` ohne Defaultwerte (mittels `exclude_unset`).
* So ersetzen Sie nur die tatsächlich vom Benutzer gesetzten Werte, statt dass bereits gespeicherte Werte mit Defaultwerten des Modells überschrieben werden.
* Erzeugen Sie eine Kopie ihres gespeicherten Modells, wobei Sie die Attribute mit den empfangenen Teil-Ersetzungen aktualisieren (mittels des `update`-Parameters).
-* Konvertieren Sie das kopierte Modell zu etwas, das in ihrer Datenbank gespeichert werden kann (indem Sie beispielsweise `jsonable_encoder` verwenden).
+* Konvertieren Sie das kopierte Modell zu etwas, das in Ihrer Datenbank gespeichert werden kann (indem Sie beispielsweise `jsonable_encoder` verwenden).
* Das ist vergleichbar dazu, die `.model_dump()`-Methode des Modells erneut aufzurufen, aber es wird sicherstellen, dass die Werte zu Daten konvertiert werden, die ihrerseits zu JSON konvertiert werden können, zum Beispiel `datetime` zu `str`.
* Speichern Sie die Daten in Ihrer Datenbank.
* Geben Sie das aktualisierte Modell zurück.
-//// tab | Python 3.10+
+{* ../../docs_src/body_updates/tutorial002_py310.py hl[28:35] *}
-```Python hl_lines="28-35"
-{!> ../../../docs_src/body_updates/tutorial002_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="30-37"
-{!> ../../../docs_src/body_updates/tutorial002_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="30-37"
-{!> ../../../docs_src/body_updates/tutorial002.py!}
-```
-
-////
-
-/// tip | "Tipp"
+/// tip | Tipp
Sie können tatsächlich die gleiche Technik mit einer HTTP `PUT` Operation verwenden.
@@ -193,7 +105,7 @@ Aber dieses Beispiel verwendet `PATCH`, da dieses für solche Anwendungsfälle g
///
-/// note | "Hinweis"
+/// note | Hinweis
Beachten Sie, dass das hereinkommende Modell immer noch validiert wird.
diff --git a/docs/de/docs/tutorial/body.md b/docs/de/docs/tutorial/body.md
index 3fdd4ade32..1e6382b6f2 100644
--- a/docs/de/docs/tutorial/body.md
+++ b/docs/de/docs/tutorial/body.md
@@ -1,68 +1,40 @@
-# Requestbody
+# Requestbody { #request-body }
-Wenn Sie Daten von einem Client (sagen wir, einem Browser) zu Ihrer API senden, dann senden Sie diese als einen **Requestbody** (Deutsch: Anfragekörper).
+Wenn Sie Daten von einem Client (sagen wir, einem Browser) zu Ihrer API senden müssen, senden Sie sie als **Requestbody**.
-Ein **Request**body sind Daten, die vom Client zu Ihrer API gesendet werden. Ein **Response**body (Deutsch: Antwortkörper) sind Daten, die Ihre API zum Client sendet.
+Ein **Request**body sind Daten, die vom Client zu Ihrer API gesendet werden. Ein **Response**body sind Daten, die Ihre API zum Client sendet.
-Ihre API sendet fast immer einen **Response**body. Aber Clients senden nicht unbedingt immer **Request**bodys (sondern nur Metadaten).
+Ihre API muss fast immer einen **Response**body senden. Aber Clients müssen nicht unbedingt immer **Requestbodys** senden, manchmal fordern sie nur einen Pfad an, vielleicht mit einigen Query-Parametern, aber senden keinen Body.
-Um einen **Request**body zu deklarieren, verwenden Sie Pydantic-Modelle mit allen deren Fähigkeiten und Vorzügen.
+Um einen **Request**body zu deklarieren, verwenden Sie Pydantic-Modelle mit all deren Fähigkeiten und Vorzügen.
-/// info
+/// info | Info
-Um Daten zu versenden, sollten Sie eines von: `POST` (meistverwendet), `PUT`, `DELETE` oder `PATCH` verwenden.
+Um Daten zu senden, sollten Sie eines von: `POST` (meistverwendet), `PUT`, `DELETE` oder `PATCH` verwenden.
-Senden Sie einen Body mit einem `GET`-Request, dann führt das laut Spezifikation zu undefiniertem Verhalten. Trotzdem wird es von FastAPI unterstützt, für sehr komplexe/extreme Anwendungsfälle.
+Das Senden eines Bodys mit einem `GET`-Request hat ein undefiniertes Verhalten in den Spezifikationen, wird aber dennoch von FastAPI unterstützt, nur für sehr komplexe/extreme Anwendungsfälle.
-Da aber davon abgeraten wird, zeigt die interaktive Dokumentation mit Swagger-Benutzeroberfläche die Dokumentation für den Body auch nicht an, wenn `GET` verwendet wird. Dazwischengeschaltete Proxys unterstützen es möglicherweise auch nicht.
+Da davon abgeraten wird, zeigt die interaktive Dokumentation mit Swagger-Benutzeroberfläche die Dokumentation für den Body nicht an, wenn `GET` verwendet wird, und zwischengeschaltete Proxys unterstützen es möglicherweise nicht.
///
-## Importieren Sie Pydantics `BaseModel`
+## Pydantics `BaseModel` importieren { #import-pydantics-basemodel }
Zuerst müssen Sie `BaseModel` von `pydantic` importieren:
-//// tab | Python 3.10+
+{* ../../docs_src/body/tutorial001_py310.py hl[2] *}
-```Python hl_lines="2"
-{!> ../../../docs_src/body/tutorial001_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="4"
-{!> ../../../docs_src/body/tutorial001.py!}
-```
-
-////
-
-## Erstellen Sie Ihr Datenmodell
+## Ihr Datenmodell erstellen { #create-your-data-model }
Dann deklarieren Sie Ihr Datenmodell als eine Klasse, die von `BaseModel` erbt.
-Verwenden Sie Standard-Python-Typen für die Klassenattribute:
+Verwenden Sie Standard-Python-Typen für alle Attribute:
-//// tab | Python 3.10+
+{* ../../docs_src/body/tutorial001_py310.py hl[5:9] *}
-```Python hl_lines="5-9"
-{!> ../../../docs_src/body/tutorial001_py310.py!}
-```
+Wie auch bei der Deklaration von Query-Parametern gilt: Wenn ein Modellattribut einen Defaultwert hat, ist das Attribut nicht erforderlich. Andernfalls ist es erforderlich. Verwenden Sie `None`, um es einfach optional zu machen.
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="7-11"
-{!> ../../../docs_src/body/tutorial001.py!}
-```
-
-////
-
-Wie auch bei Query-Parametern gilt, wenn ein Modellattribut einen Defaultwert hat, ist das Attribut nicht erforderlich. Ansonsten ist es erforderlich. Verwenden Sie `None`, um es als optional zu kennzeichnen.
-
-Zum Beispiel deklariert das obige Modell ein JSON "`object`" (oder Python-`dict`) wie dieses:
+Zum Beispiel deklariert das obige Modell ein JSON „`object`“ (oder Python-`dict`) wie dieses:
```JSON
{
@@ -73,7 +45,7 @@ Zum Beispiel deklariert das obige Modell ein JSON "`object`" (oder Python-`dict`
}
```
-Da `description` und `tax` optional sind (mit `None` als Defaultwert), wäre folgendes JSON "`object`" auch gültig:
+Da `description` und `tax` optional sind (mit `None` als Defaultwert), wäre folgendes JSON „`object`“ auch gültig:
```JSON
{
@@ -82,165 +54,120 @@ Da `description` und `tax` optional sind (mit `None` als Defaultwert), wäre fol
}
```
-## Deklarieren Sie es als Parameter
+## Als Parameter deklarieren { #declare-it-as-a-parameter }
Um es zu Ihrer *Pfadoperation* hinzuzufügen, deklarieren Sie es auf die gleiche Weise, wie Sie Pfad- und Query-Parameter deklariert haben:
-//// tab | Python 3.10+
+{* ../../docs_src/body/tutorial001_py310.py hl[16] *}
-```Python hl_lines="16"
-{!> ../../../docs_src/body/tutorial001_py310.py!}
-```
+... und deklarieren Sie dessen Typ als das Modell, welches Sie erstellt haben, `Item`.
-////
+## Resultate { #results }
-//// tab | Python 3.8+
-
-```Python hl_lines="18"
-{!> ../../../docs_src/body/tutorial001.py!}
-```
-
-////
-
-... und deklarieren Sie seinen Typ als das Modell, welches Sie erstellt haben, `Item`.
-
-## Resultate
-
-Mit nur dieser Python-Typdeklaration, wird **FastAPI**:
+Mit nur dieser Python-Typdeklaration wird **FastAPI**:
* Den Requestbody als JSON lesen.
* Die entsprechenden Typen konvertieren (falls nötig).
* Diese Daten validieren.
- * Wenn die Daten ungültig sind, einen klar lesbaren Fehler zurückgeben, der anzeigt, wo und was die inkorrekten Daten waren.
+ * Wenn die Daten ungültig sind, wird ein klar lesbarer Fehler zurückgegeben, der genau anzeigt, wo und was die inkorrekten Daten sind.
* Ihnen die erhaltenen Daten im Parameter `item` übergeben.
- * Da Sie diesen in der Funktion als vom Typ `Item` deklariert haben, erhalten Sie die ganze Editor-Unterstützung (Autovervollständigung, usw.) für alle Attribute und deren Typen.
-* Eine JSON Schema Definition für Ihr Modell generieren, welche Sie überall sonst verwenden können, wenn es für Ihr Projekt Sinn macht.
-* Diese Schemas werden Teil des generierten OpenAPI-Schemas und werden von den UIs der automatischen Dokumentation verwendet.
+ * Da Sie ihn in der Funktion als vom Typ `Item` deklariert haben, erhalten Sie auch die volle Unterstützung des Editors (Autovervollständigung, usw.) für alle Attribute und deren Typen.
+* JSON Schema-Definitionen für Ihr Modell generieren, die Sie auch überall sonst verwenden können, wenn es für Ihr Projekt Sinn macht.
+* Diese Schemas werden Teil des generierten OpenAPI-Schemas und werden von den UIs der automatischen Dokumentation genutzt.
-## Automatische Dokumentation
+## Automatische Dokumentation { #automatic-docs }
-Die JSON-Schemas Ihrer Modelle werden Teil ihrer OpenAPI-generierten Schemas und werden in der interaktiven API Dokumentation angezeigt:
+Die JSON-Schemas Ihrer Modelle werden Teil Ihres OpenAPI-generierten Schemas und in der interaktiven API-Dokumentation angezeigt:
-Und werden auch verwendet in der API-Dokumentation innerhalb jeder *Pfadoperation*, welche sie braucht:
+Und werden auch in der API-Dokumentation innerhalb jeder *Pfadoperation*, die sie benötigt, verwendet:
-## Editor Unterstützung
+## Editor-Unterstützung { #editor-support }
-In Ihrem Editor, innerhalb Ihrer Funktion, erhalten Sie Typhinweise und Code-Vervollständigung überall (was nicht der Fall wäre, wenn Sie ein `dict` anstelle eines Pydantic Modells erhalten hätten):
+In Ihrem Editor erhalten Sie innerhalb Ihrer Funktion Typhinweise und Code-Vervollständigung überall (was nicht der Fall wäre, wenn Sie ein `dict` anstelle eines Pydantic-Modells erhalten hätten):
-Sie bekommen auch Fehler-Meldungen für inkorrekte Typoperationen:
+Sie bekommen auch Fehlermeldungen für inkorrekte Typoperationen:
Das ist nicht zufällig so, das ganze Framework wurde um dieses Design herum aufgebaut.
-Und es wurde in der Designphase gründlich getestet, vor der Implementierung, um sicherzustellen, dass es mit jedem Editor funktioniert.
+Und es wurde in der Designphase gründlich getestet, bevor irgendeine Implementierung stattfand, um sicherzustellen, dass es mit allen Editoren funktioniert.
-Es gab sogar ein paar Änderungen an Pydantic selbst, um das zu unterstützen.
+Es gab sogar einige Änderungen an Pydantic selbst, um dies zu unterstützen.
-Die vorherigen Screenshots zeigten Visual Studio Code.
+Die vorherigen Screenshots wurden mit Visual Studio Code aufgenommen.
-Aber Sie bekommen die gleiche Editor-Unterstützung in PyCharm und in den meisten anderen Python-Editoren:
+Aber Sie würden die gleiche Editor-Unterstützung in PyCharm und den meisten anderen Python-Editoren erhalten:
-/// tip | "Tipp"
+/// tip | Tipp
-Wenn Sie PyCharm als Ihren Editor verwenden, probieren Sie das Pydantic PyCharm Plugin aus.
+Wenn Sie PyCharm als Ihren Editor verwenden, können Sie das Pydantic PyCharm Plugin ausprobieren.
Es verbessert die Editor-Unterstützung für Pydantic-Modelle, mit:
* Code-Vervollständigung
* Typüberprüfungen
* Refaktorisierung
-* Suchen
+* Suche
* Inspektionen
///
-## Das Modell verwenden
+## Das Modell verwenden { #use-the-model }
-Innerhalb der Funktion können Sie alle Attribute des Modells direkt verwenden:
+Innerhalb der Funktion können Sie alle Attribute des Modellobjekts direkt verwenden:
-//// tab | Python 3.10+
+{* ../../docs_src/body/tutorial002_py310.py *}
-```Python hl_lines="19"
-{!> ../../../docs_src/body/tutorial002_py310.py!}
-```
+/// info | Info
-////
+In Pydantic v1 hieß die Methode `.dict()`, sie wurde in Pydantic v2 deprecatet (aber weiterhin unterstützt) und in `.model_dump()` umbenannt.
-//// tab | Python 3.8+
-
-```Python hl_lines="21"
-{!> ../../../docs_src/body/tutorial002.py!}
-```
-
-////
-
-## Requestbody- + Pfad-Parameter
-
-Sie können Pfad- und Requestbody-Parameter gleichzeitig deklarieren.
-
-**FastAPI** erkennt, dass Funktionsparameter, die mit Pfad-Parametern übereinstimmen, **vom Pfad genommen** werden sollen, und dass Funktionsparameter, welche Pydantic-Modelle sind, **vom Requestbody genommen** werden sollen.
-
-//// tab | Python 3.10+
-
-```Python hl_lines="15-16"
-{!> ../../../docs_src/body/tutorial003_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="17-18"
-{!> ../../../docs_src/body/tutorial003.py!}
-```
-
-////
-
-## Requestbody- + Pfad- + Query-Parameter
-
-Sie können auch zur gleichen Zeit **Body-**, **Pfad-** und **Query-Parameter** deklarieren.
-
-**FastAPI** wird jeden Parameter korrekt erkennen und die Daten vom richtigen Ort holen.
-
-//// tab | Python 3.10+
-
-```Python hl_lines="16"
-{!> ../../../docs_src/body/tutorial004_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="18"
-{!> ../../../docs_src/body/tutorial004.py!}
-```
-
-////
-
-Die Funktionsparameter werden wie folgt erkannt:
-
-* Wenn der Parameter auch im **Pfad** deklariert wurde, wird er als Pfad-Parameter interpretiert.
-* Wenn der Parameter ein **einfacher Typ** ist (wie `int`, `float`, `str`, `bool`, usw.), wird er als **Query**-Parameter interpretiert.
-* Wenn der Parameter vom Typ eines **Pydantic-Modells** ist, wird er als Request**body** interpretiert.
-
-/// note | "Hinweis"
-
-FastAPI weiß, dass der Wert von `q` nicht erforderlich ist, wegen des definierten Defaultwertes `= None`
-
-Das `Union` in `Union[str, None]` wird von FastAPI nicht verwendet, aber es erlaubt Ihrem Editor, Sie besser zu unterstützen und Fehler zu erkennen.
+Die Beispiele hier verwenden `.dict()` zur Kompatibilität mit Pydantic v1, aber Sie sollten stattdessen `.model_dump()` verwenden, wenn Sie Pydantic v2 nutzen können.
///
-## Ohne Pydantic
+## Requestbody- + Pfad-Parameter { #request-body-path-parameters }
-Wenn Sie keine Pydantic-Modelle verwenden wollen, können Sie auch **Body**-Parameter nehmen. Siehe die Dokumentation unter [Body – Mehrere Parameter: Einfache Werte im Body](body-multiple-params.md#einzelne-werte-im-body){.internal-link target=\_blank}.
+Sie können Pfad-Parameter und den Requestbody gleichzeitig deklarieren.
+
+**FastAPI** erkennt, dass Funktionsparameter, die mit Pfad-Parametern übereinstimmen, **vom Pfad genommen** werden sollen, und dass Funktionsparameter, welche Pydantic-Modelle sind, **vom Requestbody genommen** werden sollen.
+
+{* ../../docs_src/body/tutorial003_py310.py hl[15:16] *}
+
+
+## Requestbody- + Pfad- + Query-Parameter { #request-body-path-query-parameters }
+
+Sie können auch zur gleichen Zeit **Body-**, **Pfad-** und **Query-Parameter** deklarieren.
+
+**FastAPI** wird jeden von ihnen korrekt erkennen und die Daten vom richtigen Ort holen.
+
+{* ../../docs_src/body/tutorial004_py310.py hl[16] *}
+
+Die Funktionsparameter werden wie folgt erkannt:
+
+* Wenn der Parameter auch im **Pfad** deklariert wurde, wird er als Pfad-Parameter verwendet.
+* Wenn der Parameter ein **einfacher Typ** ist (wie `int`, `float`, `str`, `bool`, usw.), wird er als **Query**-Parameter interpretiert.
+* Wenn der Parameter vom Typ eines **Pydantic-Modells** ist, wird er als Request**body** interpretiert.
+
+/// note | Hinweis
+
+FastAPI weiß, dass der Wert von `q` nicht erforderlich ist, aufgrund des definierten Defaultwertes `= None`.
+
+Das `str | None` (Python 3.10+) oder `Union` in `Union[str, None]` (Python 3.8+) wird von FastAPI nicht verwendet, um zu bestimmen, dass der Wert nicht erforderlich ist. FastAPI weiß, dass er nicht erforderlich ist, weil er einen Defaultwert von `= None` hat.
+
+Das Hinzufügen der Typannotationen ermöglicht jedoch Ihrem Editor, Ihnen eine bessere Unterstützung zu bieten und Fehler zu erkennen.
+
+///
+
+## Ohne Pydantic { #without-pydantic }
+
+Wenn Sie keine Pydantic-Modelle verwenden möchten, können Sie auch **Body**-Parameter verwenden. Siehe die Dokumentation unter [Body – Mehrere Parameter: Einfache Werte im Body](body-multiple-params.md#singular-values-in-body){.internal-link target=_blank}.
diff --git a/docs/de/docs/tutorial/cookie-param-models.md b/docs/de/docs/tutorial/cookie-param-models.md
new file mode 100644
index 0000000000..2baf3d70dd
--- /dev/null
+++ b/docs/de/docs/tutorial/cookie-param-models.md
@@ -0,0 +1,76 @@
+# Cookie-Parameter-Modelle { #cookie-parameter-models }
+
+Wenn Sie eine Gruppe von **Cookies** haben, die zusammengehören, können Sie ein **Pydantic-Modell** erstellen, um diese zu deklarieren. 🍪
+
+Damit können Sie das Modell an **mehreren Stellen wiederverwenden** und auch Validierungen und Metadaten für alle Parameter gleichzeitig deklarieren. 😎
+
+/// note | Hinweis
+
+Dies wird seit FastAPI Version `0.115.0` unterstützt. 🤓
+
+///
+
+/// tip | Tipp
+
+Diese gleiche Technik gilt für `Query`, `Cookie` und `Header`. 😎
+
+///
+
+## Cookies mit einem Pydantic-Modell { #cookies-with-a-pydantic-model }
+
+Deklarieren Sie die **Cookie**-Parameter, die Sie benötigen, in einem **Pydantic-Modell**, und deklarieren Sie dann den Parameter als `Cookie`:
+
+{* ../../docs_src/cookie_param_models/tutorial001_an_py310.py hl[9:12,16] *}
+
+**FastAPI** wird die Daten für **jedes Feld** aus den im Request empfangenen **Cookies** **extrahieren** und Ihnen das von Ihnen definierte Pydantic-Modell bereitstellen.
+
+## Die Dokumentation testen { #check-the-docs }
+
+Sie können die definierten Cookies in der Dokumentationsoberfläche unter `/docs` sehen:
+
+
+
+
+---
+
+Wenn Sie Pycharm verwenden, können Sie:
+
+* Das Menü „Run“ öffnen.
+* Die Option „Debug ...“ auswählen.
+* Ein Kontextmenü wird angezeigt.
+* Die zu debuggende Datei auswählen (in diesem Fall `main.py`).
+
+Der Server wird dann mit Ihrem **FastAPI**-Code gestartet, an Ihren Haltepunkten angehalten, usw.
+
+So könnte es aussehen:
+
+
diff --git a/docs/de/docs/tutorial/dependencies/classes-as-dependencies.md b/docs/de/docs/tutorial/dependencies/classes-as-dependencies.md
index 0a9f05bf90..3d4493f353 100644
--- a/docs/de/docs/tutorial/dependencies/classes-as-dependencies.md
+++ b/docs/de/docs/tutorial/dependencies/classes-as-dependencies.md
@@ -1,62 +1,12 @@
-# Klassen als Abhängigkeiten
+# Klassen als Abhängigkeiten { #classes-as-dependencies }
Bevor wir tiefer in das **Dependency Injection** System eintauchen, lassen Sie uns das vorherige Beispiel verbessern.
-## Ein `dict` aus dem vorherigen Beispiel
+## Ein `dict` aus dem vorherigen Beispiel { #a-dict-from-the-previous-example }
-Im vorherigen Beispiel haben wir ein `dict` von unserer Abhängigkeit („Dependable“) zurückgegeben:
+Im vorherigen Beispiel haben wir ein `dict` von unserer Abhängigkeit („Dependable“) zurückgegeben:
-//// tab | Python 3.10+
-
-```Python hl_lines="9"
-{!> ../../../docs_src/dependencies/tutorial001_an_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="11"
-{!> ../../../docs_src/dependencies/tutorial001_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="12"
-{!> ../../../docs_src/dependencies/tutorial001_an.py!}
-```
-
-////
-
-//// tab | Python 3.10+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="7"
-{!> ../../../docs_src/dependencies/tutorial001_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="11"
-{!> ../../../docs_src/dependencies/tutorial001.py!}
-```
-
-////
+{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[9] *}
Aber dann haben wir ein `dict` im Parameter `commons` der *Pfadoperation-Funktion*.
@@ -64,7 +14,7 @@ Und wir wissen, dass Editoren nicht viel Unterstützung (wie etwa Code-Vervollst
Das können wir besser machen ...
-## Was macht eine Abhängigkeit aus
+## Was macht eine Abhängigkeit aus { #what-makes-a-dependency }
Bisher haben Sie Abhängigkeiten gesehen, die als Funktionen deklariert wurden.
@@ -88,7 +38,7 @@ something(some_argument, some_keyword_argument="foo")
dann ist das ein „Callable“ (ein „Aufrufbares“).
-## Klassen als Abhängigkeiten
+## Klassen als Abhängigkeiten { #classes-as-dependencies_1 }
Möglicherweise stellen Sie fest, dass Sie zum Erstellen einer Instanz einer Python-Klasse die gleiche Syntax verwenden.
@@ -119,165 +69,15 @@ Das gilt auch für Callables ohne Parameter. So wie es auch für *Pfadoperation-
Dann können wir das „Dependable“ `common_parameters` der Abhängigkeit von oben in die Klasse `CommonQueryParams` ändern:
-//// tab | Python 3.10+
-
-```Python hl_lines="11-15"
-{!> ../../../docs_src/dependencies/tutorial002_an_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="11-15"
-{!> ../../../docs_src/dependencies/tutorial002_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="12-16"
-{!> ../../../docs_src/dependencies/tutorial002_an.py!}
-```
-
-////
-
-//// tab | Python 3.10+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="9-13"
-{!> ../../../docs_src/dependencies/tutorial002_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="11-15"
-{!> ../../../docs_src/dependencies/tutorial002.py!}
-```
-
-////
+{* ../../docs_src/dependencies/tutorial002_an_py310.py hl[11:15] *}
Achten Sie auf die Methode `__init__`, die zum Erstellen der Instanz der Klasse verwendet wird:
-//// tab | Python 3.10+
-
-```Python hl_lines="12"
-{!> ../../../docs_src/dependencies/tutorial002_an_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="12"
-{!> ../../../docs_src/dependencies/tutorial002_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="13"
-{!> ../../../docs_src/dependencies/tutorial002_an.py!}
-```
-
-////
-
-//// tab | Python 3.10+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="10"
-{!> ../../../docs_src/dependencies/tutorial002_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="12"
-{!> ../../../docs_src/dependencies/tutorial002.py!}
-```
-
-////
+{* ../../docs_src/dependencies/tutorial002_an_py310.py hl[12] *}
... sie hat die gleichen Parameter wie unsere vorherige `common_parameters`:
-//// tab | Python 3.10+
-
-```Python hl_lines="8"
-{!> ../../../docs_src/dependencies/tutorial001_an_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="9"
-{!> ../../../docs_src/dependencies/tutorial001_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="10"
-{!> ../../../docs_src/dependencies/tutorial001_an.py!}
-```
-
-////
-
-//// tab | Python 3.10+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="6"
-{!> ../../../docs_src/dependencies/tutorial001_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="9"
-{!> ../../../docs_src/dependencies/tutorial001.py!}
-```
-
-////
+{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[8] *}
Diese Parameter werden von **FastAPI** verwendet, um die Abhängigkeit „aufzulösen“.
@@ -289,65 +89,15 @@ In beiden Fällen wird sie haben:
In beiden Fällen werden die Daten konvertiert, validiert, im OpenAPI-Schema dokumentiert, usw.
-## Verwendung
+## Verwenden { #use-it }
Jetzt können Sie Ihre Abhängigkeit mithilfe dieser Klasse deklarieren.
-//// tab | Python 3.10+
-
-```Python hl_lines="19"
-{!> ../../../docs_src/dependencies/tutorial002_an_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="19"
-{!> ../../../docs_src/dependencies/tutorial002_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="20"
-{!> ../../../docs_src/dependencies/tutorial002_an.py!}
-```
-
-////
-
-//// tab | Python 3.10+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="17"
-{!> ../../../docs_src/dependencies/tutorial002_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="19"
-{!> ../../../docs_src/dependencies/tutorial002.py!}
-```
-
-////
+{* ../../docs_src/dependencies/tutorial002_an_py310.py hl[19] *}
**FastAPI** ruft die Klasse `CommonQueryParams` auf. Dadurch wird eine „Instanz“ dieser Klasse erstellt und die Instanz wird als Parameter `commons` an Ihre Funktion überreicht.
-## Typannotation vs. `Depends`
+## Typannotation vs. `Depends` { #type-annotation-vs-depends }
Beachten Sie, wie wir `CommonQueryParams` im obigen Code zweimal schreiben:
@@ -361,7 +111,7 @@ commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
//// tab | Python 3.8+ nicht annotiert
-/// tip | "Tipp"
+/// tip | Tipp
Bevorzugen Sie die `Annotated`-Version, falls möglich.
@@ -397,7 +147,7 @@ commons: Annotated[CommonQueryParams, ...
//// tab | Python 3.8+ nicht annotiert
-/// tip | "Tipp"
+/// tip | Tipp
Bevorzugen Sie die `Annotated`-Version, falls möglich.
@@ -423,7 +173,7 @@ commons: Annotated[Any, Depends(CommonQueryParams)]
//// tab | Python 3.8+ nicht annotiert
-/// tip | "Tipp"
+/// tip | Tipp
Bevorzugen Sie die `Annotated`-Version, falls möglich.
@@ -437,63 +187,13 @@ commons = Depends(CommonQueryParams)
... wie in:
-//// tab | Python 3.10+
-
-```Python hl_lines="19"
-{!> ../../../docs_src/dependencies/tutorial003_an_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="19"
-{!> ../../../docs_src/dependencies/tutorial003_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="20"
-{!> ../../../docs_src/dependencies/tutorial003_an.py!}
-```
-
-////
-
-//// tab | Python 3.10+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="17"
-{!> ../../../docs_src/dependencies/tutorial003_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="19"
-{!> ../../../docs_src/dependencies/tutorial003.py!}
-```
-
-////
+{* ../../docs_src/dependencies/tutorial003_an_py310.py hl[19] *}
Es wird jedoch empfohlen, den Typ zu deklarieren, da Ihr Editor so weiß, was als Parameter `commons` übergeben wird, und Ihnen dann bei der Codevervollständigung, Typprüfungen, usw. helfen kann:
-## Abkürzung
+## Abkürzung { #shortcut }
Aber Sie sehen, dass wir hier etwas Codeduplizierung haben, indem wir `CommonQueryParams` zweimal schreiben:
@@ -507,7 +207,7 @@ commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
//// tab | Python 3.8+ nicht annotiert
-/// tip | "Tipp"
+/// tip | Tipp
Bevorzugen Sie die `Annotated`-Version, falls möglich.
@@ -535,7 +235,7 @@ commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
//// tab | Python 3.8+ nicht annotiert
-/// tip | "Tipp"
+/// tip | Tipp
Bevorzugen Sie die `Annotated`-Version, falls möglich.
@@ -559,7 +259,7 @@ commons: Annotated[CommonQueryParams, Depends()]
//// tab | Python 3.8 nicht annotiert
-/// tip | "Tipp"
+/// tip | Tipp
Bevorzugen Sie die `Annotated`-Version, falls möglich.
@@ -575,61 +275,11 @@ Sie deklarieren die Abhängigkeit als Typ des Parameters und verwenden `Depends(
Dasselbe Beispiel würde dann so aussehen:
-//// tab | Python 3.10+
-
-```Python hl_lines="19"
-{!> ../../../docs_src/dependencies/tutorial004_an_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="19"
-{!> ../../../docs_src/dependencies/tutorial004_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="20"
-{!> ../../../docs_src/dependencies/tutorial004_an.py!}
-```
-
-////
-
-//// tab | Python 3.10+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="17"
-{!> ../../../docs_src/dependencies/tutorial004_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="19"
-{!> ../../../docs_src/dependencies/tutorial004.py!}
-```
-
-////
+{* ../../docs_src/dependencies/tutorial004_an_py310.py hl[19] *}
... und **FastAPI** wird wissen, was zu tun ist.
-/// tip | "Tipp"
+/// tip | Tipp
Wenn Sie das eher verwirrt, als Ihnen zu helfen, ignorieren Sie es, Sie *brauchen* es nicht.
diff --git a/docs/de/docs/tutorial/dependencies/dependencies-in-path-operation-decorators.md b/docs/de/docs/tutorial/dependencies/dependencies-in-path-operation-decorators.md
index 47d6453c26..59c9fcf48e 100644
--- a/docs/de/docs/tutorial/dependencies/dependencies-in-path-operation-decorators.md
+++ b/docs/de/docs/tutorial/dependencies/dependencies-in-path-operation-decorators.md
@@ -1,52 +1,24 @@
-# Abhängigkeiten in Pfadoperation-Dekoratoren
+# Abhängigkeiten in Pfadoperation-Dekoratoren { #dependencies-in-path-operation-decorators }
Manchmal benötigen Sie den Rückgabewert einer Abhängigkeit innerhalb Ihrer *Pfadoperation-Funktion* nicht wirklich.
Oder die Abhängigkeit gibt keinen Wert zurück.
-Aber Sie müssen Sie trotzdem ausführen/auflösen.
+Aber Sie müssen sie trotzdem ausführen/auflösen.
In diesen Fällen können Sie, anstatt einen Parameter der *Pfadoperation-Funktion* mit `Depends` zu deklarieren, eine `list`e von `dependencies` zum *Pfadoperation-Dekorator* hinzufügen.
-## `dependencies` zum *Pfadoperation-Dekorator* hinzufügen
+## `dependencies` zum *Pfadoperation-Dekorator* hinzufügen { #add-dependencies-to-the-path-operation-decorator }
Der *Pfadoperation-Dekorator* erhält ein optionales Argument `dependencies`.
Es sollte eine `list`e von `Depends()` sein:
-//// tab | Python 3.9+
-
-```Python hl_lines="19"
-{!> ../../../docs_src/dependencies/tutorial006_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="18"
-{!> ../../../docs_src/dependencies/tutorial006_an.py!}
-```
-
-////
-
-//// tab | Python 3.8 nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="17"
-{!> ../../../docs_src/dependencies/tutorial006.py!}
-```
-
-////
+{* ../../docs_src/dependencies/tutorial006_an_py39.py hl[19] *}
Diese Abhängigkeiten werden auf die gleiche Weise wie normale Abhängigkeiten ausgeführt/aufgelöst. Aber ihr Wert (falls sie einen zurückgeben) wird nicht an Ihre *Pfadoperation-Funktion* übergeben.
-/// tip | "Tipp"
+/// tip | Tipp
Einige Editoren prüfen, ob Funktionsparameter nicht verwendet werden, und zeigen das als Fehler an.
@@ -56,7 +28,7 @@ Damit wird auch vermieden, neue Entwickler möglicherweise zu verwirren, die ein
///
-/// info
+/// info | Info
In diesem Beispiel verwenden wir zwei erfundene benutzerdefinierte Header `X-Key` und `X-Token`.
@@ -64,118 +36,34 @@ Aber in realen Fällen würden Sie bei der Implementierung von Sicherheit mehr V
///
-## Abhängigkeitsfehler und -Rückgabewerte
+## Abhängigkeitsfehler und -Rückgabewerte { #dependencies-errors-and-return-values }
Sie können dieselben Abhängigkeits-*Funktionen* verwenden, die Sie normalerweise verwenden.
-### Abhängigkeitsanforderungen
+### Abhängigkeitsanforderungen { #dependency-requirements }
-Sie können Anforderungen für einen Request (wie Header) oder andere Unterabhängigkeiten deklarieren:
+Sie können Anforderungen für einen Request (wie Header) oder andere Unterabhängigkeiten deklarieren:
-//// tab | Python 3.9+
+{* ../../docs_src/dependencies/tutorial006_an_py39.py hl[8,13] *}
-```Python hl_lines="8 13"
-{!> ../../../docs_src/dependencies/tutorial006_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="7 12"
-{!> ../../../docs_src/dependencies/tutorial006_an.py!}
-```
-
-////
-
-//// tab | Python 3.8 nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="6 11"
-{!> ../../../docs_src/dependencies/tutorial006.py!}
-```
-
-////
-
-### Exceptions auslösen
+### Exceptions auslösen { #raise-exceptions }
Die Abhängigkeiten können Exceptions `raise`n, genau wie normale Abhängigkeiten:
-//// tab | Python 3.9+
+{* ../../docs_src/dependencies/tutorial006_an_py39.py hl[10,15] *}
-```Python hl_lines="10 15"
-{!> ../../../docs_src/dependencies/tutorial006_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="9 14"
-{!> ../../../docs_src/dependencies/tutorial006_an.py!}
-```
-
-////
-
-//// tab | Python 3.8 nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="8 13"
-{!> ../../../docs_src/dependencies/tutorial006.py!}
-```
-
-////
-
-### Rückgabewerte
+### Rückgabewerte { #return-values }
Und sie können Werte zurückgeben oder nicht, die Werte werden nicht verwendet.
Sie können also eine normale Abhängigkeit (die einen Wert zurückgibt), die Sie bereits an anderer Stelle verwenden, wiederverwenden, und auch wenn der Wert nicht verwendet wird, wird die Abhängigkeit ausgeführt:
-//// tab | Python 3.9+
+{* ../../docs_src/dependencies/tutorial006_an_py39.py hl[11,16] *}
-```Python hl_lines="11 16"
-{!> ../../../docs_src/dependencies/tutorial006_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="10 15"
-{!> ../../../docs_src/dependencies/tutorial006_an.py!}
-```
-
-////
-
-//// tab | Python 3.8 nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="9 14"
-{!> ../../../docs_src/dependencies/tutorial006.py!}
-```
-
-////
-
-## Abhängigkeiten für eine Gruppe von *Pfadoperationen*
+## Abhängigkeiten für eine Gruppe von *Pfadoperationen* { #dependencies-for-a-group-of-path-operations }
Wenn Sie später lesen, wie Sie größere Anwendungen strukturieren ([Größere Anwendungen – Mehrere Dateien](../../tutorial/bigger-applications.md){.internal-link target=_blank}), möglicherweise mit mehreren Dateien, lernen Sie, wie Sie einen einzelnen `dependencies`-Parameter für eine Gruppe von *Pfadoperationen* deklarieren.
-## Globale Abhängigkeiten
+## Globale Abhängigkeiten { #global-dependencies }
Als Nächstes werden wir sehen, wie man Abhängigkeiten zur gesamten `FastAPI`-Anwendung hinzufügt, sodass sie für jede *Pfadoperation* gelten.
diff --git a/docs/de/docs/tutorial/dependencies/dependencies-with-yield.md b/docs/de/docs/tutorial/dependencies/dependencies-with-yield.md
index 9c2e6dd86a..e65b073a2e 100644
--- a/docs/de/docs/tutorial/dependencies/dependencies-with-yield.md
+++ b/docs/de/docs/tutorial/dependencies/dependencies-with-yield.md
@@ -1,16 +1,16 @@
-# Abhängigkeiten mit yield
+# Abhängigkeiten mit `yield` { #dependencies-with-yield }
-FastAPI unterstützt Abhängigkeiten, die nach Abschluss einige zusätzliche Schritte ausführen.
+FastAPI unterstützt Abhängigkeiten, die nach Abschluss einige zusätzliche Schritte ausführen.
Verwenden Sie dazu `yield` statt `return` und schreiben Sie die zusätzlichen Schritte / den zusätzlichen Code danach.
-/// tip | "Tipp"
+/// tip | Tipp
Stellen Sie sicher, dass Sie `yield` nur einmal pro Abhängigkeit verwenden.
///
-/// note | "Technische Details"
+/// note | Technische Details
Jede Funktion, die dekoriert werden kann mit:
@@ -23,51 +23,43 @@ Tatsächlich verwendet FastAPI diese beiden Dekoratoren intern.
///
-## Eine Datenbank-Abhängigkeit mit `yield`.
+## Eine Datenbank-Abhängigkeit mit `yield` { #a-database-dependency-with-yield }
Sie könnten damit beispielsweise eine Datenbanksession erstellen und diese nach Abschluss schließen.
-Nur der Code vor und einschließlich der `yield`-Anweisung wird ausgeführt, bevor eine Response erzeugt wird:
+Nur der Code vor und einschließlich der `yield`-Anweisung wird ausgeführt, bevor eine Response erzeugt wird:
-```Python hl_lines="2-4"
-{!../../../docs_src/dependencies/tutorial007.py!}
-```
+{* ../../docs_src/dependencies/tutorial007.py hl[2:4] *}
Der ge`yield`ete Wert ist das, was in *Pfadoperationen* und andere Abhängigkeiten eingefügt wird:
-```Python hl_lines="4"
-{!../../../docs_src/dependencies/tutorial007.py!}
-```
+{* ../../docs_src/dependencies/tutorial007.py hl[4] *}
-Der auf die `yield`-Anweisung folgende Code wird ausgeführt, nachdem die Response gesendet wurde:
+Der auf die `yield`-Anweisung folgende Code wird nach der Response ausgeführt:
-```Python hl_lines="5-6"
-{!../../../docs_src/dependencies/tutorial007.py!}
-```
+{* ../../docs_src/dependencies/tutorial007.py hl[5:6] *}
-/// tip | "Tipp"
+/// tip | Tipp
-Sie können `async`hrone oder reguläre Funktionen verwenden.
+Sie können `async`- oder reguläre Funktionen verwenden.
**FastAPI** wird bei jeder das Richtige tun, so wie auch bei normalen Abhängigkeiten.
///
-## Eine Abhängigkeit mit `yield` und `try`.
+## Eine Abhängigkeit mit `yield` und `try` { #a-dependency-with-yield-and-try }
Wenn Sie einen `try`-Block in einer Abhängigkeit mit `yield` verwenden, empfangen Sie alle Exceptions, die bei Verwendung der Abhängigkeit geworfen wurden.
-Wenn beispielsweise ein Code irgendwann in der Mitte, in einer anderen Abhängigkeit oder in einer *Pfadoperation*, ein „Rollback“ einer Datenbanktransaktion oder einen anderen Fehler verursacht, empfangen Sie die resultierende Exception in Ihrer Abhängigkeit.
+Wenn beispielsweise ein Code irgendwann in der Mitte, in einer anderen Abhängigkeit oder in einer *Pfadoperation*, ein „Rollback“ einer Datenbanktransaktion macht oder eine andere Exception verursacht, empfangen Sie die Exception in Ihrer Abhängigkeit.
Sie können also mit `except SomeException` diese bestimmte Exception innerhalb der Abhängigkeit handhaben.
Auf die gleiche Weise können Sie `finally` verwenden, um sicherzustellen, dass die Exit-Schritte ausgeführt werden, unabhängig davon, ob eine Exception geworfen wurde oder nicht.
-```Python hl_lines="3 5"
-{!../../../docs_src/dependencies/tutorial007.py!}
-```
+{* ../../docs_src/dependencies/tutorial007.py hl[3,5] *}
-## Unterabhängigkeiten mit `yield`.
+## Unterabhängigkeiten mit `yield` { #sub-dependencies-with-yield }
Sie können Unterabhängigkeiten und „Bäume“ von Unterabhängigkeiten beliebiger Größe und Form haben, und einige oder alle davon können `yield` verwenden.
@@ -75,35 +67,7 @@ Sie können Unterabhängigkeiten und „Bäume“ von Unterabhängigkeiten belie
Beispielsweise kann `dependency_c` von `dependency_b` und `dependency_b` von `dependency_a` abhängen:
-//// tab | Python 3.9+
-
-```Python hl_lines="6 14 22"
-{!> ../../../docs_src/dependencies/tutorial008_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="5 13 21"
-{!> ../../../docs_src/dependencies/tutorial008_an.py!}
-```
-
-////
-
-//// tab | Python 3.8+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="4 12 20"
-{!> ../../../docs_src/dependencies/tutorial008.py!}
-```
-
-////
+{* ../../docs_src/dependencies/tutorial008_an_py39.py hl[6,14,22] *}
Und alle können `yield` verwenden.
@@ -111,35 +75,7 @@ In diesem Fall benötigt `dependency_c` zum Ausführen seines Exit-Codes, dass d
Und wiederum benötigt `dependency_b` den Wert von `dependency_a` (hier `dep_a` genannt) für seinen Exit-Code.
-//// tab | Python 3.9+
-
-```Python hl_lines="18-19 26-27"
-{!> ../../../docs_src/dependencies/tutorial008_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="17-18 25-26"
-{!> ../../../docs_src/dependencies/tutorial008_an.py!}
-```
-
-////
-
-//// tab | Python 3.8+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="16-17 24-25"
-{!> ../../../docs_src/dependencies/tutorial008.py!}
-```
-
-////
+{* ../../docs_src/dependencies/tutorial008_an_py39.py hl[18:19,26:27] *}
Auf die gleiche Weise könnten Sie einige Abhängigkeiten mit `yield` und einige andere Abhängigkeiten mit `return` haben, und alle können beliebig voneinander abhängen.
@@ -149,7 +85,7 @@ Sie können beliebige Kombinationen von Abhängigkeiten haben.
**FastAPI** stellt sicher, dass alles in der richtigen Reihenfolge ausgeführt wird.
-/// note | "Technische Details"
+/// note | Technische Details
Dieses funktioniert dank Pythons Kontextmanager.
@@ -157,13 +93,15 @@ Dieses funktioniert dank Pythons ../../../docs_src/dependencies/tutorial008b_an_py39.py!}
-```
+Wenn Sie Exceptions abfangen und darauf basierend eine benutzerdefinierte Response erstellen möchten, erstellen Sie einen [benutzerdefinierten Exceptionhandler](../handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank}.
-////
+## Abhängigkeiten mit `yield` und `except` { #dependencies-with-yield-and-except }
-//// tab | Python 3.8+
+Wenn Sie eine Exception mit `except` in einer Abhängigkeit mit `yield` abfangen und sie nicht erneut auslösen (oder eine neue Exception auslösen), kann FastAPI nicht feststellen, dass es eine Exception gab, genau so wie es bei normalem Python der Fall wäre:
-```Python hl_lines="17-21 30"
-{!> ../../../docs_src/dependencies/tutorial008b_an.py!}
-```
+{* ../../docs_src/dependencies/tutorial008c_an_py39.py hl[15:16] *}
-////
+In diesem Fall sieht der Client eine *HTTP 500 Internal Server Error*-Response, wie es sein sollte, da wir keine `HTTPException` oder Ähnliches auslösen, aber der Server hat **keine Logs** oder einen anderen Hinweis darauf, was der Fehler war. 😱
-//// tab | Python 3.8+ nicht annotiert
+### In Abhängigkeiten mit `yield` und `except` immer `raise` verwenden { #always-raise-in-dependencies-with-yield-and-except }
-/// tip | "Tipp"
+Wenn Sie eine Exception in einer Abhängigkeit mit `yield` abfangen, sollten Sie – sofern Sie nicht eine andere `HTTPException` oder Ähnliches auslösen – **die ursprüngliche Exception erneut auslösen**.
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
+Sie können dieselbe Exception mit `raise` erneut auslösen:
-///
+{* ../../docs_src/dependencies/tutorial008d_an_py39.py hl[17] *}
-```Python hl_lines="16-20 29"
-{!> ../../../docs_src/dependencies/tutorial008b.py!}
-```
+Jetzt erhält der Client dieselbe *HTTP 500 Internal Server Error*-Response, aber der Server enthält unseren benutzerdefinierten `InternalError` in den Logs. 😎
-////
-
-Eine Alternative zum Abfangen von Exceptions (und möglicherweise auch zum Auslösen einer weiteren `HTTPException`) besteht darin, einen [benutzerdefinierten Exceptionhandler](../handling-errors.md#benutzerdefinierte-exceptionhandler-definieren){.internal-link target=_blank} zu erstellen.
-
-## Ausführung von Abhängigkeiten mit `yield`
+## Ausführung von Abhängigkeiten mit `yield` { #execution-of-dependencies-with-yield }
Die Ausführungsreihenfolge ähnelt mehr oder weniger dem folgenden Diagramm. Die Zeit verläuft von oben nach unten. Und jede Spalte ist einer der interagierenden oder Code-ausführenden Teilnehmer.
@@ -227,22 +155,22 @@ participant tasks as Hintergrundtasks
opt Löst aus
operation -->> dep: Löst Exception aus (z. B. HTTPException)
opt Handhabt
- dep -->> dep: Kann Exception abfangen, eine neue HTTPException auslösen, andere Exceptions auslösen
- dep -->> handler: Leitet Exception automatisch weiter
+ dep -->> dep: Kann Exception abfangen, eine neue HTTPException auslösen, andere Exception auslösen
end
handler -->> client: HTTP-Error-Response
end
+
operation ->> client: Sendet Response an Client
- Note over client,operation: Response wurde gesendet, kann nicht mehr geändert werden
+ Note over client,operation: Response wurde bereits gesendet, kann nicht mehr geändert werden
opt Tasks
operation -->> tasks: Sendet Hintergrundtasks
end
opt Löst andere Exception aus
- tasks -->> tasks: Handhabt Exception im Hintergrundtask-Code
+ tasks -->> tasks: Handhabt Exceptions im Hintergrundtask-Code
end
```
-/// info
+/// info | Info
Es wird nur **eine Response** an den Client gesendet. Es kann eine Error-Response oder die Response der *Pfadoperation* sein.
@@ -250,45 +178,20 @@ Nachdem eine dieser Responses gesendet wurde, kann keine weitere Response gesend
///
-/// tip | "Tipp"
+/// tip | Tipp
-Obiges Diagramm verwendet `HTTPException`, aber Sie können auch jede andere Exception auslösen, die Sie in einer Abhängigkeit mit `yield` abfangen, oder mit einem [benutzerdefinierten Exceptionhandler](../handling-errors.md#benutzerdefinierte-exceptionhandler-definieren){.internal-link target=_blank} erstellt haben.
-
-Wenn Sie eine Exception auslösen, wird diese mit yield an die Abhängigkeiten übergeben, einschließlich `HTTPException`, und dann **erneut** an die Exceptionhandler. Wenn es für diese Exception keinen Exceptionhandler gibt, wird sie von der internen Default-`ServerErrorMiddleware` gehandhabt, was einen HTTP-Statuscode 500 zurückgibt, um den Client darüber zu informieren, dass ein Fehler auf dem Server aufgetreten ist.
+Wenn Sie in dem Code der *Pfadoperation-Funktion* irgendeine Exception auslösen, wird sie an die Abhängigkeiten mit `yield` weitergegeben, einschließlich `HTTPException`. In den meisten Fällen sollten Sie dieselbe Exception oder eine neue aus der Abhängigkeit mit `yield` erneut auslösen, um sicherzustellen, dass sie korrekt gehandhabt wird.
///
-## Abhängigkeiten mit `yield`, `HTTPException` und Hintergrundtasks
+## Abhängigkeiten mit `yield`, `HTTPException`, `except` und Hintergrundtasks { #dependencies-with-yield-httpexception-except-and-background-tasks }
-/// warning | "Achtung"
+Abhängigkeiten mit `yield` haben sich im Laufe der Zeit weiterentwickelt, um verschiedene Anwendungsfälle abzudecken und einige Probleme zu beheben.
-Sie benötigen diese technischen Details höchstwahrscheinlich nicht, Sie können diesen Abschnitt überspringen und weiter unten fortfahren.
+Wenn Sie sehen möchten, was sich in verschiedenen Versionen von FastAPI geändert hat, lesen Sie mehr dazu im fortgeschrittenen Teil, unter [Fortgeschrittene Abhängigkeiten – Abhängigkeiten mit `yield`, `HTTPException`, `except` und Hintergrundtasks](../../advanced/advanced-dependencies.md#dependencies-with-yield-httpexception-except-and-background-tasks){.internal-link target=_blank}.
+## Kontextmanager { #context-managers }
-Diese Details sind vor allem dann nützlich, wenn Sie eine Version von FastAPI vor 0.106.0 verwendet haben und Ressourcen aus Abhängigkeiten mit `yield` in Hintergrundtasks verwendet haben.
-
-///
-
-Vor FastAPI 0.106.0 war das Auslösen von Exceptions nach `yield` nicht möglich, der Exit-Code in Abhängigkeiten mit `yield` wurde ausgeführt, *nachdem* die Response gesendet wurde, die [Exceptionhandler](../handling-errors.md#benutzerdefinierte-exceptionhandler-definieren){.internal-link target=_blank} wären also bereits ausgeführt worden.
-
-Dies wurde hauptsächlich so konzipiert, damit die gleichen Objekte, die durch Abhängigkeiten ge`yield`et werden, innerhalb von Hintergrundtasks verwendet werden können, da der Exit-Code ausgeführt wird, nachdem die Hintergrundtasks abgeschlossen sind.
-
-Da dies jedoch bedeuten würde, darauf zu warten, dass die Response durch das Netzwerk reist, während eine Ressource unnötigerweise in einer Abhängigkeit mit yield gehalten wird (z. B. eine Datenbankverbindung), wurde dies in FastAPI 0.106.0 geändert.
-
-/// tip | "Tipp"
-
-Darüber hinaus handelt es sich bei einem Hintergrundtask normalerweise um einen unabhängigen Satz von Logik, der separat behandelt werden sollte, mit eigenen Ressourcen (z. B. einer eigenen Datenbankverbindung).
-
-Auf diese Weise erhalten Sie wahrscheinlich saubereren Code.
-
-///
-
-Wenn Sie sich früher auf dieses Verhalten verlassen haben, sollten Sie jetzt die Ressourcen für Hintergrundtasks innerhalb des Hintergrundtasks selbst erstellen und intern nur Daten verwenden, die nicht von den Ressourcen von Abhängigkeiten mit `yield` abhängen.
-
-Anstatt beispielsweise dieselbe Datenbanksitzung zu verwenden, würden Sie eine neue Datenbanksitzung innerhalb des Hintergrundtasks erstellen und die Objekte mithilfe dieser neuen Sitzung aus der Datenbank abrufen. Und anstatt das Objekt aus der Datenbank als Parameter an die Hintergrundtask-Funktion zu übergeben, würden Sie die ID dieses Objekts übergeben und das Objekt dann innerhalb der Hintergrundtask-Funktion erneut laden.
-
-## Kontextmanager
-
-### Was sind „Kontextmanager“
+### Was sind „Kontextmanager“ { #what-are-context-managers }
„Kontextmanager“ (Englisch „Context Manager“) sind bestimmte Python-Objekte, die Sie in einer `with`-Anweisung verwenden können.
@@ -302,13 +205,13 @@ with open("./somefile.txt") as f:
Im Hintergrund erstellt das `open("./somefile.txt")` ein Objekt, das als „Kontextmanager“ bezeichnet wird.
-Dieser stellt sicher dass, wenn der `with`-Block beendet ist, die Datei geschlossen wird, auch wenn Exceptions geworfen wurden.
+Dieser stellt sicher, dass, wenn der `with`-Block beendet ist, die Datei geschlossen wird, auch wenn Exceptions geworfen wurden.
Wenn Sie eine Abhängigkeit mit `yield` erstellen, erstellt **FastAPI** dafür intern einen Kontextmanager und kombiniert ihn mit einigen anderen zugehörigen Tools.
-### Kontextmanager in Abhängigkeiten mit `yield` verwenden
+### Kontextmanager in Abhängigkeiten mit `yield` verwenden { #using-context-managers-in-dependencies-with-yield }
-/// warning | "Achtung"
+/// warning | Achtung
Dies ist mehr oder weniger eine „fortgeschrittene“ Idee.
@@ -320,11 +223,9 @@ In Python können Sie Kontextmanager erstellen, indem Sie ../../../docs_src/dependencies/tutorial012_an_py39.py!}
-```
+Und alle Ideen aus dem Abschnitt über das [Hinzufügen von `dependencies` zu den *Pfadoperation-Dekoratoren*](dependencies-in-path-operation-decorators.md){.internal-link target=_blank} gelten weiterhin, aber in diesem Fall für alle *Pfadoperationen* in der App.
-////
+## Abhängigkeiten für Gruppen von *Pfadoperationen* { #dependencies-for-groups-of-path-operations }
-//// tab | Python 3.8+
-
-```Python hl_lines="16"
-{!> ../../../docs_src/dependencies/tutorial012_an.py!}
-```
-
-////
-
-//// tab | Python 3.8 nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="15"
-{!> ../../../docs_src/dependencies/tutorial012.py!}
-```
-
-////
-
-Und alle Ideen aus dem Abschnitt über das [Hinzufügen von `dependencies` zu den *Pfadoperation-Dekoratoren*](dependencies-in-path-operation-decorators.md){.internal-link target=_blank} gelten weiterhin, aber in diesem Fall für alle *Pfadoperationen* in der Anwendung.
-
-## Abhängigkeiten für Gruppen von *Pfadoperationen*
-
-Wenn Sie später lesen, wie Sie größere Anwendungen strukturieren ([Bigger Applications - Multiple Files](../../tutorial/bigger-applications.md){.internal-link target=_blank}), möglicherweise mit mehreren Dateien, lernen Sie, wie Sie einen einzelnen `dependencies`-Parameter für eine Gruppe von *Pfadoperationen* deklarieren.
+Wenn Sie später lesen, wie Sie größere Anwendungen strukturieren ([Größere Anwendungen – mehrere Dateien](../../tutorial/bigger-applications.md){.internal-link target=_blank}), möglicherweise mit mehreren Dateien, lernen Sie, wie Sie einen einzelnen `dependencies`-Parameter für eine Gruppe von *Pfadoperationen* deklarieren.
diff --git a/docs/de/docs/tutorial/dependencies/index.md b/docs/de/docs/tutorial/dependencies/index.md
index f7d9ed5109..cb6a612133 100644
--- a/docs/de/docs/tutorial/dependencies/index.md
+++ b/docs/de/docs/tutorial/dependencies/index.md
@@ -1,10 +1,10 @@
-# Abhängigkeiten
+# Abhängigkeiten { #dependencies }
-**FastAPI** hat ein sehr mächtiges, aber intuitives **Dependency Injection** System.
+**FastAPI** hat ein sehr mächtiges, aber intuitives **Dependency Injection** System.
Es ist so konzipiert, sehr einfach zu verwenden zu sein und es jedem Entwickler sehr leicht zu machen, andere Komponenten mit **FastAPI** zu integrieren.
-## Was ist „Dependency Injection“
+## Was ist „Dependency Injection“ { #what-is-dependency-injection }
**„Dependency Injection“** bedeutet in der Programmierung, dass es für Ihren Code (in diesem Fall Ihre *Pfadoperation-Funktionen*) eine Möglichkeit gibt, Dinge zu deklarieren, die er verwenden möchte und die er zum Funktionieren benötigt: „Abhängigkeiten“ – „Dependencies“.
@@ -19,68 +19,18 @@ Das ist sehr nützlich, wenn Sie:
All dies, während Sie Codeverdoppelung minimieren.
-## Erste Schritte
+## Erste Schritte { #first-steps }
Sehen wir uns ein sehr einfaches Beispiel an. Es ist so einfach, dass es vorerst nicht sehr nützlich ist.
Aber so können wir uns besser auf die Funktionsweise des **Dependency Injection** Systems konzentrieren.
-### Erstellen Sie eine Abhängigkeit („Dependable“)
+### Eine Abhängigkeit erstellen, oder „Dependable“ { #create-a-dependency-or-dependable }
-Konzentrieren wir uns zunächst auf die Abhängigkeit - die Dependency.
+Konzentrieren wir uns zunächst auf die Abhängigkeit – die Dependency.
Es handelt sich einfach um eine Funktion, die die gleichen Parameter entgegennimmt wie eine *Pfadoperation-Funktion*:
-//// tab | Python 3.10+
-
-```Python hl_lines="8-9"
-{!> ../../../docs_src/dependencies/tutorial001_an_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="8-11"
-{!> ../../../docs_src/dependencies/tutorial001_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="9-12"
-{!> ../../../docs_src/dependencies/tutorial001_an.py!}
-```
-
-////
-
-//// tab | Python 3.10+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="6-7"
-{!> ../../../docs_src/dependencies/tutorial001_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="8-11"
-{!> ../../../docs_src/dependencies/tutorial001.py!}
-```
-
-////
+{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[8:9] *}
Das war's schon.
@@ -98,127 +48,27 @@ In diesem Fall erwartet diese Abhängigkeit:
* Einen optionalen Query-Parameter `skip`, der ein `int` ist und standardmäßig `0` ist.
* Einen optionalen Query-Parameter `limit`, der ein `int` ist und standardmäßig `100` ist.
-Und dann wird einfach ein `dict` zurückgegeben, welches diese Werte enthält.
+Und dann wird einfach ein `dict` zurückgegeben, welches diese Werte enthält.
-/// info
+/// info | Info
FastAPI unterstützt (und empfiehlt die Verwendung von) `Annotated` seit Version 0.95.0.
Wenn Sie eine ältere Version haben, werden Sie Fehler angezeigt bekommen, wenn Sie versuchen, `Annotated` zu verwenden.
-Bitte [aktualisieren Sie FastAPI](../../deployment/versions.md#upgrade-der-fastapi-versionen){.internal-link target=_blank} daher mindestens zu Version 0.95.1, bevor Sie `Annotated` verwenden.
+Bitte [aktualisieren Sie FastAPI](../../deployment/versions.md#upgrading-the-fastapi-versions){.internal-link target=_blank} daher mindestens zu Version 0.95.1, bevor Sie `Annotated` verwenden.
///
-### `Depends` importieren
+### `Depends` importieren { #import-depends }
-//// tab | Python 3.10+
+{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[3] *}
-```Python hl_lines="3"
-{!> ../../../docs_src/dependencies/tutorial001_an_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="3"
-{!> ../../../docs_src/dependencies/tutorial001_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="3"
-{!> ../../../docs_src/dependencies/tutorial001_an.py!}
-```
-
-////
-
-//// tab | Python 3.10+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="1"
-{!> ../../../docs_src/dependencies/tutorial001_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="3"
-{!> ../../../docs_src/dependencies/tutorial001.py!}
-```
-
-////
-
-### Deklarieren der Abhängigkeit im „Dependant“
+### Die Abhängigkeit im „Dependant“ deklarieren { #declare-the-dependency-in-the-dependant }
So wie auch `Body`, `Query`, usw., verwenden Sie `Depends` mit den Parametern Ihrer *Pfadoperation-Funktion*:
-//// tab | Python 3.10+
-
-```Python hl_lines="13 18"
-{!> ../../../docs_src/dependencies/tutorial001_an_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="15 20"
-{!> ../../../docs_src/dependencies/tutorial001_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="16 21"
-{!> ../../../docs_src/dependencies/tutorial001_an.py!}
-```
-
-////
-
-//// tab | Python 3.10+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="11 16"
-{!> ../../../docs_src/dependencies/tutorial001_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="15 20"
-{!> ../../../docs_src/dependencies/tutorial001.py!}
-```
-
-////
+{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[13,18] *}
Obwohl Sie `Depends` in den Parametern Ihrer Funktion genauso verwenden wie `Body`, `Query`, usw., funktioniert `Depends` etwas anders.
@@ -230,13 +80,13 @@ Sie **rufen diese nicht direkt auf** (fügen Sie am Ende keine Klammern hinzu),
Und diese Funktion akzeptiert Parameter auf die gleiche Weise wie *Pfadoperation-Funktionen*.
-/// tip | "Tipp"
+/// tip | Tipp
Im nächsten Kapitel erfahren Sie, welche anderen „Dinge“, außer Funktionen, Sie als Abhängigkeiten verwenden können.
///
-Immer wenn ein neuer Request eintrifft, kümmert sich **FastAPI** darum:
+Immer wenn ein neuer Request eintrifft, kümmert sich **FastAPI** darum:
* Ihre Abhängigkeitsfunktion („Dependable“) mit den richtigen Parametern aufzurufen.
* Sich das Ergebnis von dieser Funktion zu holen.
@@ -255,7 +105,7 @@ common_parameters --> read_users
Auf diese Weise schreiben Sie gemeinsam genutzten Code nur einmal, und **FastAPI** kümmert sich darum, ihn für Ihre *Pfadoperationen* aufzurufen.
-/// check
+/// check | Testen
Beachten Sie, dass Sie keine spezielle Klasse erstellen und diese irgendwo an **FastAPI** übergeben müssen, um sie zu „registrieren“ oder so ähnlich.
@@ -263,7 +113,7 @@ Sie übergeben es einfach an `Depends` und **FastAPI** weiß, wie der Rest erled
///
-## `Annotated`-Abhängigkeiten wiederverwenden
+## `Annotated`-Abhängigkeiten wiederverwenden { #share-annotated-dependencies }
In den Beispielen oben sehen Sie, dass es ein kleines bisschen **Codeverdoppelung** gibt.
@@ -275,31 +125,9 @@ commons: Annotated[dict, Depends(common_parameters)]
Da wir jedoch `Annotated` verwenden, können wir diesen `Annotated`-Wert in einer Variablen speichern und an mehreren Stellen verwenden:
-//// tab | Python 3.10+
+{* ../../docs_src/dependencies/tutorial001_02_an_py310.py hl[12,16,21] *}
-```Python hl_lines="12 16 21"
-{!> ../../../docs_src/dependencies/tutorial001_02_an_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="14 18 23"
-{!> ../../../docs_src/dependencies/tutorial001_02_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="15 19 24"
-{!> ../../../docs_src/dependencies/tutorial001_02_an.py!}
-```
-
-////
-
-/// tip | "Tipp"
+/// tip | Tipp
Das ist schlicht Standard-Python, es wird als „Typalias“ bezeichnet und ist eigentlich nicht **FastAPI**-spezifisch.
@@ -311,7 +139,7 @@ Die Abhängigkeiten funktionieren weiterhin wie erwartet, und das **Beste daran*
Das ist besonders nützlich, wenn Sie es in einer **großen Codebasis** verwenden, in der Sie in **vielen *Pfadoperationen*** immer wieder **dieselben Abhängigkeiten** verwenden.
-## `async` oder nicht `async`
+## `async` oder nicht `async` { #to-async-or-not-to-async }
Da Abhängigkeiten auch von **FastAPI** aufgerufen werden (so wie Ihre *Pfadoperation-Funktionen*), gelten beim Definieren Ihrer Funktionen die gleichen Regeln.
@@ -321,13 +149,13 @@ Und Sie können Abhängigkeiten mit `async def` innerhalb normaler `def`-*Pfadop
Es spielt keine Rolle. **FastAPI** weiß, was zu tun ist.
-/// note | "Hinweis"
+/// note | Hinweis
-Wenn Ihnen das nichts sagt, lesen Sie den [Async: *„In Eile?“*](../../async.md#in-eile){.internal-link target=_blank}-Abschnitt über `async` und `await` in der Dokumentation.
+Wenn Ihnen das nichts sagt, lesen Sie den [Async: *„In Eile?“*](../../async.md#in-a-hurry){.internal-link target=_blank}-Abschnitt über `async` und `await` in der Dokumentation.
///
-## Integriert in OpenAPI
+## Integriert in OpenAPI { #integrated-with-openapi }
Alle Requestdeklarationen, -validierungen und -anforderungen Ihrer Abhängigkeiten (und Unterabhängigkeiten) werden in dasselbe OpenAPI-Schema integriert.
@@ -335,9 +163,9 @@ Die interaktive Dokumentation enthält also auch alle Informationen aus diesen A
-## Einfache Verwendung
+## Einfache Verwendung { #simple-usage }
-Näher betrachtet, werden *Pfadoperation-Funktionen* deklariert, um verwendet zu werden, wann immer ein *Pfad* und eine *Operation* übereinstimmen, und dann kümmert sich **FastAPI** darum, die Funktion mit den richtigen Parametern aufzurufen, die Daten aus der Anfrage extrahierend.
+Näher betrachtet, werden *Pfadoperation-Funktionen* deklariert, um verwendet zu werden, wann immer ein *Pfad* und eine *Operation* übereinstimmen, und dann kümmert sich **FastAPI** darum, die Funktion mit den richtigen Parametern aufzurufen, die Daten aus dem Request extrahierend.
Tatsächlich funktionieren alle (oder die meisten) Webframeworks auf die gleiche Weise.
@@ -353,7 +181,7 @@ Andere gebräuchliche Begriffe für dieselbe Idee der „Abhängigkeitsinjektion
* Injectables
* Komponenten
-## **FastAPI**-Plugins
+## **FastAPI**-Plugins { #fastapi-plug-ins }
Integrationen und „Plugins“ können mit dem **Dependency Injection** System erstellt werden. Aber tatsächlich besteht **keine Notwendigkeit, „Plugins“ zu erstellen**, da es durch die Verwendung von Abhängigkeiten möglich ist, eine unendliche Anzahl von Integrationen und Interaktionen zu deklarieren, die dann für Ihre *Pfadoperation-Funktionen* verfügbar sind.
@@ -361,7 +189,7 @@ Und Abhängigkeiten können auf sehr einfache und intuitive Weise erstellt werde
Beispiele hierfür finden Sie in den nächsten Kapiteln zu relationalen und NoSQL-Datenbanken, Sicherheit usw.
-## **FastAPI**-Kompatibilität
+## **FastAPI**-Kompatibilität { #fastapi-compatibility }
Die Einfachheit des Dependency Injection Systems macht **FastAPI** kompatibel mit:
@@ -371,10 +199,10 @@ Die Einfachheit des Dependency Injection Systems macht **FastAPI** kompatibel mi
* externen APIs
* Authentifizierungs- und Autorisierungssystemen
* API-Nutzungs-Überwachungssystemen
-* Responsedaten-Injektionssystemen
+* Responsedaten-Injektionssystemen
* usw.
-## Einfach und leistungsstark
+## Einfach und leistungsstark { #simple-and-powerful }
Obwohl das hierarchische Dependency Injection System sehr einfach zu definieren und zu verwenden ist, ist es dennoch sehr mächtig.
@@ -414,7 +242,7 @@ admin_user --> activate_user
paying_user --> pro_items
```
-## Integriert mit **OpenAPI**
+## Integriert mit **OpenAPI** { #integrated-with-openapi_1 }
Alle diese Abhängigkeiten, während sie ihre Anforderungen deklarieren, fügen auch Parameter, Validierungen, usw. zu Ihren *Pfadoperationen* hinzu.
diff --git a/docs/de/docs/tutorial/dependencies/sub-dependencies.md b/docs/de/docs/tutorial/dependencies/sub-dependencies.md
index 12664a8cd2..061952f921 100644
--- a/docs/de/docs/tutorial/dependencies/sub-dependencies.md
+++ b/docs/de/docs/tutorial/dependencies/sub-dependencies.md
@@ -1,4 +1,4 @@
-# Unterabhängigkeiten
+# Unterabhängigkeiten { #sub-dependencies }
Sie können Abhängigkeiten erstellen, die **Unterabhängigkeiten** haben.
@@ -6,121 +6,21 @@ Diese können so **tief** verschachtelt sein, wie nötig.
**FastAPI** kümmert sich darum, sie aufzulösen.
-## Erste Abhängigkeit, „Dependable“
+## Erste Abhängigkeit, „Dependable“ { #first-dependency-dependable }
Sie könnten eine erste Abhängigkeit („Dependable“) wie folgt erstellen:
-//// tab | Python 3.10+
+{* ../../docs_src/dependencies/tutorial005_an_py310.py hl[8:9] *}
-```Python hl_lines="8-9"
-{!> ../../../docs_src/dependencies/tutorial005_an_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="8-9"
-{!> ../../../docs_src/dependencies/tutorial005_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="9-10"
-{!> ../../../docs_src/dependencies/tutorial005_an.py!}
-```
-
-////
-
-//// tab | Python 3.10 nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="6-7"
-{!> ../../../docs_src/dependencies/tutorial005_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8 nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="8-9"
-{!> ../../../docs_src/dependencies/tutorial005.py!}
-```
-
-////
-
-Diese deklariert einen optionalen Abfrageparameter `q` vom Typ `str` und gibt ihn dann einfach zurück.
+Diese deklariert einen optionalen Query-Parameter `q` vom Typ `str` und gibt ihn dann einfach zurück.
Das ist recht einfach (nicht sehr nützlich), hilft uns aber dabei, uns auf die Funktionsweise der Unterabhängigkeiten zu konzentrieren.
-## Zweite Abhängigkeit, „Dependable“ und „Dependant“
+## Zweite Abhängigkeit, „Dependable“ und „Dependant“ { #second-dependency-dependable-and-dependant }
Dann können Sie eine weitere Abhängigkeitsfunktion (ein „Dependable“) erstellen, die gleichzeitig eine eigene Abhängigkeit deklariert (also auch ein „Dependant“ ist):
-//// tab | Python 3.10+
-
-```Python hl_lines="13"
-{!> ../../../docs_src/dependencies/tutorial005_an_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="13"
-{!> ../../../docs_src/dependencies/tutorial005_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="14"
-{!> ../../../docs_src/dependencies/tutorial005_an.py!}
-```
-
-////
-
-//// tab | Python 3.10 nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="11"
-{!> ../../../docs_src/dependencies/tutorial005_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8 nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="13"
-{!> ../../../docs_src/dependencies/tutorial005.py!}
-```
-
-////
+{* ../../docs_src/dependencies/tutorial005_an_py310.py hl[13] *}
Betrachten wir die deklarierten Parameter:
@@ -129,67 +29,17 @@ Betrachten wir die deklarierten Parameter:
* Sie deklariert außerdem ein optionales `last_query`-Cookie, ein `str`.
* Wenn der Benutzer keine Query `q` übermittelt hat, verwenden wir die zuletzt übermittelte Query, die wir zuvor in einem Cookie gespeichert haben.
-## Die Abhängigkeit verwenden
+## Die Abhängigkeit verwenden { #use-the-dependency }
Diese Abhängigkeit verwenden wir nun wie folgt:
-//// tab | Python 3.10+
+{* ../../docs_src/dependencies/tutorial005_an_py310.py hl[23] *}
-```Python hl_lines="23"
-{!> ../../../docs_src/dependencies/tutorial005_an_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="23"
-{!> ../../../docs_src/dependencies/tutorial005_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="24"
-{!> ../../../docs_src/dependencies/tutorial005_an.py!}
-```
-
-////
-
-//// tab | Python 3.10 nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="19"
-{!> ../../../docs_src/dependencies/tutorial005_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8 nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="22"
-{!> ../../../docs_src/dependencies/tutorial005.py!}
-```
-
-////
-
-/// info
+/// info | Info
Beachten Sie, dass wir in der *Pfadoperation-Funktion* nur eine einzige Abhängigkeit deklarieren, den `query_or_cookie_extractor`.
-Aber **FastAPI** wird wissen, dass es zuerst `query_extractor` auflösen muss, um dessen Resultat `query_or_cookie_extractor` zu übergeben, wenn dieses aufgerufen wird.
+Aber **FastAPI** wird wissen, dass es zuerst `query_extractor` auflösen muss, um dessen Resultat an `query_or_cookie_extractor` zu übergeben, wenn dieses aufgerufen wird.
///
@@ -204,13 +54,13 @@ read_query["/items/"]
query_extractor --> query_or_cookie_extractor --> read_query
```
-## Dieselbe Abhängigkeit mehrmals verwenden
+## Dieselbe Abhängigkeit mehrmals verwenden { #using-the-same-dependency-multiple-times }
-Wenn eine Ihrer Abhängigkeiten mehrmals für dieselbe *Pfadoperation* deklariert wird, beispielsweise wenn mehrere Abhängigkeiten eine gemeinsame Unterabhängigkeit haben, wird **FastAPI** diese Unterabhängigkeit nur einmal pro Request aufrufen.
+Wenn eine Ihrer Abhängigkeiten mehrmals für dieselbe *Pfadoperation* deklariert wird, beispielsweise wenn mehrere Abhängigkeiten eine gemeinsame Unterabhängigkeit haben, wird **FastAPI** diese Unterabhängigkeit nur einmal pro Request aufrufen.
Und es speichert den zurückgegebenen Wert in einem „Cache“ und übergibt diesen gecachten Wert an alle „Dependanten“, die ihn in diesem spezifischen Request benötigen, anstatt die Abhängigkeit mehrmals für denselben Request aufzurufen.
-In einem fortgeschrittenen Szenario, bei dem Sie wissen, dass die Abhängigkeit bei jedem Schritt (möglicherweise mehrmals) in derselben Anfrage aufgerufen werden muss, anstatt den zwischengespeicherten Wert zu verwenden, können Sie den Parameter `use_cache=False` festlegen, wenn Sie `Depends` verwenden:
+In einem fortgeschrittenen Szenario, bei dem Sie wissen, dass die Abhängigkeit bei jedem Schritt (möglicherweise mehrmals) in demselben Request aufgerufen werden muss, anstatt den zwischengespeicherten Wert zu verwenden, können Sie den Parameter `use_cache=False` festlegen, wenn Sie `Depends` verwenden:
//// tab | Python 3.8+
@@ -223,7 +73,7 @@ async def needy_dependency(fresh_value: Annotated[str, Depends(get_value, use_ca
//// tab | Python 3.8+ nicht annotiert
-/// tip | "Tipp"
+/// tip | Tipp
Bevorzugen Sie die `Annotated`-Version, falls möglich.
@@ -236,7 +86,7 @@ async def needy_dependency(fresh_value: str = Depends(get_value, use_cache=False
////
-## Zusammenfassung
+## Zusammenfassung { #recap }
Abgesehen von all den ausgefallenen Wörtern, die hier verwendet werden, ist das **Dependency Injection**-System recht simpel.
@@ -244,7 +94,7 @@ Einfach Funktionen, die genauso aussehen wie *Pfadoperation-Funktionen*.
Dennoch ist es sehr mächtig und ermöglicht Ihnen die Deklaration beliebig tief verschachtelter Abhängigkeits-„Graphen“ (Bäume).
-/// tip | "Tipp"
+/// tip | Tipp
All dies scheint angesichts dieser einfachen Beispiele möglicherweise nicht so nützlich zu sein.
diff --git a/docs/de/docs/tutorial/encoder.md b/docs/de/docs/tutorial/encoder.md
index 38a881b4f6..25dc6fa184 100644
--- a/docs/de/docs/tutorial/encoder.md
+++ b/docs/de/docs/tutorial/encoder.md
@@ -1,12 +1,12 @@
-# JSON-kompatibler Encoder
+# JSON-kompatibler Encoder { #json-compatible-encoder }
-Es gibt Fälle, da möchten Sie einen Datentyp (etwa ein Pydantic-Modell) in etwas konvertieren, das kompatibel mit JSON ist (etwa ein `dict`, eine `list`e, usw.).
+Es gibt Fälle, da möchten Sie einen Datentyp (etwa ein Pydantic-Modell) in etwas konvertieren, das kompatibel mit JSON ist (etwa ein `dict`, eine `list`, usw.).
Zum Beispiel, wenn Sie es in einer Datenbank speichern möchten.
Dafür bietet **FastAPI** eine Funktion `jsonable_encoder()`.
-## `jsonable_encoder` verwenden
+## `jsonable_encoder` verwenden { #using-the-jsonable-encoder }
Stellen wir uns vor, Sie haben eine Datenbank `fake_db`, die nur JSON-kompatible Daten entgegennimmt.
@@ -20,21 +20,7 @@ Sie können für diese Fälle `jsonable_encoder` verwenden.
Es nimmt ein Objekt entgegen, wie etwa ein Pydantic-Modell, und gibt eine JSON-kompatible Version zurück:
-//// tab | Python 3.10+
-
-```Python hl_lines="4 21"
-{!> ../../../docs_src/encoder/tutorial001_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="5 22"
-{!> ../../../docs_src/encoder/tutorial001.py!}
-```
-
-////
+{* ../../docs_src/encoder/tutorial001_py310.py hl[4,21] *}
In diesem Beispiel wird das Pydantic-Modell in ein `dict`, und das `datetime`-Objekt in ein `str` konvertiert.
@@ -42,7 +28,7 @@ Das Resultat dieses Aufrufs ist etwas, das mit Pythons Standard-Requests.
+* Datenkonvertierung für Response-Daten.
* Datenvalidierung.
* Automatische Annotation und Dokumentation.
-## Andere Datentypen
+## Andere Datentypen { #other-data-types }
Hier sind einige der zusätzlichen Datentypen, die Sie verwenden können:
@@ -36,11 +36,11 @@ Hier sind einige der zusätzlichen Datentypen, die Sie verwenden können:
* `datetime.timedelta`:
* Ein Python-`datetime.timedelta`.
* Wird in Requests und Responses als `float` der Gesamtsekunden dargestellt.
- * Pydantic ermöglicht auch die Darstellung als „ISO 8601 Zeitdifferenz-Kodierung“, Weitere Informationen finden Sie in der Dokumentation.
+ * Pydantic ermöglicht auch die Darstellung als „ISO 8601 Zeitdifferenz-Kodierung“, siehe die Dokumentation für weitere Informationen.
* `frozenset`:
* Wird in Requests und Responses wie ein `set` behandelt:
* Bei Requests wird eine Liste gelesen, Duplikate entfernt und in ein `set` umgewandelt.
- * Bei Responses wird das `set` in eine `list`e umgewandelt.
+ * Bei Responses wird das `set` in eine `list` umgewandelt.
* Das generierte Schema zeigt an, dass die `set`-Werte eindeutig sind (unter Verwendung von JSON Schemas `uniqueItems`).
* `bytes`:
* Standard-Python-`bytes`.
@@ -49,114 +49,14 @@ Hier sind einige der zusätzlichen Datentypen, die Sie verwenden können:
* `Decimal`:
* Standard-Python-`Decimal`.
* In Requests und Responses wird es wie ein `float` behandelt.
-* Sie können alle gültigen Pydantic-Datentypen hier überprüfen: Pydantic data types.
+* Sie können alle gültigen Pydantic-Datentypen hier überprüfen: Pydantic-Datentypen.
-## Beispiel
+## Beispiel { #example }
Hier ist ein Beispiel für eine *Pfadoperation* mit Parametern, die einige der oben genannten Typen verwenden.
-//// tab | Python 3.10+
-
-```Python hl_lines="1 3 12-16"
-{!> ../../../docs_src/extra_data_types/tutorial001_an_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="1 3 12-16"
-{!> ../../../docs_src/extra_data_types/tutorial001_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="1 3 13-17"
-{!> ../../../docs_src/extra_data_types/tutorial001_an.py!}
-```
-
-////
-
-//// tab | Python 3.10+ nicht annotiert
-
-/// tip
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="1 2 11-15"
-{!> ../../../docs_src/extra_data_types/tutorial001_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+ nicht annotiert
-
-/// tip
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="1 2 12-16"
-{!> ../../../docs_src/extra_data_types/tutorial001.py!}
-```
-
-////
+{* ../../docs_src/extra_data_types/tutorial001_an_py310.py hl[1,3,12:16] *}
Beachten Sie, dass die Parameter innerhalb der Funktion ihren natürlichen Datentyp haben und Sie beispielsweise normale Datumsmanipulationen durchführen können, wie zum Beispiel:
-//// tab | Python 3.10+
-
-```Python hl_lines="18-19"
-{!> ../../../docs_src/extra_data_types/tutorial001_an_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="18-19"
-{!> ../../../docs_src/extra_data_types/tutorial001_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="19-20"
-{!> ../../../docs_src/extra_data_types/tutorial001_an.py!}
-```
-
-////
-
-//// tab | Python 3.10+ nicht annotiert
-
-/// tip
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="17-18"
-{!> ../../../docs_src/extra_data_types/tutorial001_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+ nicht annotiert
-
-/// tip
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="18-19"
-{!> ../../../docs_src/extra_data_types/tutorial001.py!}
-```
-
-////
+{* ../../docs_src/extra_data_types/tutorial001_an_py310.py hl[18:19] *}
diff --git a/docs/de/docs/tutorial/extra-models.md b/docs/de/docs/tutorial/extra-models.md
index cfd0230eb2..967e8535b8 100644
--- a/docs/de/docs/tutorial/extra-models.md
+++ b/docs/de/docs/tutorial/extra-models.md
@@ -1,56 +1,42 @@
-# Extramodelle
+# Extramodelle { #extra-models }
-Fahren wir beim letzten Beispiel fort. Es gibt normalerweise mehrere zusammengehörende Modelle.
+Im Anschluss an das vorherige Beispiel ist es üblich, mehr als ein zusammenhängendes Modell zu haben.
-Insbesondere Benutzermodelle, denn:
+Dies gilt insbesondere für Benutzermodelle, denn:
-* Das **hereinkommende Modell** sollte ein Passwort haben können.
-* Das **herausgehende Modell** sollte kein Passwort haben.
-* Das **Datenbankmodell** sollte wahrscheinlich ein gehashtes Passwort haben.
+* Das **Eingabemodell** muss ein Passwort enthalten können.
+* Das **Ausgabemodell** sollte kein Passwort haben.
+* Das **Datenbankmodell** müsste wahrscheinlich ein gehashtes Passwort haben.
-/// danger | "Gefahr"
+/// danger | Gefahr
-Speichern Sie niemals das Klartext-Passwort eines Benutzers. Speichern Sie immer den „sicheren Hash“, den Sie verifizieren können.
+Speichern Sie niemals das Klartextpasswort eines Benutzers. Speichern Sie immer einen „sicheren Hash“, den Sie dann verifizieren können.
-Falls Ihnen das nichts sagt, in den [Sicherheits-Kapiteln](security/simple-oauth2.md#passwort-hashing){.internal-link target=_blank} werden Sie lernen, was ein „Passwort-Hash“ ist.
+Wenn Sie nicht wissen, was das ist, werden Sie in den [Sicherheitskapiteln](security/simple-oauth2.md#password-hashing){.internal-link target=_blank} lernen, was ein „Passworthash“ ist.
///
-## Mehrere Modelle
+## Mehrere Modelle { #multiple-models }
-Hier der generelle Weg, wie die Modelle mit ihren Passwort-Feldern aussehen könnten, und an welchen Orten sie verwendet werden würden.
+Hier ist eine allgemeine Idee, wie die Modelle mit ihren Passwortfeldern aussehen könnten und an welchen Stellen sie verwendet werden:
-//// tab | Python 3.10+
+{* ../../docs_src/extra_models/tutorial001_py310.py hl[7,9,14,20,22,27:28,31:33,38:39] *}
-```Python hl_lines="7 9 14 20 22 27-28 31-33 38-39"
-{!> ../../../docs_src/extra_models/tutorial001_py310.py!}
-```
+/// info | Info
-////
+In Pydantic v1 hieß die Methode `.dict()`, in Pydantic v2 wurde sie deprecatet (aber weiterhin unterstützt) und in `.model_dump()` umbenannt.
-//// tab | Python 3.8+
-
-```Python hl_lines="9 11 16 22 24 29-30 33-35 40-41"
-{!> ../../../docs_src/extra_models/tutorial001.py!}
-```
-
-////
-
-/// info
-
-In Pydantic v1 hieß diese Methode `.dict()`, in Pydantic v2 wurde sie deprecated (aber immer noch unterstützt) und in `.model_dump()` umbenannt.
-
-Die Beispiele hier verwenden `.dict()` für die Kompatibilität mit Pydantic v1, Sie sollten jedoch stattdessen `.model_dump()` verwenden, wenn Sie Pydantic v2 verwenden können.
+Die Beispiele hier verwenden `.dict()` für die Kompatibilität mit Pydantic v1, aber Sie sollten `.model_dump()` verwenden, wenn Sie Pydantic v2 verwenden können.
///
-### Über `**user_in.dict()`
+### Über `**user_in.dict()` { #about-user-in-dict }
-#### Pydantic's `.dict()`
+#### Die `.dict()`-Methode von Pydantic { #pydantics-dict }
`user_in` ist ein Pydantic-Modell der Klasse `UserIn`.
-Pydantic-Modelle haben eine `.dict()`-Methode, die ein `dict` mit den Daten des Modells zurückgibt.
+Pydantic-Modelle haben eine `.dict()`-Methode, die ein `dict` mit den Daten des Modells zurückgibt.
Wenn wir also ein Pydantic-Objekt `user_in` erstellen, etwa so:
@@ -58,21 +44,21 @@ Wenn wir also ein Pydantic-Objekt `user_in` erstellen, etwa so:
user_in = UserIn(username="john", password="secret", email="john.doe@example.com")
```
-und wir rufen seine `.dict()`-Methode auf:
+und dann aufrufen:
```Python
user_dict = user_in.dict()
```
-dann haben wir jetzt in der Variable `user_dict` ein `dict` mit den gleichen Daten (es ist ein `dict` statt eines Pydantic-Modellobjekts).
+haben wir jetzt ein `dict` mit den Daten in der Variablen `user_dict` (es ist ein `dict` statt eines Pydantic-Modellobjekts).
-Wenn wir es ausgeben:
+Und wenn wir aufrufen:
```Python
print(user_dict)
```
-bekommen wir ein Python-`dict`:
+würden wir ein Python-`dict` erhalten mit:
```Python
{
@@ -83,17 +69,17 @@ bekommen wir ein Python-`dict`:
}
```
-#### Ein `dict` entpacken
+#### Ein `dict` entpacken { #unpacking-a-dict }
-Wenn wir ein `dict` wie `user_dict` nehmen, und es einer Funktion (oder Klassenmethode) mittels `**user_dict` übergeben, wird Python es „entpacken“. Es wird die Schlüssel und Werte von `user_dict` direkt als Schlüsselwort-Argumente übergeben.
+Wenn wir ein `dict` wie `user_dict` nehmen und es einer Funktion (oder Klasse) mit `**user_dict` übergeben, wird Python es „entpacken“. Es wird die Schlüssel und Werte von `user_dict` direkt als Schlüsselwort-Argumente übergeben.
-Wenn wir also das `user_dict` von oben nehmen und schreiben:
+Setzen wir also das `user_dict` von oben ein:
```Python
UserInDB(**user_dict)
```
-dann ist das ungefähr äquivalent zu:
+so ist das äquivalent zu:
```Python
UserInDB(
@@ -104,7 +90,7 @@ UserInDB(
)
```
-Oder, präziser, `user_dict` wird direkt verwendet, welche Werte es auch immer haben mag:
+Oder genauer gesagt, dazu, `user_dict` direkt zu verwenden, mit welchen Inhalten es auch immer in der Zukunft haben mag:
```Python
UserInDB(
@@ -115,34 +101,34 @@ UserInDB(
)
```
-#### Ein Pydantic-Modell aus den Inhalten eines anderen erstellen.
+#### Ein Pydantic-Modell aus dem Inhalt eines anderen { #a-pydantic-model-from-the-contents-of-another }
-Da wir in obigem Beispiel `user_dict` mittels `user_in.dict()` erzeugt haben, ist dieser Code:
+Da wir im obigen Beispiel `user_dict` von `user_in.dict()` bekommen haben, wäre dieser Code:
```Python
user_dict = user_in.dict()
UserInDB(**user_dict)
```
-äquivalent zu:
+gleichwertig zu:
```Python
UserInDB(**user_in.dict())
```
-... weil `user_in.dict()` ein `dict` ist, und dann lassen wir Python es „entpacken“, indem wir es `UserInDB` übergeben, mit vorangestelltem `**`.
+... weil `user_in.dict()` ein `dict` ist, und dann lassen wir Python es „entpacken“, indem wir es an `UserInDB` mit vorangestelltem `**` übergeben.
-Wir erhalten also ein Pydantic-Modell aus den Daten eines anderen Pydantic-Modells.
+Auf diese Weise erhalten wir ein Pydantic-Modell aus den Daten eines anderen Pydantic-Modells.
-#### Ein `dict` entpacken und zusätzliche Schlüsselwort-Argumente
+#### Ein `dict` entpacken und zusätzliche Schlüsselwort-Argumente { #unpacking-a-dict-and-extra-keywords }
-Und dann fügen wir ein noch weiteres Schlüsselwort-Argument hinzu, `hashed_password=hashed_password`:
+Und dann fügen wir das zusätzliche Schlüsselwort-Argument `hashed_password=hashed_password` hinzu, wie in:
```Python
UserInDB(**user_in.dict(), hashed_password=hashed_password)
```
-... was am Ende ergibt:
+... was so ist wie:
```Python
UserInDB(
@@ -154,136 +140,83 @@ UserInDB(
)
```
-/// warning | "Achtung"
+/// warning | Achtung
-Die Hilfsfunktionen `fake_password_hasher` und `fake_save_user` demonstrieren nur den möglichen Fluss der Daten und bieten natürlich keine echte Sicherheit.
+Die unterstützenden zusätzlichen Funktionen `fake_password_hasher` und `fake_save_user` dienen nur zur Demo eines möglichen Datenflusses, bieten jedoch natürlich keine echte Sicherheit.
///
-## Verdopplung vermeiden
+## Verdopplung vermeiden { #reduce-duplication }
-Reduzierung von Code-Verdoppelung ist eine der Kern-Ideen von **FastAPI**.
+Die Reduzierung von Code-Verdoppelung ist eine der Kernideen von **FastAPI**.
-Weil Verdoppelung von Code die Wahrscheinlichkeit von Fehlern, Sicherheitsproblemen, Desynchronisation (Code wird nur an einer Stelle verändert, aber nicht an einer anderen), usw. erhöht.
+Da die Verdopplung von Code die Wahrscheinlichkeit von Fehlern, Sicherheitsproblemen, Problemen mit der Desynchronisation des Codes (wenn Sie an einer Stelle, aber nicht an der anderen aktualisieren) usw. erhöht.
-Unsere Modelle teilen alle eine Menge der Daten und verdoppeln Attribut-Namen und -Typen.
+Und diese Modelle teilen alle eine Menge der Daten und verdoppeln Attributnamen und -typen.
-Das können wir besser machen.
+Wir könnten es besser machen.
-Wir deklarieren ein `UserBase`-Modell, das als Basis für unsere anderen Modelle dient. Dann können wir Unterklassen erstellen, die seine Attribute (Typdeklarationen, Validierungen, usw.) erben.
+Wir können ein `UserBase`-Modell deklarieren, das als Basis für unsere anderen Modelle dient. Und dann können wir Unterklassen dieses Modells erstellen, die seine Attribute (Typdeklarationen, Validierung usw.) erben.
-Die ganze Datenkonvertierung, -validierung, -dokumentation, usw. wird immer noch wie gehabt funktionieren.
+Die ganze Datenkonvertierung, -validierung, -dokumentation usw. wird immer noch wie gewohnt funktionieren.
-Auf diese Weise beschreiben wir nur noch die Unterschiede zwischen den Modellen (mit Klartext-`password`, mit `hashed_password`, und ohne Passwort):
+Auf diese Weise können wir nur die Unterschiede zwischen den Modellen (mit Klartext-`password`, mit `hashed_password` und ohne Passwort) deklarieren:
-//// tab | Python 3.10+
+{* ../../docs_src/extra_models/tutorial002_py310.py hl[7,13:14,17:18,21:22] *}
-```Python hl_lines="7 13-14 17-18 21-22"
-{!> ../../../docs_src/extra_models/tutorial002_py310.py!}
-```
+## `Union` oder `anyOf` { #union-or-anyof }
-////
+Sie können deklarieren, dass eine Response eine `Union` mehrerer Typen ist, das bedeutet, dass die Response einer von ihnen ist.
-//// tab | Python 3.8+
+Dies wird in OpenAPI mit `anyOf` definiert.
-```Python hl_lines="9 15-16 19-20 23-24"
-{!> ../../../docs_src/extra_models/tutorial002.py!}
-```
+Um das zu tun, verwenden Sie den Standard-Python-Typhinweis `typing.Union`:
-////
+/// note | Hinweis
-## `Union`, oder `anyOf`
-
-Sie können deklarieren, dass eine Response eine `Union` mehrerer Typen ist, sprich, einer dieser Typen.
-
-Das wird in OpenAPI mit `anyOf` angezeigt.
-
-Um das zu tun, verwenden Sie Pythons Standard-Typhinweis `typing.Union`:
-
-/// note | "Hinweis"
-
-Listen Sie, wenn Sie eine `Union` definieren, denjenigen Typ zuerst, der am spezifischsten ist, gefolgt von den weniger spezifischen Typen. Im Beispiel oben, in `Union[PlaneItem, CarItem]` also den spezifischeren `PlaneItem` vor dem weniger spezifischen `CarItem`.
+Wenn Sie eine `Union` definieren, listen Sie den spezifischeren Typ zuerst auf, gefolgt vom weniger spezifischen Typ. Im Beispiel unten steht `PlaneItem` vor `CarItem` in `Union[PlaneItem, CarItem]`.
///
-//// tab | Python 3.10+
+{* ../../docs_src/extra_models/tutorial003_py310.py hl[1,14:15,18:20,33] *}
-```Python hl_lines="1 14-15 18-20 33"
-{!> ../../../docs_src/extra_models/tutorial003_py310.py!}
-```
-////
+### `Union` in Python 3.10 { #union-in-python-3-10 }
-//// tab | Python 3.8+
+In diesem Beispiel übergeben wir `Union[PlaneItem, CarItem]` als Wert des Arguments `response_model`.
-```Python hl_lines="1 14-15 18-20 33"
-{!> ../../../docs_src/extra_models/tutorial003.py!}
-```
+Da wir es als **Wert an ein Argument übergeben**, anstatt es in einer **Typannotation** zu verwenden, müssen wir `Union` verwenden, sogar in Python 3.10.
-////
-
-### `Union` in Python 3.10
-
-In diesem Beispiel übergeben wir dem Argument `response_model` den Wert `Union[PlaneItem, CarItem]`.
-
-Da wir es als **Wert einem Argument überreichen**, statt es als **Typannotation** zu verwenden, müssen wir `Union` verwenden, selbst in Python 3.10.
-
-Wenn es eine Typannotation gewesen wäre, hätten wir auch den vertikalen Trennstrich verwenden können, wie in:
+Wäre es eine Typannotation gewesen, hätten wir den vertikalen Strich verwenden können, wie in:
```Python
some_variable: PlaneItem | CarItem
```
-Aber wenn wir das in der Zuweisung `response_model=PlaneItem | CarItem` machen, erhalten wir eine Fehlermeldung, da Python versucht, eine **ungültige Operation** zwischen `PlaneItem` und `CarItem` durchzuführen, statt es als Typannotation zu interpretieren.
+Aber wenn wir das in der Zuweisung `response_model=PlaneItem | CarItem` machen, würden wir einen Fehler erhalten, weil Python versuchen würde, eine **ungültige Operation** zwischen `PlaneItem` und `CarItem` auszuführen, anstatt es als Typannotation zu interpretieren.
-## Listen von Modellen
+## Liste von Modellen { #list-of-models }
-Genauso können Sie eine Response deklarieren, die eine Liste von Objekten ist.
+Auf die gleiche Weise können Sie Responses von Listen von Objekten deklarieren.
-Verwenden Sie dafür Pythons Standard `typing.List` (oder nur `list` in Python 3.9 und darüber):
+Dafür verwenden Sie Pythons Standard-`typing.List` (oder nur `list` in Python 3.9 und höher):
-//// tab | Python 3.9+
+{* ../../docs_src/extra_models/tutorial004_py39.py hl[18] *}
-```Python hl_lines="18"
-{!> ../../../docs_src/extra_models/tutorial004_py39.py!}
-```
-////
+## Response mit beliebigem `dict` { #response-with-arbitrary-dict }
-//// tab | Python 3.8+
+Sie können auch eine Response deklarieren, die ein beliebiges `dict` zurückgibt, indem Sie nur die Typen der Schlüssel und Werte ohne ein Pydantic-Modell deklarieren.
-```Python hl_lines="1 20"
-{!> ../../../docs_src/extra_models/tutorial004.py!}
-```
+Dies ist nützlich, wenn Sie die gültigen Feld-/Attributnamen nicht im Voraus kennen (die für ein Pydantic-Modell benötigt werden würden).
-////
+In diesem Fall können Sie `typing.Dict` verwenden (oder nur `dict` in Python 3.9 und höher):
-## Response mit beliebigem `dict`
+{* ../../docs_src/extra_models/tutorial005_py39.py hl[6] *}
-Sie könne auch eine Response deklarieren, die ein beliebiges `dict` zurückgibt, bei dem nur die Typen der Schlüssel und der Werte bekannt sind, ohne ein Pydantic-Modell zu verwenden.
-Das ist nützlich, wenn Sie die gültigen Feld-/Attribut-Namen von vorneherein nicht wissen (was für ein Pydantic-Modell notwendig ist).
-
-In diesem Fall können Sie `typing.Dict` verwenden (oder nur `dict` in Python 3.9 und darüber):
-
-//// tab | Python 3.9+
-
-```Python hl_lines="6"
-{!> ../../../docs_src/extra_models/tutorial005_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="1 8"
-{!> ../../../docs_src/extra_models/tutorial005.py!}
-```
-
-////
-
-## Zusammenfassung
+## Zusammenfassung { #recap }
Verwenden Sie gerne mehrere Pydantic-Modelle und vererben Sie je nach Bedarf.
-Sie brauchen kein einzelnes Datenmodell pro Einheit, wenn diese Einheit verschiedene Zustände annehmen kann. So wie unsere Benutzer-„Einheit“, welche einen Zustand mit `password`, einen mit `password_hash` und einen ohne Passwort hatte.
+Sie brauchen kein einzelnes Datenmodell pro Einheit, wenn diese Einheit in der Lage sein muss, verschiedene „Zustände“ zu haben. Wie im Fall der Benutzer-„Einheit“ mit einem Zustand einschließlich `password`, `password_hash` und ohne Passwort.
diff --git a/docs/de/docs/tutorial/first-steps.md b/docs/de/docs/tutorial/first-steps.md
index b9e38707cb..7ec98c53b1 100644
--- a/docs/de/docs/tutorial/first-steps.md
+++ b/docs/de/docs/tutorial/first-steps.md
@@ -1,66 +1,80 @@
-# Erste Schritte
+# Erste Schritte { #first-steps }
Die einfachste FastAPI-Datei könnte wie folgt aussehen:
-```Python
-{!../../../docs_src/first_steps/tutorial001.py!}
-```
+{* ../../docs_src/first_steps/tutorial001.py *}
-Kopieren Sie dies in eine Datei `main.py`.
+Kopieren Sie das in eine Datei `main.py`.
Starten Sie den Live-Server:
get-Operation gehen
+* den Pfad `/`
+* unter der Verwendung der get-Operation gehen
-/// info | "`@decorator` Information"
+/// info | `@decorator` Info
Diese `@something`-Syntax wird in Python „Dekorator“ genannt.
@@ -279,14 +257,14 @@ Sie können auch die anderen Operationen verwenden:
* `@app.put()`
* `@app.delete()`
-Oder die exotischeren:
+Und die exotischeren:
* `@app.options()`
* `@app.head()`
* `@app.patch()`
* `@app.trace()`
-/// tip | "Tipp"
+/// tip | Tipp
Es steht Ihnen frei, jede Operation (HTTP-Methode) so zu verwenden, wie Sie es möchten.
@@ -298,7 +276,7 @@ Wenn Sie beispielsweise GraphQL verwenden, führen Sie normalerweise alle Aktion
///
-### Schritt 4: Definieren der **Pfadoperation-Funktion**
+### Schritt 4: Definieren der **Pfadoperation-Funktion** { #step-4-define-the-path-operation-function }
Das ist unsere „**Pfadoperation-Funktion**“:
@@ -306,13 +284,11 @@ Das ist unsere „**Pfadoperation-Funktion**“:
* **Operation**: ist `get`.
* **Funktion**: ist die Funktion direkt unter dem „Dekorator“ (unter `@app.get("/")`).
-```Python hl_lines="7"
-{!../../../docs_src/first_steps/tutorial001.py!}
-```
+{* ../../docs_src/first_steps/tutorial001.py hl[7] *}
Dies ist eine Python-Funktion.
-Sie wird von **FastAPI** immer dann aufgerufen, wenn sie eine Anfrage an die URL "`/`" mittels einer `GET`-Operation erhält.
+Sie wird von **FastAPI** immer dann aufgerufen, wenn sie einen Request an die URL „`/`“ mittels einer `GET`-Operation erhält.
In diesem Fall handelt es sich um eine `async`-Funktion.
@@ -320,32 +296,28 @@ In diesem Fall handelt es sich um eine `async`-Funktion.
Sie könnten sie auch als normale Funktion anstelle von `async def` definieren:
-```Python hl_lines="7"
-{!../../../docs_src/first_steps/tutorial003.py!}
-```
+{* ../../docs_src/first_steps/tutorial003.py hl[7] *}
-/// note | "Hinweis"
+/// note | Hinweis
-Wenn Sie den Unterschied nicht kennen, lesen Sie [Async: *„In Eile?“*](../async.md#in-eile){.internal-link target=_blank}.
+Wenn Sie den Unterschied nicht kennen, lesen Sie [Async: *„In Eile?“*](../async.md#in-a-hurry){.internal-link target=_blank}.
///
-### Schritt 5: den Inhalt zurückgeben
+### Schritt 5: den Inhalt zurückgeben { #step-5-return-the-content }
-```Python hl_lines="8"
-{!../../../docs_src/first_steps/tutorial001.py!}
-```
+{* ../../docs_src/first_steps/tutorial001.py hl[8] *}
Sie können ein `dict`, eine `list`, einzelne Werte wie `str`, `int`, usw. zurückgeben.
Sie können auch Pydantic-Modelle zurückgeben (dazu später mehr).
-Es gibt viele andere Objekte und Modelle, die automatisch zu JSON konvertiert werden (einschließlich ORMs usw.). Versuchen Sie, Ihre Lieblingsobjekte zu verwenden. Es ist sehr wahrscheinlich, dass sie bereits unterstützt werden.
+Es gibt viele andere Objekte und Modelle, die automatisch zu JSON konvertiert werden (einschließlich ORMs, usw.). Versuchen Sie, Ihre Lieblingsobjekte zu verwenden. Es ist sehr wahrscheinlich, dass sie bereits unterstützt werden.
-## Zusammenfassung
+## Zusammenfassung { #recap }
* Importieren Sie `FastAPI`.
* Erstellen Sie eine `app` Instanz.
-* Schreiben Sie einen **Pfadoperation-Dekorator** (wie z. B. `@app.get("/")`).
-* Schreiben Sie eine **Pfadoperation-Funktion** (wie z. B. oben `def root(): ...`).
-* Starten Sie den Entwicklungsserver (z. B. `uvicorn main:app --reload`).
+* Schreiben Sie einen **Pfadoperation-Dekorator** unter Verwendung von Dekoratoren wie `@app.get("/")`.
+* Definieren Sie eine **Pfadoperation-Funktion**, zum Beispiel `def root(): ...`.
+* Starten Sie den Entwicklungsserver mit dem Befehl `fastapi dev`.
diff --git a/docs/de/docs/tutorial/handling-errors.md b/docs/de/docs/tutorial/handling-errors.md
index 6ee47948c8..58e4607c5a 100644
--- a/docs/de/docs/tutorial/handling-errors.md
+++ b/docs/de/docs/tutorial/handling-errors.md
@@ -1,53 +1,49 @@
-# Fehlerbehandlung
+# Fehler behandeln { #handling-errors }
-Es gibt viele Situationen, in denen Sie einem Client, der Ihre API benutzt, einen Fehler zurückgeben müssen.
+Es gibt viele Situationen, in denen Sie einem Client, der Ihre API nutzt, einen Fehler mitteilen müssen.
-Dieser Client könnte ein Browser mit einem Frontend, Code von jemand anderem, ein IoT-Gerät, usw., sein.
+Dieser Client könnte ein Browser mit einem Frontend sein, ein Code von jemand anderem, ein IoT-Gerät usw.
-Sie müssten beispielsweise einem Client sagen:
+Sie könnten dem Client mitteilen müssen, dass:
-* Dass er nicht die notwendigen Berechtigungen hat, eine Aktion auszuführen.
-* Dass er zu einer Ressource keinen Zugriff hat.
-* Dass die Ressource, auf die er zugreifen möchte, nicht existiert.
+* Der Client nicht genügend Berechtigungen für diese Operation hat.
+* Der Client keinen Zugriff auf diese Ressource hat.
+* Die Ressource, auf die der Client versucht hat, zuzugreifen, nicht existiert.
* usw.
-In diesen Fällen geben Sie normalerweise einen **HTTP-Statuscode** im Bereich **400** (400 bis 499) zurück.
+In diesen Fällen würden Sie normalerweise einen **HTTP-Statuscode** im Bereich **400** (von 400 bis 499) zurückgeben.
-Das ist vergleichbar mit den HTTP-Statuscodes im Bereich 200 (von 200 bis 299). Diese „200“er Statuscodes bedeuten, dass der Request in einem bestimmten Aspekt ein „Success“ („Erfolg“) war.
+Dies ist vergleichbar mit den HTTP-Statuscodes im Bereich 200 (von 200 bis 299). Diese „200“-Statuscodes bedeuten, dass der Request in irgendeiner Weise erfolgreich war.
-Die Statuscodes im 400er-Bereich bedeuten hingegen, dass es einen Fehler gab.
+Die Statuscodes im Bereich 400 bedeuten hingegen, dass es einen Fehler seitens des Clients gab.
-Erinnern Sie sich an all diese **404 Not Found** Fehler (und Witze)?
+Erinnern Sie sich an all diese **„404 Not Found“** Fehler (und Witze)?
-## `HTTPException` verwenden
+## `HTTPException` verwenden { #use-httpexception }
-Um HTTP-Responses mit Fehlern zum Client zurückzugeben, verwenden Sie `HTTPException`.
+Um HTTP-Responses mit Fehlern an den Client zurückzugeben, verwenden Sie `HTTPException`.
-### `HTTPException` importieren
+### `HTTPException` importieren { #import-httpexception }
-```Python hl_lines="1"
-{!../../../docs_src/handling_errors/tutorial001.py!}
-```
+{* ../../docs_src/handling_errors/tutorial001.py hl[1] *}
-### Eine `HTTPException` in Ihrem Code auslösen
+### Eine `HTTPException` in Ihrem Code auslösen { #raise-an-httpexception-in-your-code }
-`HTTPException` ist eine normale Python-Exception mit einigen zusätzlichen Daten, die für APIs relevant sind.
+`HTTPException` ist eine normale Python-Exception mit zusätzlichen Daten, die für APIs relevant sind.
-Weil es eine Python-Exception ist, geben Sie sie nicht zurück, (`return`), sondern Sie lösen sie aus (`raise`).
+Weil es eine Python-Exception ist, geben Sie sie nicht zurück (`return`), sondern lösen sie aus (`raise`).
-Das bedeutet auch, wenn Sie in einer Hilfsfunktion sind, die Sie von ihrer *Pfadoperation-Funktion* aus aufrufen, und Sie lösen eine `HTTPException` von innerhalb dieser Hilfsfunktion aus, dann wird der Rest der *Pfadoperation-Funktion* nicht ausgeführt, sondern der Request wird sofort abgebrochen und der HTTP-Error der `HTTP-Exception` wird zum Client gesendet.
+Das bedeutet auch, wenn Sie sich innerhalb einer Hilfsfunktion befinden, die Sie innerhalb Ihrer *Pfadoperation-Funktion* aufrufen, und Sie die `HTTPException` aus dieser Hilfsfunktion heraus auslösen, wird der restliche Code in der *Pfadoperation-Funktion* nicht ausgeführt. Der Request wird sofort abgebrochen und der HTTP-Error der `HTTPException` wird an den Client gesendet.
-Der Vorteil, eine Exception auszulösen (`raise`), statt sie zurückzugeben (`return`) wird im Abschnitt über Abhängigkeiten und Sicherheit klarer werden.
+Der Vorteil des Auslösens einer Exception gegenüber dem Zurückgeben eines Wertes wird im Abschnitt über Abhängigkeiten und Sicherheit deutlicher werden.
-Im folgenden Beispiel lösen wir, wenn der Client eine ID anfragt, die nicht existiert, eine Exception mit dem Statuscode `404` aus.
+In diesem Beispiel lösen wir eine Exception mit einem Statuscode von `404` aus, wenn der Client einen Artikel mit einer nicht existierenden ID anfordert:
-```Python hl_lines="11"
-{!../../../docs_src/handling_errors/tutorial001.py!}
-```
+{* ../../docs_src/handling_errors/tutorial001.py hl[11] *}
-### Die resultierende Response
+### Die resultierende Response { #the-resulting-response }
-Wenn der Client `http://example.com/items/foo` anfragt (ein `item_id` `"foo"`), erhält dieser Client einen HTTP-Statuscode 200 und folgende JSON-Response:
+Wenn der Client `http://example.com/items/foo` anfordert (ein `item_id` `"foo"`), erhält dieser Client einen HTTP-Statuscode 200 und diese JSON-Response:
```JSON
{
@@ -55,7 +51,7 @@ Wenn der Client `http://example.com/items/foo` anfragt (ein `item_id` `"foo"`),
}
```
-Aber wenn der Client `http://example.com/items/bar` anfragt (ein nicht-existierendes `item_id` `"bar"`), erhält er einen HTTP-Statuscode 404 (der „Not Found“-Fehler), und eine JSON-Response wie folgt:
+Aber wenn der Client `http://example.com/items/bar` anfordert (ein nicht-existierendes `item_id` `"bar"`), erhält er einen HTTP-Statuscode 404 (der „Not Found“-Error) und eine JSON-Response wie:
```JSON
{
@@ -63,83 +59,77 @@ Aber wenn der Client `http://example.com/items/bar` anfragt (ein nicht-existiere
}
```
-/// tip | "Tipp"
+/// tip | Tipp
-Wenn Sie eine `HTTPException` auslösen, können Sie dem Parameter `detail` jeden Wert übergeben, der nach JSON konvertiert werden kann, nicht nur `str`.
+Wenn Sie eine `HTTPException` auslösen, können Sie dem Parameter `detail` jeden Wert übergeben, der in JSON konvertiert werden kann, nicht nur `str`.
-Zum Beispiel ein `dict`, eine `list`, usw.
+Sie könnten ein `dict`, eine `list`, usw. übergeben.
-Das wird automatisch von **FastAPI** gehandhabt und der Wert nach JSON konvertiert.
+Diese werden von **FastAPI** automatisch gehandhabt und in JSON konvertiert.
///
-## Benutzerdefinierte Header hinzufügen
+## Benutzerdefinierte Header hinzufügen { #add-custom-headers }
-Es gibt Situationen, da ist es nützlich, dem HTTP-Error benutzerdefinierte Header hinzufügen zu können, etwa in einigen Sicherheitsszenarien.
+Es gibt Situationen, in denen es nützlich ist, dem HTTP-Error benutzerdefinierte Header hinzuzufügen. Zum Beispiel in einigen Sicherheitsszenarien.
-Sie müssen das wahrscheinlich nicht direkt in ihrem Code verwenden.
+Sie werden es wahrscheinlich nicht direkt in Ihrem Code verwenden müssen.
-Aber falls es in einem fortgeschrittenen Szenario notwendig ist, können Sie benutzerdefinierte Header wie folgt hinzufügen:
+Aber falls Sie es für ein fortgeschrittenes Szenario benötigen, können Sie benutzerdefinierte Header hinzufügen:
-```Python hl_lines="14"
-{!../../../docs_src/handling_errors/tutorial002.py!}
-```
+{* ../../docs_src/handling_errors/tutorial002.py hl[14] *}
-## Benutzerdefinierte Exceptionhandler definieren
+## Benutzerdefinierte Exceptionhandler installieren { #install-custom-exception-handlers }
-Sie können benutzerdefinierte Exceptionhandler hinzufügen, mithilfe derselben Werkzeuge für Exceptions von Starlette.
+Sie können benutzerdefinierte Exceptionhandler mit den gleichen Exception-Werkzeugen von Starlette hinzufügen.
-Nehmen wir an, Sie haben eine benutzerdefinierte Exception `UnicornException`, die Sie (oder eine Bibliothek, die Sie verwenden) `raise`n könnten.
+Angenommen, Sie haben eine benutzerdefinierte Exception `UnicornException`, die Sie (oder eine Bibliothek, die Sie verwenden) `raise`n könnten.
Und Sie möchten diese Exception global mit FastAPI handhaben.
-Sie könnten einen benutzerdefinierten Exceptionhandler mittels `@app.exception_handler()` hinzufügen:
+Sie könnten einen benutzerdefinierten Exceptionhandler mit `@app.exception_handler()` hinzufügen:
-```Python hl_lines="5-7 13-18 24"
-{!../../../docs_src/handling_errors/tutorial003.py!}
-```
+{* ../../docs_src/handling_errors/tutorial003.py hl[5:7,13:18,24] *}
-Wenn Sie nun `/unicorns/yolo` anfragen, `raise`d die *Pfadoperation* eine `UnicornException`.
+Hier, wenn Sie `/unicorns/yolo` anfordern, wird die *Pfadoperation* eine `UnicornException` `raise`n.
Aber diese wird von `unicorn_exception_handler` gehandhabt.
-Sie erhalten also einen sauberen Error mit einem Statuscode `418` und dem JSON-Inhalt:
+Sie erhalten also einen sauberen Fehler mit einem HTTP-Statuscode von `418` und dem JSON-Inhalt:
```JSON
{"message": "Oops! yolo did something. There goes a rainbow..."}
```
-/// note | "Technische Details"
+/// note | Technische Details
-Sie können auch `from starlette.requests import Request` und `from starlette.responses import JSONResponse` verwenden.
+Sie könnten auch `from starlette.requests import Request` und `from starlette.responses import JSONResponse` verwenden.
-**FastAPI** bietet dieselben `starlette.responses` auch via `fastapi.responses` an, als Annehmlichkeit für Sie, den Entwickler. Die meisten verfügbaren Responses kommen aber direkt von Starlette. Das Gleiche gilt für `Request`.
+**FastAPI** bietet dieselben `starlette.responses` auch via `fastapi.responses` an, nur als Annehmlichkeit für Sie, den Entwickler. Aber die meisten verfügbaren Responses kommen direkt von Starlette. Dasselbe gilt für `Request`.
///
-## Die Default-Exceptionhandler überschreiben
+## Die Default-Exceptionhandler überschreiben { #override-the-default-exception-handlers }
**FastAPI** hat einige Default-Exceptionhandler.
-Diese Handler kümmern sich darum, Default-JSON-Responses zurückzugeben, wenn Sie eine `HTTPException` `raise`n, und wenn der Request ungültige Daten enthält.
+Diese Handler sind dafür verantwortlich, die Default-JSON-Responses zurückzugeben, wenn Sie eine `HTTPException` `raise`n und wenn der Request ungültige Daten enthält.
-Sie können diese Exceptionhandler mit ihren eigenen überschreiben.
+Sie können diese Exceptionhandler mit Ihren eigenen überschreiben.
-### Requestvalidierung-Exceptions überschreiben
+### Überschreiben von Request-Validierungs-Exceptions { #override-request-validation-exceptions }
Wenn ein Request ungültige Daten enthält, löst **FastAPI** intern einen `RequestValidationError` aus.
-Und bietet auch einen Default-Exceptionhandler dafür.
+Und es enthält auch einen Default-Exceptionhandler für diesen.
-Um diesen zu überschreiben, importieren Sie den `RequestValidationError` und verwenden Sie ihn in `@app.exception_handler(RequestValidationError)`, um Ihren Exceptionhandler zu dekorieren.
+Um diesen zu überschreiben, importieren Sie den `RequestValidationError` und verwenden Sie ihn mit `@app.exception_handler(RequestValidationError)`, um den Exceptionhandler zu dekorieren.
-Der Exceptionhandler wird einen `Request` und die Exception entgegennehmen.
+Der Exceptionhandler erhält einen `Request` und die Exception.
-```Python hl_lines="2 14-16"
-{!../../../docs_src/handling_errors/tutorial004.py!}
-```
+{* ../../docs_src/handling_errors/tutorial004.py hl[2,14:16] *}
-Wenn Sie nun `/items/foo` besuchen, erhalten Sie statt des Default-JSON-Errors:
+Wenn Sie nun zu `/items/foo` gehen, erhalten Sie anstelle des standardmäßigen JSON-Fehlers mit:
```JSON
{
@@ -156,7 +146,7 @@ Wenn Sie nun `/items/foo` besuchen, erhalten Sie statt des Default-JSON-Errors:
}
```
-eine Textversion:
+eine Textversion mit:
```
1 validation error
@@ -164,53 +154,49 @@ path -> item_id
value is not a valid integer (type=type_error.integer)
```
-#### `RequestValidationError` vs. `ValidationError`
+#### `RequestValidationError` vs. `ValidationError` { #requestvalidationerror-vs-validationerror }
-/// warning | "Achtung"
+/// warning | Achtung
-Das folgende sind technische Details, die Sie überspringen können, wenn sie für Sie nicht wichtig sind.
+Dies sind technische Details, die Sie überspringen können, wenn sie für Sie jetzt nicht wichtig sind.
///
-`RequestValidationError` ist eine Unterklasse von Pydantics `ValidationError`.
+`RequestValidationError` ist eine Unterklasse von Pydantics `ValidationError`.
-**FastAPI** verwendet diesen, sodass Sie, wenn Sie ein Pydantic-Modell für `response_model` verwenden, und ihre Daten fehlerhaft sind, einen Fehler in ihrem Log sehen.
+**FastAPI** verwendet diesen so, dass, wenn Sie ein Pydantic-Modell in `response_model` verwenden und Ihre Daten einen Fehler haben, Sie den Fehler in Ihrem Log sehen.
-Aber der Client/Benutzer sieht ihn nicht. Stattdessen erhält der Client einen „Internal Server Error“ mit einem HTTP-Statuscode `500`.
+Aber der Client/Benutzer wird ihn nicht sehen. Stattdessen erhält der Client einen „Internal Server Error“ mit einem HTTP-Statuscode `500`.
-Das ist, wie es sein sollte, denn wenn Sie einen Pydantic-`ValidationError` in Ihrer *Response* oder irgendwo sonst in ihrem Code haben (es sei denn, im *Request* des Clients), ist das tatsächlich ein Bug in ihrem Code.
+Es sollte so sein, denn wenn Sie einen Pydantic `ValidationError` in Ihrer *Response* oder irgendwo anders in Ihrem Code haben (nicht im *Request* des Clients), ist es tatsächlich ein Fehler in Ihrem Code.
-Und während Sie den Fehler beheben, sollten ihre Clients/Benutzer keinen Zugriff auf interne Informationen über den Fehler haben, da das eine Sicherheitslücke aufdecken könnte.
+Und während Sie den Fehler beheben, sollten Ihre Clients/Benutzer keinen Zugriff auf interne Informationen über den Fehler haben, da das eine Sicherheitslücke aufdecken könnte.
-### den `HTTPException`-Handler überschreiben
+### Überschreiben des `HTTPException`-Fehlerhandlers { #override-the-httpexception-error-handler }
-Genauso können Sie den `HTTPException`-Handler überschreiben.
+Auf die gleiche Weise können Sie den `HTTPException`-Handler überschreiben.
Zum Beispiel könnten Sie eine Klartext-Response statt JSON für diese Fehler zurückgeben wollen:
-```Python hl_lines="3-4 9-11 22"
-{!../../../docs_src/handling_errors/tutorial004.py!}
-```
+{* ../../docs_src/handling_errors/tutorial004.py hl[3:4,9:11,22] *}
-/// note | "Technische Details"
+/// note | Technische Details
-Sie können auch `from starlette.responses import PlainTextResponse` verwenden.
+Sie könnten auch `from starlette.responses import PlainTextResponse` verwenden.
-**FastAPI** bietet dieselben `starlette.responses` auch via `fastapi.responses` an, als Annehmlichkeit für Sie, den Entwickler. Die meisten verfügbaren Responses kommen aber direkt von Starlette.
+**FastAPI** bietet dieselben `starlette.responses` auch via `fastapi.responses` an, nur als Annehmlichkeit für Sie, den Entwickler. Aber die meisten verfügbaren Responses kommen direkt von Starlette.
///
-### Den `RequestValidationError`-Body verwenden
+### Verwenden des `RequestValidationError`-Bodys { #use-the-requestvalidationerror-body }
Der `RequestValidationError` enthält den empfangenen `body` mit den ungültigen Daten.
-Sie könnten diesen verwenden, während Sie Ihre Anwendung entwickeln, um den Body zu loggen und zu debuggen, ihn zum Benutzer zurückzugeben, usw.
+Sie könnten diesen während der Entwicklung Ihrer Anwendung verwenden, um den Body zu loggen und zu debuggen, ihn an den Benutzer zurückzugeben usw.
-```Python hl_lines="14"
-{!../../../docs_src/handling_errors/tutorial005.py!}
-```
+{* ../../docs_src/handling_errors/tutorial005.py hl[14] *}
-Jetzt versuchen Sie, einen ungültigen Artikel zu senden:
+Versuchen Sie nun, einen ungültigen Artikel zu senden:
```JSON
{
@@ -219,7 +205,7 @@ Jetzt versuchen Sie, einen ungültigen Artikel zu senden:
}
```
-Sie erhalten eine Response, die Ihnen sagt, dass die Daten ungültig sind, und welche den empfangenen Body enthält.
+Sie erhalten eine Response, die Ihnen sagt, dass die Daten ungültig sind und die den empfangenen Body enthält:
```JSON hl_lines="12-15"
{
@@ -240,32 +226,30 @@ Sie erhalten eine Response, die Ihnen sagt, dass die Daten ungültig sind, und w
}
```
-#### FastAPIs `HTTPException` vs. Starlettes `HTTPException`
+#### FastAPIs `HTTPException` vs. Starlettes `HTTPException` { #fastapis-httpexception-vs-starlettes-httpexception }
**FastAPI** hat seine eigene `HTTPException`.
-Und **FastAPI**s `HTTPException`-Fehlerklasse erbt von Starlettes `HTTPException`-Fehlerklasse.
+Und die `HTTPException`-Fehlerklasse von **FastAPI** erbt von der `HTTPException`-Fehlerklasse von Starlette.
-Der einzige Unterschied besteht darin, dass **FastAPIs** `HTTPException` alles für das Feld `detail` akzeptiert, was nach JSON konvertiert werden kann, während Starlettes `HTTPException` nur Strings zulässt.
+Der einzige Unterschied besteht darin, dass die `HTTPException` von **FastAPI** beliebige JSON-konvertierbare Daten für das `detail`-Feld akzeptiert, während die `HTTPException` von Starlette nur Strings dafür akzeptiert.
-Sie können also weiterhin **FastAPI**s `HTTPException` wie üblich in Ihrem Code auslösen.
+Sie können also weiterhin die `HTTPException` von **FastAPI** wie üblich in Ihrem Code auslösen.
-Aber wenn Sie einen Exceptionhandler registrieren, registrieren Sie ihn für Starlettes `HTTPException`.
+Aber wenn Sie einen Exceptionhandler registrieren, sollten Sie ihn für die `HTTPException` von Starlette registrieren.
-Auf diese Weise wird Ihr Handler, wenn irgendein Teil von Starlettes internem Code, oder eine Starlette-Erweiterung, oder -Plugin eine Starlette-`HTTPException` auslöst, in der Lage sein, diese zu fangen und zu handhaben.
+Auf diese Weise, wenn irgendein Teil des internen Codes von Starlette, oder eine Starlette-Erweiterung oder ein Plug-in, eine Starlette `HTTPException` auslöst, wird Ihr Handler in der Lage sein, diese abzufangen und zu handhaben.
-Damit wir in diesem Beispiel beide `HTTPException`s im selben Code haben können, benennen wir Starlettes Exception um zu `StarletteHTTPException`:
+Um in diesem Beispiel beide `HTTPException`s im selben Code zu haben, wird die Exception von Starlette zu `StarletteHTTPException` umbenannt:
```Python
from starlette.exceptions import HTTPException as StarletteHTTPException
```
-### **FastAPI**s Exceptionhandler wiederverwenden
+### Die Exceptionhandler von **FastAPI** wiederverwenden { #reuse-fastapis-exception-handlers }
-Wenn Sie die Exception zusammen mit denselben Default-Exceptionhandlern von **FastAPI** verwenden möchten, können Sie die Default-Exceptionhandler von `fastapi.Exception_handlers` importieren und wiederverwenden:
+Wenn Sie die Exception zusammen mit den gleichen Default-Exceptionhandlern von **FastAPI** verwenden möchten, können Sie die Default-Exceptionhandler aus `fastapi.exception_handlers` importieren und wiederverwenden:
-```Python hl_lines="2-5 15 21"
-{!../../../docs_src/handling_errors/tutorial006.py!}
-```
+{* ../../docs_src/handling_errors/tutorial006.py hl[2:5,15,21] *}
-In diesem Beispiel `print`en Sie nur den Fehler mit einer sehr ausdrucksstarken Nachricht, aber Sie sehen, worauf wir hinauswollen. Sie können mit der Exception etwas machen und dann einfach die Default-Exceptionhandler wiederverwenden.
+In diesem Beispiel geben Sie nur den Fehler mit einer sehr ausdrucksstarken Nachricht aus, aber Sie verstehen das Prinzip. Sie können die Exception verwenden und dann einfach die Default-Exceptionhandler wiederverwenden.
diff --git a/docs/de/docs/tutorial/header-param-models.md b/docs/de/docs/tutorial/header-param-models.md
new file mode 100644
index 0000000000..8c1bf61aec
--- /dev/null
+++ b/docs/de/docs/tutorial/header-param-models.md
@@ -0,0 +1,72 @@
+# Header-Parameter-Modelle { #header-parameter-models }
+
+Wenn Sie eine Gruppe verwandter **Header-Parameter** haben, können Sie ein **Pydantic-Modell** erstellen, um diese zu deklarieren.
+
+Dadurch können Sie das **Modell an mehreren Stellen wiederverwenden** und auch Validierungen und Metadaten für alle Parameter gleichzeitig deklarieren. 😎
+
+/// note | Hinweis
+
+Dies wird seit FastAPI Version `0.115.0` unterstützt. 🤓
+
+///
+
+## Header-Parameter mit einem Pydantic-Modell { #header-parameters-with-a-pydantic-model }
+
+Deklarieren Sie die erforderlichen **Header-Parameter** in einem **Pydantic-Modell** und dann den Parameter als `Header`:
+
+{* ../../docs_src/header_param_models/tutorial001_an_py310.py hl[9:14,18] *}
+
+**FastAPI** wird die Daten für **jedes Feld** aus den **Headern** des Request extrahieren und Ihnen das von Ihnen definierte Pydantic-Modell geben.
+
+## Die Dokumentation testen { #check-the-docs }
+
+Sie können die erforderlichen Header in der Dokumentationsoberfläche unter `/docs` sehen:
+
+
+contact-Felder| Parameter | Typ | Beschreibung |
|---|---|---|
name | str | Der identifizierende Name der Kontaktperson/Organisation. |
url | str | Die URL, die auf die Kontaktinformationen verweist. MUSS im Format einer URL vorliegen. |
email | str | Die E-Mail-Adresse der Kontaktperson/Organisation. MUSS im Format einer E-Mail-Adresse vorliegen. |
license_info-Felder| Parameter | Typ | Beschreibung |
|---|---|---|
name | str | ERFORDERLICH (wenn eine license_info festgelegt ist). Der für die API verwendete Lizenzname. |
identifier | str | Ein SPDX-Lizenzausdruck für die API. Das Feld identifier und das Feld url schließen sich gegenseitig aus. Verfügbar seit OpenAPI 3.1.0, FastAPI 0.99.0. |
url | str | Eine URL zur Lizenz, die für die API verwendet wird. MUSS im Format einer URL vorliegen. |
contact-Felder| Parameter | Typ | Beschreibung |
|---|---|---|
name | str | Der identifizierende Name der Kontaktperson/Organisation. |
url | str | Die URL, die auf die Kontaktinformationen verweist. MUSS im Format einer URL vorliegen. |
email | str | Die E-Mail-Adresse der Kontaktperson/Organisation. MUSS im Format einer E-Mail-Adresse vorliegen. |
license_info-Felder| Parameter | Typ | Beschreibung |
|---|---|---|
name | str | ERFORDERLICH (wenn eine license_info festgelegt ist). Der für die API verwendete Lizenzname. |
identifier | str | Ein SPDX-Lizenzausdruck für die API. Das Feld identifier und das Feld url schließen sich gegenseitig aus. Verfügbar seit OpenAPI 3.1.0, FastAPI 0.99.0. |
url | str | Eine URL zur Lizenz, die für die API verwendet wird. MUSS im Format einer URL vorliegen. |
-## Lizenz-ID
+## Lizenzkennung { #license-identifier }
Seit OpenAPI 3.1.0 und FastAPI 0.99.0 können Sie die `license_info` auch mit einem `identifier` anstelle einer `url` festlegen.
Zum Beispiel:
-```Python hl_lines="31"
-{!../../../docs_src/metadata/tutorial001_1.py!}
-```
+{* ../../docs_src/metadata/tutorial001_1.py hl[31] *}
-## Metadaten für Tags
+## Metadaten für Tags { #metadata-for-tags }
-Sie können mit dem Parameter `openapi_tags` auch zusätzliche Metadaten für die verschiedenen Tags hinzufügen, die zum Gruppieren Ihrer Pfadoperationen verwendet werden.
+Sie können auch zusätzliche Metadaten für die verschiedenen Tags hinzufügen, die zum Gruppieren Ihrer Pfadoperationen verwendet werden, mit dem Parameter `openapi_tags`.
-Es wird eine Liste benötigt, die für jedes Tag ein Dict enthält.
+Er nimmt eine Liste entgegen, die für jeden Tag ein Dictionary enthält.
-Jedes Dict kann Folgendes enthalten:
+Jedes Dictionary kann Folgendes enthalten:
* `name` (**erforderlich**): ein `str` mit demselben Tag-Namen, den Sie im Parameter `tags` in Ihren *Pfadoperationen* und `APIRouter`n verwenden.
* `description`: ein `str` mit einer kurzen Beschreibung für das Tag. Sie kann Markdown enthalten und wird in der Benutzeroberfläche der Dokumentation angezeigt.
* `externalDocs`: ein `dict`, das externe Dokumentation beschreibt mit:
- * `description`: ein `str` mit einer kurzen Beschreibung für die externe Dokumentation.
- * `url` (**erforderlich**): ein `str` mit der URL für die externe Dokumentation.
+ * `description`: ein `str` mit einer kurzen Beschreibung für die externe Dokumentation.
+ * `url` (**erforderlich**): ein `str` mit der URL für die externe Dokumentation.
-### Metadaten für Tags erstellen
+### Metadaten für Tags erstellen { #create-metadata-for-tags }
-Versuchen wir das an einem Beispiel mit Tags für `users` und `items`.
+Versuchen wir es mit einem Beispiel mit Tags für `users` und `items`.
-Erstellen Sie Metadaten für Ihre Tags und übergeben Sie sie an den Parameter `openapi_tags`:
+Erstellen Sie Metadaten für Ihre Tags und übergeben Sie diese an den Parameter `openapi_tags`:
-```Python hl_lines="3-16 18"
-{!../../../docs_src/metadata/tutorial004.py!}
-```
+{* ../../docs_src/metadata/tutorial004.py hl[3:16,18] *}
-Beachten Sie, dass Sie Markdown in den Beschreibungen verwenden können. Beispielsweise wird „login“ in Fettschrift (**login**) und „fancy“ in Kursivschrift (_fancy_) angezeigt.
+Beachten Sie, dass Sie Markdown innerhalb der Beschreibungen verwenden können. Zum Beispiel wird „login“ in Fettschrift (**login**) und „fancy“ in Kursivschrift (_fancy_) angezeigt.
-/// tip | "Tipp"
+/// tip | Tipp
Sie müssen nicht für alle von Ihnen verwendeten Tags Metadaten hinzufügen.
///
-### Ihre Tags verwenden
+### Ihre Tags verwenden { #use-your-tags }
Verwenden Sie den Parameter `tags` mit Ihren *Pfadoperationen* (und `APIRouter`n), um diese verschiedenen Tags zuzuweisen:
-```Python hl_lines="21 26"
-{!../../../docs_src/metadata/tutorial004.py!}
-```
+{* ../../docs_src/metadata/tutorial004.py hl[21,26] *}
-/// info
+/// info | Info
Lesen Sie mehr zu Tags unter [Pfadoperation-Konfiguration](path-operation-configuration.md#tags){.internal-link target=_blank}.
///
-### Die Dokumentation anschauen
+### Die Dokumentation testen { #check-the-docs }
Wenn Sie nun die Dokumentation ansehen, werden dort alle zusätzlichen Metadaten angezeigt:
-### Reihenfolge der Tags
+### Reihenfolge der Tags { #order-of-tags }
-Die Reihenfolge der Tag-Metadaten-Dicts definiert auch die Reihenfolge, in der diese in der Benutzeroberfläche der Dokumentation angezeigt werden.
+Die Reihenfolge der Tag-Metadaten-Dictionarys definiert auch die Reihenfolge, in der diese in der Benutzeroberfläche der Dokumentation angezeigt werden.
-Auch wenn beispielsweise `users` im Alphabet nach `items` kommt, wird es vor diesen angezeigt, da wir seine Metadaten als erstes Dict der Liste hinzugefügt haben.
+Auch wenn beispielsweise `users` im Alphabet nach `items` kommt, wird es vor diesen angezeigt, da wir deren Metadaten als erstes Dictionary der Liste hinzugefügt haben.
-## OpenAPI-URL
+## OpenAPI-URL { #openapi-url }
Standardmäßig wird das OpenAPI-Schema unter `/openapi.json` bereitgestellt.
@@ -108,25 +100,21 @@ Sie können das aber mit dem Parameter `openapi_url` konfigurieren.
Um beispielsweise festzulegen, dass es unter `/api/v1/openapi.json` bereitgestellt wird:
-```Python hl_lines="3"
-{!../../../docs_src/metadata/tutorial002.py!}
-```
+{* ../../docs_src/metadata/tutorial002.py hl[3] *}
Wenn Sie das OpenAPI-Schema vollständig deaktivieren möchten, können Sie `openapi_url=None` festlegen, wodurch auch die Dokumentationsbenutzeroberflächen deaktiviert werden, die es verwenden.
-## URLs der Dokumentationen
+## Dokumentations-URLs { #docs-urls }
Sie können die beiden enthaltenen Dokumentationsbenutzeroberflächen konfigurieren:
* **Swagger UI**: bereitgestellt unter `/docs`.
- * Sie können deren URL mit dem Parameter `docs_url` festlegen.
- * Sie können sie deaktivieren, indem Sie `docs_url=None` festlegen.
+ * Sie können deren URL mit dem Parameter `docs_url` festlegen.
+ * Sie können sie deaktivieren, indem Sie `docs_url=None` festlegen.
* **ReDoc**: bereitgestellt unter `/redoc`.
- * Sie können deren URL mit dem Parameter `redoc_url` festlegen.
- * Sie können sie deaktivieren, indem Sie `redoc_url=None` festlegen.
+ * Sie können deren URL mit dem Parameter `redoc_url` festlegen.
+ * Sie können sie deaktivieren, indem Sie `redoc_url=None` festlegen.
Um beispielsweise Swagger UI so einzustellen, dass sie unter `/documentation` bereitgestellt wird, und ReDoc zu deaktivieren:
-```Python hl_lines="3"
-{!../../../docs_src/metadata/tutorial003.py!}
-```
+{* ../../docs_src/metadata/tutorial003.py hl[3] *}
diff --git a/docs/de/docs/tutorial/middleware.md b/docs/de/docs/tutorial/middleware.md
index 62a0d1613f..6410deba1a 100644
--- a/docs/de/docs/tutorial/middleware.md
+++ b/docs/de/docs/tutorial/middleware.md
@@ -1,8 +1,8 @@
-# Middleware
+# Middleware { #middleware }
Sie können Middleware zu **FastAPI**-Anwendungen hinzufügen.
-Eine „Middleware“ ist eine Funktion, die mit jedem **Request** arbeitet, bevor er von einer bestimmten *Pfadoperation* verarbeitet wird. Und auch mit jeder **Response**, bevor sie zurückgegeben wird.
+Eine „Middleware“ ist eine Funktion, die mit jedem **Request** arbeitet, bevor er von einer bestimmten *Pfadoperation* verarbeitet wird. Und auch mit jeder **Response**, bevor sie zurückgegeben wird.
* Sie nimmt jeden **Request** entgegen, der an Ihre Anwendung gesendet wird.
* Sie kann dann etwas mit diesem **Request** tun oder beliebigen Code ausführen.
@@ -11,15 +11,15 @@ Eine „Middleware“ ist eine Funktion, die mit jedem **Request** arbeitet, bev
* Sie kann etwas mit dieser **Response** tun oder beliebigen Code ausführen.
* Dann gibt sie die **Response** zurück.
-/// note | "Technische Details"
+/// note | Technische Details
Wenn Sie Abhängigkeiten mit `yield` haben, wird der Exit-Code *nach* der Middleware ausgeführt.
-Wenn es Hintergrundaufgaben gab (später dokumentiert), werden sie *nach* allen Middlewares ausgeführt.
+Wenn es Hintergrundtasks gab (dies wird später im [Hintergrundtasks](background-tasks.md){.internal-link target=_blank}-Abschnitt behandelt), werden sie *nach* allen Middlewares ausgeführt.
///
-## Erstellung einer Middleware
+## Eine Middleware erstellen { #create-a-middleware }
Um eine Middleware zu erstellen, verwenden Sie den Dekorator `@app.middleware("http")` über einer Funktion.
@@ -31,19 +31,17 @@ Die Middleware-Funktion erhält:
* Dann gibt es die von der entsprechenden *Pfadoperation* generierte `response` zurück.
* Sie können die `response` dann weiter modifizieren, bevor Sie sie zurückgeben.
-```Python hl_lines="8-9 11 14"
-{!../../../docs_src/middleware/tutorial001.py!}
-```
+{* ../../docs_src/middleware/tutorial001.py hl[8:9,11,14] *}
-/// tip | "Tipp"
+/// tip | Tipp
-Beachten Sie, dass benutzerdefinierte proprietäre Header hinzugefügt werden können. Verwenden Sie dafür das Präfix 'X-'.
+Beachten Sie, dass benutzerdefinierte proprietäre Header hinzugefügt werden können unter Verwendung des `X-`-Präfixes.
-Wenn Sie jedoch benutzerdefinierte Header haben, die ein Client in einem Browser sehen soll, müssen Sie sie zu Ihrer CORS-Konfigurationen ([CORS (Cross-Origin Resource Sharing)](cors.md){.internal-link target=_blank}) hinzufügen, indem Sie den Parameter `expose_headers` verwenden, der in der Starlette-CORS-Dokumentation dokumentiert ist.
+Wenn Sie jedoch benutzerdefinierte Header haben, die ein Client in einem Browser sehen soll, müssen Sie sie zu Ihrer CORS-Konfiguration ([CORS (Cross-Origin Resource Sharing)](cors.md){.internal-link target=_blank}) hinzufügen, indem Sie den Parameter `expose_headers` verwenden, der in der Starlettes CORS-Dokumentation dokumentiert ist.
///
-/// note | "Technische Details"
+/// note | Technische Details
Sie könnten auch `from starlette.requests import Request` verwenden.
@@ -51,7 +49,7 @@ Sie könnten auch `from starlette.requests import Request` verwenden.
///
-### Vor und nach der `response`
+### Vor und nach der `response` { #before-and-after-the-response }
Sie können Code hinzufügen, der mit dem `request` ausgeführt wird, bevor dieser von einer beliebigen *Pfadoperation* empfangen wird.
@@ -59,12 +57,39 @@ Und auch nachdem die `response` generiert wurde, bevor sie zurückgegeben wird.
Sie könnten beispielsweise einen benutzerdefinierten Header `X-Process-Time` hinzufügen, der die Zeit in Sekunden enthält, die benötigt wurde, um den Request zu verarbeiten und eine Response zu generieren:
-```Python hl_lines="10 12-13"
-{!../../../docs_src/middleware/tutorial001.py!}
+{* ../../docs_src/middleware/tutorial001.py hl[10,12:13] *}
+
+/// tip | Tipp
+
+Hier verwenden wir `time.perf_counter()` anstelle von `time.time()`, da es für diese Anwendungsfälle präziser sein kann. 🤓
+
+///
+
+## Ausführungsreihenfolge bei mehreren Middlewares { #multiple-middleware-execution-order }
+
+Wenn Sie mehrere Middlewares hinzufügen, entweder mit dem `@app.middleware()` Dekorator oder der Methode `app.add_middleware()`, umschließt jede neue Middleware die Anwendung und bildet einen Stapel. Die zuletzt hinzugefügte Middleware ist die *äußerste*, und die erste ist die *innerste*.
+
+Auf dem Requestpfad läuft die *äußerste* Middleware zuerst.
+
+Auf dem Responsepfad läuft sie zuletzt.
+
+Zum Beispiel:
+
+```Python
+app.add_middleware(MiddlewareA)
+app.add_middleware(MiddlewareB)
```
-## Andere Middlewares
+Dies führt zu folgender Ausführungsreihenfolge:
-Sie können später mehr über andere Middlewares in [Handbuch für fortgeschrittene Benutzer: Fortgeschrittene Middleware](../advanced/middleware.md){.internal-link target=_blank} lesen.
+* **Request**: MiddlewareB → MiddlewareA → Route
-In der nächsten Sektion erfahren Sie, wie Sie CORS mit einer Middleware behandeln können.
+* **Response**: Route → MiddlewareA → MiddlewareB
+
+Dieses Stapelverhalten stellt sicher, dass Middlewares in einer vorhersehbaren und kontrollierbaren Reihenfolge ausgeführt werden.
+
+## Andere Middlewares { #other-middlewares }
+
+Sie können später mehr über andere Middlewares im [Handbuch für fortgeschrittene Benutzer: Fortgeschrittene Middleware](../advanced/middleware.md){.internal-link target=_blank} lesen.
+
+In der nächsten Sektion erfahren Sie, wie Sie CORS mit einer Middleware behandeln können.
diff --git a/docs/de/docs/tutorial/path-operation-configuration.md b/docs/de/docs/tutorial/path-operation-configuration.md
index 03980b7dd0..c483f4e405 100644
--- a/docs/de/docs/tutorial/path-operation-configuration.md
+++ b/docs/de/docs/tutorial/path-operation-configuration.md
@@ -1,48 +1,26 @@
-# Pfadoperation-Konfiguration
+# Pfadoperation-Konfiguration { #path-operation-configuration }
-Es gibt mehrere Konfigurations-Parameter, die Sie Ihrem *Pfadoperation-Dekorator* übergeben können.
+Es gibt mehrere Parameter, die Sie Ihrem *Pfadoperation-Dekorator* übergeben können, um ihn zu konfigurieren.
-/// warning | "Achtung"
+/// warning | Achtung
Beachten Sie, dass diese Parameter direkt dem *Pfadoperation-Dekorator* übergeben werden, nicht der *Pfadoperation-Funktion*.
///
-## Response-Statuscode
+## Response-Statuscode { #response-status-code }
-Sie können den (HTTP-)`status_code` definieren, den die Response Ihrer *Pfadoperation* verwenden soll.
+Sie können den (HTTP-)`status_code` definieren, der in der Response Ihrer *Pfadoperation* verwendet werden soll.
Sie können direkt den `int`-Code übergeben, etwa `404`.
-Aber falls Sie sich nicht mehr erinnern, wofür jede Nummer steht, können Sie die Abkürzungs-Konstanten in `status` verwenden:
+Aber falls Sie sich nicht mehr erinnern, wofür jeder Nummerncode steht, können Sie die Abkürzungs-Konstanten in `status` verwenden:
-//// tab | Python 3.10+
-
-```Python hl_lines="1 15"
-{!> ../../../docs_src/path_operation_configuration/tutorial001_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="3 17"
-{!> ../../../docs_src/path_operation_configuration/tutorial001_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="3 17"
-{!> ../../../docs_src/path_operation_configuration/tutorial001.py!}
-```
-
-////
+{* ../../docs_src/path_operation_configuration/tutorial001_py310.py hl[1,15] *}
Dieser Statuscode wird in der Response verwendet und zum OpenAPI-Schema hinzugefügt.
-/// note | "Technische Details"
+/// note | Technische Details
Sie können auch `from starlette import status` verwenden.
@@ -50,147 +28,57 @@ Sie können auch `from starlette import status` verwenden.
///
-## Tags
+## Tags { #tags }
-Sie können Ihrer *Pfadoperation* Tags hinzufügen, mittels des Parameters `tags`, dem eine `list`e von `str`s übergeben wird (in der Regel nur ein `str`):
+Sie können Ihrer *Pfadoperation* Tags hinzufügen, indem Sie dem Parameter `tags` eine `list`e von `str`s übergeben (in der Regel nur ein `str`):
-//// tab | Python 3.10+
-
-```Python hl_lines="15 20 25"
-{!> ../../../docs_src/path_operation_configuration/tutorial002_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="17 22 27"
-{!> ../../../docs_src/path_operation_configuration/tutorial002_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="17 22 27"
-{!> ../../../docs_src/path_operation_configuration/tutorial002.py!}
-```
-
-////
+{* ../../docs_src/path_operation_configuration/tutorial002_py310.py hl[15,20,25] *}
Diese werden zum OpenAPI-Schema hinzugefügt und von den automatischen Dokumentations-Benutzeroberflächen verwendet:
-### Tags mittels Enumeration
+### Tags mittels Enumeration { #tags-with-enums }
-Wenn Sie eine große Anwendung haben, können sich am Ende **viele Tags** anhäufen, und Sie möchten sicherstellen, dass Sie für verwandte *Pfadoperationen* immer den **gleichen Tag** nehmen.
+Wenn Sie eine große Anwendung haben, können sich am Ende **viele Tags** anhäufen, und Sie möchten sicherstellen, dass Sie für verwandte *Pfadoperationen* immer den **gleichen Tag** verwenden.
In diesem Fall macht es Sinn, die Tags in einem `Enum` zu speichern.
-**FastAPI** unterstützt diese genauso wie einfache Strings:
+**FastAPI** unterstützt das auf die gleiche Weise wie einfache Strings:
-```Python hl_lines="1 8-10 13 18"
-{!../../../docs_src/path_operation_configuration/tutorial002b.py!}
-```
+{* ../../docs_src/path_operation_configuration/tutorial002b.py hl[1,8:10,13,18] *}
-## Zusammenfassung und Beschreibung
+## Zusammenfassung und Beschreibung { #summary-and-description }
-Sie können eine Zusammenfassung (`summary`) und eine Beschreibung (`description`) hinzufügen:
+Sie können eine `summary` und eine `description` hinzufügen:
-//// tab | Python 3.10+
+{* ../../docs_src/path_operation_configuration/tutorial003_py310.py hl[18:19] *}
-```Python hl_lines="18-19"
-{!> ../../../docs_src/path_operation_configuration/tutorial003_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="20-21"
-{!> ../../../docs_src/path_operation_configuration/tutorial003_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="20-21"
-{!> ../../../docs_src/path_operation_configuration/tutorial003.py!}
-```
-
-////
-
-## Beschreibung mittels Docstring
+## Beschreibung mittels Docstring { #description-from-docstring }
Da Beschreibungen oft mehrere Zeilen lang sind, können Sie die Beschreibung der *Pfadoperation* im Docstring der Funktion deklarieren, und **FastAPI** wird sie daraus auslesen.
-Sie können im Docstring Markdown schreiben, es wird korrekt interpretiert und angezeigt (die Einrückung des Docstring beachtend).
+Sie können Markdown im Docstring schreiben, es wird korrekt interpretiert und angezeigt (unter Berücksichtigung der Einrückung des Docstring).
-//// tab | Python 3.10+
+{* ../../docs_src/path_operation_configuration/tutorial004_py310.py hl[17:25] *}
-```Python hl_lines="17-25"
-{!> ../../../docs_src/path_operation_configuration/tutorial004_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="19-27"
-{!> ../../../docs_src/path_operation_configuration/tutorial004_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="19-27"
-{!> ../../../docs_src/path_operation_configuration/tutorial004.py!}
-```
-
-////
-
-In der interaktiven Dokumentation sieht das dann so aus:
+Es wird in der interaktiven Dokumentation verwendet:
-## Beschreibung der Response
+## Beschreibung der Response { #response-description }
-Die Response können Sie mit dem Parameter `response_description` beschreiben:
+Sie können die Response mit dem Parameter `response_description` beschreiben:
-//// tab | Python 3.10+
+{* ../../docs_src/path_operation_configuration/tutorial005_py310.py hl[19] *}
-```Python hl_lines="19"
-{!> ../../../docs_src/path_operation_configuration/tutorial005_py310.py!}
-```
+/// info | Info
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="21"
-{!> ../../../docs_src/path_operation_configuration/tutorial005_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="21"
-{!> ../../../docs_src/path_operation_configuration/tutorial005.py!}
-```
-
-////
-
-/// info
-
-beachten Sie, dass sich `response_description` speziell auf die Response bezieht, während `description` sich generell auf die *Pfadoperation* bezieht.
+Beachten Sie, dass sich `response_description` speziell auf die Response bezieht, während `description` sich generell auf die *Pfadoperation* bezieht.
///
-/// check
+/// check | Testen
OpenAPI verlangt, dass jede *Pfadoperation* über eine Beschreibung der Response verfügt.
@@ -200,15 +88,13 @@ Daher, wenn Sie keine vergeben, wird **FastAPI** automatisch eine für „Erfolg
-## Eine *Pfadoperation* deprecaten
+## Eine *Pfadoperation* deprecaten { #deprecate-a-path-operation }
-Wenn Sie eine *Pfadoperation* als deprecated kennzeichnen möchten, ohne sie zu entfernen, fügen Sie den Parameter `deprecated` hinzu:
+Wenn Sie eine *Pfadoperation* als deprecatet kennzeichnen möchten, ohne sie zu entfernen, fügen Sie den Parameter `deprecated` hinzu:
-```Python hl_lines="16"
-{!../../../docs_src/path_operation_configuration/tutorial006.py!}
-```
+{* ../../docs_src/path_operation_configuration/tutorial006.py hl[16] *}
-Sie wird in der interaktiven Dokumentation gut sichtbar als deprecated markiert werden:
+Sie wird in der interaktiven Dokumentation gut sichtbar als deprecatet markiert werden:
@@ -216,6 +102,6 @@ Vergleichen Sie, wie deprecatete und nicht-deprecatete *Pfadoperationen* aussehe
-## Zusammenfassung
+## Zusammenfassung { #recap }
Sie können auf einfache Weise Metadaten für Ihre *Pfadoperationen* definieren, indem Sie den *Pfadoperation-Dekoratoren* Parameter hinzufügen.
diff --git a/docs/de/docs/tutorial/path-params-numeric-validations.md b/docs/de/docs/tutorial/path-params-numeric-validations.md
index 3908a0b2d5..5b74749447 100644
--- a/docs/de/docs/tutorial/path-params-numeric-validations.md
+++ b/docs/de/docs/tutorial/path-params-numeric-validations.md
@@ -1,383 +1,154 @@
-# Pfad-Parameter und Validierung von Zahlen
+# Pfad-Parameter und Validierung von Zahlen { #path-parameters-and-numeric-validations }
-So wie Sie mit `Query` für Query-Parameter zusätzliche Validierungen und Metadaten hinzufügen können, können Sie das mittels `Path` auch für Pfad-Parameter tun.
+So wie Sie mit `Query` für Query-Parameter zusätzliche Validierungen und Metadaten deklarieren können, können Sie mit `Path` die gleichen Validierungen und Metadaten für Pfad-Parameter deklarieren.
-## `Path` importieren
+## `Path` importieren { #import-path }
-Importieren Sie zuerst `Path` von `fastapi`, und importieren Sie `Annotated`.
+Importieren Sie zuerst `Path` von `fastapi`, und importieren Sie `Annotated`:
-//// tab | Python 3.10+
+{* ../../docs_src/path_params_numeric_validations/tutorial001_an_py310.py hl[1,3] *}
-```Python hl_lines="1 3"
-{!> ../../../docs_src/path_params_numeric_validations/tutorial001_an_py310.py!}
-```
+/// info | Info
-////
+FastAPI hat in Version 0.95.0 Unterstützung für `Annotated` hinzugefügt und es zur Verwendung empfohlen.
-//// tab | Python 3.9+
+Wenn Sie eine ältere Version haben, würden Fehler angezeigt werden, wenn Sie versuchen, `Annotated` zu verwenden.
-```Python hl_lines="1 3"
-{!> ../../../docs_src/path_params_numeric_validations/tutorial001_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="3-4"
-{!> ../../../docs_src/path_params_numeric_validations/tutorial001_an.py!}
-```
-
-////
-
-//// tab | Python 3.10+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
+Stellen Sie sicher, dass Sie [FastAPI aktualisieren](../deployment/versions.md#upgrading-the-fastapi-versions){.internal-link target=_blank}, auf mindestens Version 0.95.1, bevor Sie `Annotated` verwenden.
///
-```Python hl_lines="1"
-{!> ../../../docs_src/path_params_numeric_validations/tutorial001_py310.py!}
-```
+## Metadaten deklarieren { #declare-metadata }
-////
+Sie können dieselben Parameter wie für `Query` deklarieren.
-//// tab | Python 3.8+ nicht annotiert
+Um zum Beispiel einen `title`-Metadaten-Wert für den Pfad-Parameter `item_id` zu deklarieren, können Sie schreiben:
-/// tip | "Tipp"
+{* ../../docs_src/path_params_numeric_validations/tutorial001_an_py310.py hl[10] *}
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
+/// note | Hinweis
+
+Ein Pfad-Parameter ist immer erforderlich, da er Teil des Pfads sein muss. Selbst wenn Sie ihn mit `None` deklarieren oder einen Defaultwert setzen, würde das nichts ändern, er wäre dennoch immer erforderlich.
///
-```Python hl_lines="3"
-{!> ../../../docs_src/path_params_numeric_validations/tutorial001.py!}
-```
+## Die Parameter sortieren, wie Sie möchten { #order-the-parameters-as-you-need }
-////
+/// tip | Tipp
-/// info
-
-FastAPI unterstützt (und empfiehlt die Verwendung von) `Annotated` seit Version 0.95.0.
-
-Wenn Sie eine ältere Version haben, werden Sie Fehler angezeigt bekommen, wenn Sie versuchen, `Annotated` zu verwenden.
-
-Bitte [aktualisieren Sie FastAPI](../deployment/versions.md#upgrade-der-fastapi-versionen){.internal-link target=_blank} daher mindestens zu Version 0.95.1, bevor Sie `Annotated` verwenden.
+Das ist wahrscheinlich nicht so wichtig oder notwendig, wenn Sie `Annotated` verwenden.
///
-## Metadaten deklarieren
+Angenommen, Sie möchten den Query-Parameter `q` als erforderlichen `str` deklarieren.
-Sie können die gleichen Parameter deklarieren wie für `Query`.
+Und Sie müssen sonst nichts anderes für diesen Parameter deklarieren, Sie brauchen also `Query` nicht wirklich.
-Um zum Beispiel einen `title`-Metadaten-Wert für den Pfad-Parameter `item_id` zu deklarieren, schreiben Sie:
+Aber Sie müssen dennoch `Path` für den `item_id`-Pfad-Parameter verwenden. Und aus irgendeinem Grund möchten Sie `Annotated` nicht verwenden.
-//// tab | Python 3.10+
+Python wird sich beschweren, wenn Sie einen Wert mit einem „Default“ vor einem Wert ohne „Default“ setzen.
-```Python hl_lines="10"
-{!> ../../../docs_src/path_params_numeric_validations/tutorial001_an_py310.py!}
-```
+Aber Sie können die Reihenfolge ändern und den Wert ohne Default (den Query-Parameter `q`) zuerst setzen.
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="10"
-{!> ../../../docs_src/path_params_numeric_validations/tutorial001_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="11"
-{!> ../../../docs_src/path_params_numeric_validations/tutorial001_an.py!}
-```
-
-////
-
-//// tab | Python 3.10+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="8"
-{!> ../../../docs_src/path_params_numeric_validations/tutorial001_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="10"
-{!> ../../../docs_src/path_params_numeric_validations/tutorial001.py!}
-```
-
-////
-
-/// note | "Hinweis"
-
-Ein Pfad-Parameter ist immer erforderlich, weil er Teil des Pfads sein muss.
-
-Sie sollten ihn daher mit `...` deklarieren, um ihn als erforderlich auszuzeichnen.
-
-Doch selbst wenn Sie ihn mit `None` deklarieren, oder einen Defaultwert setzen, bewirkt das nichts, er bleibt immer erforderlich.
-
-///
-
-## Sortieren Sie die Parameter, wie Sie möchten
-
-/// tip | "Tipp"
-
-Wenn Sie `Annotated` verwenden, ist das folgende nicht so wichtig / nicht notwendig.
-
-///
-
-Nehmen wir an, Sie möchten den Query-Parameter `q` als erforderlichen `str` deklarieren.
-
-Und Sie müssen sonst nichts anderes für den Parameter deklarieren, Sie brauchen also nicht wirklich `Query`.
-
-Aber Sie brauchen `Path` für den `item_id`-Pfad-Parameter. Und Sie möchten aus irgendeinem Grund nicht `Annotated` verwenden.
-
-Python wird sich beschweren, wenn Sie einen Parameter mit Defaultwert vor einen Parameter ohne Defaultwert setzen.
-
-Aber Sie können die Reihenfolge der Parameter ändern, den Query-Parameter ohne Defaultwert zuerst.
-
-Für **FastAPI** ist es nicht wichtig. Es erkennt die Parameter anhand ihres Namens, ihrer Typen, und ihrer Defaultwerte (`Query`, `Path`, usw.). Es kümmert sich nicht um die Reihenfolge.
+Für **FastAPI** spielt es keine Rolle. Es erkennt die Parameter anhand ihrer Namen, Typen und Default-Deklarationen (`Query`, `Path`, usw.), es kümmert sich nicht um die Reihenfolge.
Sie können Ihre Funktion also so deklarieren:
-//// tab | Python 3.8 nicht annotiert
+{* ../../docs_src/path_params_numeric_validations/tutorial002.py hl[7] *}
-/// tip | "Tipp"
+Aber bedenken Sie, dass Sie dieses Problem nicht haben, wenn Sie `Annotated` verwenden, da es nicht darauf ankommt, dass Sie keine Funktionsparameter-Defaultwerte für `Query()` oder `Path()` verwenden.
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
+{* ../../docs_src/path_params_numeric_validations/tutorial002_an_py39.py *}
+
+## Die Parameter sortieren, wie Sie möchten: Tricks { #order-the-parameters-as-you-need-tricks }
+
+/// tip | Tipp
+
+Das ist wahrscheinlich nicht so wichtig oder notwendig, wenn Sie `Annotated` verwenden.
///
-```Python hl_lines="7"
-{!> ../../../docs_src/path_params_numeric_validations/tutorial002.py!}
-```
+Hier ist ein **kleiner Trick**, der nützlich sein kann, obwohl Sie ihn nicht oft benötigen werden.
-////
+Wenn Sie:
-Aber bedenken Sie, dass Sie dieses Problem nicht haben, wenn Sie `Annotated` verwenden, da Sie nicht die Funktions-Parameter-Defaultwerte für `Query()` oder `Path()` verwenden.
+* den `q`-Query-Parameter sowohl ohne `Query` als auch ohne Defaultwert deklarieren
+* den Pfad-Parameter `item_id` mit `Path` deklarieren
+* sie in einer anderen Reihenfolge haben
+* nicht `Annotated` verwenden
-//// tab | Python 3.9+
+... möchten, dann hat Python eine kleine Spezial-Syntax dafür.
-```Python hl_lines="10"
-{!> ../../../docs_src/path_params_numeric_validations/tutorial002_an_py39.py!}
-```
+Übergeben Sie `*`, als den ersten Parameter der Funktion.
-////
+Python wird nichts mit diesem `*` machen, aber es wird wissen, dass alle folgenden Parameter als Schlüsselwortargumente (Schlüssel-Wert-Paare) verwendet werden sollen, auch bekannt als kwargs. Selbst wenn diese keinen Defaultwert haben.
-//// tab | Python 3.8+
+{* ../../docs_src/path_params_numeric_validations/tutorial003.py hl[7] *}
-```Python hl_lines="9"
-{!> ../../../docs_src/path_params_numeric_validations/tutorial002_an.py!}
-```
+### Besser mit `Annotated` { #better-with-annotated }
-////
+Bedenken Sie, dass Sie, wenn Sie `Annotated` verwenden, da Sie keine Funktionsparameter-Defaultwerte verwenden, dieses Problem nicht haben werden und wahrscheinlich nicht `*` verwenden müssen.
-## Sortieren Sie die Parameter wie Sie möchten: Tricks
+{* ../../docs_src/path_params_numeric_validations/tutorial003_an_py39.py hl[10] *}
-/// tip | "Tipp"
+## Validierung von Zahlen: Größer oder gleich { #number-validations-greater-than-or-equal }
-Wenn Sie `Annotated` verwenden, ist das folgende nicht so wichtig / nicht notwendig.
+Mit `Query` und `Path` (und anderen, die Sie später sehen werden) können Sie Zahlenbeschränkungen deklarieren.
+
+Hier, mit `ge=1`, muss `item_id` eine ganze Zahl sein, die „`g`reater than or `e`qual to“ (größer oder gleich) `1` ist.
+
+{* ../../docs_src/path_params_numeric_validations/tutorial004_an_py39.py hl[10] *}
+
+## Validierung von Zahlen: Größer und kleiner oder gleich { #number-validations-greater-than-and-less-than-or-equal }
+
+Das Gleiche gilt für:
+
+* `gt`: `g`reater `t`han (größer als)
+* `le`: `l`ess than or `e`qual (kleiner oder gleich)
+
+{* ../../docs_src/path_params_numeric_validations/tutorial005_an_py39.py hl[10] *}
+
+## Validierung von Zahlen: Floats, größer und kleiner { #number-validations-floats-greater-than-and-less-than }
+
+Zahlenvalidierung funktioniert auch für `float`-Werte.
+
+Hier wird es wichtig, in der Lage zu sein, gt und nicht nur ge zu deklarieren. Da Sie mit dieser Option erzwingen können, dass ein Wert größer als `0` sein muss, selbst wenn er kleiner als `1` ist.
+
+Also wäre `0.5` ein gültiger Wert. Aber `0.0` oder `0` nicht.
+
+Und das Gleiche gilt für lt.
+
+{* ../../docs_src/path_params_numeric_validations/tutorial006_an_py39.py hl[13] *}
+
+## Zusammenfassung { #recap }
+
+Mit `Query`, `Path` (und anderen, die Sie noch nicht gesehen haben) können Sie Metadaten und Stringvalidierungen auf die gleichen Weisen deklarieren wie in [Query-Parameter und Stringvalidierungen](query-params-str-validations.md){.internal-link target=_blank} beschrieben.
+
+Und Sie können auch Zahlenvalidierungen deklarieren:
+
+* `gt`: `g`reater `t`han (größer als)
+* `ge`: `g`reater than or `e`qual (größer oder gleich)
+* `lt`: `l`ess `t`han (kleiner als)
+* `le`: `l`ess than or `e`qual (kleiner oder gleich)
+
+/// info | Info
+
+`Query`, `Path`, und andere Klassen, die Sie später sehen werden, sind Unterklassen einer gemeinsamen `Param`-Klasse.
+
+Alle von ihnen teilen die gleichen Parameter für zusätzliche Validierung und Metadaten, die Sie gesehen haben.
///
-Hier ein **kleiner Trick**, der nützlich sein kann, aber Sie werden ihn nicht oft brauchen.
+/// note | Technische Details
-Wenn Sie eines der folgenden Dinge tun möchten:
+Wenn Sie `Query`, `Path` und andere von `fastapi` importieren, sind sie tatsächlich Funktionen.
-* den `q`-Parameter ohne `Query` oder irgendeinem Defaultwert deklarieren
-* den Pfad-Parameter `item_id` mittels `Path` deklarieren
-* die Parameter in einer unterschiedlichen Reihenfolge haben
-* `Annotated` nicht verwenden
+Die, wenn sie aufgerufen werden, Instanzen von Klassen mit demselben Namen zurückgeben.
-... dann hat Python eine kleine Spezial-Syntax für Sie.
-
-Übergeben Sie der Funktion `*` als ersten Parameter.
-
-Python macht nichts mit diesem `*`, aber es wird wissen, dass alle folgenden Parameter als Keyword-Argumente (Schlüssel-Wert-Paare), auch bekannt als kwargs, verwendet werden. Selbst wenn diese keinen Defaultwert haben.
-
-```Python hl_lines="7"
-{!../../../docs_src/path_params_numeric_validations/tutorial003.py!}
-```
-
-### Besser mit `Annotated`
-
-Bedenken Sie, dass Sie, wenn Sie `Annotated` verwenden, dieses Problem nicht haben, weil Sie keine Defaultwerte für Ihre Funktionsparameter haben. Sie müssen daher wahrscheinlich auch nicht `*` verwenden.
-
-//// tab | Python 3.9+
-
-```Python hl_lines="10"
-{!> ../../../docs_src/path_params_numeric_validations/tutorial003_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="9"
-{!> ../../../docs_src/path_params_numeric_validations/tutorial003_an.py!}
-```
-
-////
-
-## Validierung von Zahlen: Größer oder gleich
-
-Mit `Query` und `Path` (und anderen, die Sie später kennenlernen), können Sie Zahlenbeschränkungen deklarieren.
-
-Hier, mit `ge=1`, wird festgelegt, dass `item_id` eine Ganzzahl benötigt, die größer oder gleich `1` ist (`g`reater than or `e`qual).
-//// tab | Python 3.9+
-
-```Python hl_lines="10"
-{!> ../../../docs_src/path_params_numeric_validations/tutorial004_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="9"
-{!> ../../../docs_src/path_params_numeric_validations/tutorial004_an.py!}
-```
-
-////
-
-//// tab | Python 3.8+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="8"
-{!> ../../../docs_src/path_params_numeric_validations/tutorial004.py!}
-```
-
-////
-
-## Validierung von Zahlen: Größer und kleiner oder gleich
-
-Das Gleiche trifft zu auf:
-
-* `gt`: `g`reater `t`han – größer als
-* `le`: `l`ess than or `e`qual – kleiner oder gleich
-
-//// tab | Python 3.9+
-
-```Python hl_lines="10"
-{!> ../../../docs_src/path_params_numeric_validations/tutorial005_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="9"
-{!> ../../../docs_src/path_params_numeric_validations/tutorial005_an.py!}
-```
-
-////
-
-//// tab | Python 3.8+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="9"
-{!> ../../../docs_src/path_params_numeric_validations/tutorial005.py!}
-```
-
-////
-
-## Validierung von Zahlen: Floats, größer und kleiner
-
-Zahlenvalidierung funktioniert auch für `float`-Werte.
-
-Hier wird es wichtig, in der Lage zu sein, gt zu deklarieren, und nicht nur ge, da Sie hiermit bestimmen können, dass ein Wert, zum Beispiel, größer als `0` sein muss, obwohl er kleiner als `1` ist.
-
-`0.5` wäre also ein gültiger Wert, aber nicht `0.0` oder `0`.
-
-Das gleiche gilt für lt.
-
-//// tab | Python 3.9+
-
-```Python hl_lines="13"
-{!> ../../../docs_src/path_params_numeric_validations/tutorial006_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="12"
-{!> ../../../docs_src/path_params_numeric_validations/tutorial006_an.py!}
-```
-
-////
-
-//// tab | Python 3.8+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="11"
-{!> ../../../docs_src/path_params_numeric_validations/tutorial006.py!}
-```
-
-////
-
-## Zusammenfassung
-
-Mit `Query` und `Path` (und anderen, die Sie noch nicht gesehen haben) können Sie Metadaten und Stringvalidierungen deklarieren, so wie in [Query-Parameter und Stringvalidierungen](query-params-str-validations.md){.internal-link target=_blank} beschrieben.
-
-Und Sie können auch Validierungen für Zahlen deklarieren:
-
-* `gt`: `g`reater `t`han – größer als
-* `ge`: `g`reater than or `e`qual – größer oder gleich
-* `lt`: `l`ess `t`han – kleiner als
-* `le`: `l`ess than or `e`qual – kleiner oder gleich
-
-/// info
-
-`Query`, `Path`, und andere Klassen, die Sie später kennenlernen, sind Unterklassen einer allgemeinen `Param`-Klasse.
-
-Sie alle teilen die gleichen Parameter für zusätzliche Validierung und Metadaten, die Sie gesehen haben.
-
-///
-
-/// note | "Technische Details"
-
-`Query`, `Path` und andere, die Sie von `fastapi` importieren, sind tatsächlich Funktionen.
-
-Die, wenn sie aufgerufen werden, Instanzen der Klassen mit demselben Namen zurückgeben.
-
-Sie importieren also `Query`, welches eine Funktion ist. Aber wenn Sie es aufrufen, gibt es eine Instanz der Klasse zurück, die auch `Query` genannt wird.
+Sie importieren also `Query`, was eine Funktion ist. Und wenn Sie sie aufrufen, gibt sie eine Instanz einer Klasse zurück, die auch `Query` genannt wird.
Diese Funktionen existieren (statt die Klassen direkt zu verwenden), damit Ihr Editor keine Fehlermeldungen über ihre Typen ausgibt.
-Auf diese Weise können Sie Ihren Editor und Ihre Programmier-Tools verwenden, ohne besondere Einstellungen vornehmen zu müssen, um diese Fehlermeldungen stummzuschalten.
+Auf diese Weise können Sie Ihren normalen Editor und Ihre Programmier-Tools verwenden, ohne besondere Einstellungen vornehmen zu müssen, um diese Fehlermeldungen stummzuschalten.
///
diff --git a/docs/de/docs/tutorial/path-params.md b/docs/de/docs/tutorial/path-params.md
index 2c1b691a80..1db288fb87 100644
--- a/docs/de/docs/tutorial/path-params.md
+++ b/docs/de/docs/tutorial/path-params.md
@@ -1,36 +1,32 @@
-# Pfad-Parameter
+# Pfad-Parameter { #path-parameters }
-Sie können Pfad-„Parameter“ oder -„Variablen“ mit der gleichen Syntax deklarieren, welche in Python-Format-Strings verwendet wird:
+Sie können Pfad-„Parameter“ oder -„Variablen“ mit der gleichen Syntax deklarieren, welche in Python-Formatstrings verwendet wird:
-```Python hl_lines="6-7"
-{!../../../docs_src/path_params/tutorial001.py!}
-```
+{* ../../docs_src/path_params/tutorial001.py hl[6:7] *}
Der Wert des Pfad-Parameters `item_id` wird Ihrer Funktion als das Argument `item_id` übergeben.
-Wenn Sie dieses Beispiel ausführen und auf http://127.0.0.1:8000/items/foo gehen, sehen Sie als Response:
+Wenn Sie dieses Beispiel ausführen und auf http://127.0.0.1:8000/items/foo gehen, sehen Sie als Response:
```JSON
{"item_id":"foo"}
```
-## Pfad-Parameter mit Typen
+## Pfad-Parameter mit Typen { #path-parameters-with-types }
Sie können den Typ eines Pfad-Parameters in der Argumentliste der Funktion deklarieren, mit Standard-Python-Typannotationen:
-```Python hl_lines="7"
-{!../../../docs_src/path_params/tutorial002.py!}
-```
+{* ../../docs_src/path_params/tutorial002.py hl[7] *}
In diesem Fall wird `item_id` als `int` deklariert, also als Ganzzahl.
-/// check
+/// check | Testen
Dadurch erhalten Sie Editor-Unterstützung innerhalb Ihrer Funktion, mit Fehlerprüfungen, Codevervollständigung, usw.
///
-## Daten-Konversion
+## Daten-Konversion { #data-conversion }
Wenn Sie dieses Beispiel ausführen und Ihren Browser unter http://127.0.0.1:8000/items/3 öffnen, sehen Sie als Response:
@@ -38,15 +34,15 @@ Wenn Sie dieses Beispiel ausführen und Ihren Browser unter „parsen“.
+Sprich, mit dieser Typdeklaration wird **FastAPI** den Request automatisch „parsen“.
///
-## Datenvalidierung
+## Datenvalidierung { #data-validation }
Wenn Sie aber im Browser http://127.0.0.1:8000/items/foo besuchen, erhalten Sie eine hübsche HTTP-Fehlermeldung:
@@ -60,8 +56,7 @@ Wenn Sie aber im Browser http://127.0.0.1:8000/items/4.2
-/// check
+/// check | Testen
Sprich, mit der gleichen Python-Typdeklaration gibt Ihnen **FastAPI** Datenvalidierung.
Beachten Sie, dass die Fehlermeldung auch direkt die Stelle anzeigt, wo die Validierung nicht erfolgreich war.
-Das ist unglaublich hilfreich, wenn Sie Code entwickeln und debuggen, welcher mit ihrer API interagiert.
+Das ist unglaublich hilfreich, wenn Sie Code entwickeln und debuggen, welcher mit Ihrer API interagiert.
///
-## Dokumentation
+## Dokumentation { #documentation }
Wenn Sie die Seite http://127.0.0.1:8000/docs in Ihrem Browser öffnen, sehen Sie eine automatische, interaktive API-Dokumentation:
-/// check
+/// check | Testen
Wiederum, mit dieser gleichen Python-Typdeklaration gibt Ihnen **FastAPI** eine automatische, interaktive Dokumentation (verwendet die Swagger-Benutzeroberfläche).
@@ -95,7 +90,7 @@ Beachten Sie, dass der Pfad-Parameter dort als Ganzzahl deklariert ist.
///
-## Nützliche Standards. Alternative Dokumentation
+## Nützliche Standards, alternative Dokumentation { #standards-based-benefits-alternative-documentation }
Und weil das generierte Schema vom OpenAPI-Standard kommt, gibt es viele kompatible Tools.
@@ -105,15 +100,15 @@ Zum Beispiel bietet **FastAPI** selbst eine alternative API-Dokumentation (verwe
Und viele weitere kompatible Tools. Inklusive Codegenerierung für viele Sprachen.
-## Pydantic
+## Pydantic { #pydantic }
-Die ganze Datenvalidierung wird hinter den Kulissen von Pydantic durchgeführt, Sie profitieren also von dessen Vorteilen. Und Sie wissen, dass Sie in guten Händen sind.
+Die ganze Datenvalidierung wird hinter den Kulissen von Pydantic durchgeführt, Sie profitieren also von dessen Vorteilen. Und Sie wissen, dass Sie in guten Händen sind.
-Sie können für Typ Deklarationen auch `str`, `float`, `bool` und viele andere komplexe Datentypen verwenden.
+Sie können für Typdeklarationen auch `str`, `float`, `bool` und viele andere komplexe Datentypen verwenden.
Mehrere davon werden wir in den nächsten Kapiteln erkunden.
-## Die Reihenfolge ist wichtig
+## Die Reihenfolge ist wichtig { #order-matters }
Wenn Sie *Pfadoperationen* erstellen, haben Sie manchmal einen fixen Pfad.
@@ -123,97 +118,83 @@ Und Sie haben auch einen Pfad `/users/{user_id}`, um Daten über einen spezifisc
Weil *Pfadoperationen* in ihrer Reihenfolge ausgewertet werden, müssen Sie sicherstellen, dass der Pfad `/users/me` vor `/users/{user_id}` deklariert wurde:
-```Python hl_lines="6 11"
-{!../../../docs_src/path_params/tutorial003.py!}
-```
+{* ../../docs_src/path_params/tutorial003.py hl[6,11] *}
Ansonsten würde der Pfad für `/users/{user_id}` auch `/users/me` auswerten, und annehmen, dass ein Parameter `user_id` mit dem Wert `"me"` übergeben wurde.
Sie können eine Pfadoperation auch nicht erneut definieren:
-```Python hl_lines="6 11"
-{!../../../docs_src/path_params/tutorial003b.py!}
-```
+{* ../../docs_src/path_params/tutorial003b.py hl[6,11] *}
Die erste Definition wird immer verwendet werden, da ihr Pfad zuerst übereinstimmt.
-## Vordefinierte Parameterwerte
+## Vordefinierte Parameterwerte { #predefined-values }
-Wenn Sie eine *Pfadoperation* haben, welche einen *Pfad-Parameter* hat, aber Sie wollen, dass dessen gültige Werte vordefiniert sind, können Sie ein Standard-Python `Enum` verwenden.
+Wenn Sie eine *Pfadoperation* haben, welche einen *Pfad-Parameter* hat, aber Sie wollen, dass dessen gültige Werte vordefiniert sind, können Sie ein Standard-Python `Enum` verwenden.
-### Erstellen Sie eine `Enum`-Klasse
+### Eine `Enum`-Klasse erstellen { #create-an-enum-class }
Importieren Sie `Enum` und erstellen Sie eine Unterklasse, die von `str` und `Enum` erbt.
-Indem Sie von `str` erben, weiß die API Dokumentation, dass die Werte des Enums vom Typ `str` sein müssen, und wird in der Lage sein, korrekt zu rendern.
+Indem Sie von `str` erben, weiß die API-Dokumentation, dass die Werte vom Typ `str` sein müssen, und wird in der Lage sein, korrekt zu rendern.
Erstellen Sie dann Klassen-Attribute mit festgelegten Werten, welches die erlaubten Werte sein werden:
-```Python hl_lines="1 6-9"
-{!../../../docs_src/path_params/tutorial005.py!}
-```
+{* ../../docs_src/path_params/tutorial005.py hl[1,6:9] *}
-/// info
+/// info | Info
-Enumerationen (oder kurz Enums) gibt es in Python seit Version 3.4.
+Enumerationen (oder Enums) gibt es in Python seit Version 3.4.
///
-/// tip | "Tipp"
+/// tip | Tipp
Falls Sie sich fragen, was „AlexNet“, „ResNet“ und „LeNet“ ist, das sind Namen von Modellen für maschinelles Lernen.
///
-### Deklarieren Sie einen *Pfad-Parameter*
+### Einen *Pfad-Parameter* deklarieren { #declare-a-path-parameter }
Dann erstellen Sie einen *Pfad-Parameter*, der als Typ die gerade erstellte Enum-Klasse hat (`ModelName`):
-```Python hl_lines="16"
-{!../../../docs_src/path_params/tutorial005.py!}
-```
+{* ../../docs_src/path_params/tutorial005.py hl[16] *}
-### Testen Sie es in der API-Dokumentation
+### Die API-Dokumentation testen { #check-the-docs }
Weil die erlaubten Werte für den *Pfad-Parameter* nun vordefiniert sind, kann die interaktive Dokumentation sie als Auswahl-Drop-Down anzeigen:
-### Mit Python-*Enums* arbeiten
+### Mit Python-*Enumerationen* arbeiten { #working-with-python-enumerations }
-Der *Pfad-Parameter* wird ein *Member eines Enums* sein.
+Der *Pfad-Parameter* wird ein *Member einer Enumeration* sein.
-#### *Enum-Member* vergleichen
+#### *Enumeration-Member* vergleichen { #compare-enumeration-members }
-Sie können ihn mit einem Member Ihres Enums `ModelName` vergleichen:
+Sie können ihn mit einem Member Ihrer Enumeration `ModelName` vergleichen:
-```Python hl_lines="17"
-{!../../../docs_src/path_params/tutorial005.py!}
-```
+{* ../../docs_src/path_params/tutorial005.py hl[17] *}
-#### *Enum-Wert* erhalten
+#### *Enumerations-Wert* erhalten { #get-the-enumeration-value }
-Den tatsächlichen Wert (in diesem Fall ein `str`) erhalten Sie via `model_name.value`, oder generell, `ihr_enum_member.value`:
+Den tatsächlichen Wert (in diesem Fall ein `str`) erhalten Sie via `model_name.value`, oder generell, `your_enum_member.value`:
-```Python hl_lines="20"
-{!../../../docs_src/path_params/tutorial005.py!}
-```
+{* ../../docs_src/path_params/tutorial005.py hl[20] *}
-/// tip | "Tipp"
+/// tip | Tipp
Sie können den Wert `"lenet"` außerdem mittels `ModelName.lenet.value` abrufen.
///
-#### *Enum-Member* zurückgeben
+#### *Enumeration-Member* zurückgeben { #return-enumeration-members }
Sie können *Enum-Member* in ihrer *Pfadoperation* zurückgeben, sogar verschachtelt in einem JSON-Body (z. B. als `dict`).
Diese werden zu ihren entsprechenden Werten konvertiert (in diesem Fall Strings), bevor sie zum Client übertragen werden:
-```Python hl_lines="18 21 23"
-{!../../../docs_src/path_params/tutorial005.py!}
-```
+{* ../../docs_src/path_params/tutorial005.py hl[18,21,23] *}
In Ihrem Client erhalten Sie eine JSON-Response, wie etwa:
@@ -224,7 +205,7 @@ In Ihrem Client erhalten Sie eine JSON-Response, wie etwa:
}
```
-## Pfad Parameter die Pfade enthalten
+## Pfad-Parameter, die Pfade enthalten { #path-parameters-containing-paths }
Angenommen, Sie haben eine *Pfadoperation* mit einem Pfad `/files/{file_path}`.
@@ -232,7 +213,7 @@ Aber `file_path` soll selbst einen *Pfad* enthalten, etwa `home/johndoe/myfile.t
Sprich, die URL für diese Datei wäre etwas wie: `/files/home/johndoe/myfile.txt`.
-### OpenAPI Unterstützung
+### OpenAPI-Unterstützung { #openapi-support }
OpenAPI bietet nicht die Möglichkeit, dass ein *Pfad-Parameter* seinerseits einen *Pfad* enthalten kann, das würde zu Szenarios führen, die schwierig zu testen und zu definieren sind.
@@ -240,7 +221,7 @@ Trotzdem können Sie das in **FastAPI** tun, indem Sie eines der internen Tools
Die Dokumentation würde weiterhin funktionieren, allerdings wird nicht dokumentiert werden, dass der Parameter ein Pfad sein sollte.
-### Pfad Konverter
+### Pfad-Konverter { #path-convertor }
Mittels einer Option direkt von Starlette können Sie einen *Pfad-Parameter* deklarieren, der einen Pfad enthalten soll, indem Sie eine URL wie folgt definieren:
@@ -252,11 +233,9 @@ In diesem Fall ist der Name des Parameters `file_path`. Der letzte Teil, `:path`
Sie verwenden das also wie folgt:
-```Python hl_lines="6"
-{!../../../docs_src/path_params/tutorial004.py!}
-```
+{* ../../docs_src/path_params/tutorial004.py hl[6] *}
-/// tip | "Tipp"
+/// tip | Tipp
Der Parameter könnte einen führenden Schrägstrich (`/`) haben, wie etwa in `/home/johndoe/myfile.txt`.
@@ -264,12 +243,12 @@ In dem Fall wäre die URL: `/files//home/johndoe/myfile.txt`, mit einem doppelte
///
-## Zusammenfassung
+## Zusammenfassung { #recap }
In **FastAPI** erhalten Sie mittels kurzer, intuitiver Typdeklarationen:
* Editor-Unterstützung: Fehlerprüfungen, Codevervollständigung, usw.
-* Daten "parsen"
+* Daten "parsen"
* Datenvalidierung
* API-Annotationen und automatische Dokumentation
diff --git a/docs/de/docs/tutorial/query-param-models.md b/docs/de/docs/tutorial/query-param-models.md
new file mode 100644
index 0000000000..7d3f2d32e8
--- /dev/null
+++ b/docs/de/docs/tutorial/query-param-models.md
@@ -0,0 +1,68 @@
+# Query-Parameter-Modelle { #query-parameter-models }
+
+Wenn Sie eine Gruppe von **Query-Parametern** haben, die miteinander in Beziehung stehen, können Sie ein **Pydantic-Modell** erstellen, um diese zu deklarieren.
+
+Dadurch können Sie das **Modell an mehreren Stellen wiederverwenden** und gleichzeitig Validierungen und Metadaten für alle Parameter auf einmal deklarieren. 😎
+
+/// note | Hinweis
+
+Dies wird seit FastAPI Version `0.115.0` unterstützt. 🤓
+
+///
+
+## Query-Parameter mit einem Pydantic-Modell { #query-parameters-with-a-pydantic-model }
+
+Deklarieren Sie die benötigten **Query-Parameter** in einem **Pydantic-Modell** und dann den Parameter als `Query`:
+
+{* ../../docs_src/query_param_models/tutorial001_an_py310.py hl[9:13,17] *}
+
+**FastAPI** wird die Daten für **jedes Feld** aus den **Query-Parametern** des Request extrahieren und Ihnen das definierte Pydantic-Modell bereitstellen.
+
+## Die Dokumentation testen { #check-the-docs }
+
+Sie können die Query-Parameter in der Dokumentations-Oberfläche unter `/docs` einsehen:
+
+
+
-### Query-Parameter-Liste / Mehrere Werte mit Defaults
+### Query-Parameter-Liste / Mehrere Werte mit Defaults { #query-parameter-list-multiple-values-with-defaults }
-Und Sie können auch eine Default-`list`e von Werten definieren, wenn keine übergeben werden:
+Sie können auch eine Default-`list` von Werten definieren, wenn keine bereitgestellt werden:
-//// tab | Python 3.9+
+{* ../../docs_src/query_params_str_validations/tutorial012_an_py39.py hl[9] *}
-```Python hl_lines="9"
-{!> ../../../docs_src/query_params_str_validations/tutorial012_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="10"
-{!> ../../../docs_src/query_params_str_validations/tutorial012_an.py!}
-```
-
-////
-
-//// tab | Python 3.9+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="7"
-{!> ../../../docs_src/query_params_str_validations/tutorial012_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="9"
-{!> ../../../docs_src/query_params_str_validations/tutorial012.py!}
-```
-
-////
-
-Wenn Sie auf:
+Wenn Sie zu:
```
http://localhost:8000/items/
```
-gehen, wird der Default für `q` verwendet: `["foo", "bar"]`, und als Response erhalten Sie:
+gehen, wird der Default für `q` sein: `["foo", "bar"]`, und Ihre Response wird sein:
```JSON
{
@@ -803,173 +325,45 @@ gehen, wird der Default für `q` verwendet: `["foo", "bar"]`, und als Response e
}
```
-#### `list` alleine verwenden
+#### Nur `list` verwenden { #using-just-list }
-Sie können auch `list` direkt verwenden, anstelle von `List[str]` (oder `list[str]` in Python 3.9+):
+Sie können auch `list` direkt verwenden, anstelle von `list[str]`:
-//// tab | Python 3.9+
+{* ../../docs_src/query_params_str_validations/tutorial013_an_py39.py hl[9] *}
-```Python hl_lines="9"
-{!> ../../../docs_src/query_params_str_validations/tutorial013_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="8"
-{!> ../../../docs_src/query_params_str_validations/tutorial013_an.py!}
-```
-
-////
-
-//// tab | Python 3.8+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="7"
-{!> ../../../docs_src/query_params_str_validations/tutorial013.py!}
-```
-
-////
-
-/// note | "Hinweis"
+/// note | Hinweis
Beachten Sie, dass FastAPI in diesem Fall den Inhalt der Liste nicht überprüft.
-Zum Beispiel würde `List[int]` überprüfen (und dokumentieren) dass die Liste Ganzzahlen enthält. `list` alleine macht das nicht.
+Zum Beispiel würde `list[int]` überprüfen (und dokumentieren), dass der Inhalt der Liste Ganzzahlen sind. Aber `list` alleine würde das nicht.
///
-## Deklarieren von mehr Metadaten
+## Mehr Metadaten deklarieren { #declare-more-metadata }
-Sie können mehr Informationen zum Parameter hinzufügen.
+Sie können mehr Informationen über den Parameter hinzufügen.
-Diese Informationen werden zur generierten OpenAPI hinzugefügt, und von den Dokumentations-Oberflächen und von externen Tools verwendet.
+Diese Informationen werden in das generierte OpenAPI aufgenommen und von den Dokumentationsoberflächen und externen Tools verwendet.
-/// note | "Hinweis"
+/// note | Hinweis
-Beachten Sie, dass verschiedene Tools OpenAPI möglicherweise unterschiedlich gut unterstützen.
+Beachten Sie, dass verschiedene Tools möglicherweise unterschiedliche Unterstützungslevels für OpenAPI haben.
-Einige könnten noch nicht alle zusätzlichen Informationen anzeigen, die Sie deklariert haben, obwohl in den meisten Fällen geplant ist, das fehlende Feature zu implementieren.
+Einige davon könnten noch nicht alle zusätzlichen Informationen anzuzeigen, die Sie erklärten, obwohl in den meisten Fällen die fehlende Funktionalität bereits in der Entwicklung geplant ist.
///
-Sie können einen Titel hinzufügen – `title`:
+Sie können einen `title` hinzufügen:
-//// tab | Python 3.10+
+{* ../../docs_src/query_params_str_validations/tutorial007_an_py310.py hl[10] *}
-```Python hl_lines="10"
-{!> ../../../docs_src/query_params_str_validations/tutorial007_an_py310.py!}
-```
+Und eine `description`:
-////
+{* ../../docs_src/query_params_str_validations/tutorial008_an_py310.py hl[14] *}
-//// tab | Python 3.9+
+## Alias-Parameter { #alias-parameters }
-```Python hl_lines="10"
-{!> ../../../docs_src/query_params_str_validations/tutorial007_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="11"
-{!> ../../../docs_src/query_params_str_validations/tutorial007_an.py!}
-```
-
-////
-
-//// tab | Python 3.10+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="8"
-{!> ../../../docs_src/query_params_str_validations/tutorial007_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="10"
-{!> ../../../docs_src/query_params_str_validations/tutorial007.py!}
-```
-
-////
-
-Und eine Beschreibung – `description`:
-
-//// tab | Python 3.10+
-
-```Python hl_lines="14"
-{!> ../../../docs_src/query_params_str_validations/tutorial008_an_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="14"
-{!> ../../../docs_src/query_params_str_validations/tutorial008_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="15"
-{!> ../../../docs_src/query_params_str_validations/tutorial008_an.py!}
-```
-
-////
-
-//// tab | Python 3.10+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="11"
-{!> ../../../docs_src/query_params_str_validations/tutorial008_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="13"
-{!> ../../../docs_src/query_params_str_validations/tutorial008.py!}
-```
-
-////
-
-## Alias-Parameter
-
-Stellen Sie sich vor, der Parameter soll `item-query` sein.
+Stellen Sie sich vor, Sie möchten, dass der Parameter `item-query` ist.
Wie in:
@@ -979,187 +373,99 @@ http://127.0.0.1:8000/items/?item-query=foobaritems
Aber `item-query` ist kein gültiger Name für eine Variable in Python.
-Am ähnlichsten wäre `item_query`.
+Der am ähnlichsten wäre `item_query`.
-Aber Sie möchten dennoch exakt `item-query` verwenden.
+Aber Sie benötigen dennoch, dass er genau `item-query` ist ...
-Dann können Sie einen `alias` deklarieren, und dieser Alias wird verwendet, um den Parameter-Wert zu finden:
+Dann können Sie ein `alias` deklarieren, und dieser Alias wird verwendet, um den Parameterwert zu finden:
-//// tab | Python 3.10+
+{* ../../docs_src/query_params_str_validations/tutorial009_an_py310.py hl[9] *}
-```Python hl_lines="9"
-{!> ../../../docs_src/query_params_str_validations/tutorial009_an_py310.py!}
-```
+## Parameter als deprecatet ausweisen { #deprecating-parameters }
-////
+Nehmen wir an, Ihnen gefällt dieser Parameter nicht mehr.
-//// tab | Python 3.9+
+Sie müssen ihn eine Weile dort belassen, da es Clients gibt, die ihn verwenden, aber Sie möchten, dass die Dokumentation ihn klar als deprecatet anzeigt.
-```Python hl_lines="9"
-{!> ../../../docs_src/query_params_str_validations/tutorial009_an_py39.py!}
-```
+Dann übergeben Sie den Parameter `deprecated=True` an `Query`:
-////
+{* ../../docs_src/query_params_str_validations/tutorial010_an_py310.py hl[19] *}
-//// tab | Python 3.8+
-
-```Python hl_lines="10"
-{!> ../../../docs_src/query_params_str_validations/tutorial009_an.py!}
-```
-
-////
-
-//// tab | Python 3.10+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="7"
-{!> ../../../docs_src/query_params_str_validations/tutorial009_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="9"
-{!> ../../../docs_src/query_params_str_validations/tutorial009.py!}
-```
-
-////
-
-## Parameter als deprecated ausweisen
-
-Nehmen wir an, Sie mögen diesen Parameter nicht mehr.
-
-Sie müssen ihn eine Weile dort belassen, weil Clients ihn benutzen, aber Sie möchten, dass die Dokumentation klar anzeigt, dass er deprecated ist.
-
-In diesem Fall fügen Sie den Parameter `deprecated=True` zu `Query` hinzu.
-
-//// tab | Python 3.10+
-
-```Python hl_lines="19"
-{!> ../../../docs_src/query_params_str_validations/tutorial010_an_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="19"
-{!> ../../../docs_src/query_params_str_validations/tutorial010_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="20"
-{!> ../../../docs_src/query_params_str_validations/tutorial010_an.py!}
-```
-
-////
-
-//// tab | Python 3.10+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="16"
-{!> ../../../docs_src/query_params_str_validations/tutorial010_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="18"
-{!> ../../../docs_src/query_params_str_validations/tutorial010.py!}
-```
-
-////
-
-Die Dokumentation wird das so anzeigen:
+Die Dokumentation wird es so anzeigen:
-## Parameter von OpenAPI ausschließen
+## Parameter von OpenAPI ausschließen { #exclude-parameters-from-openapi }
-Um einen Query-Parameter vom generierten OpenAPI-Schema auszuschließen (und daher von automatischen Dokumentations-Systemen), setzen Sie den Parameter `include_in_schema` in `Query` auf `False`.
+Um einen Query-Parameter aus dem generierten OpenAPI-Schema auszuschließen (und somit aus den automatischen Dokumentationssystemen), setzen Sie den Parameter `include_in_schema` von `Query` auf `False`:
-//// tab | Python 3.10+
+{* ../../docs_src/query_params_str_validations/tutorial014_an_py310.py hl[10] *}
-```Python hl_lines="10"
-{!> ../../../docs_src/query_params_str_validations/tutorial014_an_py310.py!}
-```
+## Benutzerdefinierte Validierung { #custom-validation }
-////
+Es kann Fälle geben, in denen Sie eine **benutzerdefinierte Validierung** durchführen müssen, die nicht mit den oben gezeigten Parametern durchgeführt werden kann.
-//// tab | Python 3.9+
+In diesen Fällen können Sie eine **benutzerdefinierte Validierungsfunktion** verwenden, die nach der normalen Validierung angewendet wird (z. B. nach der Validierung, dass der Wert ein `str` ist).
-```Python hl_lines="10"
-{!> ../../../docs_src/query_params_str_validations/tutorial014_an_py39.py!}
-```
+Sie können dies mit Pydantic's `AfterValidator` innerhalb von `Annotated` erreichen.
-////
+/// tip | Tipp
-//// tab | Python 3.8+
-
-```Python hl_lines="11"
-{!> ../../../docs_src/query_params_str_validations/tutorial014_an.py!}
-```
-
-////
-
-//// tab | Python 3.10+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
+Pydantic unterstützt auch `BeforeValidator` und andere. 🤓
///
-```Python hl_lines="8"
-{!> ../../../docs_src/query_params_str_validations/tutorial014_py310.py!}
-```
+Zum Beispiel überprüft dieser benutzerdefinierte Validator, ob die Artikel-ID mit `isbn-` für eine ISBN-Buchnummer oder mit `imdb-` für eine IMDB-Film-URL-ID beginnt:
-////
+{* ../../docs_src/query_params_str_validations/tutorial015_an_py310.py hl[5,16:19,24] *}
-//// tab | Python 3.8+ nicht annotiert
+/// info | Info
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
+Dies ist verfügbar seit Pydantic Version 2 oder höher. 😎
///
-```Python hl_lines="10"
-{!> ../../../docs_src/query_params_str_validations/tutorial014.py!}
-```
+/// tip | Tipp
-////
+Wenn Sie irgendeine Art von Validierung durchführen müssen, die eine Kommunikation mit einer **externen Komponente** erfordert, wie z. B. einer Datenbank oder einer anderen API, sollten Sie stattdessen **FastAPI-Abhängigkeiten** verwenden. Sie werden diese später kennenlernen.
-## Zusammenfassung
+Diese benutzerdefinierten Validatoren sind für Dinge gedacht, die einfach mit denselben **Daten** überprüft werden können, die im Request bereitgestellt werden.
-Sie können zusätzliche Validierungen und Metadaten zu ihren Parametern hinzufügen.
+///
+
+### Dieses Codebeispiel verstehen { #understand-that-code }
+
+Der wichtige Punkt ist einfach die Verwendung von **`AfterValidator` mit einer Funktion innerhalb von `Annotated`**. Fühlen Sie sich frei, diesen Teil zu überspringen. 🤸
+
+---
+
+Aber wenn Sie neugierig auf dieses spezielle Codebeispiel sind und immer noch Spaß haben, hier sind einige zusätzliche Details.
+
+#### Zeichenkette mit `value.startswith()` { #string-with-value-startswith }
+
+Haben Sie bemerkt? Eine Zeichenkette mit `value.startswith()` kann ein Tuple übernehmen, und es wird jeden Wert im Tuple überprüfen:
+
+{* ../../docs_src/query_params_str_validations/tutorial015_an_py310.py ln[16:19] hl[17] *}
+
+#### Ein zufälliges Item { #a-random-item }
+
+Mit `data.items()` erhalten wir ein iterierbares Objekt mit Tupeln, die Schlüssel und Wert für jedes Dictionary-Element enthalten.
+
+Wir konvertieren dieses iterierbare Objekt mit `list(data.items())` in eine richtige `list`.
+
+Dann können wir mit `random.choice()` einen **zufälligen Wert** aus der Liste erhalten, also bekommen wir ein Tuple mit `(id, name)`. Es wird etwas wie `("imdb-tt0371724", "The Hitchhiker's Guide to the Galaxy")` sein.
+
+Dann **weisen wir diese beiden Werte** des Tupels den Variablen `id` und `name` zu.
+
+Wenn der Benutzer also keine Artikel-ID bereitgestellt hat, erhält er trotzdem einen zufälligen Vorschlag.
+
+... wir tun all dies in einer **einzelnen einfachen Zeile**. 🤯 Lieben Sie nicht auch Python? 🐍
+
+{* ../../docs_src/query_params_str_validations/tutorial015_an_py310.py ln[22:30] hl[29] *}
+
+## Zusammenfassung { #recap }
+
+Sie können zusätzliche Validierungen und Metadaten für Ihre Parameter deklarieren.
Allgemeine Validierungen und Metadaten:
@@ -1168,12 +474,14 @@ Allgemeine Validierungen und Metadaten:
* `description`
* `deprecated`
-Validierungen spezifisch für Strings:
+Validierungen, die spezifisch für Strings sind:
* `min_length`
* `max_length`
* `pattern`
-In diesen Beispielen haben Sie gesehen, wie Sie Validierungen für Strings hinzufügen.
+Benutzerdefinierte Validierungen mit `AfterValidator`.
-In den nächsten Kapiteln sehen wir, wie man Validierungen für andere Typen hinzufügt, etwa für Zahlen.
+In diesen Beispielen haben Sie gesehen, wie Sie Validierungen für `str`-Werte deklarieren.
+
+Sehen Sie sich die nächsten Kapitel an, um zu erfahren, wie Sie Validierungen für andere Typen, wie z. B. Zahlen, deklarieren.
diff --git a/docs/de/docs/tutorial/query-params.md b/docs/de/docs/tutorial/query-params.md
index 136852216e..e46f31ad00 100644
--- a/docs/de/docs/tutorial/query-params.md
+++ b/docs/de/docs/tutorial/query-params.md
@@ -1,12 +1,10 @@
-# Query-Parameter
+# Query-Parameter { #query-parameters }
-Wenn Sie in ihrer Funktion Parameter deklarieren, die nicht Teil der Pfad-Parameter sind, dann werden diese automatisch als „Query“-Parameter interpretiert.
+Wenn Sie in Ihrer Funktion andere Parameter deklarieren, die nicht Teil der Pfad-Parameter sind, dann werden diese automatisch als „Query“-Parameter interpretiert.
-```Python hl_lines="9"
-{!../../../docs_src/query_params/tutorial001.py!}
-```
+{* ../../docs_src/query_params/tutorial001.py hl[9] *}
-Query-Parameter (Deutsch: Abfrage-Parameter) sind die Schlüssel-Wert-Paare, die nach dem `?` in einer URL aufgelistet sind, getrennt durch `&`-Zeichen.
+Die Query ist die Menge von Schlüssel-Wert-Paaren, die nach dem `?` in einer URL folgen und durch `&`-Zeichen getrennt sind.
Zum Beispiel sind in der URL:
@@ -21,18 +19,18 @@ http://127.0.0.1:8000/items/?skip=0&limit=10
Da sie Teil der URL sind, sind sie „naturgemäß“ Strings.
-Aber wenn Sie sie mit Python-Typen deklarieren (im obigen Beispiel als `int`), werden sie zu diesem Typ konvertiert, und gegen diesen validiert.
+Aber wenn Sie sie mit Python-Typen deklarieren (im obigen Beispiel als `int`), werden sie zu diesem Typ konvertiert und gegen diesen validiert.
-Die gleichen Prozesse, die für Pfad-Parameter stattfinden, werden auch auf Query-Parameter angewendet:
+Die gleichen Prozesse, die für Pfad-Parameter gelten, werden auch auf Query-Parameter angewendet:
* Editor Unterstützung (natürlich)
-* „Parsen“ der Daten
+* Daten-„Parsen“
* Datenvalidierung
* Automatische Dokumentation
-## Defaultwerte
+## Defaultwerte { #defaults }
-Da Query-Parameter nicht ein festgelegter Teil des Pfades sind, können sie optional sein und Defaultwerte haben.
+Da Query-Parameter kein fester Teil eines Pfades sind, können sie optional sein und Defaultwerte haben.
Im obigen Beispiel haben sie die Defaultwerte `skip=0` und `limit=10`.
@@ -54,58 +52,30 @@ Aber wenn Sie zum Beispiel zu:
http://127.0.0.1:8000/items/?skip=20
```
-gehen, werden die Parameter-Werte Ihrer Funktion sein:
+gehen, werden die Parameterwerte Ihrer Funktion sein:
* `skip=20`: da Sie das in der URL gesetzt haben
* `limit=10`: weil das der Defaultwert ist
-## Optionale Parameter
+## Optionale Parameter { #optional-parameters }
Auf die gleiche Weise können Sie optionale Query-Parameter deklarieren, indem Sie deren Defaultwert auf `None` setzen:
-//// tab | Python 3.10+
+{* ../../docs_src/query_params/tutorial002_py310.py hl[7] *}
-```Python hl_lines="7"
-{!> ../../../docs_src/query_params/tutorial002_py310.py!}
-```
+In diesem Fall wird der Funktionsparameter `q` optional und standardmäßig `None` sein.
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="9"
-{!> ../../../docs_src/query_params/tutorial002.py!}
-```
-
-////
-
-In diesem Fall wird der Funktionsparameter `q` optional, und standardmäßig `None` sein.
-
-/// check
+/// check | Testen
Beachten Sie auch, dass **FastAPI** intelligent genug ist, um zu erkennen, dass `item_id` ein Pfad-Parameter ist und `q` keiner, daher muss letzteres ein Query-Parameter sein.
///
-## Query-Parameter Typkonvertierung
+## Query-Parameter Typkonvertierung { #query-parameter-type-conversion }
-Sie können auch `bool`-Typen deklarieren und sie werden konvertiert:
+Sie können auch `bool`-Typen deklarieren, und sie werden konvertiert:
-//// tab | Python 3.10+
-
-```Python hl_lines="7"
-{!> ../../../docs_src/query_params/tutorial003_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="9"
-{!> ../../../docs_src/query_params/tutorial003.py!}
-```
-
-////
+{* ../../docs_src/query_params/tutorial003_py310.py hl[7] *}
Wenn Sie nun zu:
@@ -139,31 +109,17 @@ http://127.0.0.1:8000/items/foo?short=yes
gehen, oder zu irgendeiner anderen Variante der Groß-/Kleinschreibung (Alles groß, Anfangsbuchstabe groß, usw.), dann wird Ihre Funktion den Parameter `short` mit dem `bool`-Wert `True` sehen, ansonsten mit dem Wert `False`.
-## Mehrere Pfad- und Query-Parameter
+## Mehrere Pfad- und Query-Parameter { #multiple-path-and-query-parameters }
-Sie können mehrere Pfad-Parameter und Query-Parameter gleichzeitig deklarieren, **FastAPI** weiß, was welches ist.
+Sie können mehrere Pfad-Parameter und Query-Parameter gleichzeitig deklarieren, **FastAPI** weiß, welches welcher ist.
Und Sie müssen sie auch nicht in einer spezifischen Reihenfolge deklarieren.
Parameter werden anhand ihres Namens erkannt:
-//// tab | Python 3.10+
+{* ../../docs_src/query_params/tutorial004_py310.py hl[6,8] *}
-```Python hl_lines="6 8"
-{!> ../../../docs_src/query_params/tutorial004_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="8 10"
-{!> ../../../docs_src/query_params/tutorial004.py!}
-```
-
-////
-
-## Erforderliche Query-Parameter
+## Erforderliche Query-Parameter { #required-query-parameters }
Wenn Sie einen Defaultwert für Nicht-Pfad-Parameter deklarieren (Bis jetzt haben wir nur Query-Parameter gesehen), dann ist der Parameter nicht erforderlich.
@@ -171,9 +127,7 @@ Wenn Sie keinen spezifischen Wert haben wollen, sondern der Parameter einfach op
Aber wenn Sie wollen, dass ein Query-Parameter erforderlich ist, vergeben Sie einfach keinen Defaultwert:
-```Python hl_lines="6-7"
-{!../../../docs_src/query_params/tutorial005.py!}
-```
+{* ../../docs_src/query_params/tutorial005.py hl[6:7] *}
Hier ist `needy` ein erforderlicher Query-Parameter vom Typ `str`.
@@ -195,8 +149,7 @@ http://127.0.0.1:8000/items/foo-item
"needy"
],
"msg": "Field required",
- "input": null,
- "url": "https://errors.pydantic.dev/2.1/v/missing"
+ "input": null
}
]
}
@@ -219,21 +172,7 @@ http://127.0.0.1:8000/items/foo-item?needy=sooooneedy
Und natürlich können Sie einige Parameter als erforderlich, einige mit Defaultwert, und einige als vollständig optional definieren:
-//// tab | Python 3.10+
-
-```Python hl_lines="8"
-{!> ../../../docs_src/query_params/tutorial006_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="10"
-{!> ../../../docs_src/query_params/tutorial006.py!}
-```
-
-////
+{* ../../docs_src/query_params/tutorial006_py310.py hl[8] *}
In diesem Fall gibt es drei Query-Parameter:
@@ -241,8 +180,8 @@ In diesem Fall gibt es drei Query-Parameter:
* `skip`, ein `int` mit einem Defaultwert `0`.
* `limit`, ein optionales `int`.
-/// tip | "Tipp"
+/// tip | Tipp
-Sie können auch `Enum`s verwenden, auf die gleiche Weise wie mit [Pfad-Parametern](path-params.md#vordefinierte-parameterwerte){.internal-link target=_blank}.
+Sie können auch `Enum`s verwenden, auf die gleiche Weise wie mit [Pfad-Parametern](path-params.md#predefined-values){.internal-link target=_blank}.
///
diff --git a/docs/de/docs/tutorial/request-files.md b/docs/de/docs/tutorial/request-files.md
index cf44df5f0a..0aee898b9c 100644
--- a/docs/de/docs/tutorial/request-files.md
+++ b/docs/de/docs/tutorial/request-files.md
@@ -1,96 +1,44 @@
-# Dateien im Request
+# Dateien im Request { #request-files }
-Mit `File` können sie vom Client hochzuladende Dateien definieren.
+Sie können Dateien, die vom Client hochgeladen werden, mithilfe von `File` definieren.
-/// info
+/// info | Info
-Um hochgeladene Dateien zu empfangen, installieren Sie zuerst `python-multipart`.
+Um hochgeladene Dateien zu empfangen, installieren Sie zuerst `python-multipart`.
-Z. B. `pip install python-multipart`.
+Stellen Sie sicher, dass Sie eine [virtuelle Umgebung](../virtual-environments.md){.internal-link target=_blank} erstellen, sie aktivieren und dann das Paket installieren, zum Beispiel:
-Das, weil hochgeladene Dateien als „Formulardaten“ gesendet werden.
+```console
+$ pip install python-multipart
+```
+
+Das liegt daran, dass hochgeladene Dateien als „Formulardaten“ gesendet werden.
///
-## `File` importieren
+## `File` importieren { #import-file }
Importieren Sie `File` und `UploadFile` von `fastapi`:
-//// tab | Python 3.9+
+{* ../../docs_src/request_files/tutorial001_an_py39.py hl[3] *}
-```Python hl_lines="3"
-{!> ../../../docs_src/request_files/tutorial001_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="1"
-{!> ../../../docs_src/request_files/tutorial001_an.py!}
-```
-
-////
-
-//// tab | Python 3.8+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="1"
-{!> ../../../docs_src/request_files/tutorial001.py!}
-```
-
-////
-
-## `File`-Parameter definieren
+## `File`-Parameter definieren { #define-file-parameters }
Erstellen Sie Datei-Parameter, so wie Sie es auch mit `Body` und `Form` machen würden:
-//// tab | Python 3.9+
+{* ../../docs_src/request_files/tutorial001_an_py39.py hl[9] *}
-```Python hl_lines="9"
-{!> ../../../docs_src/request_files/tutorial001_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="8"
-{!> ../../../docs_src/request_files/tutorial001_an.py!}
-```
-
-////
-
-//// tab | Python 3.8+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="7"
-{!> ../../../docs_src/request_files/tutorial001.py!}
-```
-
-////
-
-/// info
+/// info | Info
`File` ist eine Klasse, die direkt von `Form` erbt.
-Aber erinnern Sie sich, dass, wenn Sie `Query`, `Path`, `File` und andere von `fastapi` importieren, diese tatsächlich Funktionen sind, welche spezielle Klassen zurückgeben
+Aber erinnern Sie sich, dass, wenn Sie `Query`, `Path`, `File` und andere von `fastapi` importieren, diese tatsächlich Funktionen sind, welche spezielle Klassen zurückgeben.
///
-/// tip | "Tipp"
+/// tip | Tipp
-Um Dateibodys zu deklarieren, müssen Sie `File` verwenden, da diese Parameter sonst als Query-Parameter oder Body(-JSON)-Parameter interpretiert werden würden.
+Um Dateibodys zu deklarieren, müssen Sie `File` verwenden, da diese Parameter sonst als Query-Parameter oder Body (JSON)-Parameter interpretiert werden würden.
///
@@ -102,57 +50,29 @@ Bedenken Sie, dass das bedeutet, dass sich der gesamte Inhalt der Datei im Arbei
Aber es gibt viele Fälle, in denen Sie davon profitieren, `UploadFile` zu verwenden.
-## Datei-Parameter mit `UploadFile`
+## Datei-Parameter mit `UploadFile` { #file-parameters-with-uploadfile }
Definieren Sie einen Datei-Parameter mit dem Typ `UploadFile`:
-//// tab | Python 3.9+
-
-```Python hl_lines="14"
-{!> ../../../docs_src/request_files/tutorial001_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="13"
-{!> ../../../docs_src/request_files/tutorial001_an.py!}
-```
-
-////
-
-//// tab | Python 3.8+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="12"
-{!> ../../../docs_src/request_files/tutorial001.py!}
-```
-
-////
+{* ../../docs_src/request_files/tutorial001_an_py39.py hl[14] *}
`UploadFile` zu verwenden, hat mehrere Vorzüge gegenüber `bytes`:
* Sie müssen `File()` nicht als Parameter-Defaultwert verwenden.
-* Es wird eine „Spool“-Datei verwendet:
+* Es wird eine „gespoolte“ Datei verwendet:
* Eine Datei, die bis zu einem bestimmten Größen-Limit im Arbeitsspeicher behalten wird, und wenn das Limit überschritten wird, auf der Festplatte gespeichert wird.
* Das bedeutet, es wird für große Dateien wie Bilder, Videos, große Binärdateien, usw. gut funktionieren, ohne den ganzen Arbeitsspeicher aufzubrauchen.
* Sie können Metadaten aus der hochgeladenen Datei auslesen.
-* Es hat eine file-like `async`hrone Schnittstelle.
-* Es stellt ein tatsächliches Python-`SpooledTemporaryFile`-Objekt bereit, welches Sie direkt anderen Bibliotheken übergeben können, die ein dateiartiges Objekt erwarten.
+* Es hat eine dateiartige `async`hrone Schnittstelle.
+* Es stellt ein tatsächliches Python-`SpooledTemporaryFile`-Objekt bereit, welches Sie direkt anderen Bibliotheken übergeben können, die ein dateiartiges Objekt erwarten.
-### `UploadFile`
+### `UploadFile` { #uploadfile }
`UploadFile` hat die folgenden Attribute:
* `filename`: Ein `str` mit dem ursprünglichen Namen der hochgeladenen Datei (z. B. `meinbild.jpg`).
* `content_type`: Ein `str` mit dem Inhaltstyp (MIME-Typ / Medientyp) (z. B. `image/jpeg`).
-* `file`: Ein `SpooledTemporaryFile` (ein file-like Objekt). Das ist das tatsächliche Python-Objekt, das Sie direkt anderen Funktionen oder Bibliotheken übergeben können, welche ein „file-like“-Objekt erwarten.
+* `file`: Ein `SpooledTemporaryFile` (ein dateiartiges Objekt). Das ist das tatsächliche Python-Objekt, das Sie direkt anderen Funktionen oder Bibliotheken übergeben können, welche ein „file-like“-Objekt erwarten.
`UploadFile` hat die folgenden `async`hronen Methoden. Sie alle rufen die entsprechenden Methoden des darunterliegenden Datei-Objekts auf (wobei intern `SpooledTemporaryFile` verwendet wird).
@@ -163,7 +83,7 @@ Bevorzugen Sie die `Annotated`-Version, falls möglich.
* Das ist besonders dann nützlich, wenn Sie `await myfile.read()` einmal ausführen und dann diese Inhalte erneut auslesen müssen.
* `close()`: Schließt die Datei.
-Da alle diese Methoden `async`hron sind, müssen Sie sie `await`en („erwarten“).
+Da alle diese Methoden `async`hron sind, müssen Sie sie „await“en („erwarten“).
Zum Beispiel können Sie innerhalb einer `async` *Pfadoperation-Funktion* den Inhalt wie folgt auslesen:
@@ -177,133 +97,55 @@ Wenn Sie sich innerhalb einer normalen `def`-*Pfadoperation-Funktion* befinden,
contents = myfile.file.read()
```
-/// note | "Technische Details zu `async`"
+/// note | Technische Details zu `async`
Wenn Sie die `async`-Methoden verwenden, führt **FastAPI** die Datei-Methoden in einem Threadpool aus und erwartet sie.
///
-/// note | "Technische Details zu Starlette"
+/// note | Technische Details zu Starlette
**FastAPI**s `UploadFile` erbt direkt von **Starlette**s `UploadFile`, fügt aber ein paar notwendige Teile hinzu, um es kompatibel mit **Pydantic** und anderen Teilen von FastAPI zu machen.
///
-## Was sind „Formulardaten“
+## Was sind „Formulardaten“ { #what-is-form-data }
-HTML-Formulare (``) senden die Daten in einer „speziellen“ Kodierung zum Server, welche sich von JSON unterscheidet.
+Der Weg, wie HTML-Formulare (``) die Daten zum Server senden, verwendet normalerweise eine „spezielle“ Kodierung für diese Daten. Diese unterscheidet sich von JSON.
**FastAPI** stellt sicher, dass diese Daten korrekt ausgelesen werden, statt JSON zu erwarten.
-/// note | "Technische Details"
+/// note | Technische Details
Daten aus Formularen werden, wenn es keine Dateien sind, normalerweise mit dem „media type“ `application/x-www-form-urlencoded` kodiert.
Sollte das Formular aber Dateien enthalten, dann werden diese mit `multipart/form-data` kodiert. Wenn Sie `File` verwenden, wird **FastAPI** wissen, dass es die Dateien vom korrekten Teil des Bodys holen muss.
-Wenn Sie mehr über Formularfelder und ihre Kodierungen lesen möchten, besuchen Sie die MDN-Webdokumentation für POST.
+Wenn Sie mehr über diese Kodierungen und Formularfelder lesen möchten, besuchen Sie die MDN-Webdokumentation für POST.
///
-/// warning | "Achtung"
+/// warning | Achtung
-Sie können mehrere `File`- und `Form`-Parameter in einer *Pfadoperation* deklarieren, aber Sie können nicht gleichzeitig auch `Body`-Felder deklarieren, welche Sie als JSON erwarten, da der Request den Body mittels `multipart/form-data` statt `application/json` kodiert.
+Sie können mehrere `File`- und `Form`-Parameter in einer *Pfadoperation* deklarieren, aber Sie können nicht gleichzeitig auch `Body`-Felder deklarieren, welche Sie als JSON erwarten, da der Request den Body mittels `multipart/form-data` statt `application/json` kodiert.
Das ist keine Limitation von **FastAPI**, sondern Teil des HTTP-Protokolls.
///
-## Optionaler Datei-Upload
+## Optionaler Datei-Upload { #optional-file-upload }
Sie können eine Datei optional machen, indem Sie Standard-Typannotationen verwenden und den Defaultwert auf `None` setzen:
-//// tab | Python 3.10+
+{* ../../docs_src/request_files/tutorial001_02_an_py310.py hl[9,17] *}
-```Python hl_lines="9 17"
-{!> ../../../docs_src/request_files/tutorial001_02_an_py310.py!}
-```
+## `UploadFile` mit zusätzlichen Metadaten { #uploadfile-with-additional-metadata }
-////
+Sie können auch `File()` mit `UploadFile` verwenden, um zum Beispiel zusätzliche Metadaten zu setzen:
-//// tab | Python 3.9+
+{* ../../docs_src/request_files/tutorial001_03_an_py39.py hl[9,15] *}
-```Python hl_lines="9 17"
-{!> ../../../docs_src/request_files/tutorial001_02_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="10 18"
-{!> ../../../docs_src/request_files/tutorial001_02_an.py!}
-```
-
-////
-
-//// tab | Python 3.10+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="7 15"
-{!> ../../../docs_src/request_files/tutorial001_02_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="9 17"
-{!> ../../../docs_src/request_files/tutorial001_02.py!}
-```
-
-////
-
-## `UploadFile` mit zusätzlichen Metadaten
-
-Sie können auch `File()` zusammen mit `UploadFile` verwenden, um zum Beispiel zusätzliche Metadaten zu setzen:
-
-//// tab | Python 3.9+
-
-```Python hl_lines="9 15"
-{!> ../../../docs_src/request_files/tutorial001_03_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="8 14"
-{!> ../../../docs_src/request_files/tutorial001_03_an.py!}
-```
-
-////
-
-//// tab | Python 3.8+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="7 13"
-{!> ../../../docs_src/request_files/tutorial001_03.py!}
-```
-
-////
-
-## Mehrere Datei-Uploads
+## Mehrere Datei-Uploads { #multiple-file-uploads }
Es ist auch möglich, mehrere Dateien gleichzeitig hochzuladen.
@@ -311,108 +153,24 @@ Diese werden demselben Formularfeld zugeordnet, welches mit den Formulardaten ge
Um das zu machen, deklarieren Sie eine Liste von `bytes` oder `UploadFile`s:
-//// tab | Python 3.9+
+{* ../../docs_src/request_files/tutorial002_an_py39.py hl[10,15] *}
-```Python hl_lines="10 15"
-{!> ../../../docs_src/request_files/tutorial002_an_py39.py!}
-```
+Sie erhalten, wie deklariert, eine `list` von `bytes` oder `UploadFile`s.
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="11 16"
-{!> ../../../docs_src/request_files/tutorial002_an.py!}
-```
-
-////
-
-//// tab | Python 3.9+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="8 13"
-{!> ../../../docs_src/request_files/tutorial002_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="10 15"
-{!> ../../../docs_src/request_files/tutorial002.py!}
-```
-
-////
-
-Sie erhalten, wie deklariert, eine `list`e von `bytes` oder `UploadFile`s.
-
-/// note | "Technische Details"
+/// note | Technische Details
Sie können auch `from starlette.responses import HTMLResponse` verwenden.
-**FastAPI** bietet dieselben `starlette.responses` auch via `fastapi.responses` an, als Annehmlichkeit für Sie, den Entwickler. Die meisten verfügbaren Responses kommen aber direkt von Starlette.
+**FastAPI** bietet dieselben `starlette.responses` auch via `fastapi.responses` an, als Annehmlichkeit für Sie, den Entwickler. Die meisten verfügbaren Responses kommen aber direkt von Starlette.
///
-### Mehrere Datei-Uploads mit zusätzlichen Metadaten
+### Mehrere Datei-Uploads mit zusätzlichen Metadaten { #multiple-file-uploads-with-additional-metadata }
Und so wie zuvor können Sie `File()` verwenden, um zusätzliche Parameter zu setzen, sogar für `UploadFile`:
-//// tab | Python 3.9+
+{* ../../docs_src/request_files/tutorial003_an_py39.py hl[11,18:20] *}
-```Python hl_lines="11 18-20"
-{!> ../../../docs_src/request_files/tutorial003_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="12 19-21"
-{!> ../../../docs_src/request_files/tutorial003_an.py!}
-```
-
-////
-
-//// tab | Python 3.9+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="9 16"
-{!> ../../../docs_src/request_files/tutorial003_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="11 18"
-{!> ../../../docs_src/request_files/tutorial003.py!}
-```
-
-////
-
-## Zusammenfassung
+## Zusammenfassung { #recap }
Verwenden Sie `File`, `bytes` und `UploadFile`, um hochladbare Dateien im Request zu deklarieren, die als Formulardaten gesendet werden.
diff --git a/docs/de/docs/tutorial/request-form-models.md b/docs/de/docs/tutorial/request-form-models.md
new file mode 100644
index 0000000000..fbc6c094c7
--- /dev/null
+++ b/docs/de/docs/tutorial/request-form-models.md
@@ -0,0 +1,78 @@
+# Formularmodelle { #form-models }
+
+Sie können **Pydantic-Modelle** verwenden, um **Formularfelder** in FastAPI zu deklarieren.
+
+/// info | Info
+
+Um Formulare zu verwenden, installieren Sie zuerst `python-multipart`.
+
+Stellen Sie sicher, dass Sie eine [virtuelle Umgebung](../virtual-environments.md){.internal-link target=_blank} erstellen, sie aktivieren und es dann installieren, zum Beispiel:
+
+```console
+$ pip install python-multipart
+```
+
+///
+
+/// note | Hinweis
+
+Dies wird seit FastAPI Version `0.113.0` unterstützt. 🤓
+
+///
+
+## Pydantic-Modelle für Formulare { #pydantic-models-for-forms }
+
+Sie müssen nur ein **Pydantic-Modell** mit den Feldern deklarieren, die Sie als **Formularfelder** erhalten möchten, und dann den Parameter als `Form` deklarieren:
+
+{* ../../docs_src/request_form_models/tutorial001_an_py39.py hl[9:11,15] *}
+
+**FastAPI** wird die Daten für **jedes Feld** aus den **Formulardaten** im Request **extrahieren** und Ihnen das von Ihnen definierte Pydantic-Modell übergeben.
+
+## Die Dokumentation testen { #check-the-docs }
+
+Sie können dies in der Dokumentations-UI unter `/docs` testen:
+
+
+POST.
+Wenn Sie mehr über Formularfelder und ihre Kodierungen lesen möchten, besuchen Sie die MDN-Webdokumentation für POST.
///
-/// warning | "Achtung"
+/// warning | Achtung
-Sie können mehrere `Form`-Parameter in einer *Pfadoperation* deklarieren, aber Sie können nicht gleichzeitig auch `Body`-Felder deklarieren, welche Sie als JSON erwarten, da der Request den Body mittels `application/x-www-form-urlencoded` statt `application/json` kodiert.
+Sie können mehrere `Form`-Parameter in einer *Pfadoperation* deklarieren, aber Sie können nicht gleichzeitig auch `Body`-Felder deklarieren, welche Sie als JSON erwarten, da der Request den Body mittels `application/x-www-form-urlencoded` statt `application/json` kodiert.
Das ist keine Limitation von **FastAPI**, sondern Teil des HTTP-Protokolls.
///
-## Zusammenfassung
+## Zusammenfassung { #recap }
Verwenden Sie `Form`, um Eingabe-Parameter für Formulardaten zu deklarieren.
diff --git a/docs/de/docs/tutorial/response-model.md b/docs/de/docs/tutorial/response-model.md
index b480780bca..7b77125cb5 100644
--- a/docs/de/docs/tutorial/response-model.md
+++ b/docs/de/docs/tutorial/response-model.md
@@ -1,37 +1,15 @@
-# Responsemodell – Rückgabetyp
+# Responsemodell – Rückgabetyp { #response-model-return-type }
-Sie können den Typ der Response deklarieren, indem Sie den **Rückgabetyp** der *Pfadoperation* annotieren.
+Sie können den Typ der Response deklarieren, indem Sie den **Rückgabetyp** der *Pfadoperation* annotieren.
Hierbei können Sie **Typannotationen** genauso verwenden, wie Sie es bei Werten von Funktions-**Parametern** machen; verwenden Sie Pydantic-Modelle, Listen, Dicts und skalare Werte wie Nummern, Booleans, usw.
-//// tab | Python 3.10+
-
-```Python hl_lines="16 21"
-{!> ../../../docs_src/response_model/tutorial001_01_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="18 23"
-{!> ../../../docs_src/response_model/tutorial001_01_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="18 23"
-{!> ../../../docs_src/response_model/tutorial001_01.py!}
-```
-
-////
+{* ../../docs_src/response_model/tutorial001_01_py310.py hl[16,21] *}
FastAPI wird diesen Rückgabetyp verwenden, um:
* Die zurückzugebenden Daten zu **validieren**.
- * Wenn die Daten ungültig sind (Sie haben z. B. ein Feld vergessen), bedeutet das, *Ihr* Anwendungscode ist fehlerhaft, er gibt nicht zurück, was er sollte, und daher wird ein Server-Error ausgegeben, statt falscher Daten. So können Sie und ihre Clients sicher sein, dass diese die erwarteten Daten, in der richtigen Form erhalten.
+ * Wenn die Daten ungültig sind (Sie haben z. B. ein Feld vergessen), bedeutet das, *Ihr* Anwendungscode ist fehlerhaft, er gibt nicht zurück, was er sollte, und daher wird ein Server-Error ausgegeben, statt falscher Daten. So können Sie und Ihre Clients sicher sein, dass diese die erwarteten Daten, in der richtigen Form erhalten.
* In der OpenAPI *Pfadoperation* ein **JSON-Schema** für die Response hinzuzufügen.
* Dieses wird von der **automatischen Dokumentation** verwendet.
* Es wird auch von automatisch Client-Code-generierenden Tools verwendet.
@@ -41,11 +19,11 @@ Aber am wichtigsten:
* Es wird die Ausgabedaten auf das **limitieren und filtern**, was im Rückgabetyp definiert ist.
* Das ist insbesondere für die **Sicherheit** wichtig, mehr dazu unten.
-## `response_model`-Parameter
+## `response_model`-Parameter { #response-model-parameter }
Es gibt Fälle, da möchten oder müssen Sie Daten zurückgeben, die nicht genau dem entsprechen, was der Typ deklariert.
-Zum Beispiel könnten Sie **ein Dict zurückgeben** wollen, oder ein Datenbank-Objekt, aber **es als Pydantic-Modell deklarieren**. Auf diese Weise übernimmt das Pydantic-Modell alle Datendokumentation, -validierung, usw. für das Objekt, welches Sie zurückgeben (z. B. ein Dict oder ein Datenbank-Objekt).
+Zum Beispiel könnten Sie **ein Dictionary zurückgeben** wollen, oder ein Datenbank-Objekt, aber **es als Pydantic-Modell deklarieren**. Auf diese Weise übernimmt das Pydantic-Modell alle Datendokumentation, -validierung, usw. für das Objekt, welches Sie zurückgeben (z. B. ein Dictionary oder ein Datenbank-Objekt).
Würden Sie eine hierfür eine Rückgabetyp-Annotation verwenden, dann würden Tools und Editoren (korrekterweise) Fehler ausgeben, die Ihnen sagen, dass Ihre Funktion einen Typ zurückgibt (z. B. ein Dict), der sich unterscheidet von dem, was Sie deklariert haben (z. B. ein Pydantic-Modell).
@@ -59,33 +37,11 @@ Sie können `response_model` in jeder möglichen *Pfadoperation* verwenden:
* `@app.delete()`
* usw.
-//// tab | Python 3.10+
+{* ../../docs_src/response_model/tutorial001_py310.py hl[17,22,24:27] *}
-```Python hl_lines="17 22 24-27"
-{!> ../../../docs_src/response_model/tutorial001_py310.py!}
-```
+/// note | Hinweis
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="17 22 24-27"
-{!> ../../../docs_src/response_model/tutorial001_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="17 22 24-27"
-{!> ../../../docs_src/response_model/tutorial001.py!}
-```
-
-////
-
-/// note | "Hinweis"
-
-Beachten Sie, dass `response_model` ein Parameter der „Dekorator“-Methode ist (`get`, `post`, usw.). Nicht der *Pfadoperation-Funktion*, so wie die anderen Parameter.
+Beachten Sie, dass `response_model` ein Parameter der „Dekorator“-Methode ist (`get`, `post`, usw.). Nicht der *Pfadoperation-Funktion*, so wie die anderen Parameter und der Body.
///
@@ -93,68 +49,49 @@ Beachten Sie, dass `response_model` ein Parameter der „Dekorator“-Methode is
FastAPI wird dieses `response_model` nehmen, um die Daten zu dokumentieren, validieren, usw. und auch, um **die Ausgabedaten** entsprechend der Typdeklaration **zu konvertieren und filtern**.
-/// tip | "Tipp"
+/// tip | Tipp
-Wenn Sie in Ihrem Editor strikte Typchecks haben, mypy, usw., können Sie den Funktions-Rückgabetyp als `Any` deklarieren.
+Wenn Sie in Ihrem Editor strikte Typchecks haben, mypy, usw., können Sie den Funktions-Rückgabetyp als `Any` deklarieren.
So sagen Sie dem Editor, dass Sie absichtlich *irgendetwas* zurückgeben. Aber FastAPI wird trotzdem die Dokumentation, Validierung, Filterung, usw. der Daten übernehmen, via `response_model`.
///
-### `response_model`-Priorität
+### `response_model`-Priorität { #response-model-priority }
Wenn sowohl Rückgabetyp als auch `response_model` deklariert sind, hat `response_model` die Priorität und wird von FastAPI bevorzugt verwendet.
-So können Sie korrekte Typannotationen zu ihrer Funktion hinzufügen, die von ihrem Editor und Tools wie mypy verwendet werden. Und dennoch übernimmt FastAPI die Validierung und Dokumentation, usw., der Daten anhand von `response_model`.
+So können Sie korrekte Typannotationen zu Ihrer Funktion hinzufügen, die von Ihrem Editor und Tools wie mypy verwendet werden. Und dennoch übernimmt FastAPI die Validierung und Dokumentation, usw., der Daten anhand von `response_model`.
-Sie können auch `response_model=None` verwenden, um das Erstellen eines Responsemodells für diese *Pfadoperation* zu unterbinden. Sie könnten das tun wollen, wenn sie Dinge annotieren, die nicht gültige Pydantic-Felder sind. Ein Beispiel dazu werden Sie in einer der Abschnitte unten sehen.
+Sie können auch `response_model=None` verwenden, um das Erstellen eines Responsemodells für diese *Pfadoperation* zu unterbinden. Sie könnten das tun wollen, wenn Sie Dinge annotieren, die nicht gültige Pydantic-Felder sind. Ein Beispiel dazu werden Sie in einer der Abschnitte unten sehen.
-## Dieselben Eingabedaten zurückgeben
+## Dieselben Eingabedaten zurückgeben { #return-the-same-input-data }
Im Folgenden deklarieren wir ein `UserIn`-Modell; es enthält ein Klartext-Passwort:
-//// tab | Python 3.10+
+{* ../../docs_src/response_model/tutorial002_py310.py hl[7,9] *}
-```Python hl_lines="7 9"
-{!> ../../../docs_src/response_model/tutorial002_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="9 11"
-{!> ../../../docs_src/response_model/tutorial002.py!}
-```
-
-////
-
-/// info
+/// info | Info
Um `EmailStr` zu verwenden, installieren Sie zuerst `email-validator`.
-Z. B. `pip install email-validator`
-oder `pip install pydantic[email]`.
+Stellen Sie sicher, dass Sie eine [virtuelle Umgebung](../virtual-environments.md){.internal-link target=_blank} erstellen, sie aktivieren und es dann installieren, zum Beispiel:
+
+```console
+$ pip install email-validator
+```
+
+oder mit:
+
+```console
+$ pip install "pydantic[email]"
+```
///
Wir verwenden dieses Modell, um sowohl unsere Eingabe- als auch Ausgabedaten zu deklarieren:
-//// tab | Python 3.10+
-
-```Python hl_lines="16"
-{!> ../../../docs_src/response_model/tutorial002_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="18"
-{!> ../../../docs_src/response_model/tutorial002.py!}
-```
-
-////
+{* ../../docs_src/response_model/tutorial002_py310.py hl[16] *}
Immer wenn jetzt ein Browser einen Benutzer mit Passwort erzeugt, gibt die API dasselbe Passwort in der Response zurück.
@@ -162,71 +99,29 @@ Hier ist das möglicherweise kein Problem, da es derselbe Benutzer ist, der das
Aber wenn wir dasselbe Modell für eine andere *Pfadoperation* verwenden, könnten wir das Passwort dieses Benutzers zu jedem Client schicken.
-/// danger | "Gefahr"
+/// danger | Gefahr
Speichern Sie niemals das Klartext-Passwort eines Benutzers, oder versenden Sie es in einer Response wie dieser, wenn Sie sich nicht der resultierenden Gefahren bewusst sind und nicht wissen, was Sie tun.
///
-## Ausgabemodell hinzufügen
+## Ausgabemodell hinzufügen { #add-an-output-model }
Wir können stattdessen ein Eingabemodell mit dem Klartext-Passwort, und ein Ausgabemodell ohne das Passwort erstellen:
-//// tab | Python 3.10+
-
-```Python hl_lines="9 11 16"
-{!> ../../../docs_src/response_model/tutorial003_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="9 11 16"
-{!> ../../../docs_src/response_model/tutorial003.py!}
-```
-
-////
+{* ../../docs_src/response_model/tutorial003_py310.py hl[9,11,16] *}
Obwohl unsere *Pfadoperation-Funktion* hier denselben `user` von der Eingabe zurückgibt, der das Passwort enthält:
-//// tab | Python 3.10+
-
-```Python hl_lines="24"
-{!> ../../../docs_src/response_model/tutorial003_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="24"
-{!> ../../../docs_src/response_model/tutorial003.py!}
-```
-
-////
+{* ../../docs_src/response_model/tutorial003_py310.py hl[24] *}
... haben wir deklariert, dass `response_model` das Modell `UserOut` ist, welches das Passwort nicht enthält:
-//// tab | Python 3.10+
-
-```Python hl_lines="22"
-{!> ../../../docs_src/response_model/tutorial003_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="22"
-{!> ../../../docs_src/response_model/tutorial003.py!}
-```
-
-////
+{* ../../docs_src/response_model/tutorial003_py310.py hl[22] *}
Darum wird **FastAPI** sich darum kümmern, dass alle Daten, die nicht im Ausgabemodell deklariert sind, herausgefiltert werden (mittels Pydantic).
-### `response_model` oder Rückgabewert
+### `response_model` oder Rückgabewert { #response-model-or-return-type }
Da unsere zwei Modelle in diesem Fall unterschiedlich sind, würde, wenn wir den Rückgabewert der Funktion als `UserOut` deklarieren, der Editor sich beschweren, dass wir einen ungültigen Typ zurückgeben, weil das unterschiedliche Klassen sind.
@@ -234,11 +129,11 @@ Darum müssen wir es in diesem Fall im `response_model`-Parameter deklarieren.
... aber lesen Sie weiter, um zu sehen, wie man das anders lösen kann.
-## Rückgabewert und Datenfilterung
+## Rückgabewert und Datenfilterung { #return-type-and-data-filtering }
-Führen wir unser vorheriges Beispiel fort. Wir wollten **die Funktion mit einem Typ annotieren**, aber etwas zurückgeben, das **weniger Daten** enthält.
+Führen wir unser vorheriges Beispiel fort. Wir wollten **die Funktion mit einem Typ annotieren**, aber wir wollten in der Funktion tatsächlich etwas zurückgeben, das **mehr Daten** enthält.
-Wir möchten auch, dass FastAPI die Daten weiterhin, dem Responsemodell entsprechend, **filtert**.
+Wir möchten, dass FastAPI die Daten weiterhin mithilfe des Responsemodells **filtert**. Selbst wenn die Funktion mehr Daten zurückgibt, soll die Response nur die Felder enthalten, die im Responsemodell deklariert sind.
Im vorherigen Beispiel mussten wir den `response_model`-Parameter verwenden, weil die Klassen unterschiedlich waren. Das bedeutet aber auch, wir bekommen keine Unterstützung vom Editor und anderen Tools, die den Funktions-Rückgabewert überprüfen.
@@ -246,37 +141,23 @@ Aber in den meisten Fällen, wenn wir so etwas machen, wollen wir nur, dass das
Und in solchen Fällen können wir Klassen und Vererbung verwenden, um Vorteil aus den Typannotationen in der Funktion zu ziehen, was vom Editor und von Tools besser unterstützt wird, während wir gleichzeitig FastAPIs **Datenfilterung** behalten.
-//// tab | Python 3.10+
-
-```Python hl_lines="7-10 13-14 18"
-{!> ../../../docs_src/response_model/tutorial003_01_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="9-13 15-16 20"
-{!> ../../../docs_src/response_model/tutorial003_01.py!}
-```
-
-////
+{* ../../docs_src/response_model/tutorial003_01_py310.py hl[7:10,13:14,18] *}
Damit erhalten wir Tool-Unterstützung, vom Editor und mypy, da dieser Code hinsichtlich der Typen korrekt ist, aber wir erhalten auch die Datenfilterung von FastAPI.
Wie funktioniert das? Schauen wir uns das mal an. 🤓
-### Typannotationen und Tooling
+### Typannotationen und Tooling { #type-annotations-and-tooling }
Sehen wir uns zunächst an, wie Editor, mypy und andere Tools dies sehen würden.
-`BaseUser` verfügt über die Basis-Felder. Dann erbt `UserIn` von `BaseUser` und fügt das Feld `Passwort` hinzu, sodass dass es nun alle Felder beider Modelle hat.
+`BaseUser` verfügt über die Basis-Felder. Dann erbt `UserIn` von `BaseUser` und fügt das Feld `password` hinzu, sodass es nun alle Felder beider Modelle hat.
Wir annotieren den Funktionsrückgabetyp als `BaseUser`, geben aber tatsächlich eine `UserIn`-Instanz zurück.
Für den Editor, mypy und andere Tools ist das kein Problem, da `UserIn` eine Unterklasse von `BaseUser` ist (Salopp: `UserIn` ist ein `BaseUser`). Es handelt sich um einen *gültigen* Typ, solange irgendetwas überreicht wird, das ein `BaseUser` ist.
-### FastAPI Datenfilterung
+### FastAPI Datenfilterung { #fastapi-data-filtering }
FastAPI seinerseits wird den Rückgabetyp sehen und sicherstellen, dass das, was zurückgegeben wird, **nur** diejenigen Felder enthält, welche im Typ deklariert sind.
@@ -284,7 +165,7 @@ FastAPI macht intern mehrere Dinge mit Pydantic, um sicherzustellen, dass obige
Auf diese Weise erhalten Sie das beste beider Welten: Sowohl Typannotationen mit **Tool-Unterstützung** als auch **Datenfilterung**.
-## Anzeige in der Dokumentation
+## Anzeige in der Dokumentation { #see-it-in-the-docs }
Wenn Sie sich die automatische Dokumentation betrachten, können Sie sehen, dass Eingabe- und Ausgabemodell beide ihr eigenes JSON-Schema haben:
@@ -294,57 +175,39 @@ Und beide Modelle werden auch in der interaktiven API-Dokumentation verwendet:
-## Andere Rückgabetyp-Annotationen
+## Andere Rückgabetyp-Annotationen { #other-return-type-annotations }
Es kann Fälle geben, bei denen Sie etwas zurückgeben, das kein gültiges Pydantic-Feld ist, und Sie annotieren es in der Funktion nur, um Unterstützung von Tools zu erhalten (Editor, mypy, usw.).
-### Eine Response direkt zurückgeben
+### Eine Response direkt zurückgeben { #return-a-response-directly }
Der häufigste Anwendungsfall ist, wenn Sie [eine Response direkt zurückgeben, wie es später im Handbuch für fortgeschrittene Benutzer erläutert wird](../advanced/response-directly.md){.internal-link target=_blank}.
-```Python hl_lines="8 10-11"
-{!> ../../../docs_src/response_model/tutorial003_02.py!}
-```
+{* ../../docs_src/response_model/tutorial003_02.py hl[8,10:11] *}
Dieser einfache Anwendungsfall wird automatisch von FastAPI gehandhabt, weil die Annotation des Rückgabetyps die Klasse (oder eine Unterklasse von) `Response` ist.
Und Tools werden auch glücklich sein, weil sowohl `RedirectResponse` als auch `JSONResponse` Unterklassen von `Response` sind, die Typannotation ist daher korrekt.
-### Eine Unterklasse von Response annotieren
+### Eine Unterklasse von Response annotieren { #annotate-a-response-subclass }
Sie können auch eine Unterklasse von `Response` in der Typannotation verwenden.
-```Python hl_lines="8-9"
-{!> ../../../docs_src/response_model/tutorial003_03.py!}
-```
+{* ../../docs_src/response_model/tutorial003_03.py hl[8:9] *}
Das wird ebenfalls funktionieren, weil `RedirectResponse` eine Unterklasse von `Response` ist, und FastAPI sich um diesen einfachen Anwendungsfall automatisch kümmert.
-### Ungültige Rückgabetyp-Annotationen
+### Ungültige Rückgabetyp-Annotationen { #invalid-return-type-annotations }
Aber wenn Sie ein beliebiges anderes Objekt zurückgeben, das kein gültiger Pydantic-Typ ist (z. B. ein Datenbank-Objekt), und Sie annotieren es so in der Funktion, wird FastAPI versuchen, ein Pydantic-Responsemodell von dieser Typannotation zu erstellen, und scheitern.
-Das gleiche wird passieren, wenn Sie eine Union mehrerer Typen haben, und einer oder mehrere sind nicht gültige Pydantic-Typen. Zum Beispiel funktioniert folgendes nicht 💥:
+Das gleiche wird passieren, wenn Sie eine Union mehrerer Typen haben, und einer oder mehrere sind nicht gültige Pydantic-Typen. Zum Beispiel funktioniert folgendes nicht 💥:
-//// tab | Python 3.10+
-
-```Python hl_lines="8"
-{!> ../../../docs_src/response_model/tutorial003_04_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="10"
-{!> ../../../docs_src/response_model/tutorial003_04.py!}
-```
-
-////
+{* ../../docs_src/response_model/tutorial003_04_py310.py hl[8] *}
... das scheitert, da die Typannotation kein Pydantic-Typ ist, und auch keine einzelne `Response`-Klasse, oder -Unterklasse, es ist eine Union (eines von beiden) von `Response` und `dict`.
-### Responsemodell deaktivieren
+### Responsemodell deaktivieren { #disable-response-model }
Beim Beispiel oben fortsetzend, mögen Sie vielleicht die standardmäßige Datenvalidierung, -Dokumentation, -Filterung, usw., die von FastAPI durchgeführt wird, nicht haben.
@@ -352,51 +215,15 @@ Aber Sie möchten dennoch den Rückgabetyp in der Funktion annotieren, um Unters
In diesem Fall können Sie die Generierung des Responsemodells abschalten, indem Sie `response_model=None` setzen:
-//// tab | Python 3.10+
-
-```Python hl_lines="7"
-{!> ../../../docs_src/response_model/tutorial003_05_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="9"
-{!> ../../../docs_src/response_model/tutorial003_05.py!}
-```
-
-////
+{* ../../docs_src/response_model/tutorial003_05_py310.py hl[7] *}
Das bewirkt, dass FastAPI die Generierung des Responsemodells unterlässt, und damit können Sie jede gewünschte Rückgabetyp-Annotation haben, ohne dass es Ihre FastAPI-Anwendung beeinflusst. 🤓
-## Parameter für die Enkodierung des Responsemodells
+## Parameter für die Enkodierung des Responsemodells { #response-model-encoding-parameters }
Ihr Responsemodell könnte Defaultwerte haben, wie:
-//// tab | Python 3.10+
-
-```Python hl_lines="9 11-12"
-{!> ../../../docs_src/response_model/tutorial004_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="11 13-14"
-{!> ../../../docs_src/response_model/tutorial004_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="11 13-14"
-{!> ../../../docs_src/response_model/tutorial004.py!}
-```
-
-////
+{* ../../docs_src/response_model/tutorial004_py310.py hl[9,11:12] *}
* `description: Union[str, None] = None` (oder `str | None = None` in Python 3.10) hat einen Defaultwert `None`.
* `tax: float = 10.5` hat einen Defaultwert `10.5`.
@@ -406,33 +233,11 @@ Aber Sie möchten diese vielleicht vom Resultat ausschließen, wenn Sie gar nich
Wenn Sie zum Beispiel Modelle mit vielen optionalen Attributen in einer NoSQL-Datenbank haben, und Sie möchten nicht ellenlange JSON-Responses voller Defaultwerte senden.
-### Den `response_model_exclude_unset`-Parameter verwenden
+### Den `response_model_exclude_unset`-Parameter verwenden { #use-the-response-model-exclude-unset-parameter }
Sie können den *Pfadoperation-Dekorator*-Parameter `response_model_exclude_unset=True` setzen:
-//// tab | Python 3.10+
-
-```Python hl_lines="22"
-{!> ../../../docs_src/response_model/tutorial004_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="24"
-{!> ../../../docs_src/response_model/tutorial004_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="24"
-{!> ../../../docs_src/response_model/tutorial004.py!}
-```
-
-////
+{* ../../docs_src/response_model/tutorial004_py310.py hl[22] *}
Die Defaultwerte werden dann nicht in der Response enthalten sein, sondern nur die tatsächlich gesetzten Werte.
@@ -445,21 +250,21 @@ Wenn Sie also den Artikel mit der ID `foo` bei der *Pfadoperation* anfragen, wir
}
```
-/// info
+/// info | Info
-In Pydantic v1 hieß diese Methode `.dict()`, in Pydantic v2 wurde sie deprecated (aber immer noch unterstützt) und in `.model_dump()` umbenannt.
+In Pydantic v1 hieß diese Methode `.dict()`, in Pydantic v2 wurde sie deprecatet (aber immer noch unterstützt) und in `.model_dump()` umbenannt.
Die Beispiele hier verwenden `.dict()` für die Kompatibilität mit Pydantic v1, Sie sollten jedoch stattdessen `.model_dump()` verwenden, wenn Sie Pydantic v2 verwenden können.
///
-/// info
+/// info | Info
FastAPI verwendet `.dict()` von Pydantic Modellen, mit dessen `exclude_unset`-Parameter, um das zu erreichen.
///
-/// info
+/// info | Info
Sie können auch:
@@ -470,9 +275,9 @@ verwenden, wie in der ../../../docs_src/response_model/tutorial005_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="31 37"
-{!> ../../../docs_src/response_model/tutorial005.py!}
-```
-
-////
-
-/// tip | "Tipp"
+/// tip | Tipp
Die Syntax `{"name", "description"}` erzeugt ein `set` mit diesen zwei Werten.
@@ -553,27 +344,13 @@ Die Syntax `{"name", "description"}` erzeugt ein `set` mit diesen zwei Werten.
///
-#### `list`en statt `set`s verwenden
+#### `list`en statt `set`s verwenden { #using-lists-instead-of-sets }
Wenn Sie vergessen, ein `set` zu verwenden, und stattdessen eine `list`e oder ein `tuple` übergeben, wird FastAPI die dennoch in ein `set` konvertieren, und es wird korrekt funktionieren:
-//// tab | Python 3.10+
+{* ../../docs_src/response_model/tutorial006_py310.py hl[29,35] *}
-```Python hl_lines="29 35"
-{!> ../../../docs_src/response_model/tutorial006_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="31 37"
-{!> ../../../docs_src/response_model/tutorial006.py!}
-```
-
-////
-
-## Zusammenfassung
+## Zusammenfassung { #recap }
Verwenden Sie den Parameter `response_model` im *Pfadoperation-Dekorator*, um Responsemodelle zu definieren, und besonders, um private Daten herauszufiltern.
diff --git a/docs/de/docs/tutorial/response-status-code.md b/docs/de/docs/tutorial/response-status-code.md
index 5f96b83e4c..928003c3fc 100644
--- a/docs/de/docs/tutorial/response-status-code.md
+++ b/docs/de/docs/tutorial/response-status-code.md
@@ -1,6 +1,6 @@
-# Response-Statuscode
+# Response-Statuscode { #response-status-code }
-So wie ein Responsemodell, können Sie auch einen HTTP-Statuscode für die Response deklarieren, mithilfe des Parameters `status_code`, und zwar in jeder der *Pfadoperationen*:
+Genauso wie Sie ein Responsemodell angeben können, können Sie auch den HTTP-Statuscode für die Response mit dem Parameter `status_code` in jeder der *Pfadoperationen* deklarieren:
* `@app.get()`
* `@app.post()`
@@ -8,100 +8,94 @@ So wie ein Responsemodell, können Sie auch einen HTTP-Statuscode für die Respo
* `@app.delete()`
* usw.
-```Python hl_lines="6"
-{!../../../docs_src/response_status_code/tutorial001.py!}
-```
+{* ../../docs_src/response_status_code/tutorial001.py hl[6] *}
-/// note | "Hinweis"
+/// note | Hinweis
-Beachten Sie, dass `status_code` ein Parameter der „Dekorator“-Methode ist (`get`, `post`, usw.). Nicht der *Pfadoperation-Funktion*, so wie die anderen Parameter und der Body.
+Beachten Sie, dass `status_code` ein Parameter der „Dekorator“-Methode ist (`get`, `post`, usw.). Nicht der *Pfadoperation-Funktion*, wie alle anderen Parameter und der Body.
///
Dem `status_code`-Parameter wird eine Zahl mit dem HTTP-Statuscode übergeben.
-/// info
+/// info | Info
-Alternativ kann `status_code` auch ein `IntEnum` erhalten, so wie Pythons `http.HTTPStatus`.
+Alternativ kann `status_code` auch ein `IntEnum` erhalten, wie etwa Pythons `http.HTTPStatus`.
///
-Das wird:
+Dies wird:
* Diesen Statuscode mit der Response zurücksenden.
-* Ihn als solchen im OpenAPI-Schema dokumentieren (und somit in den Benutzeroberflächen):
+* Diesen im OpenAPI-Schema dokumentieren (und somit in den Benutzeroberflächen):
-/// note | "Hinweis"
+/// note | Hinweis
-Einige Responsecodes (siehe nächster Abschnitt) kennzeichnen, dass die Response keinen Body hat.
+Einige Responsecodes (siehe nächsten Abschnitt) kennzeichnen, dass die Response keinen Body hat.
-FastAPI versteht das und wird in der OpenAPI-Dokumentation anzeigen, dass es keinen Responsebody gibt.
+FastAPI erkennt dies und erstellt eine OpenAPI-Dokumentation, die zeigt, dass es keinen Responsebody gibt.
///
-## Über HTTP-Statuscodes
+## Über HTTP-Statuscodes { #about-http-status-codes }
-/// note | "Hinweis"
+/// note | Hinweis
-Wenn Sie bereits wissen, was HTTP-Statuscodes sind, überspringen Sie dieses Kapitel und fahren Sie mit dem nächsten fort.
+Wenn Sie bereits wissen, was HTTP-Statuscodes sind, können Sie diesen Abschnitt überspringen und mit dem nächsten fortfahren.
///
-In HTTP senden Sie als Teil der Response einen aus drei Ziffern bestehenden numerischen Statuscode.
+In HTTP senden Sie einen numerischen Statuscode mit 3 Ziffern als Teil der Response.
-Diese Statuscodes haben einen Namen zugeordnet, um sie besser zu erkennen, aber der wichtige Teil ist die Zahl.
+Diese Statuscodes haben einen zugeordneten Namen, um sie leichter zu erkennen, aber der wichtige Teil ist die Zahl.
-Kurz:
+Kurz gefasst:
-* `100` und darüber stehen für „Information“. Diese verwenden Sie selten direkt. Responses mit diesen Statuscodes können keinen Body haben.
-* **`200`** und darüber stehen für Responses, die „Successful“ („Erfolgreich“) waren. Diese verwenden Sie am häufigsten.
- * `200` ist der Default-Statuscode, welcher bedeutet, alles ist „OK“.
- * Ein anderes Beispiel ist `201`, „Created“ („Erzeugt“). Wird in der Regel verwendet, wenn ein neuer Datensatz in der Datenbank erzeugt wurde.
- * Ein spezieller Fall ist `204`, „No Content“ („Kein Inhalt“). Diese Response wird verwendet, wenn es keinen Inhalt gibt, der zum Client zurückgeschickt wird, diese Response hat also keinen Body.
-* **`300`** und darüber steht für „Redirection“ („Umleitung“). Responses mit diesen Statuscodes können einen oder keinen Body haben, mit Ausnahme von `304`, „Not Modified“ („Nicht verändert“), welche keinen haben darf.
-* **`400`** und darüber stehen für „Client error“-Responses („Client-Fehler“). Auch diese verwenden Sie am häufigsten.
+* `100 - 199` stehen für „Information“. Sie verwenden diese selten direkt. Responses mit diesen Statuscodes dürfen keinen Body haben.
+* **`200 - 299`** stehen für „Successful“-Responses („Erfolgreich“). Diese werden Sie am häufigsten verwenden.
+ * `200` ist der Default-Statuscode, was bedeutet, alles ist „OK“.
+ * Ein weiteres Beispiel wäre `201`, „Created“ („Erzeugt“). Dieser wird üblicherweise verwendet, nachdem ein neuer Datensatz in der Datenbank erstellt wurde.
+ * Ein spezieller Fall ist `204`, „No Content“ („Kein Inhalt“). Diese Response wird verwendet, wenn es keinen Inhalt gibt, der an den Client zurückgeschickt werden soll, und diese Response darf daher keinen Body haben.
+* **`300 - 399`** stehen für „Redirection“ („Umleitung“). Responses mit diesen Statuscodes können einen Body haben oder nicht, außer bei `304`, „Not Modified“ („Nicht verändert“), die keinen haben darf.
+* **`400 - 499`** stehen für „Client error“-Responses („Client-Fehler“). Diese sind die zweithäufigsten, die Sie vermutlich verwenden werden.
* Ein Beispiel ist `404`, für eine „Not Found“-Response („Nicht gefunden“).
* Für allgemeine Fehler beim Client können Sie einfach `400` verwenden.
-* `500` und darüber stehen für Server-Fehler. Diese verwenden Sie fast nie direkt. Wenn etwas an irgendeiner Stelle in Ihrem Anwendungscode oder im Server schiefläuft, wird automatisch einer dieser Fehler-Statuscodes zurückgegeben.
+* `500 - 599` stehen für Server-Fehler. Diese verwenden Sie fast nie direkt. Wenn in Ihrem Anwendungscode oder Server etwas schiefgeht, wird automatisch einer dieser Fehler-Statuscodes zurückgegeben.
-/// tip | "Tipp"
+/// tip | Tipp
-Um mehr über Statuscodes zu lernen, und welcher wofür verwendet wird, lesen Sie die MDN Dokumentation über HTTP-Statuscodes.
+Um mehr über die einzelnen Statuscodes zu erfahren und welcher wofür verwendet wird, sehen Sie sich die MDN Dokumentation über HTTP-Statuscodes an.
///
-## Abkürzung, um die Namen zu erinnern
+## Abkürzung zur Erinnerung an die Namen { #shortcut-to-remember-the-names }
-Schauen wir uns das vorherige Beispiel noch einmal an:
+Lassen Sie uns das vorherige Beispiel noch einmal anschauen:
-```Python hl_lines="6"
-{!../../../docs_src/response_status_code/tutorial001.py!}
-```
+{* ../../docs_src/response_status_code/tutorial001.py hl[6] *}
`201` ist der Statuscode für „Created“ („Erzeugt“).
-Aber Sie müssen sich nicht daran erinnern, welcher dieser Codes was bedeutet.
+Aber Sie müssen sich nicht merken, was jeder dieser Codes bedeutet.
-Sie können die Hilfsvariablen von `fastapi.status` verwenden.
+Sie können die Annehmlichkeit von Variablen aus `fastapi.status` nutzen.
-```Python hl_lines="1 6"
-{!../../../docs_src/response_status_code/tutorial002.py!}
-```
+{* ../../docs_src/response_status_code/tutorial002.py hl[1,6] *}
-Diese sind nur eine Annehmlichkeit und enthalten dieselbe Nummer, aber auf diese Weise können Sie die Autovervollständigung Ihres Editors verwenden, um sie zu finden:
+Diese sind nur eine Annehmlichkeit, sie enthalten dieselbe Zahl, aber so können Sie die Autovervollständigung Ihres Editors verwenden, um sie zu finden:
-/// note | "Technische Details"
+/// note | Technische Details
-Sie können auch `from starlette import status` verwenden.
+Sie könnten auch `from starlette import status` verwenden.
-**FastAPI** bietet dieselben `starlette.status`-Codes auch via `fastapi.status` an, als Annehmlichkeit für Sie, den Entwickler. Sie kommen aber direkt von Starlette.
+**FastAPI** bietet dieselben `starlette.status`-Codes auch via `fastapi.status` an, rein zu Ihrer Annehmlichkeit als Entwickler. Aber sie stammen direkt von Starlette.
///
-## Den Defaultwert ändern
+## Den Defaultwert ändern { #changing-the-default }
-Später sehen Sie, im [Handbuch für fortgeschrittene Benutzer](../advanced/response-change-status-code.md){.internal-link target=_blank}, wie Sie einen anderen Statuscode zurückgeben können, als den Default, den Sie hier deklarieren.
+Später im [Handbuch für fortgeschrittene Benutzer](../advanced/response-change-status-code.md){.internal-link target=_blank} werden Sie sehen, wie Sie einen anderen Statuscode zurückgeben können, als den Default, den Sie hier deklarieren.
diff --git a/docs/de/docs/tutorial/schema-extra-example.md b/docs/de/docs/tutorial/schema-extra-example.md
index 07b4bb7596..e2ffed292e 100644
--- a/docs/de/docs/tutorial/schema-extra-example.md
+++ b/docs/de/docs/tutorial/schema-extra-example.md
@@ -1,42 +1,22 @@
-# Beispiel-Request-Daten deklarieren
+# Beispiel-Request-Daten deklarieren { #declare-request-example-data }
-Sie können Beispiele für die Daten deklarieren, die Ihre Anwendung empfangen kann.
+Sie können Beispiele für die Daten deklarieren, die Ihre App empfangen kann.
Hier sind mehrere Möglichkeiten, das zu tun.
-## Zusätzliche JSON-Schemadaten in Pydantic-Modellen
+## Zusätzliche JSON-Schemadaten in Pydantic-Modellen { #extra-json-schema-data-in-pydantic-models }
Sie können `examples` („Beispiele“) für ein Pydantic-Modell deklarieren, welche dem generierten JSON-Schema hinzugefügt werden.
-//// tab | Python 3.10+ Pydantic v2
+//// tab | Pydantic v2
-```Python hl_lines="13-24"
-{!> ../../../docs_src/schema_extra_example/tutorial001_py310.py!}
-```
+{* ../../docs_src/schema_extra_example/tutorial001_py310.py hl[13:24] *}
////
-//// tab | Python 3.10+ Pydantic v1
+//// tab | Pydantic v1
-```Python hl_lines="13-23"
-{!> ../../../docs_src/schema_extra_example/tutorial001_py310_pv1.py!}
-```
-
-////
-
-//// tab | Python 3.8+ Pydantic v2
-
-```Python hl_lines="15-26"
-{!> ../../../docs_src/schema_extra_example/tutorial001.py!}
-```
-
-////
-
-//// tab | Python 3.8+ Pydantic v1
-
-```Python hl_lines="15-25"
-{!> ../../../docs_src/schema_extra_example/tutorial001_pv1.py!}
-```
+{* ../../docs_src/schema_extra_example/tutorial001_pv1_py310.py hl[13:23] *}
////
@@ -44,7 +24,7 @@ Diese zusätzlichen Informationen werden unverändert zum für dieses Modell aus
//// tab | Pydantic v2
-In Pydantic Version 2 würden Sie das Attribut `model_config` verwenden, das ein `dict` akzeptiert, wie beschrieben in Pydantic-Dokumentation: Configuration.
+In Pydantic Version 2 würden Sie das Attribut `model_config` verwenden, das ein `dict` akzeptiert, wie beschrieben in Pydantic-Dokumentation: Configuration.
Sie können `json_schema_extra` setzen, mit einem `dict`, das alle zusätzlichen Daten enthält, die im generierten JSON-Schema angezeigt werden sollen, einschließlich `examples`.
@@ -58,7 +38,7 @@ Sie können `schema_extra` setzen, mit einem `dict`, das alle zusätzlichen Date
////
-/// tip | "Tipp"
+/// tip | Tipp
Mit derselben Technik können Sie das JSON-Schema erweitern und Ihre eigenen benutzerdefinierten Zusatzinformationen hinzufügen.
@@ -66,37 +46,23 @@ Sie könnten das beispielsweise verwenden, um Metadaten für eine Frontend-Benut
///
-/// info
+/// info | Info
OpenAPI 3.1.0 (verwendet seit FastAPI 0.99.0) hat Unterstützung für `examples` hinzugefügt, was Teil des **JSON Schema** Standards ist.
-Zuvor unterstützte es nur das Schlüsselwort `example` mit einem einzigen Beispiel. Dieses wird weiterhin von OpenAPI 3.1.0 unterstützt, ist jedoch deprecated und nicht Teil des JSON Schema Standards. Wir empfehlen Ihnen daher, von `example` nach `examples` zu migrieren. 🤓
+Zuvor unterstützte es nur das Schlüsselwort `example` mit einem einzigen Beispiel. Dieses wird weiterhin von OpenAPI 3.1.0 unterstützt, ist jedoch deprecatet und nicht Teil des JSON Schema Standards. Wir empfehlen Ihnen daher, von `example` nach `examples` zu migrieren. 🤓
Mehr erfahren Sie am Ende dieser Seite.
///
-## Zusätzliche Argumente für `Field`
+## Zusätzliche Argumente für `Field` { #field-additional-arguments }
Wenn Sie `Field()` mit Pydantic-Modellen verwenden, können Sie ebenfalls zusätzliche `examples` deklarieren:
-//// tab | Python 3.10+
+{* ../../docs_src/schema_extra_example/tutorial002_py310.py hl[2,8:11] *}
-```Python hl_lines="2 8-11"
-{!> ../../../docs_src/schema_extra_example/tutorial002_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="4 10-13"
-{!> ../../../docs_src/schema_extra_example/tutorial002.py!}
-```
-
-////
-
-## `examples` im JSON-Schema – OpenAPI
+## `examples` im JSON-Schema – OpenAPI { #examples-in-json-schema-openapi }
Bei Verwendung von:
@@ -110,129 +76,29 @@ Bei Verwendung von:
können Sie auch eine Gruppe von `examples` mit zusätzlichen Informationen deklarieren, die zu ihren **JSON-Schemas** innerhalb von **OpenAPI** hinzugefügt werden.
-### `Body` mit `examples`
+### `Body` mit `examples` { #body-with-examples }
Hier übergeben wir `examples`, welches ein einzelnes Beispiel für die in `Body()` erwarteten Daten enthält:
-//// tab | Python 3.10+
+{* ../../docs_src/schema_extra_example/tutorial003_an_py310.py hl[22:29] *}
-```Python hl_lines="22-29"
-{!> ../../../docs_src/schema_extra_example/tutorial003_an_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="22-29"
-{!> ../../../docs_src/schema_extra_example/tutorial003_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="23-30"
-{!> ../../../docs_src/schema_extra_example/tutorial003_an.py!}
-```
-
-////
-
-//// tab | Python 3.10+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="18-25"
-{!> ../../../docs_src/schema_extra_example/tutorial003_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="20-27"
-{!> ../../../docs_src/schema_extra_example/tutorial003.py!}
-```
-
-////
-
-### Beispiel in der Dokumentations-Benutzeroberfläche
+### Beispiel in der Dokumentations-Benutzeroberfläche { #example-in-the-docs-ui }
Mit jeder der oben genannten Methoden würde es in `/docs` so aussehen:
-### `Body` mit mehreren `examples`
+### `Body` mit mehreren `examples` { #body-with-multiple-examples }
Sie können natürlich auch mehrere `examples` übergeben:
-//// tab | Python 3.10+
-
-```Python hl_lines="23-38"
-{!> ../../../docs_src/schema_extra_example/tutorial004_an_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="23-38"
-{!> ../../../docs_src/schema_extra_example/tutorial004_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="24-39"
-{!> ../../../docs_src/schema_extra_example/tutorial004_an.py!}
-```
-
-////
-
-//// tab | Python 3.10+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="19-34"
-{!> ../../../docs_src/schema_extra_example/tutorial004_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="21-36"
-{!> ../../../docs_src/schema_extra_example/tutorial004.py!}
-```
-
-////
+{* ../../docs_src/schema_extra_example/tutorial004_an_py310.py hl[23:38] *}
Wenn Sie das tun, werden die Beispiele Teil des internen **JSON-Schemas** für diese Body-Daten.
-Während dies geschrieben wird, unterstützt Swagger UI, das für die Anzeige der Dokumentations-Benutzeroberfläche zuständige Tool, jedoch nicht die Anzeige mehrerer Beispiele für die Daten in **JSON Schema**. Aber lesen Sie unten für einen Workaround weiter.
+Während dies geschrieben wird, unterstützt Swagger UI, das für die Anzeige der Dokumentations-Benutzeroberfläche zuständige Tool, jedoch nicht die Anzeige mehrerer Beispiele für die Daten in **JSON Schema**. Aber lesen Sie unten für einen Workaround weiter.
-### OpenAPI-spezifische `examples`
+### OpenAPI-spezifische `examples` { #openapi-specific-examples }
Schon bevor **JSON Schema** `examples` unterstützte, unterstützte OpenAPI ein anderes Feld, das auch `examples` genannt wurde.
@@ -240,11 +106,11 @@ Diese **OpenAPI-spezifischen** `examples` finden sich in einem anderen Abschnitt
Und Swagger UI unterstützt dieses spezielle Feld `examples` schon seit einiger Zeit. Sie können es also verwenden, um verschiedene **Beispiele in der Benutzeroberfläche der Dokumentation anzuzeigen**.
-Das Format dieses OpenAPI-spezifischen Felds `examples` ist ein `dict` mit **mehreren Beispielen** (anstelle einer `list`e), jedes mit zusätzlichen Informationen, die auch zu **OpenAPI** hinzugefügt werden.
+Das Format dieses OpenAPI-spezifischen Felds `examples` ist ein `dict` mit **mehreren Beispielen** (anstelle einer `list`), jedes mit zusätzlichen Informationen, die auch zu **OpenAPI** hinzugefügt werden.
Dies erfolgt nicht innerhalb jedes in OpenAPI enthaltenen JSON-Schemas, sondern außerhalb, in der *Pfadoperation*.
-### Verwendung des Parameters `openapi_examples`
+### Verwendung des Parameters `openapi_examples` { #using-the-openapi-examples-parameter }
Sie können die OpenAPI-spezifischen `examples` in FastAPI mit dem Parameter `openapi_examples` deklarieren, für:
@@ -256,7 +122,7 @@ Sie können die OpenAPI-spezifischen `examples` in FastAPI mit dem Parameter `op
* `Form()`
* `File()`
-Die Schlüssel des `dict` identifizieren jedes Beispiel, und jeder Wert (`"value"`) ist ein weiteres `dict`.
+Die Schlüssel des `dict` identifizieren jedes Beispiel, und jeder Wert ist ein weiteres `dict`.
Jedes spezifische Beispiel-`dict` in den `examples` kann Folgendes enthalten:
@@ -267,67 +133,17 @@ Jedes spezifische Beispiel-`dict` in den `examples` kann Folgendes enthalten:
Sie können es so verwenden:
-//// tab | Python 3.10+
+{* ../../docs_src/schema_extra_example/tutorial005_an_py310.py hl[23:49] *}
-```Python hl_lines="23-49"
-{!> ../../../docs_src/schema_extra_example/tutorial005_an_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="23-49"
-{!> ../../../docs_src/schema_extra_example/tutorial005_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="24-50"
-{!> ../../../docs_src/schema_extra_example/tutorial005_an.py!}
-```
-
-////
-
-//// tab | Python 3.10+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="19-45"
-{!> ../../../docs_src/schema_extra_example/tutorial005_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="21-47"
-{!> ../../../docs_src/schema_extra_example/tutorial005.py!}
-```
-
-////
-
-### OpenAPI-Beispiele in der Dokumentations-Benutzeroberfläche
+### OpenAPI-Beispiele in der Dokumentations-Benutzeroberfläche { #openapi-examples-in-the-docs-ui }
Wenn `openapi_examples` zu `Body()` hinzugefügt wird, würde `/docs` so aussehen:
-## Technische Details
+## Technische Details { #technical-details }
-/// tip | "Tipp"
+/// tip | Tipp
Wenn Sie bereits **FastAPI** Version **0.99.0 oder höher** verwenden, können Sie diese Details wahrscheinlich **überspringen**.
@@ -337,7 +153,7 @@ Sie können dies als eine kurze **Geschichtsstunde** zu OpenAPI und JSON Schema
///
-/// warning | "Achtung"
+/// warning | Achtung
Dies sind sehr technische Details zu den Standards **JSON Schema** und **OpenAPI**.
@@ -361,23 +177,23 @@ OpenAPI fügte auch die Felder `example` und `examples` zu anderen Teilen der Sp
* `File()`
* `Form()`
-/// info
+/// info | Info
Dieser alte, OpenAPI-spezifische `examples`-Parameter heißt seit FastAPI `0.103.0` jetzt `openapi_examples`.
///
-### JSON Schemas Feld `examples`
+### JSON Schemas Feld `examples` { #json-schemas-examples-field }
Aber dann fügte JSON Schema ein `examples`-Feld zu einer neuen Version der Spezifikation hinzu.
Und dann basierte das neue OpenAPI 3.1.0 auf der neuesten Version (JSON Schema 2020-12), die dieses neue Feld `examples` enthielt.
-Und jetzt hat dieses neue `examples`-Feld Vorrang vor dem alten (und benutzerdefinierten) `example`-Feld, im Singular, das jetzt deprecated ist.
+Und jetzt hat dieses neue `examples`-Feld Vorrang vor dem alten (und benutzerdefinierten) `example`-Feld, im Singular, das jetzt deprecatet ist.
-Dieses neue `examples`-Feld in JSON Schema ist **nur eine `list`e** von Beispielen, kein Dict mit zusätzlichen Metadaten wie an den anderen Stellen in OpenAPI (oben beschrieben).
+Dieses neue `examples`-Feld in JSON Schema ist **nur eine `list`** von Beispielen, kein Dict mit zusätzlichen Metadaten wie an den anderen Stellen in OpenAPI (oben beschrieben).
-/// info
+/// info | Info
Selbst, nachdem OpenAPI 3.1.0 veröffentlicht wurde, mit dieser neuen, einfacheren Integration mit JSON Schema, unterstützte Swagger UI, das Tool, das die automatische Dokumentation bereitstellt, eine Zeit lang OpenAPI 3.1.0 nicht (das tut es seit Version 5.0.0 🎉).
@@ -385,7 +201,7 @@ Aus diesem Grund verwendeten Versionen von FastAPI vor 0.99.0 immer noch Version
///
-### Pydantic- und FastAPI-`examples`
+### Pydantic- und FastAPI-`examples` { #pydantic-and-fastapi-examples }
Wenn Sie `examples` innerhalb eines Pydantic-Modells hinzufügen, indem Sie `schema_extra` oder `Field(examples=["something"])` verwenden, wird dieses Beispiel dem **JSON-Schema** für dieses Pydantic-Modell hinzugefügt.
@@ -395,14 +211,14 @@ In Versionen von FastAPI vor 0.99.0 (0.99.0 und höher verwenden das neuere Open
Aber jetzt, da FastAPI 0.99.0 und höher, OpenAPI 3.1.0 verwendet, das JSON Schema 2020-12 verwendet, und Swagger UI 5.0.0 und höher, ist alles konsistenter und die Beispiele sind in JSON Schema enthalten.
-### Swagger-Benutzeroberfläche und OpenAPI-spezifische `examples`.
+### Swagger-Benutzeroberfläche und OpenAPI-spezifische `examples` { #swagger-ui-and-openapi-specific-examples }
Da die Swagger-Benutzeroberfläche derzeit nicht mehrere JSON Schema Beispiele unterstützt (Stand: 26.08.2023), hatten Benutzer keine Möglichkeit, mehrere Beispiele in der Dokumentation anzuzeigen.
Um dieses Problem zu lösen, hat FastAPI `0.103.0` **Unterstützung** für die Deklaration desselben alten **OpenAPI-spezifischen** `examples`-Felds mit dem neuen Parameter `openapi_examples` hinzugefügt. 🤓
-### Zusammenfassung
+### Zusammenfassung { #summary }
Ich habe immer gesagt, dass ich Geschichte nicht so sehr mag ... und jetzt schauen Sie mich an, wie ich „Technikgeschichte“-Unterricht gebe. 😅
-Kurz gesagt: **Upgraden Sie auf FastAPI 0.99.0 oder höher**, und die Dinge sind viel **einfacher, konsistenter und intuitiver**, und Sie müssen nicht alle diese historischen Details kennen. 😎
+Kurz gesagt: **Aktualisieren Sie auf FastAPI 0.99.0 oder höher**, und die Dinge sind viel **einfacher, konsistenter und intuitiver**, und Sie müssen nicht alle diese historischen Details kennen. 😎
diff --git a/docs/de/docs/tutorial/security/first-steps.md b/docs/de/docs/tutorial/security/first-steps.md
index 6bc42cf6c3..20fcd0c007 100644
--- a/docs/de/docs/tutorial/security/first-steps.md
+++ b/docs/de/docs/tutorial/security/first-steps.md
@@ -1,8 +1,8 @@
-# Sicherheit – Erste Schritte
+# Sicherheit – Erste Schritte { #security-first-steps }
Stellen wir uns vor, dass Sie Ihre **Backend**-API auf einer Domain haben.
-Und Sie haben ein **Frontend** auf einer anderen Domain oder in einem anderen Pfad derselben Domain (oder in einer mobilen Anwendung).
+Und Sie haben ein **Frontend** auf einer anderen Domain oder in einem anderen Pfad derselben Domain (oder in einer Mobile-Anwendung).
Und Sie möchten eine Möglichkeit haben, dass sich das Frontend mithilfe eines **Benutzernamens** und eines **Passworts** beim Backend authentisieren kann.
@@ -12,53 +12,33 @@ Aber ersparen wir Ihnen die Zeit, die gesamte lange Spezifikation zu lesen, nur
Lassen Sie uns die von **FastAPI** bereitgestellten Tools verwenden, um Sicherheit zu gewährleisten.
-## Wie es aussieht
+## Wie es aussieht { #how-it-looks }
Lassen Sie uns zunächst einfach den Code verwenden und sehen, wie er funktioniert, und dann kommen wir zurück, um zu verstehen, was passiert.
-## `main.py` erstellen
+## `main.py` erstellen { #create-main-py }
Kopieren Sie das Beispiel in eine Datei `main.py`:
-//// tab | Python 3.9+
+{* ../../docs_src/security/tutorial001_an_py39.py *}
-```Python
-{!> ../../../docs_src/security/tutorial001_an_py39.py!}
+## Ausführen { #run-it }
+
+/// info | Info
+
+Das Paket `python-multipart` wird automatisch mit **FastAPI** installiert, wenn Sie den Befehl `pip install "fastapi[standard]"` ausführen.
+
+Wenn Sie jedoch den Befehl `pip install fastapi` verwenden, ist das Paket `python-multipart` nicht standardmäßig enthalten.
+
+Um es manuell zu installieren, stellen Sie sicher, dass Sie eine [Virtuelle Umgebung](../../virtual-environments.md){.internal-link target=_blank} erstellen, sie aktivieren und es dann mit:
+
+```console
+$ pip install python-multipart
```
-////
+installieren.
-//// tab | Python 3.8+
-
-```Python
-{!> ../../../docs_src/security/tutorial001_an.py!}
-```
-
-////
-
-//// tab | Python 3.8+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python
-{!> ../../../docs_src/security/tutorial001.py!}
-```
-
-////
-
-## Ausführen
-
-/// info
-
-Um hochgeladene Dateien zu empfangen, installieren Sie zuerst `python-multipart`.
-
-Z. B. `pip install python-multipart`.
-
-Das, weil **OAuth2** „Formulardaten“ zum Senden von `username` und `password` verwendet.
+Das liegt daran, dass **OAuth2** „Formulardaten“ zum Senden von `username` und `password` verwendet.
///
@@ -67,14 +47,14 @@ Führen Sie das Beispiel aus mit:
-/// check | "Authorize-Button!"
+/// check | Authorize-Button!
Sie haben bereits einen glänzenden, neuen „Authorize“-Button.
@@ -94,7 +74,7 @@ Und wenn Sie darauf klicken, erhalten Sie ein kleines Anmeldeformular zur Eingab
-/// note | "Hinweis"
+/// note | Hinweis
Es spielt keine Rolle, was Sie in das Formular eingeben, es wird noch nicht funktionieren. Wir kommen dahin.
@@ -108,7 +88,7 @@ Es kann von Anwendungen und Systemen Dritter verwendet werden.
Und es kann auch von Ihnen selbst verwendet werden, um dieselbe Anwendung zu debuggen, zu prüfen und zu testen.
-## Der `password`-Flow
+## Der `password`-Flow { #the-password-flow }
Lassen Sie uns nun etwas zurückgehen und verstehen, was das alles ist.
@@ -131,16 +111,16 @@ Betrachten wir es also aus dieser vereinfachten Sicht:
* Der Benutzer klickt im Frontend, um zu einem anderen Abschnitt der Frontend-Web-Anwendung zu gelangen.
* Das Frontend muss weitere Daten von der API abrufen.
* Es benötigt jedoch eine Authentifizierung für diesen bestimmten Endpunkt.
- * Um sich also bei unserer API zu authentifizieren, sendet es einen Header `Authorization` mit dem Wert `Bearer` plus dem Token.
+ * Um sich also bei unserer API zu authentifizieren, sendet es einen Header `Authorization` mit dem Wert `Bearer ` plus dem Token.
* Wenn der Token `foobar` enthielte, wäre der Inhalt des `Authorization`-Headers: `Bearer foobar`.
-## **FastAPI**s `OAuth2PasswordBearer`
+## **FastAPI**s `OAuth2PasswordBearer` { #fastapis-oauth2passwordbearer }
**FastAPI** bietet mehrere Tools auf unterschiedlichen Abstraktionsebenen zur Implementierung dieser Sicherheitsfunktionen.
In diesem Beispiel verwenden wir **OAuth2** mit dem **Password**-Flow und einem **Bearer**-Token. Wir machen das mit der Klasse `OAuth2PasswordBearer`.
-/// info
+/// info | Info
Ein „Bearer“-Token ist nicht die einzige Option.
@@ -154,37 +134,9 @@ In dem Fall gibt Ihnen **FastAPI** ebenfalls die Tools, die Sie zum Erstellen br
Wenn wir eine Instanz der Klasse `OAuth2PasswordBearer` erstellen, übergeben wir den Parameter `tokenUrl`. Dieser Parameter enthält die URL, die der Client (das Frontend, das im Browser des Benutzers ausgeführt wird) verwendet, wenn er den `username` und das `password` sendet, um einen Token zu erhalten.
-//// tab | Python 3.9+
+{* ../../docs_src/security/tutorial001_an_py39.py hl[8] *}
-```Python hl_lines="8"
-{!> ../../../docs_src/security/tutorial001_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="7"
-{!> ../../../docs_src/security/tutorial001_an.py!}
-```
-
-////
-
-//// tab | Python 3.8+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="6"
-{!> ../../../docs_src/security/tutorial001.py!}
-```
-
-////
-
-/// tip | "Tipp"
+/// tip | Tipp
Hier bezieht sich `tokenUrl="token"` auf eine relative URL `token`, die wir noch nicht erstellt haben. Da es sich um eine relative URL handelt, entspricht sie `./token`.
@@ -198,7 +150,7 @@ Dieser Parameter erstellt nicht diesen Endpunkt / diese *Pfadoperation*, sondern
Wir werden demnächst auch die eigentliche Pfadoperation erstellen.
-/// info
+/// info | Info
Wenn Sie ein sehr strenger „Pythonista“ sind, missfällt Ihnen möglicherweise die Schreibweise des Parameternamens `tokenUrl` anstelle von `token_url`.
@@ -216,45 +168,17 @@ oauth2_scheme(some, parameters)
Es kann also mit `Depends` verwendet werden.
-### Verwendung
+### Verwenden { #use-it }
Jetzt können Sie dieses `oauth2_scheme` als Abhängigkeit `Depends` übergeben.
-//// tab | Python 3.9+
-
-```Python hl_lines="12"
-{!> ../../../docs_src/security/tutorial001_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="11"
-{!> ../../../docs_src/security/tutorial001_an.py!}
-```
-
-////
-
-//// tab | Python 3.8+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="10"
-{!> ../../../docs_src/security/tutorial001.py!}
-```
-
-////
+{* ../../docs_src/security/tutorial001_an_py39.py hl[12] *}
Diese Abhängigkeit stellt einen `str` bereit, der dem Parameter `token` der *Pfadoperation-Funktion* zugewiesen wird.
**FastAPI** weiß, dass es diese Abhängigkeit verwenden kann, um ein „Sicherheitsschema“ im OpenAPI-Schema (und der automatischen API-Dokumentation) zu definieren.
-/// info | "Technische Details"
+/// info | Technische Details
**FastAPI** weiß, dass es die Klasse `OAuth2PasswordBearer` (deklariert in einer Abhängigkeit) verwenden kann, um das Sicherheitsschema in OpenAPI zu definieren, da es von `fastapi.security.oauth2.OAuth2` erbt, das wiederum von `fastapi.security.base.SecurityBase` erbt.
@@ -262,11 +186,11 @@ Alle Sicherheits-Werkzeuge, die in OpenAPI integriert sind (und die automatische
///
-## Was es macht
+## Was es macht { #what-it-does }
-FastAPI wird im Request nach diesem `Authorization`-Header suchen, prüfen, ob der Wert `Bearer` plus ein Token ist, und den Token als `str` zurückgeben.
+FastAPI wird im Request nach diesem `Authorization`-Header suchen, prüfen, ob der Wert `Bearer ` plus ein Token ist, und den Token als `str` zurückgeben.
-Wenn es keinen `Authorization`-Header sieht, oder der Wert keinen `Bearer`-Token hat, antwortet es direkt mit einem 401-Statuscode-Error (`UNAUTHORIZED`).
+Wenn es keinen `Authorization`-Header sieht, oder der Wert keinen `Bearer `-Token hat, antwortet es direkt mit einem 401-Statuscode-Error (`UNAUTHORIZED`).
Sie müssen nicht einmal prüfen, ob der Token existiert, um einen Fehler zurückzugeben. Seien Sie sicher, dass Ihre Funktion, wenn sie ausgeführt wird, ein `str` in diesem Token enthält.
@@ -276,6 +200,6 @@ Sie können das bereits in der interaktiven Dokumentation ausprobieren:
Wir überprüfen im Moment noch nicht die Gültigkeit des Tokens, aber das ist bereits ein Anfang.
-## Zusammenfassung
+## Zusammenfassung { #recap }
-Mit nur drei oder vier zusätzlichen Zeilen haben Sie also bereits eine primitive Form der Sicherheit.
+Mit nur drei oder vier zusätzlichen Zeilen haben Sie so bereits eine primitive Form der Sicherheit.
diff --git a/docs/de/docs/tutorial/security/get-current-user.md b/docs/de/docs/tutorial/security/get-current-user.md
index 8a68deeeff..e32e366698 100644
--- a/docs/de/docs/tutorial/security/get-current-user.md
+++ b/docs/de/docs/tutorial/security/get-current-user.md
@@ -1,100 +1,22 @@
-# Aktuellen Benutzer abrufen
+# Aktuellen Benutzer abrufen { #get-current-user }
Im vorherigen Kapitel hat das Sicherheitssystem (das auf dem Dependency Injection System basiert) der *Pfadoperation-Funktion* einen `token` vom Typ `str` überreicht:
-//// tab | Python 3.9+
-
-```Python hl_lines="12"
-{!> ../../../docs_src/security/tutorial001_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="11"
-{!> ../../../docs_src/security/tutorial001_an.py!}
-```
-
-////
-
-//// tab | Python 3.8+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="10"
-{!> ../../../docs_src/security/tutorial001.py!}
-```
-
-////
+{* ../../docs_src/security/tutorial001_an_py39.py hl[12] *}
Aber das ist immer noch nicht so nützlich.
Lassen wir es uns den aktuellen Benutzer überreichen.
-## Ein Benutzermodell erstellen
+## Ein Benutzermodell erstellen { #create-a-user-model }
Erstellen wir zunächst ein Pydantic-Benutzermodell.
So wie wir Pydantic zum Deklarieren von Bodys verwenden, können wir es auch überall sonst verwenden:
-//// tab | Python 3.10+
+{* ../../docs_src/security/tutorial002_an_py310.py hl[5,12:6] *}
-```Python hl_lines="5 12-16"
-{!> ../../../docs_src/security/tutorial002_an_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="5 12-16"
-{!> ../../../docs_src/security/tutorial002_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="5 13-17"
-{!> ../../../docs_src/security/tutorial002_an.py!}
-```
-
-////
-
-//// tab | Python 3.10+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="3 10-14"
-{!> ../../../docs_src/security/tutorial002_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="5 12-16"
-{!> ../../../docs_src/security/tutorial002.py!}
-```
-
-////
-
-## Eine `get_current_user`-Abhängigkeit erstellen
+## Eine `get_current_user`-Abhängigkeit erstellen { #create-a-get-current-user-dependency }
Erstellen wir eine Abhängigkeit `get_current_user`.
@@ -104,183 +26,33 @@ Erinnern Sie sich, dass Abhängigkeiten Unterabhängigkeiten haben können?
So wie wir es zuvor in der *Pfadoperation* direkt gemacht haben, erhält unsere neue Abhängigkeit `get_current_user` von der Unterabhängigkeit `oauth2_scheme` einen `token` vom Typ `str`:
-//// tab | Python 3.10+
+{* ../../docs_src/security/tutorial002_an_py310.py hl[25] *}
-```Python hl_lines="25"
-{!> ../../../docs_src/security/tutorial002_an_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="25"
-{!> ../../../docs_src/security/tutorial002_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="26"
-{!> ../../../docs_src/security/tutorial002_an.py!}
-```
-
-////
-
-//// tab | Python 3.10+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="23"
-{!> ../../../docs_src/security/tutorial002_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="25"
-{!> ../../../docs_src/security/tutorial002.py!}
-```
-
-////
-
-## Den Benutzer holen
+## Den Benutzer abrufen { #get-the-user }
`get_current_user` wird eine von uns erstellte (gefakte) Hilfsfunktion verwenden, welche einen Token vom Typ `str` entgegennimmt und unser Pydantic-`User`-Modell zurückgibt:
-//// tab | Python 3.10+
+{* ../../docs_src/security/tutorial002_an_py310.py hl[19:22,26:27] *}
-```Python hl_lines="19-22 26-27"
-{!> ../../../docs_src/security/tutorial002_an_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="19-22 26-27"
-{!> ../../../docs_src/security/tutorial002_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="20-23 27-28"
-{!> ../../../docs_src/security/tutorial002_an.py!}
-```
-
-////
-
-//// tab | Python 3.10+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="17-20 24-25"
-{!> ../../../docs_src/security/tutorial002_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="19-22 26-27"
-{!> ../../../docs_src/security/tutorial002.py!}
-```
-
-////
-
-## Den aktuellen Benutzer einfügen
+## Den aktuellen Benutzer einfügen { #inject-the-current-user }
Und jetzt können wir wiederum `Depends` mit unserem `get_current_user` in der *Pfadoperation* verwenden:
-//// tab | Python 3.10+
-
-```Python hl_lines="31"
-{!> ../../../docs_src/security/tutorial002_an_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="31"
-{!> ../../../docs_src/security/tutorial002_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="32"
-{!> ../../../docs_src/security/tutorial002_an.py!}
-```
-
-////
-
-//// tab | Python 3.10+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="29"
-{!> ../../../docs_src/security/tutorial002_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="31"
-{!> ../../../docs_src/security/tutorial002.py!}
-```
-
-////
+{* ../../docs_src/security/tutorial002_an_py310.py hl[31] *}
Beachten Sie, dass wir als Typ von `current_user` das Pydantic-Modell `User` deklarieren.
Das wird uns innerhalb der Funktion bei Codevervollständigung und Typprüfungen helfen.
-/// tip | "Tipp"
+/// tip | Tipp
-Sie erinnern sich vielleicht, dass Requestbodys ebenfalls mit Pydantic-Modellen deklariert werden.
+Sie erinnern sich vielleicht, dass Requestbodys ebenfalls mit Pydantic-Modellen deklariert werden.
Weil Sie `Depends` verwenden, wird **FastAPI** hier aber nicht verwirrt.
///
-/// check
+/// check | Testen
Die Art und Weise, wie dieses System von Abhängigkeiten konzipiert ist, ermöglicht es uns, verschiedene Abhängigkeiten (verschiedene „Dependables“) zu haben, die alle ein `User`-Modell zurückgeben.
@@ -288,7 +60,7 @@ Wir sind nicht darauf beschränkt, nur eine Abhängigkeit zu haben, die diesen T
///
-## Andere Modelle
+## Andere Modelle { #other-models }
Sie können jetzt den aktuellen Benutzer direkt in den *Pfadoperation-Funktionen* abrufen und die Sicherheitsmechanismen auf **Dependency Injection** Ebene handhaben, mittels `Depends`.
@@ -304,7 +76,7 @@ Sie haben eigentlich keine Benutzer, die sich bei Ihrer Anwendung anmelden, sond
Verwenden Sie einfach jede Art von Modell, jede Art von Klasse, jede Art von Datenbank, die Sie für Ihre Anwendung benötigen. **FastAPI** deckt das alles mit seinem Dependency Injection System ab.
-## Codegröße
+## Codegröße { #code-size }
Dieses Beispiel mag ausführlich erscheinen. Bedenken Sie, dass wir Sicherheit, Datenmodelle, Hilfsfunktionen und *Pfadoperationen* in derselben Datei vermischen.
@@ -320,59 +92,9 @@ Und alle (oder beliebige Teile davon) können Vorteil ziehen aus der Wiederverwe
Und alle diese Tausenden von *Pfadoperationen* können nur drei Zeilen lang sein:
-//// tab | Python 3.10+
+{* ../../docs_src/security/tutorial002_an_py310.py hl[30:32] *}
-```Python hl_lines="30-32"
-{!> ../../../docs_src/security/tutorial002_an_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="30-32"
-{!> ../../../docs_src/security/tutorial002_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="31-33"
-{!> ../../../docs_src/security/tutorial002_an.py!}
-```
-
-////
-
-//// tab | Python 3.10+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="28-30"
-{!> ../../../docs_src/security/tutorial002_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="30-32"
-{!> ../../../docs_src/security/tutorial002.py!}
-```
-
-////
-
-## Zusammenfassung
+## Zusammenfassung { #recap }
Sie können jetzt den aktuellen Benutzer direkt in Ihrer *Pfadoperation-Funktion* abrufen.
diff --git a/docs/de/docs/tutorial/security/index.md b/docs/de/docs/tutorial/security/index.md
index ad09273610..39b0b93c98 100644
--- a/docs/de/docs/tutorial/security/index.md
+++ b/docs/de/docs/tutorial/security/index.md
@@ -1,4 +1,4 @@
-# Sicherheit
+# Sicherheit { #security }
Es gibt viele Wege, Sicherheit, Authentifizierung und Autorisierung zu handhaben.
@@ -10,11 +10,11 @@ In vielen Frameworks und Systemen erfordert allein die Handhabung von Sicherheit
Aber schauen wir uns zunächst ein paar kleine Konzepte an.
-## In Eile?
+## In Eile? { #in-a-hurry }
Wenn Ihnen diese Begriffe egal sind und Sie einfach *jetzt* Sicherheit mit Authentifizierung basierend auf Benutzername und Passwort hinzufügen müssen, fahren Sie mit den nächsten Kapiteln fort.
-## OAuth2
+## OAuth2 { #oauth2 }
OAuth2 ist eine Spezifikation, die verschiedene Möglichkeiten zur Handhabung von Authentifizierung und Autorisierung definiert.
@@ -22,9 +22,9 @@ Es handelt sich um eine recht umfangreiche Spezifikation, und sie deckt mehrere
Sie umfasst Möglichkeiten zur Authentifizierung mithilfe eines „Dritten“ („third party“).
-Das ist es, was alle diese „Login mit Facebook, Google, Twitter, GitHub“-Systeme unter der Haube verwenden.
+Das ist es, was alle diese „Login mit Facebook, Google, X (Twitter), GitHub“-Systeme unter der Haube verwenden.
-### OAuth 1
+### OAuth 1 { #oauth-1 }
Es gab ein OAuth 1, das sich stark von OAuth2 unterscheidet und komplexer ist, da es direkte Spezifikationen enthält, wie die Kommunikation verschlüsselt wird.
@@ -32,13 +32,13 @@ Heutzutage ist es nicht sehr populär und wird kaum verwendet.
OAuth2 spezifiziert nicht, wie die Kommunikation verschlüsselt werden soll, sondern erwartet, dass Ihre Anwendung mit HTTPS bereitgestellt wird.
-/// tip | "Tipp"
+/// tip | Tipp
Im Abschnitt über **Deployment** erfahren Sie, wie Sie HTTPS mithilfe von Traefik und Let's Encrypt kostenlos einrichten.
///
-## OpenID Connect
+## OpenID Connect { #openid-connect }
OpenID Connect ist eine weitere Spezifikation, die auf **OAuth2** basiert.
@@ -48,7 +48,7 @@ Beispielsweise verwendet der Google Login OpenID Connect (welches seinerseits OA
Aber der Facebook Login unterstützt OpenID Connect nicht. Es hat seine eigene Variante von OAuth2.
-### OpenID (nicht „OpenID Connect“)
+### OpenID (nicht „OpenID Connect“) { #openid-not-openid-connect }
Es gab auch eine „OpenID“-Spezifikation. Sie versuchte das Gleiche zu lösen wie **OpenID Connect**, basierte aber nicht auf OAuth2.
@@ -56,7 +56,7 @@ Es handelte sich also um ein komplett zusätzliches System.
Heutzutage ist es nicht sehr populär und wird kaum verwendet.
-## OpenAPI
+## OpenAPI { #openapi }
OpenAPI (früher bekannt als Swagger) ist die offene Spezifikation zum Erstellen von APIs (jetzt Teil der Linux Foundation).
@@ -75,11 +75,11 @@ OpenAPI definiert die folgenden Sicherheitsschemas:
* Einem Header.
* Einem Cookie.
* `http`: Standard-HTTP-Authentifizierungssysteme, einschließlich:
- * `bearer`: ein Header `Authorization` mit dem Wert `Bearer` plus einem Token. Dies wird von OAuth2 geerbt.
+ * `bearer`: ein Header `Authorization` mit dem Wert `Bearer ` plus einem Token. Dies wird von OAuth2 geerbt.
* HTTP Basic Authentication.
* HTTP Digest, usw.
* `oauth2`: Alle OAuth2-Methoden zum Umgang mit Sicherheit (genannt „Flows“).
- * Mehrere dieser Flows eignen sich zum Aufbau eines OAuth 2.0-Authentifizierungsanbieters (wie Google, Facebook, Twitter, GitHub usw.):
+ * Mehrere dieser Flows eignen sich zum Aufbau eines OAuth 2.0-Authentifizierungsanbieters (wie Google, Facebook, X (Twitter), GitHub usw.):
* `implicit`
* `clientCredentials`
* `authorizationCode`
@@ -89,15 +89,15 @@ OpenAPI definiert die folgenden Sicherheitsschemas:
* Diese automatische Erkennung ist es, die in der OpenID Connect Spezifikation definiert ist.
-/// tip | "Tipp"
+/// tip | Tipp
-Auch die Integration anderer Authentifizierungs-/Autorisierungsanbieter wie Google, Facebook, Twitter, GitHub, usw. ist möglich und relativ einfach.
+Auch die Integration anderer Authentifizierungs-/Autorisierungsanbieter wie Google, Facebook, X (Twitter), GitHub, usw. ist möglich und relativ einfach.
Das komplexeste Problem besteht darin, einen Authentifizierungs-/Autorisierungsanbieter wie solche aufzubauen, aber **FastAPI** reicht Ihnen die Tools, das einfach zu erledigen, während Ihnen die schwere Arbeit abgenommen wird.
///
-## **FastAPI** Tools
+## **FastAPI** Tools { #fastapi-utilities }
FastAPI stellt für jedes dieser Sicherheitsschemas im Modul `fastapi.security` verschiedene Tools bereit, die die Verwendung dieser Sicherheitsmechanismen vereinfachen.
diff --git a/docs/de/docs/tutorial/security/oauth2-jwt.md b/docs/de/docs/tutorial/security/oauth2-jwt.md
index 88f0dbe42e..0d5ce587ba 100644
--- a/docs/de/docs/tutorial/security/oauth2-jwt.md
+++ b/docs/de/docs/tutorial/security/oauth2-jwt.md
@@ -1,4 +1,4 @@
-# OAuth2 mit Password (und Hashing), Bearer mit JWT-Tokens
+# OAuth2 mit Passwort (und Hashing), Bearer mit JWT-Tokens { #oauth2-with-password-and-hashing-bearer-with-jwt-tokens }
Da wir nun über den gesamten Sicherheitsablauf verfügen, machen wir die Anwendung tatsächlich sicher, indem wir JWT-Tokens und sicheres Passwort-Hashing verwenden.
@@ -6,7 +6,7 @@ Diesen Code können Sie tatsächlich in Ihrer Anwendung verwenden, die Passwort-
Wir bauen auf dem vorherigen Kapitel auf.
-## Über JWT
+## Über JWT { #about-jwt }
JWT bedeutet „JSON Web Tokens“.
@@ -26,33 +26,31 @@ Nach einer Woche läuft der Token ab und der Benutzer wird nicht autorisiert und
Wenn Sie mit JWT-Tokens spielen und sehen möchten, wie sie funktionieren, schauen Sie sich https://jwt.io an.
-## `python-jose` installieren.
+## `PyJWT` installieren { #install-pyjwt }
-Wir müssen `python-jose` installieren, um die JWT-Tokens in Python zu generieren und zu verifizieren:
+Wir müssen `PyJWT` installieren, um die JWT-Tokens in Python zu generieren und zu verifizieren.
+
+Stellen Sie sicher, dass Sie eine [virtuelle Umgebung](../../virtual-environments.md){.internal-link target=_blank} erstellen, sie aktivieren und dann `pyjwt` installieren:
-Wenn Sie die Developer Tools öffnen, können Sie sehen, dass die gesendeten Daten nur den Token enthalten. Das Passwort wird nur bei der ersten Anfrage gesendet, um den Benutzer zu authentisieren und diesen Zugriffstoken zu erhalten, aber nicht mehr danach:
+Wenn Sie die Developer Tools öffnen, können Sie sehen, dass die gesendeten Daten nur den Token enthalten. Das Passwort wird nur beim ersten Request gesendet, um den Benutzer zu authentisieren und diesen Zugriffstoken zu erhalten, aber nicht mehr danach:
-/// note | "Hinweis"
+/// note | Hinweis
-Beachten Sie den Header `Authorization` mit einem Wert, der mit `Bearer` beginnt.
+Beachten Sie den Header `Authorization` mit einem Wert, der mit `Bearer ` beginnt.
///
-## Fortgeschrittene Verwendung mit `scopes`
+## Fortgeschrittene Verwendung mit `scopes` { #advanced-usage-with-scopes }
OAuth2 hat ein Konzept von „Scopes“.
@@ -452,7 +250,7 @@ Anschließend können Sie diesen Token einem Benutzer direkt oder einem Dritten
Wie Sie sie verwenden und wie sie in **FastAPI** integriert sind, erfahren Sie später im **Handbuch für fortgeschrittene Benutzer**.
-## Zusammenfassung
+## Zusammenfassung { #recap }
Mit dem, was Sie bis hier gesehen haben, können Sie eine sichere **FastAPI**-Anwendung mithilfe von Standards wie OAuth2 und JWT einrichten.
@@ -466,10 +264,10 @@ Viele Packages, die es stark vereinfachen, müssen viele Kompromisse beim Datenm
Es gibt Ihnen die volle Flexibilität, diejenigen auszuwählen, die am besten zu Ihrem Projekt passen.
-Und Sie können viele gut gepflegte und weit verbreitete Packages wie `passlib` und `python-jose` direkt verwenden, da **FastAPI** keine komplexen Mechanismen zur Integration externer Pakete erfordert.
+Und Sie können viele gut gepflegte und weit verbreitete Packages wie `pwdlib` und `PyJWT` direkt verwenden, da **FastAPI** keine komplexen Mechanismen zur Integration externer Pakete erfordert.
Aber es bietet Ihnen die Werkzeuge, um den Prozess so weit wie möglich zu vereinfachen, ohne Kompromisse bei Flexibilität, Robustheit oder Sicherheit einzugehen.
Und Sie können sichere Standardprotokolle wie OAuth2 auf relativ einfache Weise verwenden und implementieren.
-Im **Handbuch für fortgeschrittene Benutzer** erfahren Sie mehr darüber, wie Sie OAuth2-„Scopes“ für ein feingranuliertes Berechtigungssystem verwenden, das denselben Standards folgt. OAuth2 mit Scopes ist der Mechanismus, der von vielen großen Authentifizierungsanbietern wie Facebook, Google, GitHub, Microsoft, Twitter, usw. verwendet wird, um Drittanbieteranwendungen zu autorisieren, im Namen ihrer Benutzer mit ihren APIs zu interagieren.
+Im **Handbuch für fortgeschrittene Benutzer** erfahren Sie mehr darüber, wie Sie OAuth2-„Scopes“ für ein feingranuliertes Berechtigungssystem verwenden, das denselben Standards folgt. OAuth2 mit Scopes ist der Mechanismus, der von vielen großen Authentifizierungsanbietern wie Facebook, Google, GitHub, Microsoft, X (Twitter), usw. verwendet wird, um Drittanbieteranwendungen zu autorisieren, im Namen ihrer Benutzer mit ihren APIs zu interagieren.
diff --git a/docs/de/docs/tutorial/security/simple-oauth2.md b/docs/de/docs/tutorial/security/simple-oauth2.md
index 3b1c4ae28a..28cb83ba93 100644
--- a/docs/de/docs/tutorial/security/simple-oauth2.md
+++ b/docs/de/docs/tutorial/security/simple-oauth2.md
@@ -1,8 +1,8 @@
-# Einfaches OAuth2 mit Password und Bearer
+# Einfaches OAuth2 mit Password und Bearer { #simple-oauth2-with-password-and-bearer }
Lassen Sie uns nun auf dem vorherigen Kapitel aufbauen und die fehlenden Teile hinzufügen, um einen vollständigen Sicherheits-Flow zu erhalten.
-## `username` und `password` entgegennehmen
+## `username` und `password` entgegennehmen { #get-the-username-and-password }
Wir werden **FastAPIs** Sicherheits-Werkzeuge verwenden, um den `username` und das `password` entgegenzunehmen.
@@ -18,7 +18,7 @@ Aber für die Login-*Pfadoperation* müssen wir diese Namen verwenden, um mit de
Die Spezifikation besagt auch, dass `username` und `password` als Formulardaten gesendet werden müssen (hier also kein JSON).
-### `scope`
+### `scope` { #scope }
Ferner sagt die Spezifikation, dass der Client ein weiteres Formularfeld "`scope`" („Geltungsbereich“) senden kann.
@@ -32,7 +32,7 @@ Diese werden normalerweise verwendet, um bestimmte Sicherheitsberechtigungen zu
* `instagram_basic` wird von Facebook / Instagram verwendet.
* `https://www.googleapis.com/auth/drive` wird von Google verwendet.
-/// info
+/// info | Info
In OAuth2 ist ein „Scope“ nur ein String, der eine bestimmte erforderliche Berechtigung deklariert.
@@ -44,74 +44,24 @@ Für OAuth2 sind es einfach nur Strings.
///
-## Code, um `username` und `password` entgegenzunehmen.
+## Code, um `username` und `password` entgegenzunehmen { #code-to-get-the-username-and-password }
Lassen Sie uns nun die von **FastAPI** bereitgestellten Werkzeuge verwenden, um das zu erledigen.
-### `OAuth2PasswordRequestForm`
+### `OAuth2PasswordRequestForm` { #oauth2passwordrequestform }
Importieren Sie zunächst `OAuth2PasswordRequestForm` und verwenden Sie es als Abhängigkeit mit `Depends` in der *Pfadoperation* für `/token`:
-//// tab | Python 3.10+
-
-```Python hl_lines="4 78"
-{!> ../../../docs_src/security/tutorial003_an_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="4 78"
-{!> ../../../docs_src/security/tutorial003_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="4 79"
-{!> ../../../docs_src/security/tutorial003_an.py!}
-```
-
-////
-
-//// tab | Python 3.10+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="2 74"
-{!> ../../../docs_src/security/tutorial003_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="4 76"
-{!> ../../../docs_src/security/tutorial003.py!}
-```
-
-////
+{* ../../docs_src/security/tutorial003_an_py310.py hl[4,78] *}
`OAuth2PasswordRequestForm` ist eine Klassenabhängigkeit, die einen Formularbody deklariert mit:
* Dem `username`.
* Dem `password`.
* Einem optionalen `scope`-Feld als langem String, bestehend aus durch Leerzeichen getrennten Strings.
-* Einem optionalen `grant_type` („Art der Anmeldung“).
+* Einem optionalen `grant_type`.
-/// tip | "Tipp"
+/// tip | Tipp
Die OAuth2-Spezifikation *erfordert* tatsächlich ein Feld `grant_type` mit dem festen Wert `password`, aber `OAuth2PasswordRequestForm` erzwingt dies nicht.
@@ -122,7 +72,7 @@ Wenn Sie es erzwingen müssen, verwenden Sie `OAuth2PasswordRequestFormStrict` a
* Eine optionale `client_id` (benötigen wir für unser Beispiel nicht).
* Ein optionales `client_secret` (benötigen wir für unser Beispiel nicht).
-/// info
+/// info | Info
`OAuth2PasswordRequestForm` ist keine spezielle Klasse für **FastAPI**, so wie `OAuth2PasswordBearer`.
@@ -134,9 +84,9 @@ Da es sich jedoch um einen häufigen Anwendungsfall handelt, wird er zur Vereinf
///
-### Die Formulardaten verwenden
+### Die Formulardaten verwenden { #use-the-form-data }
-/// tip | "Tipp"
+/// tip | Tipp
Die Instanz der Klassenabhängigkeit `OAuth2PasswordRequestForm` verfügt, statt eines Attributs `scope` mit dem durch Leerzeichen getrennten langen String, über das Attribut `scopes` mit einer tatsächlichen Liste von Strings, einem für jeden gesendeten Scope.
@@ -150,59 +100,9 @@ Wenn es keinen solchen Benutzer gibt, geben wir die Fehlermeldung „Incorrect u
Für den Fehler verwenden wir die Exception `HTTPException`:
-//// tab | Python 3.10+
+{* ../../docs_src/security/tutorial003_an_py310.py hl[3,79:81] *}
-```Python hl_lines="3 79-81"
-{!> ../../../docs_src/security/tutorial003_an_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="3 79-81"
-{!> ../../../docs_src/security/tutorial003_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="3 80-82"
-{!> ../../../docs_src/security/tutorial003_an.py!}
-```
-
-////
-
-//// tab | Python 3.10+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="1 75-77"
-{!> ../../../docs_src/security/tutorial003_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="3 77-79"
-{!> ../../../docs_src/security/tutorial003.py!}
-```
-
-////
-
-### Das Passwort überprüfen
+### Das Passwort überprüfen { #check-the-password }
Zu diesem Zeitpunkt liegen uns die Benutzerdaten aus unserer Datenbank vor, das Passwort haben wir jedoch noch nicht überprüft.
@@ -212,7 +112,7 @@ Sie sollten niemals Klartext-Passwörter speichern, daher verwenden wir ein (gef
Wenn die Passwörter nicht übereinstimmen, geben wir denselben Fehler zurück.
-#### Passwort-Hashing
+#### Passwort-Hashing { #password-hashing }
„Hashing“ bedeutet: Konvertieren eines Inhalts (in diesem Fall eines Passworts) in eine Folge von Bytes (ein schlichter String), die wie Kauderwelsch aussieht.
@@ -220,65 +120,15 @@ Immer wenn Sie genau den gleichen Inhalt (genau das gleiche Passwort) übergeben
Sie können jedoch nicht vom Kauderwelsch zurück zum Passwort konvertieren.
-##### Warum Passwort-Hashing verwenden?
+##### Warum Passwort-Hashing verwenden? { #why-use-password-hashing }
Wenn Ihre Datenbank gestohlen wird, hat der Dieb nicht die Klartext-Passwörter Ihrer Benutzer, sondern nur die Hashes.
Der Dieb kann also nicht versuchen, die gleichen Passwörter in einem anderen System zu verwenden (da viele Benutzer überall das gleiche Passwort verwenden, wäre dies gefährlich).
-//// tab | Python 3.10+
+{* ../../docs_src/security/tutorial003_an_py310.py hl[82:85] *}
-```Python hl_lines="82-85"
-{!> ../../../docs_src/security/tutorial003_an_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="82-85"
-{!> ../../../docs_src/security/tutorial003_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="83-86"
-{!> ../../../docs_src/security/tutorial003_an.py!}
-```
-
-////
-
-//// tab | Python 3.10+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="78-81"
-{!> ../../../docs_src/security/tutorial003_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="80-83"
-{!> ../../../docs_src/security/tutorial003.py!}
-```
-
-////
-
-#### Über `**user_dict`
+#### Über `**user_dict` { #about-user-dict }
`UserInDB(**user_dict)` bedeutet:
@@ -294,15 +144,15 @@ UserInDB(
)
```
-/// info
+/// info | Info
-Eine ausführlichere Erklärung von `**user_dict` finden Sie in [der Dokumentation für **Extra Modelle**](../extra-models.md#uber-user_indict){.internal-link target=_blank}.
+Eine ausführlichere Erklärung von `**user_dict` finden Sie in [der Dokumentation für **Extra Modelle**](../extra-models.md#about-user-in-dict){.internal-link target=_blank}.
///
-## Den Token zurückgeben
+## Den Token zurückgeben { #return-the-token }
-Die Response des `token`-Endpunkts muss ein JSON-Objekt sein.
+Die Response des `token`-Endpunkts muss ein JSON-Objekt sein.
Es sollte einen `token_type` haben. Da wir in unserem Fall „Bearer“-Token verwenden, sollte der Token-Typ "`bearer`" sein.
@@ -310,7 +160,7 @@ Und es sollte einen `access_token` haben, mit einem String, der unseren Zugriffs
In diesem einfachen Beispiel gehen wir einfach völlig unsicher vor und geben denselben `username` wie der Token zurück.
-/// tip | "Tipp"
+/// tip | Tipp
Im nächsten Kapitel sehen Sie eine wirklich sichere Implementierung mit Passwort-Hashing und JWT-Tokens.
@@ -318,59 +168,9 @@ Aber konzentrieren wir uns zunächst auf die spezifischen Details, die wir benö
///
-//// tab | Python 3.10+
+{* ../../docs_src/security/tutorial003_an_py310.py hl[87] *}
-```Python hl_lines="87"
-{!> ../../../docs_src/security/tutorial003_an_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="87"
-{!> ../../../docs_src/security/tutorial003_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="88"
-{!> ../../../docs_src/security/tutorial003_an.py!}
-```
-
-////
-
-//// tab | Python 3.10+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="83"
-{!> ../../../docs_src/security/tutorial003_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="85"
-{!> ../../../docs_src/security/tutorial003.py!}
-```
-
-////
-
-/// tip | "Tipp"
+/// tip | Tipp
Gemäß der Spezifikation sollten Sie ein JSON mit einem `access_token` und einem `token_type` zurückgeben, genau wie in diesem Beispiel.
@@ -382,7 +182,7 @@ Den Rest erledigt **FastAPI** für Sie.
///
-## Die Abhängigkeiten aktualisieren
+## Die Abhängigkeiten aktualisieren { #update-the-dependencies }
Jetzt werden wir unsere Abhängigkeiten aktualisieren.
@@ -394,59 +194,9 @@ Beide Abhängigkeiten geben nur dann einen HTTP-Error zurück, wenn der Benutzer
In unserem Endpunkt erhalten wir also nur dann einen Benutzer, wenn der Benutzer existiert, korrekt authentifiziert wurde und aktiv ist:
-//// tab | Python 3.10+
+{* ../../docs_src/security/tutorial003_an_py310.py hl[58:66,69:74,94] *}
-```Python hl_lines="58-66 69-74 94"
-{!> ../../../docs_src/security/tutorial003_an_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="58-66 69-74 94"
-{!> ../../../docs_src/security/tutorial003_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="59-67 70-75 95"
-{!> ../../../docs_src/security/tutorial003_an.py!}
-```
-
-////
-
-//// tab | Python 3.10+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="56-64 67-70 88"
-{!> ../../../docs_src/security/tutorial003_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+ nicht annotiert
-
-/// tip | "Tipp"
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
-```Python hl_lines="58-66 69-72 90"
-{!> ../../../docs_src/security/tutorial003.py!}
-```
-
-////
-
-/// info
+/// info | Info
Der zusätzliche Header `WWW-Authenticate` mit dem Wert `Bearer`, den wir hier zurückgeben, ist ebenfalls Teil der Spezifikation.
@@ -464,11 +214,11 @@ Das ist der Vorteil von Standards ...
///
-## Es in Aktion sehen
+## Es in Aktion sehen { #see-it-in-action }
Öffnen Sie die interaktive Dokumentation: http://127.0.0.1:8000/docs.
-### Authentifizieren
+### Authentifizieren { #authenticate }
Klicken Sie auf den Button „Authorize“.
@@ -484,7 +234,7 @@ Nach der Authentifizierung im System sehen Sie Folgendes:
-### Die eigenen Benutzerdaten ansehen
+### Die eigenen Benutzerdaten ansehen { #get-your-own-user-data }
Verwenden Sie nun die Operation `GET` mit dem Pfad `/users/me`.
@@ -510,7 +260,7 @@ Wenn Sie auf das Schlosssymbol klicken und sich abmelden und dann den gleichen V
}
```
-### Inaktiver Benutzer
+### Inaktiver Benutzer { #inactive-user }
Versuchen Sie es nun mit einem inaktiven Benutzer und authentisieren Sie sich mit:
@@ -528,7 +278,7 @@ Sie erhalten die Fehlermeldung „Inactive user“:
}
```
-## Zusammenfassung
+## Zusammenfassung { #recap }
Sie verfügen jetzt über die Tools, um ein vollständiges Sicherheitssystem basierend auf `username` und `password` für Ihre API zu implementieren.
diff --git a/docs/de/docs/tutorial/sql-databases.md b/docs/de/docs/tutorial/sql-databases.md
new file mode 100644
index 0000000000..cf9731aee6
--- /dev/null
+++ b/docs/de/docs/tutorial/sql-databases.md
@@ -0,0 +1,357 @@
+# SQL (Relationale) Datenbanken { #sql-relational-databases }
+
+**FastAPI** erfordert nicht, dass Sie eine SQL (relationale) Datenbank verwenden. Sondern Sie können **jede beliebige Datenbank** verwenden, die Sie möchten.
+
+Hier werden wir ein Beispiel mit SQLModel sehen.
+
+**SQLModel** basiert auf SQLAlchemy und Pydantic. Es wurde vom selben Autor wie **FastAPI** entwickelt, um die perfekte Ergänzung für FastAPI-Anwendungen zu sein, die **SQL-Datenbanken** verwenden müssen.
+
+/// tip | Tipp
+
+Sie könnten jede andere SQL- oder NoSQL-Datenbankbibliothek verwenden, die Sie möchten (in einigen Fällen als „ORMs“ bezeichnet), FastAPI zwingt Sie nicht, irgendetwas zu verwenden. 😎
+
+///
+
+Da SQLModel auf SQLAlchemy basiert, können Sie problemlos **jede von SQLAlchemy unterstützte Datenbank** verwenden (was auch bedeutet, dass sie von SQLModel unterstützt werden), wie:
+
+* PostgreSQL
+* MySQL
+* SQLite
+* Oracle
+* Microsoft SQL Server, usw.
+
+In diesem Beispiel verwenden wir **SQLite**, da es eine einzelne Datei verwendet und Python integrierte Unterstützung bietet. Sie können also dieses Beispiel kopieren und direkt ausführen.
+
+Später, für Ihre Produktionsanwendung, möchten Sie möglicherweise einen Datenbankserver wie **PostgreSQL** verwenden.
+
+/// tip | Tipp
+
+Es gibt einen offiziellen Projektgenerator mit **FastAPI** und **PostgreSQL**, einschließlich eines Frontends und weiterer Tools: https://github.com/fastapi/full-stack-fastapi-template
+
+///
+
+Dies ist ein sehr einfaches und kurzes Tutorial. Wenn Sie mehr über Datenbanken im Allgemeinen, über SQL oder fortgeschrittenere Funktionen erfahren möchten, besuchen Sie die SQLModel-Dokumentation.
+
+## `SQLModel` installieren { #install-sqlmodel }
+
+Stellen Sie zunächst sicher, dass Sie Ihre [virtuelle Umgebung](../virtual-environments.md){.internal-link target=_blank} erstellen, sie aktivieren und dann `sqlmodel` installieren:
+
+
+
+
-
+
@@ -87,13 +87,13 @@ FastAPI 🏛, ⏩ (↕-🎭), 🕸 🛠️ 🏗 🛠️ ⏮️ 🐍 3️⃣.8️
"_👤 🤭 🌕 😄 🔃 **FastAPI**. ⚫️ 🎊 ❗_"
-
+
---
"_🤙, ⚫️❔ 👆 ✔️ 🏗 👀 💎 💠 & 🇵🇱. 📚 🌌, ⚫️ ⚫️❔ 👤 💚 **🤗** - ⚫️ 🤙 😍 👀 👱 🏗 👈._"
-
+
---
@@ -101,7 +101,7 @@ FastAPI 🏛, ⏩ (↕-🎭), 🕸 🛠️ 🏗 🛠️ ⏮️ 🐍 3️⃣.8️
"_👥 ✔️ 🎛 🤭 **FastAPI** 👆 **🔗** [...] 👤 💭 👆 🔜 💖 ⚫️ [...]_"
-
+
---
@@ -125,7 +125,7 @@ FastAPI 🏛, ⏩ (↕-🎭), 🕸 🛠️ 🏗 🛠️ ⏮️ 🐍 3️⃣.8️
FastAPI 🧍 🔛 ⌚ 🐘:
-* 💃 🕸 🍕.
+* 💃 🕸 🍕.
* Pydantic 📊 🍕.
## 👷♂
@@ -140,7 +140,7 @@ $ pip install "fastapi[standard]"
uvicorn - 💽 👈 📐 & 🍦 👆 🈸.
+* uvicorn - 💽 👈 📐 & 🍦 👆 🈸.
* orjson - ✔ 🚥 👆 💚 ⚙️ `ORJSONResponse`.
* ujson - ✔ 🚥 👆 💚 ⚙️ `UJSONResponse`.
diff --git a/docs/em/docs/python-types.md b/docs/em/docs/python-types.md
index 202c90f94c..d2af23bb9a 100644
--- a/docs/em/docs/python-types.md
+++ b/docs/em/docs/python-types.md
@@ -23,7 +23,7 @@
➡️ ▶️ ⏮️ 🙅 🖼:
```Python
-{!../../../docs_src/python_types/tutorial001.py!}
+{!../../docs_src/python_types/tutorial001.py!}
```
🤙 👉 📋 🔢:
@@ -39,7 +39,7 @@ John Doe
* 🔢 👫 ⏮️ 🚀 🖕.
```Python hl_lines="2"
-{!../../../docs_src/python_types/tutorial001.py!}
+{!../../docs_src/python_types/tutorial001.py!}
```
### ✍ ⚫️
@@ -83,7 +83,7 @@ John Doe
👈 "🆎 🔑":
```Python hl_lines="1"
-{!../../../docs_src/python_types/tutorial002.py!}
+{!../../docs_src/python_types/tutorial002.py!}
```
👈 🚫 🎏 📣 🔢 💲 💖 🔜 ⏮️:
@@ -113,7 +113,7 @@ John Doe
✅ 👉 🔢, ⚫️ ⏪ ✔️ 🆎 🔑:
```Python hl_lines="1"
-{!../../../docs_src/python_types/tutorial003.py!}
+{!../../docs_src/python_types/tutorial003.py!}
```
↩️ 👨🎨 💭 🆎 🔢, 👆 🚫 🕴 🤚 🛠️, 👆 🤚 ❌ ✅:
@@ -123,7 +123,7 @@ John Doe
🔜 👆 💭 👈 👆 ✔️ 🔧 ⚫️, 🗜 `age` 🎻 ⏮️ `str(age)`:
```Python hl_lines="2"
-{!../../../docs_src/python_types/tutorial004.py!}
+{!../../docs_src/python_types/tutorial004.py!}
```
## 📣 🆎
@@ -144,7 +144,7 @@ John Doe
* `bytes`
```Python hl_lines="1"
-{!../../../docs_src/python_types/tutorial005.py!}
+{!../../docs_src/python_types/tutorial005.py!}
```
### 💊 🆎 ⏮️ 🆎 🔢
@@ -172,7 +172,7 @@ John Doe
⚪️➡️ `typing`, 🗄 `List` (⏮️ 🔠 `L`):
```Python hl_lines="1"
-{!> ../../../docs_src/python_types/tutorial006.py!}
+{!> ../../docs_src/python_types/tutorial006.py!}
```
📣 🔢, ⏮️ 🎏 ❤ (`:`) ❕.
@@ -182,7 +182,7 @@ John Doe
📇 🆎 👈 🔌 🔗 🆎, 👆 🚮 👫 ⬜ 🗜:
```Python hl_lines="4"
-{!> ../../../docs_src/python_types/tutorial006.py!}
+{!> ../../docs_src/python_types/tutorial006.py!}
```
////
@@ -196,7 +196,7 @@ John Doe
📇 🆎 👈 🔌 🔗 🆎, 👆 🚮 👫 ⬜ 🗜:
```Python hl_lines="1"
-{!> ../../../docs_src/python_types/tutorial006_py39.py!}
+{!> ../../docs_src/python_types/tutorial006_py39.py!}
```
////
@@ -234,7 +234,7 @@ John Doe
//// tab | 🐍 3️⃣.6️⃣ & 🔛
```Python hl_lines="1 4"
-{!> ../../../docs_src/python_types/tutorial007.py!}
+{!> ../../docs_src/python_types/tutorial007.py!}
```
////
@@ -242,7 +242,7 @@ John Doe
//// tab | 🐍 3️⃣.9️⃣ & 🔛
```Python hl_lines="1"
-{!> ../../../docs_src/python_types/tutorial007_py39.py!}
+{!> ../../docs_src/python_types/tutorial007_py39.py!}
```
////
@@ -263,7 +263,7 @@ John Doe
//// tab | 🐍 3️⃣.6️⃣ & 🔛
```Python hl_lines="1 4"
-{!> ../../../docs_src/python_types/tutorial008.py!}
+{!> ../../docs_src/python_types/tutorial008.py!}
```
////
@@ -271,7 +271,7 @@ John Doe
//// tab | 🐍 3️⃣.9️⃣ & 🔛
```Python hl_lines="1"
-{!> ../../../docs_src/python_types/tutorial008_py39.py!}
+{!> ../../docs_src/python_types/tutorial008_py39.py!}
```
////
@@ -293,7 +293,7 @@ John Doe
//// tab | 🐍 3️⃣.6️⃣ & 🔛
```Python hl_lines="1 4"
-{!> ../../../docs_src/python_types/tutorial008b.py!}
+{!> ../../docs_src/python_types/tutorial008b.py!}
```
////
@@ -301,7 +301,7 @@ John Doe
//// tab | 🐍 3️⃣.1️⃣0️⃣ & 🔛
```Python hl_lines="1"
-{!> ../../../docs_src/python_types/tutorial008b_py310.py!}
+{!> ../../docs_src/python_types/tutorial008b_py310.py!}
```
////
@@ -315,7 +315,7 @@ John Doe
🐍 3️⃣.6️⃣ & 🔛 (✅ 🐍 3️⃣.1️⃣0️⃣) 👆 💪 📣 ⚫️ 🏭 & ⚙️ `Optional` ⚪️➡️ `typing` 🕹.
```Python hl_lines="1 4"
-{!../../../docs_src/python_types/tutorial009.py!}
+{!../../docs_src/python_types/tutorial009.py!}
```
⚙️ `Optional[str]` ↩️ `str` 🔜 ➡️ 👨🎨 ℹ 👆 🔍 ❌ 🌐❔ 👆 💪 🤔 👈 💲 🕧 `str`, 🕐❔ ⚫️ 💪 🤙 `None` 💁♂️.
@@ -327,7 +327,7 @@ John Doe
//// tab | 🐍 3️⃣.6️⃣ & 🔛
```Python hl_lines="1 4"
-{!> ../../../docs_src/python_types/tutorial009.py!}
+{!> ../../docs_src/python_types/tutorial009.py!}
```
////
@@ -335,7 +335,7 @@ John Doe
//// tab | 🐍 3️⃣.6️⃣ & 🔛 - 🎛
```Python hl_lines="1 4"
-{!> ../../../docs_src/python_types/tutorial009b.py!}
+{!> ../../docs_src/python_types/tutorial009b.py!}
```
////
@@ -343,7 +343,7 @@ John Doe
//// tab | 🐍 3️⃣.1️⃣0️⃣ & 🔛
```Python hl_lines="1"
-{!> ../../../docs_src/python_types/tutorial009_py310.py!}
+{!> ../../docs_src/python_types/tutorial009_py310.py!}
```
////
@@ -364,7 +364,7 @@ John Doe
🖼, ➡️ ✊ 👉 🔢:
```Python hl_lines="1 4"
-{!../../../docs_src/python_types/tutorial009c.py!}
+{!../../docs_src/python_types/tutorial009c.py!}
```
🔢 `name` 🔬 `Optional[str]`, ✋️ ⚫️ **🚫 📦**, 👆 🚫🔜 🤙 🔢 🍵 🔢:
@@ -382,7 +382,7 @@ say_hi(name=None) # This works, None is valid 🎉
👍 📰, 🕐 👆 🔛 🐍 3️⃣.1️⃣0️⃣ 👆 🏆 🚫 ✔️ 😟 🔃 👈, 👆 🔜 💪 🎯 ⚙️ `|` 🔬 🇪🇺 🆎:
```Python hl_lines="1 4"
-{!../../../docs_src/python_types/tutorial009c_py310.py!}
+{!../../docs_src/python_types/tutorial009c_py310.py!}
```
& ⤴️ 👆 🏆 🚫 ✔️ 😟 🔃 📛 💖 `Optional` & `Union`. 👶
@@ -446,13 +446,13 @@ say_hi(name=None) # This works, None is valid 🎉
➡️ 💬 👆 ✔️ 🎓 `Person`, ⏮️ 📛:
```Python hl_lines="1-3"
-{!../../../docs_src/python_types/tutorial010.py!}
+{!../../docs_src/python_types/tutorial010.py!}
```
⤴️ 👆 💪 📣 🔢 🆎 `Person`:
```Python hl_lines="6"
-{!../../../docs_src/python_types/tutorial010.py!}
+{!../../docs_src/python_types/tutorial010.py!}
```
& ⤴️, 🔄, 👆 🤚 🌐 👨🎨 🐕🦺:
@@ -476,7 +476,7 @@ say_hi(name=None) # This works, None is valid 🎉
//// tab | 🐍 3️⃣.6️⃣ & 🔛
```Python
-{!> ../../../docs_src/python_types/tutorial011.py!}
+{!> ../../docs_src/python_types/tutorial011.py!}
```
////
@@ -484,7 +484,7 @@ say_hi(name=None) # This works, None is valid 🎉
//// tab | 🐍 3️⃣.9️⃣ & 🔛
```Python
-{!> ../../../docs_src/python_types/tutorial011_py39.py!}
+{!> ../../docs_src/python_types/tutorial011_py39.py!}
```
////
@@ -492,7 +492,7 @@ say_hi(name=None) # This works, None is valid 🎉
//// tab | 🐍 3️⃣.1️⃣0️⃣ & 🔛
```Python
-{!> ../../../docs_src/python_types/tutorial011_py310.py!}
+{!> ../../docs_src/python_types/tutorial011_py310.py!}
```
////
diff --git a/docs/em/docs/tutorial/background-tasks.md b/docs/em/docs/tutorial/background-tasks.md
index 1d17a0e4ea..4cbcbc710d 100644
--- a/docs/em/docs/tutorial/background-tasks.md
+++ b/docs/em/docs/tutorial/background-tasks.md
@@ -15,9 +15,7 @@
🥇, 🗄 `BackgroundTasks` & 🔬 🔢 👆 *➡ 🛠️ 🔢* ⏮️ 🆎 📄 `BackgroundTasks`:
-```Python hl_lines="1 13"
-{!../../../docs_src/background_tasks/tutorial001.py!}
-```
+{* ../../docs_src/background_tasks/tutorial001.py hl[1,13] *}
**FastAPI** 🔜 ✍ 🎚 🆎 `BackgroundTasks` 👆 & 🚶♀️ ⚫️ 👈 🔢.
@@ -33,17 +31,13 @@
& ✍ 🛠️ 🚫 ⚙️ `async` & `await`, 👥 🔬 🔢 ⏮️ 😐 `def`:
-```Python hl_lines="6-9"
-{!../../../docs_src/background_tasks/tutorial001.py!}
-```
+{* ../../docs_src/background_tasks/tutorial001.py hl[6:9] *}
## 🚮 🖥 📋
🔘 👆 *➡ 🛠️ 🔢*, 🚶♀️ 👆 📋 🔢 *🖥 📋* 🎚 ⏮️ 👩🔬 `.add_task()`:
-```Python hl_lines="14"
-{!../../../docs_src/background_tasks/tutorial001.py!}
-```
+{* ../../docs_src/background_tasks/tutorial001.py hl[14] *}
`.add_task()` 📨 ❌:
@@ -57,21 +51,7 @@
**FastAPI** 💭 ⚫️❔ 🔠 💼 & ❔ 🏤-⚙️ 🎏 🎚, 👈 🌐 🖥 📋 🔗 👯♂️ & 🏃 🖥 ⏮️:
-//// tab | 🐍 3️⃣.6️⃣ & 🔛
-
-```Python hl_lines="13 15 22 25"
-{!> ../../../docs_src/background_tasks/tutorial002.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.1️⃣0️⃣ & 🔛
-
-```Python hl_lines="11 13 20 23"
-{!> ../../../docs_src/background_tasks/tutorial002_py310.py!}
-```
-
-////
+{* ../../docs_src/background_tasks/tutorial002.py hl[13,15,22,25] *}
👉 🖼, 📧 🔜 ✍ `log.txt` 📁 *⏮️* 📨 📨.
@@ -81,7 +61,7 @@
## 📡 ℹ
-🎓 `BackgroundTasks` 👟 🔗 ⚪️➡️ `starlette.background`.
+🎓 `BackgroundTasks` 👟 🔗 ⚪️➡️ `starlette.background`.
⚫️ 🗄/🔌 🔗 🔘 FastAPI 👈 👆 💪 🗄 ⚫️ ⚪️➡️ `fastapi` & ❎ 😫 🗄 🎛 `BackgroundTask` (🍵 `s` 🔚) ⚪️➡️ `starlette.background`.
@@ -89,7 +69,7 @@
⚫️ 💪 ⚙️ `BackgroundTask` 😞 FastAPI, ✋️ 👆 ✔️ ✍ 🎚 👆 📟 & 📨 💃 `Response` 🔌 ⚫️.
-👆 💪 👀 🌖 ℹ 💃 🛂 🩺 🖥 📋.
+👆 💪 👀 🌖 ℹ 💃 🛂 🩺 🖥 📋.
## ⚠
@@ -97,8 +77,6 @@
👫 😑 🚚 🌖 🏗 📳, 📧/👨🏭 📤 👨💼, 💖 ✳ ⚖️ ✳, ✋️ 👫 ✔ 👆 🏃 🖥 📋 💗 🛠️, & ✴️, 💗 💽.
-👀 🖼, ✅ [🏗 🚂](../project-generation.md){.internal-link target=_blank}, 👫 🌐 🔌 🥒 ⏪ 📶.
-
✋️ 🚥 👆 💪 🔐 🔢 & 🎚 ⚪️➡️ 🎏 **FastAPI** 📱, ⚖️ 👆 💪 🎭 🤪 🖥 📋 (💖 📨 📧 📨), 👆 💪 🎯 ⚙️ `BackgroundTasks`.
## 🌃
diff --git a/docs/em/docs/tutorial/bigger-applications.md b/docs/em/docs/tutorial/bigger-applications.md
index 693a75d28d..78a321ae6a 100644
--- a/docs/em/docs/tutorial/bigger-applications.md
+++ b/docs/em/docs/tutorial/bigger-applications.md
@@ -52,7 +52,7 @@ from app.routers import items
* 📤 📁 `app/internal/` ⏮️ ➕1️⃣ 📁 `__init__.py`, ⚫️ ➕1️⃣ "🐍 📦": `app.internal`.
* & 📁 `app/internal/admin.py` ➕1️⃣ 🔁: `app.internal.admin`.
-get 🛠️
-/// info | "`@decorator` ℹ"
+/// info | `@decorator` ℹ
👈 `@something` ❕ 🐍 🤙 "👨🎨".
@@ -306,9 +296,7 @@ https://example.com/items/foo
* **🛠️**: `get`.
* **🔢**: 🔢 🔛 "👨🎨" (🔛 `@app.get("/")`).
-```Python hl_lines="7"
-{!../../../docs_src/first_steps/tutorial001.py!}
-```
+{* ../../docs_src/first_steps/tutorial001.py hl[7] *}
👉 🐍 🔢.
@@ -320,9 +308,7 @@ https://example.com/items/foo
👆 💪 🔬 ⚫️ 😐 🔢 ↩️ `async def`:
-```Python hl_lines="7"
-{!../../../docs_src/first_steps/tutorial003.py!}
-```
+{* ../../docs_src/first_steps/tutorial003.py hl[7] *}
/// note
@@ -332,9 +318,7 @@ https://example.com/items/foo
### 🔁 5️⃣: 📨 🎚
-```Python hl_lines="8"
-{!../../../docs_src/first_steps/tutorial001.py!}
-```
+{* ../../docs_src/first_steps/tutorial001.py hl[8] *}
👆 💪 📨 `dict`, `list`, ⭐ 💲 `str`, `int`, ♒️.
diff --git a/docs/em/docs/tutorial/handling-errors.md b/docs/em/docs/tutorial/handling-errors.md
index ed32ab53a2..6d72775976 100644
--- a/docs/em/docs/tutorial/handling-errors.md
+++ b/docs/em/docs/tutorial/handling-errors.md
@@ -25,9 +25,7 @@
### 🗄 `HTTPException`
-```Python hl_lines="1"
-{!../../../docs_src/handling_errors/tutorial001.py!}
-```
+{* ../../docs_src/handling_errors/tutorial001.py hl[1] *}
### 🤚 `HTTPException` 👆 📟
@@ -41,9 +39,7 @@
👉 🖼, 🕐❔ 👩💻 📨 🏬 🆔 👈 🚫 🔀, 🤚 ⚠ ⏮️ 👔 📟 `404`:
-```Python hl_lines="11"
-{!../../../docs_src/handling_errors/tutorial001.py!}
-```
+{* ../../docs_src/handling_errors/tutorial001.py hl[11] *}
### 📉 📨
@@ -81,13 +77,11 @@
✋️ 💼 👆 💪 ⚫️ 🏧 😐, 👆 💪 🚮 🛃 🎚:
-```Python hl_lines="14"
-{!../../../docs_src/handling_errors/tutorial002.py!}
-```
+{* ../../docs_src/handling_errors/tutorial002.py hl[14] *}
## ❎ 🛃 ⚠ 🐕🦺
-👆 💪 🚮 🛃 ⚠ 🐕🦺 ⏮️ 🎏 ⚠ 🚙 ⚪️➡️ 💃.
+👆 💪 🚮 🛃 ⚠ 🐕🦺 ⏮️ 🎏 ⚠ 🚙 ⚪️➡️ 💃.
➡️ 💬 👆 ✔️ 🛃 ⚠ `UnicornException` 👈 👆 (⚖️ 🗃 👆 ⚙️) 💪 `raise`.
@@ -95,9 +89,7 @@
👆 💪 🚮 🛃 ⚠ 🐕🦺 ⏮️ `@app.exception_handler()`:
-```Python hl_lines="5-7 13-18 24"
-{!../../../docs_src/handling_errors/tutorial003.py!}
-```
+{* ../../docs_src/handling_errors/tutorial003.py hl[5:7,13:18,24] *}
📥, 🚥 👆 📨 `/unicorns/yolo`, *➡ 🛠️* 🔜 `raise` `UnicornException`.
@@ -109,7 +101,7 @@
{"message": "Oops! yolo did something. There goes a rainbow..."}
```
-/// note | "📡 ℹ"
+/// note | 📡 ℹ
👆 💪 ⚙️ `from starlette.requests import Request` & `from starlette.responses import JSONResponse`.
@@ -135,9 +127,7 @@
⚠ 🐕🦺 🔜 📨 `Request` & ⚠.
-```Python hl_lines="2 14-16"
-{!../../../docs_src/handling_errors/tutorial004.py!}
-```
+{* ../../docs_src/handling_errors/tutorial004.py hl[2,14:16] *}
🔜, 🚥 👆 🚶 `/items/foo`, ↩️ 💆♂ 🔢 🎻 ❌ ⏮️:
@@ -188,11 +178,9 @@ path -> item_id
🖼, 👆 💪 💚 📨 ✅ ✍ 📨 ↩️ 🎻 👫 ❌:
-```Python hl_lines="3-4 9-11 22"
-{!../../../docs_src/handling_errors/tutorial004.py!}
-```
+{* ../../docs_src/handling_errors/tutorial004.py hl[3:4,9:11,22] *}
-/// note | "📡 ℹ"
+/// note | 📡 ℹ
👆 💪 ⚙️ `from starlette.responses import PlainTextResponse`.
@@ -206,9 +194,7 @@ path -> item_id
👆 💪 ⚙️ ⚫️ ⏪ 🛠️ 👆 📱 🕹 💪 & ℹ ⚫️, 📨 ⚫️ 👩💻, ♒️.
-```Python hl_lines="14"
-{!../../../docs_src/handling_errors/tutorial005.py!}
-```
+{* ../../docs_src/handling_errors/tutorial005.py hl[14] *}
🔜 🔄 📨 ❌ 🏬 💖:
@@ -266,8 +252,6 @@ from starlette.exceptions import HTTPException as StarletteHTTPException
🚥 👆 💚 ⚙️ ⚠ ⤴️ ⏮️ 🎏 🔢 ⚠ 🐕🦺 ⚪️➡️ **FastAPI**, 👆 💪 🗄 & 🏤-⚙️ 🔢 ⚠ 🐕🦺 ⚪️➡️ `fastapi.exception_handlers`:
-```Python hl_lines="2-5 15 21"
-{!../../../docs_src/handling_errors/tutorial006.py!}
-```
+{* ../../docs_src/handling_errors/tutorial006.py hl[2:5,15,21] *}
👉 🖼 👆 `print`😅 ❌ ⏮️ 📶 🎨 📧, ✋️ 👆 🤚 💭. 👆 💪 ⚙️ ⚠ & ⤴️ 🏤-⚙️ 🔢 ⚠ 🐕🦺.
diff --git a/docs/em/docs/tutorial/header-params.md b/docs/em/docs/tutorial/header-params.md
index 82583c7c33..fa5e3a22b4 100644
--- a/docs/em/docs/tutorial/header-params.md
+++ b/docs/em/docs/tutorial/header-params.md
@@ -6,21 +6,7 @@
🥇 🗄 `Header`:
-//// tab | 🐍 3️⃣.6️⃣ & 🔛
-
-```Python hl_lines="3"
-{!> ../../../docs_src/header_params/tutorial001.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.1️⃣0️⃣ & 🔛
-
-```Python hl_lines="1"
-{!> ../../../docs_src/header_params/tutorial001_py310.py!}
-```
-
-////
+{* ../../docs_src/header_params/tutorial001.py hl[3] *}
## 📣 `Header` 🔢
@@ -28,23 +14,9 @@
🥇 💲 🔢 💲, 👆 💪 🚶♀️ 🌐 ➕ 🔬 ⚖️ ✍ 🔢:
-//// tab | 🐍 3️⃣.6️⃣ & 🔛
+{* ../../docs_src/header_params/tutorial001.py hl[9] *}
-```Python hl_lines="9"
-{!> ../../../docs_src/header_params/tutorial001.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.1️⃣0️⃣ & 🔛
-
-```Python hl_lines="7"
-{!> ../../../docs_src/header_params/tutorial001_py310.py!}
-```
-
-////
-
-/// note | "📡 ℹ"
+/// note | 📡 ℹ
`Header` "👭" 🎓 `Path`, `Query` & `Cookie`. ⚫️ 😖 ⚪️➡️ 🎏 ⚠ `Param` 🎓.
@@ -74,21 +46,7 @@
🚥 🤔 👆 💪 ❎ 🏧 🛠️ 🎦 🔠, ⚒ 🔢 `convert_underscores` `Header` `False`:
-//// tab | 🐍 3️⃣.6️⃣ & 🔛
-
-```Python hl_lines="10"
-{!> ../../../docs_src/header_params/tutorial002.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.1️⃣0️⃣ & 🔛
-
-```Python hl_lines="8"
-{!> ../../../docs_src/header_params/tutorial002_py310.py!}
-```
-
-////
+{* ../../docs_src/header_params/tutorial002.py hl[10] *}
/// warning
@@ -106,29 +64,7 @@
🖼, 📣 🎚 `X-Token` 👈 💪 😑 🌅 🌘 🕐, 👆 💪 ✍:
-//// tab | 🐍 3️⃣.6️⃣ & 🔛
-
-```Python hl_lines="9"
-{!> ../../../docs_src/header_params/tutorial003.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.9️⃣ & 🔛
-
-```Python hl_lines="9"
-{!> ../../../docs_src/header_params/tutorial003_py39.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.1️⃣0️⃣ & 🔛
-
-```Python hl_lines="7"
-{!> ../../../docs_src/header_params/tutorial003_py310.py!}
-```
-
-////
+{* ../../docs_src/header_params/tutorial003.py hl[9] *}
🚥 👆 🔗 ⏮️ 👈 *➡ 🛠️* 📨 2️⃣ 🇺🇸🔍 🎚 💖:
diff --git a/docs/em/docs/tutorial/metadata.md b/docs/em/docs/tutorial/metadata.md
index 6caeed4cd0..eaf605de1e 100644
--- a/docs/em/docs/tutorial/metadata.md
+++ b/docs/em/docs/tutorial/metadata.md
@@ -17,9 +17,7 @@
👆 💪 ⚒ 👫 ⏩:
-```Python hl_lines="3-16 19-31"
-{!../../../docs_src/metadata/tutorial001.py!}
-```
+{* ../../docs_src/metadata/tutorial001.py hl[3:16,19:31] *}
/// tip
@@ -51,9 +49,7 @@
✍ 🗃 👆 🔖 & 🚶♀️ ⚫️ `openapi_tags` 🔢:
-```Python hl_lines="3-16 18"
-{!../../../docs_src/metadata/tutorial004.py!}
-```
+{* ../../docs_src/metadata/tutorial004.py hl[3:16,18] *}
👀 👈 👆 💪 ⚙️ ✍ 🔘 📛, 🖼 "💳" 🔜 🎦 🦁 (**💳**) & "🎀" 🔜 🎦 ❕ (_🎀_).
@@ -67,9 +63,7 @@
⚙️ `tags` 🔢 ⏮️ 👆 *➡ 🛠️* (& `APIRouter`Ⓜ) 🛠️ 👫 🎏 🔖:
-```Python hl_lines="21 26"
-{!../../../docs_src/metadata/tutorial004.py!}
-```
+{* ../../docs_src/metadata/tutorial004.py hl[21,26] *}
/// info
@@ -97,9 +91,7 @@
🖼, ⚒ ⚫️ 🍦 `/api/v1/openapi.json`:
-```Python hl_lines="3"
-{!../../../docs_src/metadata/tutorial002.py!}
-```
+{* ../../docs_src/metadata/tutorial002.py hl[3] *}
🚥 👆 💚 ❎ 🗄 🔗 🍕 👆 💪 ⚒ `openapi_url=None`, 👈 🔜 ❎ 🧾 👩💻 🔢 👈 ⚙️ ⚫️.
@@ -116,6 +108,4 @@
🖼, ⚒ 🦁 🎚 🍦 `/documentation` & ❎ 📄:
-```Python hl_lines="3"
-{!../../../docs_src/metadata/tutorial003.py!}
-```
+{* ../../docs_src/metadata/tutorial003.py hl[3] *}
diff --git a/docs/em/docs/tutorial/middleware.md b/docs/em/docs/tutorial/middleware.md
index b9bb12e005..c77b105544 100644
--- a/docs/em/docs/tutorial/middleware.md
+++ b/docs/em/docs/tutorial/middleware.md
@@ -11,7 +11,7 @@
* ⚫️ 💪 🕳 👈 **📨** ⚖️ 🏃 🙆 💪 📟.
* ⤴️ ⚫️ 📨 **📨**.
-/// note | "📡 ℹ"
+/// note | 📡 ℹ
🚥 👆 ✔️ 🔗 ⏮️ `yield`, 🚪 📟 🔜 🏃 *⏮️* 🛠️.
@@ -31,19 +31,17 @@
* ⤴️ ⚫️ 📨 `response` 🏗 🔗 *➡ 🛠️*.
* 👆 💪 ⤴️ 🔀 🌅 `response` ⏭ 🛬 ⚫️.
-```Python hl_lines="8-9 11 14"
-{!../../../docs_src/middleware/tutorial001.py!}
-```
+{* ../../docs_src/middleware/tutorial001.py hl[8:9,11,14] *}
/// tip
✔️ 🤯 👈 🛃 © 🎚 💪 🚮 ⚙️ '✖-' 🔡.
-✋️ 🚥 👆 ✔️ 🛃 🎚 👈 👆 💚 👩💻 🖥 💪 👀, 👆 💪 🚮 👫 👆 ⚜ 📳 ([⚜ (✖️-🇨🇳 ℹ 🤝)](cors.md){.internal-link target=_blank}) ⚙️ 🔢 `expose_headers` 📄 💃 ⚜ 🩺.
+✋️ 🚥 👆 ✔️ 🛃 🎚 👈 👆 💚 👩💻 🖥 💪 👀, 👆 💪 🚮 👫 👆 ⚜ 📳 ([⚜ (✖️-🇨🇳 ℹ 🤝)](cors.md){.internal-link target=_blank}) ⚙️ 🔢 `expose_headers` 📄 💃 ⚜ 🩺.
///
-/// note | "📡 ℹ"
+/// note | 📡 ℹ
👆 💪 ⚙️ `from starlette.requests import Request`.
@@ -59,9 +57,7 @@
🖼, 👆 💪 🚮 🛃 🎚 `X-Process-Time` ⚗ 🕰 🥈 👈 ⚫️ ✊ 🛠️ 📨 & 🏗 📨:
-```Python hl_lines="10 12-13"
-{!../../../docs_src/middleware/tutorial001.py!}
-```
+{* ../../docs_src/middleware/tutorial001.py hl[10,12:13] *}
## 🎏 🛠️
diff --git a/docs/em/docs/tutorial/path-operation-configuration.md b/docs/em/docs/tutorial/path-operation-configuration.md
index 1979bed2b2..c6030c0890 100644
--- a/docs/em/docs/tutorial/path-operation-configuration.md
+++ b/docs/em/docs/tutorial/path-operation-configuration.md
@@ -16,33 +16,11 @@
✋️ 🚥 👆 🚫 💭 ⚫️❔ 🔠 🔢 📟, 👆 💪 ⚙️ ⌨ 📉 `status`:
-//// tab | 🐍 3️⃣.6️⃣ & 🔛
-
-```Python hl_lines="3 17"
-{!> ../../../docs_src/path_operation_configuration/tutorial001.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.9️⃣ & 🔛
-
-```Python hl_lines="3 17"
-{!> ../../../docs_src/path_operation_configuration/tutorial001_py39.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.1️⃣0️⃣ & 🔛
-
-```Python hl_lines="1 15"
-{!> ../../../docs_src/path_operation_configuration/tutorial001_py310.py!}
-```
-
-////
+{* ../../docs_src/path_operation_configuration/tutorial001.py hl[3,17] *}
👈 👔 📟 🔜 ⚙️ 📨 & 🔜 🚮 🗄 🔗.
-/// note | "📡 ℹ"
+/// note | 📡 ℹ
👆 💪 ⚙️ `from starlette import status`.
@@ -54,29 +32,7 @@
👆 💪 🚮 🔖 👆 *➡ 🛠️*, 🚶♀️ 🔢 `tags` ⏮️ `list` `str` (🛎 1️⃣ `str`):
-//// tab | 🐍 3️⃣.6️⃣ & 🔛
-
-```Python hl_lines="17 22 27"
-{!> ../../../docs_src/path_operation_configuration/tutorial002.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.9️⃣ & 🔛
-
-```Python hl_lines="17 22 27"
-{!> ../../../docs_src/path_operation_configuration/tutorial002_py39.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.1️⃣0️⃣ & 🔛
-
-```Python hl_lines="15 20 25"
-{!> ../../../docs_src/path_operation_configuration/tutorial002_py310.py!}
-```
-
-////
+{* ../../docs_src/path_operation_configuration/tutorial002.py hl[17,22,27] *}
👫 🔜 🚮 🗄 🔗 & ⚙️ 🏧 🧾 🔢:
@@ -90,37 +46,13 @@
**FastAPI** 🐕🦺 👈 🎏 🌌 ⏮️ ✅ 🎻:
-```Python hl_lines="1 8-10 13 18"
-{!../../../docs_src/path_operation_configuration/tutorial002b.py!}
-```
+{* ../../docs_src/path_operation_configuration/tutorial002b.py hl[1,8:10,13,18] *}
## 📄 & 📛
👆 💪 🚮 `summary` & `description`:
-//// tab | 🐍 3️⃣.6️⃣ & 🔛
-
-```Python hl_lines="20-21"
-{!> ../../../docs_src/path_operation_configuration/tutorial003.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.9️⃣ & 🔛
-
-```Python hl_lines="20-21"
-{!> ../../../docs_src/path_operation_configuration/tutorial003_py39.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.1️⃣0️⃣ & 🔛
-
-```Python hl_lines="18-19"
-{!> ../../../docs_src/path_operation_configuration/tutorial003_py310.py!}
-```
-
-////
+{* ../../docs_src/path_operation_configuration/tutorial003.py hl[20:21] *}
## 📛 ⚪️➡️ #️⃣
@@ -128,29 +60,7 @@
👆 💪 ✍ ✍ #️⃣ , ⚫️ 🔜 🔬 & 🖥 ☑ (✊ 🔘 🏧 #️⃣ 📐).
-//// tab | 🐍 3️⃣.6️⃣ & 🔛
-
-```Python hl_lines="19-27"
-{!> ../../../docs_src/path_operation_configuration/tutorial004.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.9️⃣ & 🔛
-
-```Python hl_lines="19-27"
-{!> ../../../docs_src/path_operation_configuration/tutorial004_py39.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.1️⃣0️⃣ & 🔛
-
-```Python hl_lines="17-25"
-{!> ../../../docs_src/path_operation_configuration/tutorial004_py310.py!}
-```
-
-////
+{* ../../docs_src/path_operation_configuration/tutorial004.py hl[19:27] *}
⚫️ 🔜 ⚙️ 🎓 🩺:
@@ -160,29 +70,7 @@
👆 💪 ✔ 📨 📛 ⏮️ 🔢 `response_description`:
-//// tab | 🐍 3️⃣.6️⃣ & 🔛
-
-```Python hl_lines="21"
-{!> ../../../docs_src/path_operation_configuration/tutorial005.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.9️⃣ & 🔛
-
-```Python hl_lines="21"
-{!> ../../../docs_src/path_operation_configuration/tutorial005_py39.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.1️⃣0️⃣ & 🔛
-
-```Python hl_lines="19"
-{!> ../../../docs_src/path_operation_configuration/tutorial005_py310.py!}
-```
-
-////
+{* ../../docs_src/path_operation_configuration/tutorial005.py hl[21] *}
/// info
@@ -204,9 +92,7 @@
🚥 👆 💪 ™ *➡ 🛠️* 😢, ✋️ 🍵 ❎ ⚫️, 🚶♀️ 🔢 `deprecated`:
-```Python hl_lines="16"
-{!../../../docs_src/path_operation_configuration/tutorial006.py!}
-```
+{* ../../docs_src/path_operation_configuration/tutorial006.py hl[16] *}
⚫️ 🔜 🎯 ™ 😢 🎓 🩺:
diff --git a/docs/em/docs/tutorial/path-params-numeric-validations.md b/docs/em/docs/tutorial/path-params-numeric-validations.md
index a7952984cd..b45e0557b6 100644
--- a/docs/em/docs/tutorial/path-params-numeric-validations.md
+++ b/docs/em/docs/tutorial/path-params-numeric-validations.md
@@ -6,21 +6,7 @@
🥇, 🗄 `Path` ⚪️➡️ `fastapi`:
-//// tab | 🐍 3️⃣.6️⃣ & 🔛
-
-```Python hl_lines="3"
-{!> ../../../docs_src/path_params_numeric_validations/tutorial001.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.1️⃣0️⃣ & 🔛
-
-```Python hl_lines="1"
-{!> ../../../docs_src/path_params_numeric_validations/tutorial001_py310.py!}
-```
-
-////
+{* ../../docs_src/path_params_numeric_validations/tutorial001.py hl[3] *}
## 📣 🗃
@@ -28,21 +14,7 @@
🖼, 📣 `title` 🗃 💲 ➡ 🔢 `item_id` 👆 💪 🆎:
-//// tab | 🐍 3️⃣.6️⃣ & 🔛
-
-```Python hl_lines="10"
-{!> ../../../docs_src/path_params_numeric_validations/tutorial001.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.1️⃣0️⃣ & 🔛
-
-```Python hl_lines="8"
-{!> ../../../docs_src/path_params_numeric_validations/tutorial001_py310.py!}
-```
-
-////
+{* ../../docs_src/path_params_numeric_validations/tutorial001.py hl[10] *}
/// note
@@ -70,9 +42,7 @@
, 👆 💪 📣 👆 🔢:
-```Python hl_lines="7"
-{!../../../docs_src/path_params_numeric_validations/tutorial002.py!}
-```
+{* ../../docs_src/path_params_numeric_validations/tutorial002.py hl[7] *}
## ✔ 🔢 👆 💪, 🎱
@@ -82,9 +52,7 @@
🐍 🏆 🚫 🕳 ⏮️ 👈 `*`, ✋️ ⚫️ 🔜 💭 👈 🌐 📄 🔢 🔜 🤙 🇨🇻 ❌ (🔑-💲 👫), 💭 kwargs. 🚥 👫 🚫 ✔️ 🔢 💲.
-```Python hl_lines="7"
-{!../../../docs_src/path_params_numeric_validations/tutorial003.py!}
-```
+{* ../../docs_src/path_params_numeric_validations/tutorial003.py hl[7] *}
## 🔢 🔬: 👑 🌘 ⚖️ 🌓
@@ -92,9 +60,7 @@
📥, ⏮️ `ge=1`, `item_id` 🔜 💪 🔢 🔢 "`g`🅾 🌘 ⚖️ `e`🅾" `1`.
-```Python hl_lines="8"
-{!../../../docs_src/path_params_numeric_validations/tutorial004.py!}
-```
+{* ../../docs_src/path_params_numeric_validations/tutorial004.py hl[8] *}
## 🔢 🔬: 🌘 🌘 & 🌘 🌘 ⚖️ 🌓
@@ -103,9 +69,7 @@
* `gt`: `g`🅾 `t`👲
* `le`: `l`👭 🌘 ⚖️ `e`🅾
-```Python hl_lines="9"
-{!../../../docs_src/path_params_numeric_validations/tutorial005.py!}
-```
+{* ../../docs_src/path_params_numeric_validations/tutorial005.py hl[9] *}
## 🔢 🔬: 🎈, 🌘 🌘 & 🌘 🌘
@@ -117,9 +81,7 @@
& 🎏 lt.
-```Python hl_lines="11"
-{!../../../docs_src/path_params_numeric_validations/tutorial006.py!}
-```
+{* ../../docs_src/path_params_numeric_validations/tutorial006.py hl[11] *}
## 🌃
@@ -140,7 +102,7 @@
///
-/// note | "📡 ℹ"
+/// note | 📡 ℹ
🕐❔ 👆 🗄 `Query`, `Path` & 🎏 ⚪️➡️ `fastapi`, 👫 🤙 🔢.
diff --git a/docs/em/docs/tutorial/path-params.md b/docs/em/docs/tutorial/path-params.md
index e0d51a1df9..a914dc9050 100644
--- a/docs/em/docs/tutorial/path-params.md
+++ b/docs/em/docs/tutorial/path-params.md
@@ -2,9 +2,7 @@
👆 💪 📣 ➡ "🔢" ⚖️ "🔢" ⏮️ 🎏 ❕ ⚙️ 🐍 📁 🎻:
-```Python hl_lines="6-7"
-{!../../../docs_src/path_params/tutorial001.py!}
-```
+{* ../../docs_src/path_params/tutorial001.py hl[6:7] *}
💲 ➡ 🔢 `item_id` 🔜 🚶♀️ 👆 🔢 ❌ `item_id`.
@@ -18,9 +16,7 @@
👆 💪 📣 🆎 ➡ 🔢 🔢, ⚙️ 🐩 🐍 🆎 ✍:
-```Python hl_lines="7"
-{!../../../docs_src/path_params/tutorial002.py!}
-```
+{* ../../docs_src/path_params/tutorial002.py hl[7] *}
👉 💼, `item_id` 📣 `int`.
@@ -121,17 +117,13 @@
↩️ *➡ 🛠️* 🔬 ✔, 👆 💪 ⚒ 💭 👈 ➡ `/users/me` 📣 ⏭ 1️⃣ `/users/{user_id}`:
-```Python hl_lines="6 11"
-{!../../../docs_src/path_params/tutorial003.py!}
-```
+{* ../../docs_src/path_params/tutorial003.py hl[6,11] *}
⏪, ➡ `/users/{user_id}` 🔜 🏏 `/users/me`, "💭" 👈 ⚫️ 📨 🔢 `user_id` ⏮️ 💲 `"me"`.
➡, 👆 🚫🔜 ↔ ➡ 🛠️:
-```Python hl_lines="6 11"
-{!../../../docs_src/path_params/tutorial003b.py!}
-```
+{* ../../docs_src/path_params/tutorial003b.py hl[6,11] *}
🥇 🕐 🔜 🕧 ⚙️ ↩️ ➡ 🏏 🥇.
@@ -147,9 +139,7 @@
⤴️ ✍ 🎓 🔢 ⏮️ 🔧 💲, ❔ 🔜 💪 ☑ 💲:
-```Python hl_lines="1 6-9"
-{!../../../docs_src/path_params/tutorial005.py!}
-```
+{* ../../docs_src/path_params/tutorial005.py hl[1,6:9] *}
/// info
@@ -167,9 +157,7 @@
⤴️ ✍ *➡ 🔢* ⏮️ 🆎 ✍ ⚙️ 🔢 🎓 👆 ✍ (`ModelName`):
-```Python hl_lines="16"
-{!../../../docs_src/path_params/tutorial005.py!}
-```
+{* ../../docs_src/path_params/tutorial005.py hl[16] *}
### ✅ 🩺
@@ -185,17 +173,13 @@
👆 💪 🔬 ⚫️ ⏮️ *🔢 👨🎓* 👆 ✍ 🔢 `ModelName`:
-```Python hl_lines="17"
-{!../../../docs_src/path_params/tutorial005.py!}
-```
+{* ../../docs_src/path_params/tutorial005.py hl[17] *}
#### 🤚 *🔢 💲*
👆 💪 🤚 ☑ 💲 ( `str` 👉 💼) ⚙️ `model_name.value`, ⚖️ 🏢, `your_enum_member.value`:
-```Python hl_lines="20"
-{!../../../docs_src/path_params/tutorial005.py!}
-```
+{* ../../docs_src/path_params/tutorial005.py hl[20] *}
/// tip
@@ -209,9 +193,7 @@
👫 🔜 🗜 👫 🔗 💲 (🎻 👉 💼) ⏭ 🛬 👫 👩💻:
-```Python hl_lines="18 21 23"
-{!../../../docs_src/path_params/tutorial005.py!}
-```
+{* ../../docs_src/path_params/tutorial005.py hl[18,21,23] *}
👆 👩💻 👆 🔜 🤚 🎻 📨 💖:
@@ -250,9 +232,7 @@
, 👆 💪 ⚙️ ⚫️ ⏮️:
-```Python hl_lines="6"
-{!../../../docs_src/path_params/tutorial004.py!}
-```
+{* ../../docs_src/path_params/tutorial004.py hl[6] *}
/// tip
diff --git a/docs/em/docs/tutorial/query-params-str-validations.md b/docs/em/docs/tutorial/query-params-str-validations.md
index 23873155ed..fd077bf8f7 100644
--- a/docs/em/docs/tutorial/query-params-str-validations.md
+++ b/docs/em/docs/tutorial/query-params-str-validations.md
@@ -4,21 +4,7 @@
➡️ ✊ 👉 🈸 🖼:
-//// tab | 🐍 3️⃣.6️⃣ & 🔛
-
-```Python hl_lines="9"
-{!> ../../../docs_src/query_params_str_validations/tutorial001.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.1️⃣0️⃣ & 🔛
-
-```Python hl_lines="7"
-{!> ../../../docs_src/query_params_str_validations/tutorial001_py310.py!}
-```
-
-////
+{* ../../docs_src/query_params_str_validations/tutorial001.py hl[9] *}
🔢 🔢 `q` 🆎 `Union[str, None]` (⚖️ `str | None` 🐍 3️⃣.1️⃣0️⃣), 👈 ⛓ 👈 ⚫️ 🆎 `str` ✋️ 💪 `None`, & 👐, 🔢 💲 `None`, FastAPI 🔜 💭 ⚫️ 🚫 ✔.
@@ -38,41 +24,13 @@ FastAPI 🔜 💭 👈 💲 `q` 🚫 ✔ ↩️ 🔢 💲 `= None`.
🏆 👈, 🥇 🗄 `Query` ⚪️➡️ `fastapi`:
-//// tab | 🐍 3️⃣.6️⃣ & 🔛
-
-```Python hl_lines="3"
-{!> ../../../docs_src/query_params_str_validations/tutorial002.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.1️⃣0️⃣ & 🔛
-
-```Python hl_lines="1"
-{!> ../../../docs_src/query_params_str_validations/tutorial002_py310.py!}
-```
-
-////
+{* ../../docs_src/query_params_str_validations/tutorial002.py hl[3] *}
## ⚙️ `Query` 🔢 💲
& 🔜 ⚙️ ⚫️ 🔢 💲 👆 🔢, ⚒ 🔢 `max_length` 5️⃣0️⃣:
-//// tab | 🐍 3️⃣.6️⃣ & 🔛
-
-```Python hl_lines="9"
-{!> ../../../docs_src/query_params_str_validations/tutorial002.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.1️⃣0️⃣ & 🔛
-
-```Python hl_lines="7"
-{!> ../../../docs_src/query_params_str_validations/tutorial002_py310.py!}
-```
-
-////
+{* ../../docs_src/query_params_str_validations/tutorial002.py hl[9] *}
👥 ✔️ ❎ 🔢 💲 `None` 🔢 ⏮️ `Query()`, 👥 💪 🔜 ⚒ 🔢 💲 ⏮️ 🔢 `Query(default=None)`, ⚫️ 🍦 🎏 🎯 ⚖ 👈 🔢 💲.
@@ -134,41 +92,13 @@ q: Union[str, None] = Query(default=None, max_length=50)
👆 💪 🚮 🔢 `min_length`:
-//// tab | 🐍 3️⃣.6️⃣ & 🔛
-
-```Python hl_lines="10"
-{!> ../../../docs_src/query_params_str_validations/tutorial003.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.1️⃣0️⃣ & 🔛
-
-```Python hl_lines="7"
-{!> ../../../docs_src/query_params_str_validations/tutorial003_py310.py!}
-```
-
-////
+{* ../../docs_src/query_params_str_validations/tutorial003.py hl[10] *}
## 🚮 🥔 🧬
👆 💪 🔬 🥔 🧬 👈 🔢 🔜 🏏:
-//// tab | 🐍 3️⃣.6️⃣ & 🔛
-
-```Python hl_lines="11"
-{!> ../../../docs_src/query_params_str_validations/tutorial004.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.1️⃣0️⃣ & 🔛
-
-```Python hl_lines="9"
-{!> ../../../docs_src/query_params_str_validations/tutorial004_py310.py!}
-```
-
-////
+{* ../../docs_src/query_params_str_validations/tutorial004.py hl[11] *}
👉 🎯 🥔 🧬 ✅ 👈 📨 🔢 💲:
@@ -186,9 +116,7 @@ q: Union[str, None] = Query(default=None, max_length=50)
➡️ 💬 👈 👆 💚 📣 `q` 🔢 🔢 ✔️ `min_length` `3`, & ✔️ 🔢 💲 `"fixedquery"`:
-```Python hl_lines="7"
-{!../../../docs_src/query_params_str_validations/tutorial005.py!}
-```
+{* ../../docs_src/query_params_str_validations/tutorial005.py hl[7] *}
/// note
@@ -218,27 +146,7 @@ q: Union[str, None] = Query(default=None, min_length=3)
, 🕐❔ 👆 💪 📣 💲 ✔ ⏪ ⚙️ `Query`, 👆 💪 🎯 🚫 📣 🔢 💲:
-```Python hl_lines="7"
-{!../../../docs_src/query_params_str_validations/tutorial006.py!}
-```
-
-### ✔ ⏮️ ❕ (`...`)
-
-📤 🎛 🌌 🎯 📣 👈 💲 ✔. 👆 💪 ⚒ `default` 🔢 🔑 💲 `...`:
-
-```Python hl_lines="7"
-{!../../../docs_src/query_params_str_validations/tutorial006b.py!}
-```
-
-/// info
-
-🚥 👆 🚫 👀 👈 `...` ⏭: ⚫️ 🎁 👁 💲, ⚫️ 🍕 🐍 & 🤙 "❕".
-
-⚫️ ⚙️ Pydantic & FastAPI 🎯 📣 👈 💲 ✔.
-
-///
-
-👉 🔜 ➡️ **FastAPI** 💭 👈 👉 🔢 ✔.
+{* ../../docs_src/query_params_str_validations/tutorial006.py hl[7] *}
### ✔ ⏮️ `None`
@@ -246,21 +154,7 @@ q: Union[str, None] = Query(default=None, min_length=3)
👈, 👆 💪 📣 👈 `None` ☑ 🆎 ✋️ ⚙️ `default=...`:
-//// tab | 🐍 3️⃣.6️⃣ & 🔛
-
-```Python hl_lines="9"
-{!> ../../../docs_src/query_params_str_validations/tutorial006c.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.1️⃣0️⃣ & 🔛
-
-```Python hl_lines="7"
-{!> ../../../docs_src/query_params_str_validations/tutorial006c_py310.py!}
-```
-
-////
+{* ../../docs_src/query_params_str_validations/tutorial006c.py hl[9] *}
/// tip
@@ -268,49 +162,13 @@ Pydantic, ❔ ⚫️❔ 🏋️ 🌐 💽 🔬 & 🛠️ FastAPI, ✔️
///
-### ⚙️ Pydantic `Required` ↩️ ❕ (`...`)
-
-🚥 👆 💭 😬 ⚙️ `...`, 👆 💪 🗄 & ⚙️ `Required` ⚪️➡️ Pydantic:
-
-```Python hl_lines="2 8"
-{!../../../docs_src/query_params_str_validations/tutorial006d.py!}
-```
-
-/// tip
-
-💭 👈 🌅 💼, 🕐❔ 🕳 🚚, 👆 💪 🎯 🚫 `default` 🔢, 👆 🛎 🚫 ✔️ ⚙️ `...` 🚫 `Required`.
-
-///
-
## 🔢 🔢 📇 / 💗 💲
🕐❔ 👆 🔬 🔢 🔢 🎯 ⏮️ `Query` 👆 💪 📣 ⚫️ 📨 📇 💲, ⚖️ 🙆♀ 🎏 🌌, 📨 💗 💲.
🖼, 📣 🔢 🔢 `q` 👈 💪 😑 💗 🕰 📛, 👆 💪 ✍:
-//// tab | 🐍 3️⃣.6️⃣ & 🔛
-
-```Python hl_lines="9"
-{!> ../../../docs_src/query_params_str_validations/tutorial011.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.9️⃣ & 🔛
-
-```Python hl_lines="9"
-{!> ../../../docs_src/query_params_str_validations/tutorial011_py39.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.1️⃣0️⃣ & 🔛
-
-```Python hl_lines="7"
-{!> ../../../docs_src/query_params_str_validations/tutorial011_py310.py!}
-```
-
-////
+{* ../../docs_src/query_params_str_validations/tutorial011.py hl[9] *}
⤴️, ⏮️ 📛 💖:
@@ -345,21 +203,7 @@ http://localhost:8000/items/?q=foo&q=bar
& 👆 💪 🔬 🔢 `list` 💲 🚥 👌 🚚:
-//// tab | 🐍 3️⃣.6️⃣ & 🔛
-
-```Python hl_lines="9"
-{!> ../../../docs_src/query_params_str_validations/tutorial012.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.9️⃣ & 🔛
-
-```Python hl_lines="7"
-{!> ../../../docs_src/query_params_str_validations/tutorial012_py39.py!}
-```
-
-////
+{* ../../docs_src/query_params_str_validations/tutorial012.py hl[9] *}
🚥 👆 🚶:
@@ -382,9 +226,7 @@ http://localhost:8000/items/
👆 💪 ⚙️ `list` 🔗 ↩️ `List[str]` (⚖️ `list[str]` 🐍 3️⃣.9️⃣ ➕):
-```Python hl_lines="7"
-{!../../../docs_src/query_params_str_validations/tutorial013.py!}
-```
+{* ../../docs_src/query_params_str_validations/tutorial013.py hl[7] *}
/// note
@@ -410,39 +252,11 @@ http://localhost:8000/items/
👆 💪 🚮 `title`:
-//// tab | 🐍 3️⃣.6️⃣ & 🔛
-
-```Python hl_lines="10"
-{!> ../../../docs_src/query_params_str_validations/tutorial007.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.1️⃣0️⃣ & 🔛
-
-```Python hl_lines="8"
-{!> ../../../docs_src/query_params_str_validations/tutorial007_py310.py!}
-```
-
-////
+{* ../../docs_src/query_params_str_validations/tutorial007.py hl[10] *}
& `description`:
-//// tab | 🐍 3️⃣.6️⃣ & 🔛
-
-```Python hl_lines="13"
-{!> ../../../docs_src/query_params_str_validations/tutorial008.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.1️⃣0️⃣ & 🔛
-
-```Python hl_lines="11"
-{!> ../../../docs_src/query_params_str_validations/tutorial008_py310.py!}
-```
-
-////
+{* ../../docs_src/query_params_str_validations/tutorial008.py hl[13] *}
## 📛 🔢
@@ -462,21 +276,7 @@ http://127.0.0.1:8000/items/?item-query=foobaritems
⤴️ 👆 💪 📣 `alias`, & 👈 📛 ⚫️❔ 🔜 ⚙️ 🔎 🔢 💲:
-//// tab | 🐍 3️⃣.6️⃣ & 🔛
-
-```Python hl_lines="9"
-{!> ../../../docs_src/query_params_str_validations/tutorial009.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.1️⃣0️⃣ & 🔛
-
-```Python hl_lines="7"
-{!> ../../../docs_src/query_params_str_validations/tutorial009_py310.py!}
-```
-
-////
+{* ../../docs_src/query_params_str_validations/tutorial009.py hl[9] *}
## 😛 🔢
@@ -486,21 +286,7 @@ http://127.0.0.1:8000/items/?item-query=foobaritems
⤴️ 🚶♀️ 🔢 `deprecated=True` `Query`:
-//// tab | 🐍 3️⃣.6️⃣ & 🔛
-
-```Python hl_lines="18"
-{!> ../../../docs_src/query_params_str_validations/tutorial010.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.1️⃣0️⃣ & 🔛
-
-```Python hl_lines="16"
-{!> ../../../docs_src/query_params_str_validations/tutorial010_py310.py!}
-```
-
-////
+{* ../../docs_src/query_params_str_validations/tutorial010.py hl[18] *}
🩺 🔜 🎦 ⚫️ 💖 👉:
@@ -510,21 +296,7 @@ http://127.0.0.1:8000/items/?item-query=foobaritems
🚫 🔢 🔢 ⚪️➡️ 🏗 🗄 🔗 (& ➡️, ⚪️➡️ 🏧 🧾 ⚙️), ⚒ 🔢 `include_in_schema` `Query` `False`:
-//// tab | 🐍 3️⃣.6️⃣ & 🔛
-
-```Python hl_lines="10"
-{!> ../../../docs_src/query_params_str_validations/tutorial014.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.1️⃣0️⃣ & 🔛
-
-```Python hl_lines="8"
-{!> ../../../docs_src/query_params_str_validations/tutorial014_py310.py!}
-```
-
-////
+{* ../../docs_src/query_params_str_validations/tutorial014.py hl[10] *}
## 🌃
diff --git a/docs/em/docs/tutorial/query-params.md b/docs/em/docs/tutorial/query-params.md
index 9bdab9e3c1..5c8d868a9a 100644
--- a/docs/em/docs/tutorial/query-params.md
+++ b/docs/em/docs/tutorial/query-params.md
@@ -2,9 +2,7 @@
🕐❔ 👆 📣 🎏 🔢 🔢 👈 🚫 🍕 ➡ 🔢, 👫 🔁 🔬 "🔢" 🔢.
-```Python hl_lines="9"
-{!../../../docs_src/query_params/tutorial001.py!}
-```
+{* ../../docs_src/query_params/tutorial001.py hl[9] *}
🔢 ⚒ 🔑-💲 👫 👈 🚶 ⏮️ `?` 📛, 🎏 `&` 🦹.
@@ -63,21 +61,7 @@ http://127.0.0.1:8000/items/?skip=20
🎏 🌌, 👆 💪 📣 📦 🔢 🔢, ⚒ 👫 🔢 `None`:
-//// tab | 🐍 3️⃣.6️⃣ & 🔛
-
-```Python hl_lines="9"
-{!> ../../../docs_src/query_params/tutorial002.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.1️⃣0️⃣ & 🔛
-
-```Python hl_lines="7"
-{!> ../../../docs_src/query_params/tutorial002_py310.py!}
-```
-
-////
+{* ../../docs_src/query_params/tutorial002.py hl[9] *}
👉 💼, 🔢 🔢 `q` 🔜 📦, & 🔜 `None` 🔢.
@@ -91,21 +75,7 @@ http://127.0.0.1:8000/items/?skip=20
👆 💪 📣 `bool` 🆎, & 👫 🔜 🗜:
-//// tab | 🐍 3️⃣.6️⃣ & 🔛
-
-```Python hl_lines="9"
-{!> ../../../docs_src/query_params/tutorial003.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.1️⃣0️⃣ & 🔛
-
-```Python hl_lines="7"
-{!> ../../../docs_src/query_params/tutorial003_py310.py!}
-```
-
-////
+{* ../../docs_src/query_params/tutorial003.py hl[9] *}
👉 💼, 🚥 👆 🚶:
@@ -148,21 +118,7 @@ http://127.0.0.1:8000/items/foo?short=yes
👫 🔜 🔬 📛:
-//// tab | 🐍 3️⃣.6️⃣ & 🔛
-
-```Python hl_lines="8 10"
-{!> ../../../docs_src/query_params/tutorial004.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.1️⃣0️⃣ & 🔛
-
-```Python hl_lines="6 8"
-{!> ../../../docs_src/query_params/tutorial004_py310.py!}
-```
-
-////
+{* ../../docs_src/query_params/tutorial004.py hl[8,10] *}
## ✔ 🔢 🔢
@@ -172,9 +128,7 @@ http://127.0.0.1:8000/items/foo?short=yes
✋️ 🕐❔ 👆 💚 ⚒ 🔢 🔢 ✔, 👆 💪 🚫 📣 🙆 🔢 💲:
-```Python hl_lines="6-7"
-{!../../../docs_src/query_params/tutorial005.py!}
-```
+{* ../../docs_src/query_params/tutorial005.py hl[6:7] *}
📥 🔢 🔢 `needy` ✔ 🔢 🔢 🆎 `str`.
@@ -218,21 +172,7 @@ http://127.0.0.1:8000/items/foo-item?needy=sooooneedy
& ↗️, 👆 💪 🔬 🔢 ✔, ✔️ 🔢 💲, & 🍕 📦:
-//// tab | 🐍 3️⃣.6️⃣ & 🔛
-
-```Python hl_lines="10"
-{!> ../../../docs_src/query_params/tutorial006.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.1️⃣0️⃣ & 🔛
-
-```Python hl_lines="8"
-{!> ../../../docs_src/query_params/tutorial006_py310.py!}
-```
-
-////
+{* ../../docs_src/query_params/tutorial006.py hl[10] *}
👉 💼, 📤 3️⃣ 🔢 🔢:
diff --git a/docs/em/docs/tutorial/request-files.md b/docs/em/docs/tutorial/request-files.md
index 010aa76bf3..c3bdeafd48 100644
--- a/docs/em/docs/tutorial/request-files.md
+++ b/docs/em/docs/tutorial/request-files.md
@@ -16,17 +16,13 @@
🗄 `File` & `UploadFile` ⚪️➡️ `fastapi`:
-```Python hl_lines="1"
-{!../../../docs_src/request_files/tutorial001.py!}
-```
+{* ../../docs_src/request_files/tutorial001.py hl[1] *}
## 🔬 `File` 🔢
✍ 📁 🔢 🎏 🌌 👆 🔜 `Body` ⚖️ `Form`:
-```Python hl_lines="7"
-{!../../../docs_src/request_files/tutorial001.py!}
-```
+{* ../../docs_src/request_files/tutorial001.py hl[7] *}
/// info
@@ -54,9 +50,7 @@
🔬 📁 🔢 ⏮️ 🆎 `UploadFile`:
-```Python hl_lines="12"
-{!../../../docs_src/request_files/tutorial001.py!}
-```
+{* ../../docs_src/request_files/tutorial001.py hl[12] *}
⚙️ `UploadFile` ✔️ 📚 📈 🤭 `bytes`:
@@ -99,13 +93,13 @@ contents = await myfile.read()
contents = myfile.file.read()
```
-/// note | "`async` 📡 ℹ"
+/// note | `async` 📡 ℹ
🕐❔ 👆 ⚙️ `async` 👩🔬, **FastAPI** 🏃 📁 👩🔬 🧵 & ⌛ 👫.
///
-/// note | "💃 📡 ℹ"
+/// note | 💃 📡 ℹ
**FastAPI**'Ⓜ `UploadFile` 😖 🔗 ⚪️➡️ **💃**'Ⓜ `UploadFile`, ✋️ 🚮 💪 🍕 ⚒ ⚫️ 🔗 ⏮️ **Pydantic** & 🎏 🍕 FastAPI.
@@ -117,7 +111,7 @@ contents = myfile.file.read()
**FastAPI** 🔜 ⚒ 💭 ✍ 👈 📊 ⚪️➡️ ▶️️ 🥉 ↩️ 🎻.
-/// note | "📡 ℹ"
+/// note | 📡 ℹ
📊 ⚪️➡️ 📨 🛎 🗜 ⚙️ "📻 🆎" `application/x-www-form-urlencoded` 🕐❔ ⚫️ 🚫 🔌 📁.
@@ -139,29 +133,13 @@ contents = myfile.file.read()
👆 💪 ⚒ 📁 📦 ⚙️ 🐩 🆎 ✍ & ⚒ 🔢 💲 `None`:
-//// tab | 🐍 3️⃣.6️⃣ & 🔛
-
-```Python hl_lines="9 17"
-{!> ../../../docs_src/request_files/tutorial001_02.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.1️⃣0️⃣ & 🔛
-
-```Python hl_lines="7 14"
-{!> ../../../docs_src/request_files/tutorial001_02_py310.py!}
-```
-
-////
+{* ../../docs_src/request_files/tutorial001_02.py hl[9,17] *}
## `UploadFile` ⏮️ 🌖 🗃
👆 💪 ⚙️ `File()` ⏮️ `UploadFile`, 🖼, ⚒ 🌖 🗃:
-```Python hl_lines="13"
-{!../../../docs_src/request_files/tutorial001_03.py!}
-```
+{* ../../docs_src/request_files/tutorial001_03.py hl[13] *}
## 💗 📁 📂
@@ -171,25 +149,11 @@ contents = myfile.file.read()
⚙️ 👈, 📣 📇 `bytes` ⚖️ `UploadFile`:
-//// tab | 🐍 3️⃣.6️⃣ & 🔛
-
-```Python hl_lines="10 15"
-{!> ../../../docs_src/request_files/tutorial002.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.9️⃣ & 🔛
-
-```Python hl_lines="8 13"
-{!> ../../../docs_src/request_files/tutorial002_py39.py!}
-```
-
-////
+{* ../../docs_src/request_files/tutorial002.py hl[10,15] *}
👆 🔜 📨, 📣, `list` `bytes` ⚖️ `UploadFile`Ⓜ.
-/// note | "📡 ℹ"
+/// note | 📡 ℹ
👆 💪 ⚙️ `from starlette.responses import HTMLResponse`.
@@ -201,21 +165,7 @@ contents = myfile.file.read()
& 🎏 🌌 ⏭, 👆 💪 ⚙️ `File()` ⚒ 🌖 🔢, `UploadFile`:
-//// tab | 🐍 3️⃣.6️⃣ & 🔛
-
-```Python hl_lines="18"
-{!> ../../../docs_src/request_files/tutorial003.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.9️⃣ & 🔛
-
-```Python hl_lines="16"
-{!> ../../../docs_src/request_files/tutorial003_py39.py!}
-```
-
-////
+{* ../../docs_src/request_files/tutorial003.py hl[18] *}
## 🌃
diff --git a/docs/em/docs/tutorial/request-forms-and-files.md b/docs/em/docs/tutorial/request-forms-and-files.md
index ab39d1b949..680b1a96a5 100644
--- a/docs/em/docs/tutorial/request-forms-and-files.md
+++ b/docs/em/docs/tutorial/request-forms-and-files.md
@@ -12,17 +12,13 @@
## 🗄 `File` & `Form`
-```Python hl_lines="1"
-{!../../../docs_src/request_forms_and_files/tutorial001.py!}
-```
+{* ../../docs_src/request_forms_and_files/tutorial001.py hl[1] *}
## 🔬 `File` & `Form` 🔢
✍ 📁 & 📨 🔢 🎏 🌌 👆 🔜 `Body` ⚖️ `Query`:
-```Python hl_lines="8"
-{!../../../docs_src/request_forms_and_files/tutorial001.py!}
-```
+{* ../../docs_src/request_forms_and_files/tutorial001.py hl[8] *}
📁 & 📨 🏑 🔜 📂 📨 📊 & 👆 🔜 📨 📁 & 📨 🏑.
diff --git a/docs/em/docs/tutorial/request-forms.md b/docs/em/docs/tutorial/request-forms.md
index 74117c47d3..1cc1ea5dcb 100644
--- a/docs/em/docs/tutorial/request-forms.md
+++ b/docs/em/docs/tutorial/request-forms.md
@@ -14,17 +14,13 @@
🗄 `Form` ⚪️➡️ `fastapi`:
-```Python hl_lines="1"
-{!../../../docs_src/request_forms/tutorial001.py!}
-```
+{* ../../docs_src/request_forms/tutorial001.py hl[1] *}
## 🔬 `Form` 🔢
✍ 📨 🔢 🎏 🌌 👆 🔜 `Body` ⚖️ `Query`:
-```Python hl_lines="7"
-{!../../../docs_src/request_forms/tutorial001.py!}
-```
+{* ../../docs_src/request_forms/tutorial001.py hl[7] *}
🖼, 1️⃣ 🌌 Oauth2️⃣ 🔧 💪 ⚙️ (🤙 "🔐 💧") ⚫️ ✔ 📨 `username` & `password` 📨 🏑.
@@ -50,7 +46,7 @@
**FastAPI** 🔜 ⚒ 💭 ✍ 👈 📊 ⚪️➡️ ▶️️ 🥉 ↩️ 🎻.
-/// note | "📡 ℹ"
+/// note | 📡 ℹ
📊 ⚪️➡️ 📨 🛎 🗜 ⚙️ "📻 🆎" `application/x-www-form-urlencoded`.
diff --git a/docs/em/docs/tutorial/response-model.md b/docs/em/docs/tutorial/response-model.md
index 9483508aa1..477376458d 100644
--- a/docs/em/docs/tutorial/response-model.md
+++ b/docs/em/docs/tutorial/response-model.md
@@ -4,29 +4,7 @@
👆 💪 ⚙️ **🆎 ✍** 🎏 🌌 👆 🔜 🔢 💽 🔢 **🔢**, 👆 💪 ⚙️ Pydantic 🏷, 📇, 📖, 📊 💲 💖 🔢, 🎻, ♒️.
-//// tab | 🐍 3️⃣.6️⃣ & 🔛
-
-```Python hl_lines="18 23"
-{!> ../../../docs_src/response_model/tutorial001_01.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.9️⃣ & 🔛
-
-```Python hl_lines="18 23"
-{!> ../../../docs_src/response_model/tutorial001_01_py39.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.1️⃣0️⃣ & 🔛
-
-```Python hl_lines="16 21"
-{!> ../../../docs_src/response_model/tutorial001_01_py310.py!}
-```
-
-////
+{* ../../docs_src/response_model/tutorial001_01.py hl[18,23] *}
FastAPI 🔜 ⚙️ 👉 📨 🆎:
@@ -59,29 +37,7 @@ FastAPI 🔜 ⚙️ 👉 📨 🆎:
* `@app.delete()`
* ♒️.
-//// tab | 🐍 3️⃣.6️⃣ & 🔛
-
-```Python hl_lines="17 22 24-27"
-{!> ../../../docs_src/response_model/tutorial001.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.9️⃣ & 🔛
-
-```Python hl_lines="17 22 24-27"
-{!> ../../../docs_src/response_model/tutorial001_py39.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.1️⃣0️⃣ & 🔛
-
-```Python hl_lines="17 22 24-27"
-{!> ../../../docs_src/response_model/tutorial001_py310.py!}
-```
-
-////
+{* ../../docs_src/response_model/tutorial001.py hl[17,22,24:27] *}
/// note
@@ -113,21 +69,7 @@ FastAPI 🔜 ⚙️ 👉 `response_model` 🌐 💽 🧾, 🔬, ♒️. & **
📥 👥 📣 `UserIn` 🏷, ⚫️ 🔜 🔌 🔢 🔐:
-//// tab | 🐍 3️⃣.6️⃣ & 🔛
-
-```Python hl_lines="9 11"
-{!> ../../../docs_src/response_model/tutorial002.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.1️⃣0️⃣ & 🔛
-
-```Python hl_lines="7 9"
-{!> ../../../docs_src/response_model/tutorial002_py310.py!}
-```
-
-////
+{* ../../docs_src/response_model/tutorial002.py hl[9,11] *}
/// info
@@ -140,21 +82,7 @@ FastAPI 🔜 ⚙️ 👉 `response_model` 🌐 💽 🧾, 🔬, ♒️. & **
& 👥 ⚙️ 👉 🏷 📣 👆 🔢 & 🎏 🏷 📣 👆 🔢:
-//// tab | 🐍 3️⃣.6️⃣ & 🔛
-
-```Python hl_lines="18"
-{!> ../../../docs_src/response_model/tutorial002.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.1️⃣0️⃣ & 🔛
-
-```Python hl_lines="16"
-{!> ../../../docs_src/response_model/tutorial002_py310.py!}
-```
-
-////
+{* ../../docs_src/response_model/tutorial002.py hl[18] *}
🔜, 🕐❔ 🖥 🏗 👩💻 ⏮️ 🔐, 🛠️ 🔜 📨 🎏 🔐 📨.
@@ -172,57 +100,15 @@ FastAPI 🔜 ⚙️ 👉 `response_model` 🌐 💽 🧾, 🔬, ♒️. & **
👥 💪 ↩️ ✍ 🔢 🏷 ⏮️ 🔢 🔐 & 🔢 🏷 🍵 ⚫️:
-//// tab | 🐍 3️⃣.6️⃣ & 🔛
-
-```Python hl_lines="9 11 16"
-{!> ../../../docs_src/response_model/tutorial003.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.1️⃣0️⃣ & 🔛
-
-```Python hl_lines="9 11 16"
-{!> ../../../docs_src/response_model/tutorial003_py310.py!}
-```
-
-////
+{* ../../docs_src/response_model/tutorial003.py hl[9,11,16] *}
📥, ✋️ 👆 *➡ 🛠️ 🔢* 🛬 🎏 🔢 👩💻 👈 🔌 🔐:
-//// tab | 🐍 3️⃣.6️⃣ & 🔛
-
-```Python hl_lines="24"
-{!> ../../../docs_src/response_model/tutorial003.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.1️⃣0️⃣ & 🔛
-
-```Python hl_lines="24"
-{!> ../../../docs_src/response_model/tutorial003_py310.py!}
-```
-
-////
+{* ../../docs_src/response_model/tutorial003.py hl[24] *}
...👥 📣 `response_model` 👆 🏷 `UserOut`, 👈 🚫 🔌 🔐:
-//// tab | 🐍 3️⃣.6️⃣ & 🔛
-
-```Python hl_lines="22"
-{!> ../../../docs_src/response_model/tutorial003.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.1️⃣0️⃣ & 🔛
-
-```Python hl_lines="22"
-{!> ../../../docs_src/response_model/tutorial003_py310.py!}
-```
-
-////
+{* ../../docs_src/response_model/tutorial003.py hl[22] *}
, **FastAPI** 🔜 ✊ 💅 🖥 👅 🌐 💽 👈 🚫 📣 🔢 🏷 (⚙️ Pydantic).
@@ -246,21 +132,7 @@ FastAPI 🔜 ⚙️ 👉 `response_model` 🌐 💽 🧾, 🔬, ♒️. & **
& 👈 💼, 👥 💪 ⚙️ 🎓 & 🧬 ✊ 📈 🔢 **🆎 ✍** 🤚 👍 🐕🦺 👨🎨 & 🧰, & 🤚 FastAPI **💽 🖥**.
-//// tab | 🐍 3️⃣.6️⃣ & 🔛
-
-```Python hl_lines="9-13 15-16 20"
-{!> ../../../docs_src/response_model/tutorial003_01.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.1️⃣0️⃣ & 🔛
-
-```Python hl_lines="7-10 13-14 18"
-{!> ../../../docs_src/response_model/tutorial003_01_py310.py!}
-```
-
-////
+{* ../../docs_src/response_model/tutorial003_01.py hl[9:13,15:16,20] *}
⏮️ 👉, 👥 🤚 🏭 🐕🦺, ⚪️➡️ 👨🎨 & ✍ 👉 📟 ☑ ⚖ 🆎, ✋️ 👥 🤚 💽 🖥 ⚪️➡️ FastAPI.
@@ -302,9 +174,7 @@ FastAPI 🔨 📚 👜 🔘 ⏮️ Pydantic ⚒ 💭 👈 📚 🎏 🚫 🎓
🏆 ⚠ 💼 🔜 [🛬 📨 🔗 🔬 ⏪ 🏧 🩺](../advanced/response-directly.md){.internal-link target=_blank}.
-```Python hl_lines="8 10-11"
-{!> ../../../docs_src/response_model/tutorial003_02.py!}
-```
+{* ../../docs_src/response_model/tutorial003_02.py hl[8,10:11] *}
👉 🙅 💼 🍵 🔁 FastAPI ↩️ 📨 🆎 ✍ 🎓 (⚖️ 🏿) `Response`.
@@ -314,9 +184,7 @@ FastAPI 🔨 📚 👜 🔘 ⏮️ Pydantic ⚒ 💭 👈 📚 🎏 🚫 🎓
👆 💪 ⚙️ 🏿 `Response` 🆎 ✍:
-```Python hl_lines="8-9"
-{!> ../../../docs_src/response_model/tutorial003_03.py!}
-```
+{* ../../docs_src/response_model/tutorial003_03.py hl[8:9] *}
👉 🔜 👷 ↩️ `RedirectResponse` 🏿 `Response`, & FastAPI 🔜 🔁 🍵 👉 🙅 💼.
@@ -326,21 +194,7 @@ FastAPI 🔨 📚 👜 🔘 ⏮️ Pydantic ⚒ 💭 👈 📚 🎏 🚫 🎓
🎏 🔜 🔨 🚥 👆 ✔️ 🕳 💖 🇪🇺 🖖 🎏 🆎 🌐❔ 1️⃣ ⚖️ 🌅 👫 🚫 ☑ Pydantic 🆎, 🖼 👉 🔜 ❌ 👶:
-//// tab | 🐍 3️⃣.6️⃣ & 🔛
-
-```Python hl_lines="10"
-{!> ../../../docs_src/response_model/tutorial003_04.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.1️⃣0️⃣ & 🔛
-
-```Python hl_lines="8"
-{!> ../../../docs_src/response_model/tutorial003_04_py310.py!}
-```
-
-////
+{* ../../docs_src/response_model/tutorial003_04.py hl[10] *}
...👉 ❌ ↩️ 🆎 ✍ 🚫 Pydantic 🆎 & 🚫 👁 `Response` 🎓 ⚖️ 🏿, ⚫️ 🇪🇺 (🙆 2️⃣) 🖖 `Response` & `dict`.
@@ -352,21 +206,7 @@ FastAPI 🔨 📚 👜 🔘 ⏮️ Pydantic ⚒ 💭 👈 📚 🎏 🚫 🎓
👉 💼, 👆 💪 ❎ 📨 🏷 ⚡ ⚒ `response_model=None`:
-//// tab | 🐍 3️⃣.6️⃣ & 🔛
-
-```Python hl_lines="9"
-{!> ../../../docs_src/response_model/tutorial003_05.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.1️⃣0️⃣ & 🔛
-
-```Python hl_lines="7"
-{!> ../../../docs_src/response_model/tutorial003_05_py310.py!}
-```
-
-////
+{* ../../docs_src/response_model/tutorial003_05.py hl[9] *}
👉 🔜 ⚒ FastAPI 🚶 📨 🏷 ⚡ & 👈 🌌 👆 💪 ✔️ 🙆 📨 🆎 ✍ 👆 💪 🍵 ⚫️ 🤕 👆 FastAPI 🈸. 👶
@@ -374,29 +214,7 @@ FastAPI 🔨 📚 👜 🔘 ⏮️ Pydantic ⚒ 💭 👈 📚 🎏 🚫 🎓
👆 📨 🏷 💪 ✔️ 🔢 💲, 💖:
-//// tab | 🐍 3️⃣.6️⃣ & 🔛
-
-```Python hl_lines="11 13-14"
-{!> ../../../docs_src/response_model/tutorial004.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.9️⃣ & 🔛
-
-```Python hl_lines="11 13-14"
-{!> ../../../docs_src/response_model/tutorial004_py39.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.1️⃣0️⃣ & 🔛
-
-```Python hl_lines="9 11-12"
-{!> ../../../docs_src/response_model/tutorial004_py310.py!}
-```
-
-////
+{* ../../docs_src/response_model/tutorial004.py hl[11,13:14] *}
* `description: Union[str, None] = None` (⚖️ `str | None = None` 🐍 3️⃣.1️⃣0️⃣) ✔️ 🔢 `None`.
* `tax: float = 10.5` ✔️ 🔢 `10.5`.
@@ -410,29 +228,7 @@ FastAPI 🔨 📚 👜 🔘 ⏮️ Pydantic ⚒ 💭 👈 📚 🎏 🚫 🎓
👆 💪 ⚒ *➡ 🛠️ 👨🎨* 🔢 `response_model_exclude_unset=True`:
-//// tab | 🐍 3️⃣.6️⃣ & 🔛
-
-```Python hl_lines="24"
-{!> ../../../docs_src/response_model/tutorial004.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.9️⃣ & 🔛
-
-```Python hl_lines="24"
-{!> ../../../docs_src/response_model/tutorial004_py39.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.1️⃣0️⃣ & 🔛
-
-```Python hl_lines="22"
-{!> ../../../docs_src/response_model/tutorial004_py310.py!}
-```
-
-////
+{* ../../docs_src/response_model/tutorial004.py hl[24] *}
& 👈 🔢 💲 🏆 🚫 🔌 📨, 🕴 💲 🤙 ⚒.
@@ -521,21 +317,7 @@ FastAPI 🙃 🥃 (🤙, Pydantic 🙃 🥃) 🤔 👈, ✋️ `description`, `t
///
-//// tab | 🐍 3️⃣.6️⃣ & 🔛
-
-```Python hl_lines="31 37"
-{!> ../../../docs_src/response_model/tutorial005.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.1️⃣0️⃣ & 🔛
-
-```Python hl_lines="29 35"
-{!> ../../../docs_src/response_model/tutorial005_py310.py!}
-```
-
-////
+{* ../../docs_src/response_model/tutorial005.py hl[31,37] *}
/// tip
@@ -549,21 +331,7 @@ FastAPI 🙃 🥃 (🤙, Pydantic 🙃 🥃) 🤔 👈, ✋️ `description`, `t
🚥 👆 💭 ⚙️ `set` & ⚙️ `list` ⚖️ `tuple` ↩️, FastAPI 🔜 🗜 ⚫️ `set` & ⚫️ 🔜 👷 ☑:
-//// tab | 🐍 3️⃣.6️⃣ & 🔛
-
-```Python hl_lines="31 37"
-{!> ../../../docs_src/response_model/tutorial006.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.1️⃣0️⃣ & 🔛
-
-```Python hl_lines="29 35"
-{!> ../../../docs_src/response_model/tutorial006_py310.py!}
-```
-
-////
+{* ../../docs_src/response_model/tutorial006.py hl[31,37] *}
## 🌃
diff --git a/docs/em/docs/tutorial/response-status-code.md b/docs/em/docs/tutorial/response-status-code.md
index 57c44777a3..413ceb9169 100644
--- a/docs/em/docs/tutorial/response-status-code.md
+++ b/docs/em/docs/tutorial/response-status-code.md
@@ -8,9 +8,7 @@
* `@app.delete()`
* ♒️.
-```Python hl_lines="6"
-{!../../../docs_src/response_status_code/tutorial001.py!}
-```
+{* ../../docs_src/response_status_code/tutorial001.py hl[6] *}
/// note
@@ -76,9 +74,7 @@ FastAPI 💭 👉, & 🔜 🏭 🗄 🩺 👈 🇵🇸 📤 🙅♂ 📨
➡️ 👀 ⏮️ 🖼 🔄:
-```Python hl_lines="6"
-{!../../../docs_src/response_status_code/tutorial001.py!}
-```
+{* ../../docs_src/response_status_code/tutorial001.py hl[6] *}
`201` 👔 📟 "✍".
@@ -86,15 +82,13 @@ FastAPI 💭 👉, & 🔜 🏭 🗄 🩺 👈 🇵🇸 📤 🙅♂ 📨
👆 💪 ⚙️ 🏪 🔢 ⚪️➡️ `fastapi.status`.
-```Python hl_lines="1 6"
-{!../../../docs_src/response_status_code/tutorial002.py!}
-```
+{* ../../docs_src/response_status_code/tutorial002.py hl[1,6] *}
👫 🏪, 👫 🧑🤝🧑 🎏 🔢, ✋️ 👈 🌌 👆 💪 ⚙️ 👨🎨 📋 🔎 👫:
-/// note | "📡 ℹ"
+/// note | 📡 ℹ
👆 💪 ⚙️ `from starlette import status`.
diff --git a/docs/em/docs/tutorial/schema-extra-example.md b/docs/em/docs/tutorial/schema-extra-example.md
index 8562de09cf..1bd314c513 100644
--- a/docs/em/docs/tutorial/schema-extra-example.md
+++ b/docs/em/docs/tutorial/schema-extra-example.md
@@ -8,21 +8,7 @@
👆 💪 📣 `example` Pydantic 🏷 ⚙️ `Config` & `schema_extra`, 🔬 Pydantic 🩺: 🔗 🛃:
-//// tab | 🐍 3️⃣.6️⃣ & 🔛
-
-```Python hl_lines="15-23"
-{!> ../../../docs_src/schema_extra_example/tutorial001.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.1️⃣0️⃣ & 🔛
-
-```Python hl_lines="13-21"
-{!> ../../../docs_src/schema_extra_example/tutorial001_py310.py!}
-```
-
-////
+{* ../../docs_src/schema_extra_example/tutorial001.py hl[15:23] *}
👈 ➕ ℹ 🔜 🚮-🔢 **🎻 🔗** 👈 🏷, & ⚫️ 🔜 ⚙️ 🛠️ 🩺.
@@ -40,21 +26,7 @@
👆 💪 ⚙️ 👉 🚮 `example` 🔠 🏑:
-//// tab | 🐍 3️⃣.6️⃣ & 🔛
-
-```Python hl_lines="4 10-13"
-{!> ../../../docs_src/schema_extra_example/tutorial002.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.1️⃣0️⃣ & 🔛
-
-```Python hl_lines="2 8-11"
-{!> ../../../docs_src/schema_extra_example/tutorial002_py310.py!}
-```
-
-////
+{* ../../docs_src/schema_extra_example/tutorial002.py hl[4,10:13] *}
/// warning
@@ -80,21 +52,7 @@
📥 👥 🚶♀️ `example` 📊 ⌛ `Body()`:
-//// tab | 🐍 3️⃣.6️⃣ & 🔛
-
-```Python hl_lines="20-25"
-{!> ../../../docs_src/schema_extra_example/tutorial003.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.1️⃣0️⃣ & 🔛
-
-```Python hl_lines="18-23"
-{!> ../../../docs_src/schema_extra_example/tutorial003_py310.py!}
-```
-
-////
+{* ../../docs_src/schema_extra_example/tutorial003.py hl[20:25] *}
### 🖼 🩺 🎚
@@ -115,21 +73,7 @@
* `value`: 👉 ☑ 🖼 🎦, ✅ `dict`.
* `externalValue`: 🎛 `value`, 📛 ☝ 🖼. 👐 👉 5️⃣📆 🚫 🐕🦺 📚 🧰 `value`.
-//// tab | 🐍 3️⃣.6️⃣ & 🔛
-
-```Python hl_lines="21-47"
-{!> ../../../docs_src/schema_extra_example/tutorial004.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.1️⃣0️⃣ & 🔛
-
-```Python hl_lines="19-45"
-{!> ../../../docs_src/schema_extra_example/tutorial004_py310.py!}
-```
-
-////
+{* ../../docs_src/schema_extra_example/tutorial004.py hl[21:47] *}
### 🖼 🩺 🎚
diff --git a/docs/em/docs/tutorial/security/first-steps.md b/docs/em/docs/tutorial/security/first-steps.md
index 538ea7b0a6..8fb459a651 100644
--- a/docs/em/docs/tutorial/security/first-steps.md
+++ b/docs/em/docs/tutorial/security/first-steps.md
@@ -20,9 +20,7 @@
📁 🖼 📁 `main.py`:
-```Python
-{!../../../docs_src/security/tutorial001.py!}
-```
+{* ../../docs_src/security/tutorial001.py *}
## 🏃 ⚫️
@@ -56,7 +54,7 @@ $ uvicorn main:app --reload
-/// check | "✔ 🔼 ❗"
+/// check | ✔ 🔼 ❗
👆 ⏪ ✔️ ✨ 🆕 "✔" 🔼.
@@ -128,9 +126,7 @@ Oauth2️⃣ 🔧 👈 👩💻 ⚖️ 🛠️ 💪 🔬 💽 👈 🔓 👩
🕐❔ 👥 ✍ 👐 `OAuth2PasswordBearer` 🎓 👥 🚶♀️ `tokenUrl` 🔢. 👉 🔢 🔌 📛 👈 👩💻 (🕸 🏃 👩💻 🖥) 🔜 ⚙️ 📨 `username` & `password` ✔ 🤚 🤝.
-```Python hl_lines="6"
-{!../../../docs_src/security/tutorial001.py!}
-```
+{* ../../docs_src/security/tutorial001.py hl[6] *}
/// tip
@@ -168,15 +164,13 @@ oauth2_scheme(some, parameters)
🔜 👆 💪 🚶♀️ 👈 `oauth2_scheme` 🔗 ⏮️ `Depends`.
-```Python hl_lines="10"
-{!../../../docs_src/security/tutorial001.py!}
-```
+{* ../../docs_src/security/tutorial001.py hl[10] *}
👉 🔗 🔜 🚚 `str` 👈 🛠️ 🔢 `token` *➡ 🛠️ 🔢*.
**FastAPI** 🔜 💭 👈 ⚫️ 💪 ⚙️ 👉 🔗 🔬 "💂♂ ⚖" 🗄 🔗 (& 🏧 🛠️ 🩺).
-/// info | "📡 ℹ"
+/// info | 📡 ℹ
**FastAPI** 🔜 💭 👈 ⚫️ 💪 ⚙️ 🎓 `OAuth2PasswordBearer` (📣 🔗) 🔬 💂♂ ⚖ 🗄 ↩️ ⚫️ 😖 ⚪️➡️ `fastapi.security.oauth2.OAuth2`, ❔ 🔄 😖 ⚪️➡️ `fastapi.security.base.SecurityBase`.
diff --git a/docs/em/docs/tutorial/security/get-current-user.md b/docs/em/docs/tutorial/security/get-current-user.md
index 15545f4278..2f4a26f352 100644
--- a/docs/em/docs/tutorial/security/get-current-user.md
+++ b/docs/em/docs/tutorial/security/get-current-user.md
@@ -2,9 +2,7 @@
⏮️ 📃 💂♂ ⚙️ (❔ 🧢 🔛 🔗 💉 ⚙️) 🤝 *➡ 🛠️ 🔢* `token` `str`:
-```Python hl_lines="10"
-{!../../../docs_src/security/tutorial001.py!}
-```
+{* ../../docs_src/security/tutorial001.py hl[10] *}
✋️ 👈 🚫 👈 ⚠.
@@ -16,21 +14,7 @@
🎏 🌌 👥 ⚙️ Pydantic 📣 💪, 👥 💪 ⚙️ ⚫️ 🙆 🙆:
-//// tab | 🐍 3️⃣.6️⃣ & 🔛
-
-```Python hl_lines="5 12-16"
-{!> ../../../docs_src/security/tutorial002.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.1️⃣0️⃣ & 🔛
-
-```Python hl_lines="3 10-14"
-{!> ../../../docs_src/security/tutorial002_py310.py!}
-```
-
-////
+{* ../../docs_src/security/tutorial002.py hl[5,12:16] *}
## ✍ `get_current_user` 🔗
@@ -42,61 +26,19 @@
🎏 👥 🔨 ⏭ *➡ 🛠️* 🔗, 👆 🆕 🔗 `get_current_user` 🔜 📨 `token` `str` ⚪️➡️ 🎧-🔗 `oauth2_scheme`:
-//// tab | 🐍 3️⃣.6️⃣ & 🔛
-
-```Python hl_lines="25"
-{!> ../../../docs_src/security/tutorial002.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.1️⃣0️⃣ & 🔛
-
-```Python hl_lines="23"
-{!> ../../../docs_src/security/tutorial002_py310.py!}
-```
-
-////
+{* ../../docs_src/security/tutorial002.py hl[25] *}
## 🤚 👩💻
`get_current_user` 🔜 ⚙️ (❌) 🚙 🔢 👥 ✍, 👈 ✊ 🤝 `str` & 📨 👆 Pydantic `User` 🏷:
-//// tab | 🐍 3️⃣.6️⃣ & 🔛
-
-```Python hl_lines="19-22 26-27"
-{!> ../../../docs_src/security/tutorial002.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.1️⃣0️⃣ & 🔛
-
-```Python hl_lines="17-20 24-25"
-{!> ../../../docs_src/security/tutorial002_py310.py!}
-```
-
-////
+{* ../../docs_src/security/tutorial002.py hl[19:22,26:27] *}
## 💉 ⏮️ 👩💻
🔜 👥 💪 ⚙️ 🎏 `Depends` ⏮️ 👆 `get_current_user` *➡ 🛠️*:
-//// tab | 🐍 3️⃣.6️⃣ & 🔛
-
-```Python hl_lines="31"
-{!> ../../../docs_src/security/tutorial002.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.1️⃣0️⃣ & 🔛
-
-```Python hl_lines="29"
-{!> ../../../docs_src/security/tutorial002_py310.py!}
-```
-
-////
+{* ../../docs_src/security/tutorial002.py hl[31] *}
👀 👈 👥 📣 🆎 `current_user` Pydantic 🏷 `User`.
@@ -150,21 +92,7 @@
& 🌐 👉 💯 *➡ 🛠️* 💪 🤪 3️⃣ ⏸:
-//// tab | 🐍 3️⃣.6️⃣ & 🔛
-
-```Python hl_lines="30-32"
-{!> ../../../docs_src/security/tutorial002.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.1️⃣0️⃣ & 🔛
-
-```Python hl_lines="28-30"
-{!> ../../../docs_src/security/tutorial002_py310.py!}
-```
-
-////
+{* ../../docs_src/security/tutorial002.py hl[30:32] *}
## 🌃
diff --git a/docs/em/docs/tutorial/security/oauth2-jwt.md b/docs/em/docs/tutorial/security/oauth2-jwt.md
index 3ab8cc9867..ee7bc2d28a 100644
--- a/docs/em/docs/tutorial/security/oauth2-jwt.md
+++ b/docs/em/docs/tutorial/security/oauth2-jwt.md
@@ -118,21 +118,7 @@ $ pip install "passlib[bcrypt]"
& ➕1️⃣ 1️⃣ 🔓 & 📨 👩💻.
-//// tab | 🐍 3️⃣.6️⃣ & 🔛
-
-```Python hl_lines="7 48 55-56 59-60 69-75"
-{!> ../../../docs_src/security/tutorial004.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.1️⃣0️⃣ & 🔛
-
-```Python hl_lines="6 47 54-55 58-59 68-74"
-{!> ../../../docs_src/security/tutorial004_py310.py!}
-```
-
-////
+{* ../../docs_src/security/tutorial004.py hl[7,48,55:56,59:60,69:75] *}
/// note
@@ -168,21 +154,7 @@ $ openssl rand -hex 32
✍ 🚙 🔢 🏗 🆕 🔐 🤝.
-//// tab | 🐍 3️⃣.6️⃣ & 🔛
-
-```Python hl_lines="6 12-14 28-30 78-86"
-{!> ../../../docs_src/security/tutorial004.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.1️⃣0️⃣ & 🔛
-
-```Python hl_lines="5 11-13 27-29 77-85"
-{!> ../../../docs_src/security/tutorial004_py310.py!}
-```
-
-////
+{* ../../docs_src/security/tutorial004.py hl[6,12:14,28:30,78:86] *}
## ℹ 🔗
@@ -192,21 +164,7 @@ $ openssl rand -hex 32
🚥 🤝 ❌, 📨 🇺🇸🔍 ❌ ▶️️ ↖️.
-//// tab | 🐍 3️⃣.6️⃣ & 🔛
-
-```Python hl_lines="89-106"
-{!> ../../../docs_src/security/tutorial004.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.1️⃣0️⃣ & 🔛
-
-```Python hl_lines="88-105"
-{!> ../../../docs_src/security/tutorial004_py310.py!}
-```
-
-////
+{* ../../docs_src/security/tutorial004.py hl[89:106] *}
## ℹ `/token` *➡ 🛠️*
@@ -214,21 +172,7 @@ $ openssl rand -hex 32
✍ 🎰 🥙 🔐 🤝 & 📨 ⚫️.
-//// tab | 🐍 3️⃣.6️⃣ & 🔛
-
-```Python hl_lines="115-130"
-{!> ../../../docs_src/security/tutorial004.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.1️⃣0️⃣ & 🔛
-
-```Python hl_lines="114-129"
-{!> ../../../docs_src/security/tutorial004_py310.py!}
-```
-
-////
+{* ../../docs_src/security/tutorial004.py hl[115:130] *}
### 📡 ℹ 🔃 🥙 "📄" `sub`
diff --git a/docs/em/docs/tutorial/security/simple-oauth2.md b/docs/em/docs/tutorial/security/simple-oauth2.md
index 937546be88..1fd513d48d 100644
--- a/docs/em/docs/tutorial/security/simple-oauth2.md
+++ b/docs/em/docs/tutorial/security/simple-oauth2.md
@@ -52,21 +52,7 @@ Oauth2️⃣ 👫 🎻.
🥇, 🗄 `OAuth2PasswordRequestForm`, & ⚙️ ⚫️ 🔗 ⏮️ `Depends` *➡ 🛠️* `/token`:
-//// tab | 🐍 3️⃣.6️⃣ & 🔛
-
-```Python hl_lines="4 76"
-{!> ../../../docs_src/security/tutorial003.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.1️⃣0️⃣ & 🔛
-
-```Python hl_lines="2 74"
-{!> ../../../docs_src/security/tutorial003_py310.py!}
-```
-
-////
+{* ../../docs_src/security/tutorial003.py hl[4,76] *}
`OAuth2PasswordRequestForm` 🎓 🔗 👈 📣 📨 💪 ⏮️:
@@ -114,21 +100,7 @@ Oauth2️⃣ 🔌 🤙 *🚚* 🏑 `grant_type` ⏮️ 🔧 💲 `password`, ✋
❌, 👥 ⚙️ ⚠ `HTTPException`:
-//// tab | 🐍 3️⃣.6️⃣ & 🔛
-
-```Python hl_lines="3 77-79"
-{!> ../../../docs_src/security/tutorial003.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.1️⃣0️⃣ & 🔛
-
-```Python hl_lines="1 75-77"
-{!> ../../../docs_src/security/tutorial003_py310.py!}
-```
-
-////
+{* ../../docs_src/security/tutorial003.py hl[3,77:79] *}
### ✅ 🔐
@@ -154,21 +126,7 @@ Oauth2️⃣ 🔌 🤙 *🚚* 🏑 `grant_type` ⏮️ 🔧 💲 `password`, ✋
, 🧙♀ 🏆 🚫 💪 🔄 ⚙️ 👈 🎏 🔐 ➕1️⃣ ⚙️ (📚 👩💻 ⚙️ 🎏 🔐 🌐, 👉 🔜 ⚠).
-//// tab | 🐍 3️⃣.6️⃣ & 🔛
-
-```Python hl_lines="80-83"
-{!> ../../../docs_src/security/tutorial003.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.1️⃣0️⃣ & 🔛
-
-```Python hl_lines="78-81"
-{!> ../../../docs_src/security/tutorial003_py310.py!}
-```
-
-////
+{* ../../docs_src/security/tutorial003.py hl[80:83] *}
#### 🔃 `**user_dict`
@@ -210,21 +168,7 @@ UserInDB(
///
-//// tab | 🐍 3️⃣.6️⃣ & 🔛
-
-```Python hl_lines="85"
-{!> ../../../docs_src/security/tutorial003.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.1️⃣0️⃣ & 🔛
-
-```Python hl_lines="83"
-{!> ../../../docs_src/security/tutorial003_py310.py!}
-```
-
-////
+{* ../../docs_src/security/tutorial003.py hl[85] *}
/// tip
@@ -250,21 +194,7 @@ UserInDB(
, 👆 🔗, 👥 🔜 🕴 🤚 👩💻 🚥 👩💻 🔀, ☑ 🔓, & 🦁:
-//// tab | 🐍 3️⃣.6️⃣ & 🔛
-
-```Python hl_lines="58-66 69-72 90"
-{!> ../../../docs_src/security/tutorial003.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.1️⃣0️⃣ & 🔛
-
-```Python hl_lines="55-64 67-70 88"
-{!> ../../../docs_src/security/tutorial003_py310.py!}
-```
-
-////
+{* ../../docs_src/security/tutorial003.py hl[58:66,69:72,90] *}
/// info
diff --git a/docs/em/docs/tutorial/sql-databases.md b/docs/em/docs/tutorial/sql-databases.md
deleted file mode 100644
index 2492c87087..0000000000
--- a/docs/em/docs/tutorial/sql-databases.md
+++ /dev/null
@@ -1,900 +0,0 @@
-# 🗄 (🔗) 💽
-
-**FastAPI** 🚫 🚚 👆 ⚙️ 🗄 (🔗) 💽.
-
-✋️ 👆 💪 ⚙️ 🙆 🔗 💽 👈 👆 💚.
-
-📥 👥 🔜 👀 🖼 ⚙️ 🇸🇲.
-
-👆 💪 💪 🛠️ ⚫️ 🙆 💽 🐕🦺 🇸🇲, 💖:
-
-* ✳
-* ✳
-* 🗄
-* 🐸
-* 🤸♂ 🗄 💽, ♒️.
-
-👉 🖼, 👥 🔜 ⚙️ **🗄**, ↩️ ⚫️ ⚙️ 👁 📁 & 🐍 ✔️ 🛠️ 🐕🦺. , 👆 💪 📁 👉 🖼 & 🏃 ⚫️.
-
-⏪, 👆 🏭 🈸, 👆 💪 💚 ⚙️ 💽 💽 💖 **✳**.
-
-/// tip
-
-📤 🛂 🏗 🚂 ⏮️ **FastAPI** & **✳**, 🌐 ⚓️ 🔛 **☁**, 🔌 🕸 & 🌖 🧰: https://github.com/tiangolo/full-stack-fastapi-postgresql
-
-///
-
-/// note
-
-👀 👈 📚 📟 🐩 `SQLAlchemy` 📟 👆 🔜 ⚙️ ⏮️ 🙆 🛠️.
-
- **FastAPI** 🎯 📟 🤪 🕧.
-
-///
-
-## 🐜
-
-**FastAPI** 👷 ⏮️ 🙆 💽 & 🙆 👗 🗃 💬 💽.
-
-⚠ ⚓ ⚙️ "🐜": "🎚-🔗 🗺" 🗃.
-
-🐜 ✔️ 🧰 🗜 ("*🗺*") 🖖 *🎚* 📟 & 💽 🏓 ("*🔗*").
-
-⏮️ 🐜, 👆 🛎 ✍ 🎓 👈 🎨 🏓 🗄 💽, 🔠 🔢 🎓 🎨 🏓, ⏮️ 📛 & 🆎.
-
-🖼 🎓 `Pet` 💪 🎨 🗄 🏓 `pets`.
-
-& 🔠 *👐* 🎚 👈 🎓 🎨 ⏭ 💽.
-
-🖼 🎚 `orion_cat` (👐 `Pet`) 💪 ✔️ 🔢 `orion_cat.type`, 🏓 `type`. & 💲 👈 🔢 💪, ✅ `"cat"`.
-
-👫 🐜 ✔️ 🧰 ⚒ 🔗 ⚖️ 🔗 🖖 🏓 ⚖️ 👨💼.
-
-👉 🌌, 👆 💪 ✔️ 🔢 `orion_cat.owner` & 👨💼 🔜 🔌 💽 👉 🐶 👨💼, ✊ ⚪️➡️ 🏓 *👨💼*.
-
-, `orion_cat.owner.name` 💪 📛 (⚪️➡️ `name` 🏓 `owners` 🏓) 👉 🐶 👨💼.
-
-⚫️ 💪 ✔️ 💲 💖 `"Arquilian"`.
-
-& 🐜 🔜 🌐 👷 🤚 ℹ ⚪️➡️ 🔗 🏓 *👨💼* 🕐❔ 👆 🔄 🔐 ⚫️ ⚪️➡️ 👆 🐶 🎚.
-
-⚠ 🐜 🖼: ✳-🐜 (🍕 ✳ 🛠️), 🇸🇲 🐜 (🍕 🇸🇲, 🔬 🛠️) & 🏒 (🔬 🛠️), 👪 🎏.
-
-📥 👥 🔜 👀 ❔ 👷 ⏮️ **🇸🇲 🐜**.
-
-🎏 🌌 👆 💪 ⚙️ 🙆 🎏 🐜.
-
-/// tip
-
-📤 🌓 📄 ⚙️ 🏒 📥 🩺.
-
-///
-
-## 📁 📊
-
-👫 🖼, ➡️ 💬 👆 ✔️ 📁 📛 `my_super_project` 👈 🔌 🎧-📁 🤙 `sql_app` ⏮️ 📊 💖 👉:
-
-```
-.
-└── sql_app
- ├── __init__.py
- ├── crud.py
- ├── database.py
- ├── main.py
- ├── models.py
- └── schemas.py
-```
-
-📁 `__init__.py` 🛁 📁, ✋️ ⚫️ 💬 🐍 👈 `sql_app` ⏮️ 🌐 🚮 🕹 (🐍 📁) 📦.
-
-🔜 ➡️ 👀 ⚫️❔ 🔠 📁/🕹 🔨.
-
-## ❎ `SQLAlchemy`
-
-🥇 👆 💪 ❎ `SQLAlchemy`:
-
-
-
-## 🔗 ⏮️ 💽 🔗
-
-🚥 👆 💚 🔬 🗄 💽 (📁) 🔗, ➡ FastAPI, ℹ 🚮 🎚, 🚮 🏓, 🏓, ⏺, 🔀 📊, ♒️. 👆 💪 ⚙️ 💽 🖥 🗄.
-
-⚫️ 🔜 👀 💖 👉:
-
-
-
-👆 💪 ⚙️ 💳 🗄 🖥 💖 🗄 📋 ⚖️ ExtendsClass.
-
-## 🎛 💽 🎉 ⏮️ 🛠️
-
-🚥 👆 💪 🚫 ⚙️ 🔗 ⏮️ `yield` - 🖼, 🚥 👆 🚫 ⚙️ **🐍 3️⃣.7️⃣** & 💪 🚫 ❎ "🐛" 🤔 🔛 **🐍 3️⃣.6️⃣** - 👆 💪 ⚒ 🆙 🎉 "🛠️" 🎏 🌌.
-
-"🛠️" 🌖 🔢 👈 🕧 🛠️ 🔠 📨, ⏮️ 📟 🛠️ ⏭, & 📟 🛠️ ⏮️ 🔗 🔢.
-
-### ✍ 🛠️
-
-🛠️ 👥 🔜 🚮 (🔢) 🔜 ✍ 🆕 🇸🇲 `SessionLocal` 🔠 📨, 🚮 ⚫️ 📨 & ⤴️ 🔐 ⚫️ 🕐 📨 🏁.
-
-//// tab | 🐍 3️⃣.6️⃣ & 🔛
-
-```Python hl_lines="14-22"
-{!> ../../../docs_src/sql_databases/sql_app/alt_main.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.9️⃣ & 🔛
-
-```Python hl_lines="12-20"
-{!> ../../../docs_src/sql_databases/sql_app_py39/alt_main.py!}
-```
-
-////
-
-/// info
-
-👥 🚮 🏗 `SessionLocal()` & 🚚 📨 `try` 🍫.
-
- & ⤴️ 👥 🔐 ⚫️ `finally` 🍫.
-
-👉 🌌 👥 ⚒ 💭 💽 🎉 🕧 📪 ⏮️ 📨. 🚥 📤 ⚠ ⏪ 🏭 📨.
-
-///
-
-### 🔃 `request.state`
-
-`request.state` 🏠 🔠 `Request` 🎚. ⚫️ 📤 🏪 ❌ 🎚 📎 📨 ⚫️, 💖 💽 🎉 👉 💼. 👆 💪 ✍ 🌅 🔃 ⚫️ 💃 🩺 🔃 `Request` 🇵🇸.
-
-👥 👉 💼, ⚫️ ℹ 👥 🚚 👁 💽 🎉 ⚙️ 🔘 🌐 📨, & ⤴️ 🔐 ⏮️ (🛠️).
-
-### 🔗 ⏮️ `yield` ⚖️ 🛠️
-
-❎ **🛠️** 📥 🎏 ⚫️❔ 🔗 ⏮️ `yield` 🔨, ⏮️ 🔺:
-
-* ⚫️ 🚚 🌖 📟 & 👄 🌅 🏗.
-* 🛠️ ✔️ `async` 🔢.
- * 🚥 📤 📟 ⚫️ 👈 ✔️ "⌛" 🕸, ⚫️ 💪 "🍫" 👆 🈸 📤 & 📉 🎭 🍖.
- * 👐 ⚫️ 🎲 🚫 📶 ⚠ 📥 ⏮️ 🌌 `SQLAlchemy` 👷.
- * ✋️ 🚥 👆 🚮 🌖 📟 🛠️ 👈 ✔️ 📚 👤/🅾 ⌛, ⚫️ 💪 ⤴️ ⚠.
-* 🛠️ 🏃 *🔠* 📨.
- * , 🔗 🔜 ✍ 🔠 📨.
- * 🕐❔ *➡ 🛠️* 👈 🍵 👈 📨 🚫 💪 💽.
-
-/// tip
-
-⚫️ 🎲 👍 ⚙️ 🔗 ⏮️ `yield` 🕐❔ 👫 🥃 ⚙️ 💼.
-
-///
-
-/// info
-
-🔗 ⏮️ `yield` 🚮 ⏳ **FastAPI**.
-
-⏮️ ⏬ 👉 🔰 🕴 ✔️ 🖼 ⏮️ 🛠️ & 📤 🎲 📚 🈸 ⚙️ 🛠️ 💽 🎉 🧾.
-
-///
diff --git a/docs/em/docs/tutorial/static-files.md b/docs/em/docs/tutorial/static-files.md
index 3305746c29..27685c06d9 100644
--- a/docs/em/docs/tutorial/static-files.md
+++ b/docs/em/docs/tutorial/static-files.md
@@ -7,11 +7,9 @@
* 🗄 `StaticFiles`.
* "🗻" `StaticFiles()` 👐 🎯 ➡.
-```Python hl_lines="2 6"
-{!../../../docs_src/static_files/tutorial001.py!}
-```
+{* ../../docs_src/static_files/tutorial001.py hl[2,6] *}
-/// note | "📡 ℹ"
+/// note | 📡 ℹ
👆 💪 ⚙️ `from starlette.staticfiles import StaticFiles`.
@@ -39,4 +37,4 @@
## 🌅 ℹ
-🌖 ℹ & 🎛 ✅ 💃 🩺 🔃 🎻 📁.
+🌖 ℹ & 🎛 ✅ 💃 🩺 🔃 🎻 📁.
diff --git a/docs/em/docs/tutorial/testing.md b/docs/em/docs/tutorial/testing.md
index 75dd2d2958..2e4a531f7d 100644
--- a/docs/em/docs/tutorial/testing.md
+++ b/docs/em/docs/tutorial/testing.md
@@ -1,6 +1,6 @@
# 🔬
-👏 💃, 🔬 **FastAPI** 🈸 ⏩ & 😌.
+👏 💃, 🔬 **FastAPI** 🈸 ⏩ & 😌.
⚫️ ⚓️ 🔛 🇸🇲, ❔ 🔄 🏗 ⚓️ 🔛 📨, ⚫️ 📶 😰 & 🏋️.
@@ -26,9 +26,7 @@
✍ 🙅 `assert` 📄 ⏮️ 🐩 🐍 🧬 👈 👆 💪 ✅ (🔄, 🐩 `pytest`).
-```Python hl_lines="2 12 15-18"
-{!../../../docs_src/app_testing/tutorial001.py!}
-```
+{* ../../docs_src/app_testing/tutorial001.py hl[2,12,15:18] *}
/// tip
@@ -40,7 +38,7 @@
///
-/// note | "📡 ℹ"
+/// note | 📡 ℹ
👆 💪 ⚙️ `from starlette.testclient import TestClient`.
@@ -74,9 +72,7 @@
📁 `main.py` 👆 ✔️ 👆 **FastAPI** 📱:
-```Python
-{!../../../docs_src/app_testing/main.py!}
-```
+{* ../../docs_src/app_testing/main.py *}
### 🔬 📁
@@ -92,9 +88,7 @@
↩️ 👉 📁 🎏 📦, 👆 💪 ⚙️ ⚖ 🗄 🗄 🎚 `app` ⚪️➡️ `main` 🕹 (`main.py`):
-```Python hl_lines="3"
-{!../../../docs_src/app_testing/test_main.py!}
-```
+{* ../../docs_src/app_testing/test_main.py hl[3] *}
...& ✔️ 📟 💯 💖 ⏭.
@@ -122,29 +116,13 @@
👯♂️ *➡ 🛠️* 🚚 `X-Token` 🎚.
-//// tab | 🐍 3️⃣.6️⃣ & 🔛
-
-```Python
-{!> ../../../docs_src/app_testing/app_b/main.py!}
-```
-
-////
-
-//// tab | 🐍 3️⃣.1️⃣0️⃣ & 🔛
-
-```Python
-{!> ../../../docs_src/app_testing/app_b_py310/main.py!}
-```
-
-////
+{* ../../docs_src/app_testing/app_b/main.py *}
### ↔ 🔬 📁
👆 💪 ⤴️ ℹ `test_main.py` ⏮️ ↔ 💯:
-```Python
-{!> ../../../docs_src/app_testing/app_b/test_main.py!}
-```
+{* ../../docs_src/app_testing/app_b/test_main.py *}
🕐❔ 👆 💪 👩💻 🚶♀️ ℹ 📨 & 👆 🚫 💭 ❔, 👆 💪 🔎 (🇺🇸🔍) ❔ ⚫️ `httpx`, ⚖️ ❔ ⚫️ ⏮️ `requests`, 🇸🇲 🔧 ⚓️ 🔛 📨' 🔧.
diff --git a/docs/en/data/contributors.yml b/docs/en/data/contributors.yml
new file mode 100644
index 0000000000..c892d8baf0
--- /dev/null
+++ b/docs/en/data/contributors.yml
@@ -0,0 +1,565 @@
+tiangolo:
+ login: tiangolo
+ count: 782
+ avatarUrl: https://avatars.githubusercontent.com/u/1326112?u=cb5d06e73a9e1998141b1641aa88e443c6717651&v=4
+ url: https://github.com/tiangolo
+dependabot:
+ login: dependabot
+ count: 117
+ avatarUrl: https://avatars.githubusercontent.com/in/29110?v=4
+ url: https://github.com/apps/dependabot
+alejsdev:
+ login: alejsdev
+ count: 52
+ avatarUrl: https://avatars.githubusercontent.com/u/90076947?u=447d12a1b347f466b35378bee4c7104cc9b2c571&v=4
+ url: https://github.com/alejsdev
+pre-commit-ci:
+ login: pre-commit-ci
+ count: 45
+ avatarUrl: https://avatars.githubusercontent.com/in/68672?v=4
+ url: https://github.com/apps/pre-commit-ci
+github-actions:
+ login: github-actions
+ count: 26
+ avatarUrl: https://avatars.githubusercontent.com/in/15368?v=4
+ url: https://github.com/apps/github-actions
+Kludex:
+ login: Kludex
+ count: 24
+ avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=df8a3f06ba8f55ae1967a3e2d5ed882903a4e330&v=4
+ url: https://github.com/Kludex
+dmontagu:
+ login: dmontagu
+ count: 17
+ avatarUrl: https://avatars.githubusercontent.com/u/35119617?u=540f30c937a6450812628b9592a1dfe91bbe148e&v=4
+ url: https://github.com/dmontagu
+euri10:
+ login: euri10
+ count: 13
+ avatarUrl: https://avatars.githubusercontent.com/u/1104190?u=321a2e953e6645a7d09b732786c7a8061e0f8a8b&v=4
+ url: https://github.com/euri10
+nilslindemann:
+ login: nilslindemann
+ count: 13
+ avatarUrl: https://avatars.githubusercontent.com/u/6892179?u=1dca6a22195d6cd1ab20737c0e19a4c55d639472&v=4
+ url: https://github.com/nilslindemann
+kantandane:
+ login: kantandane
+ count: 13
+ avatarUrl: https://avatars.githubusercontent.com/u/3978368?u=cccc199291f991a73b1ebba5abc735a948e0bd16&v=4
+ url: https://github.com/kantandane
+svlandeg:
+ login: svlandeg
+ count: 11
+ avatarUrl: https://avatars.githubusercontent.com/u/8796347?u=556c97650c27021911b0b9447ec55e75987b0e8a&v=4
+ url: https://github.com/svlandeg
+zhaohan-dong:
+ login: zhaohan-dong
+ count: 11
+ avatarUrl: https://avatars.githubusercontent.com/u/65422392?u=8260f8781f50248410ebfa4c9bf70e143fe5c9f2&v=4
+ url: https://github.com/zhaohan-dong
+YuriiMotov:
+ login: YuriiMotov
+ count: 10
+ avatarUrl: https://avatars.githubusercontent.com/u/109919500?u=b9b13d598dddfab529a52d264df80a900bfe7060&v=4
+ url: https://github.com/YuriiMotov
+mariacamilagl:
+ login: mariacamilagl
+ count: 9
+ avatarUrl: https://avatars.githubusercontent.com/u/11489395?u=4adb6986bf3debfc2b8216ae701f2bd47d73da7d&v=4
+ url: https://github.com/mariacamilagl
+handabaldeep:
+ login: handabaldeep
+ count: 9
+ avatarUrl: https://avatars.githubusercontent.com/u/12239103?u=6c39ef15d14c6d5211f5dd775cc4842f8d7f2f3a&v=4
+ url: https://github.com/handabaldeep
+vishnuvskvkl:
+ login: vishnuvskvkl
+ count: 8
+ avatarUrl: https://avatars.githubusercontent.com/u/84698110?u=8af5de0520dd4fa195f53c2850a26f57c0f6bc64&v=4
+ url: https://github.com/vishnuvskvkl
+alissadb:
+ login: alissadb
+ count: 6
+ avatarUrl: https://avatars.githubusercontent.com/u/96190409?u=be42d85938c241be781505a5a872575be28b2906&v=4
+ url: https://github.com/alissadb
+alv2017:
+ login: alv2017
+ count: 6
+ avatarUrl: https://avatars.githubusercontent.com/u/31544722?v=4
+ url: https://github.com/alv2017
+wshayes:
+ login: wshayes
+ count: 5
+ avatarUrl: https://avatars.githubusercontent.com/u/365303?u=07ca03c5ee811eb0920e633cc3c3db73dbec1aa5&v=4
+ url: https://github.com/wshayes
+samuelcolvin:
+ login: samuelcolvin
+ count: 5
+ avatarUrl: https://avatars.githubusercontent.com/u/4039449?u=42eb3b833047c8c4b4f647a031eaef148c16d93f&v=4
+ url: https://github.com/samuelcolvin
+waynerv:
+ login: waynerv
+ count: 5
+ avatarUrl: https://avatars.githubusercontent.com/u/39515546?u=ec35139777597cdbbbddda29bf8b9d4396b429a9&v=4
+ url: https://github.com/waynerv
+krishnamadhavan:
+ login: krishnamadhavan
+ count: 5
+ avatarUrl: https://avatars.githubusercontent.com/u/31798870?u=950693b28f3ae01105fd545c046e46ca3d31ab06&v=4
+ url: https://github.com/krishnamadhavan
+jekirl:
+ login: jekirl
+ count: 4
+ avatarUrl: https://avatars.githubusercontent.com/u/2546697?u=a027452387d85bd4a14834e19d716c99255fb3b7&v=4
+ url: https://github.com/jekirl
+hitrust:
+ login: hitrust
+ count: 4
+ avatarUrl: https://avatars.githubusercontent.com/u/3360631?u=5fa1f475ad784d64eb9666bdd43cc4d285dcc773&v=4
+ url: https://github.com/hitrust
+ShahriyarR:
+ login: ShahriyarR
+ count: 4
+ avatarUrl: https://avatars.githubusercontent.com/u/3852029?u=631b2ae59360ab380c524b32bc3d245aff1165af&v=4
+ url: https://github.com/ShahriyarR
+adriangb:
+ login: adriangb
+ count: 4
+ avatarUrl: https://avatars.githubusercontent.com/u/1755071?u=612704256e38d6ac9cbed24f10e4b6ac2da74ecb&v=4
+ url: https://github.com/adriangb
+iudeen:
+ login: iudeen
+ count: 4
+ avatarUrl: https://avatars.githubusercontent.com/u/10519440?u=f09cdd745e5bf16138f29b42732dd57c7f02bee1&v=4
+ url: https://github.com/iudeen
+musicinmybrain:
+ login: musicinmybrain
+ count: 4
+ avatarUrl: https://avatars.githubusercontent.com/u/6898909?u=9010312053e7141383b9bdf538036c7f37fbaba0&v=4
+ url: https://github.com/musicinmybrain
+philipokiokio:
+ login: philipokiokio
+ count: 4
+ avatarUrl: https://avatars.githubusercontent.com/u/55271518?u=d30994d339aaaf1f6bf1b8fc810132016fbd4fdc&v=4
+ url: https://github.com/philipokiokio
+AlexWendland:
+ login: AlexWendland
+ count: 4
+ avatarUrl: https://avatars.githubusercontent.com/u/3949212?u=c4c0c615e0ea33d00bfe16b779cf6ebc0f58071c&v=4
+ url: https://github.com/AlexWendland
+divums:
+ login: divums
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/1397556?v=4
+ url: https://github.com/divums
+prostomarkeloff:
+ login: prostomarkeloff
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/28061158?u=6918e39a1224194ba636e897461a02a20126d7ad&v=4
+ url: https://github.com/prostomarkeloff
+nsidnev:
+ login: nsidnev
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/22559461?u=a9cc3238217e21dc8796a1a500f01b722adb082c&v=4
+ url: https://github.com/nsidnev
+pawamoy:
+ login: pawamoy
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/3999221?u=b030e4c89df2f3a36bc4710b925bdeb6745c9856&v=4
+ url: https://github.com/pawamoy
+patrickmckenna:
+ login: patrickmckenna
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/3589536?u=53aef07250d226d35e526768e26891964907b41a&v=4
+ url: https://github.com/patrickmckenna
+hukkin:
+ login: hukkin
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/3275109?u=77bb83759127965eacbfe67e2ca983066e964fde&v=4
+ url: https://github.com/hukkin
+marcosmmb:
+ login: marcosmmb
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/6181089?u=03c50eec631857d84df5232890780d00a3f76903&v=4
+ url: https://github.com/marcosmmb
+Serrones:
+ login: Serrones
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/22691749?u=4795b880e13ca33a73e52fc0ef7dc9c60c8fce47&v=4
+ url: https://github.com/Serrones
+uriyyo:
+ login: uriyyo
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/32038156?u=0c68019beb28381ce5205a838937c61e0fe3fee2&v=4
+ url: https://github.com/uriyyo
+andrew222651:
+ login: andrew222651
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/889657?u=d70187989940b085bcbfa3bedad8dbc5f3ab1fe7&v=4
+ url: https://github.com/andrew222651
+rkbeatss:
+ login: rkbeatss
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/23391143?u=56ab6bff50be950fa8cae5cf736f2ae66e319ff3&v=4
+ url: https://github.com/rkbeatss
+asheux:
+ login: asheux
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/22955146?u=4553ebf5b5a7c7fe031a46182083aa224faba2e1&v=4
+ url: https://github.com/asheux
+blkst8:
+ login: blkst8
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/49960770?u=7d8a6d5f0a75a5e9a865a2527edfd48895ea27ae&v=4
+ url: https://github.com/blkst8
+ghandic:
+ login: ghandic
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/23500353?u=e2e1d736f924d9be81e8bfc565b6d8836ba99773&v=4
+ url: https://github.com/ghandic
+TeoZosa:
+ login: TeoZosa
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/13070236?u=96fdae85800ef85dcfcc4b5f8281dc8778c8cb7d&v=4
+ url: https://github.com/TeoZosa
+graingert:
+ login: graingert
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/413772?v=4
+ url: https://github.com/graingert
+jaystone776:
+ login: jaystone776
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/11191137?u=299205a95e9b6817a43144a48b643346a5aac5cc&v=4
+ url: https://github.com/jaystone776
+zanieb:
+ login: zanieb
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/2586601?u=e5c86f7ff3b859e7e183187ac2b17fd6ee32b3ab&v=4
+ url: https://github.com/zanieb
+MicaelJarniac:
+ login: MicaelJarniac
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/19514231?u=158c91874ea98d6e9e6f0c6db37ee2ce60c55ff2&v=4
+ url: https://github.com/MicaelJarniac
+papb:
+ login: papb
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/20914054?u=890511fae7ea90d887e2a65ce44a1775abba38d5&v=4
+ url: https://github.com/papb
+tamird:
+ login: tamird
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/1535036?v=4
+ url: https://github.com/tamird
+Nimitha-jagadeesha:
+ login: Nimitha-jagadeesha
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/58389915?v=4
+ url: https://github.com/Nimitha-jagadeesha
+lucaromagnoli:
+ login: lucaromagnoli
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/38782977?u=e66396859f493b4ddcb3a837a1b2b2039c805417&v=4
+ url: https://github.com/lucaromagnoli
+salmantec:
+ login: salmantec
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/41512228?u=443551b893ff2425c59d5d021644f098cf7c68d5&v=4
+ url: https://github.com/salmantec
+OCE1960:
+ login: OCE1960
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/45076670?u=0e9a44712b92ffa89ddfbaa83c112f3f8e1d68e2&v=4
+ url: https://github.com/OCE1960
+hamidrasti:
+ login: hamidrasti
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/43915620?v=4
+ url: https://github.com/hamidrasti
+valentinDruzhinin:
+ login: valentinDruzhinin
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/12831905?u=aae1ebc675c91e8fa582df4fcc4fc4128106344d&v=4
+ url: https://github.com/valentinDruzhinin
+kkinder:
+ login: kkinder
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/1115018?u=c5e90284a9f5c5049eae1bb029e3655c7dc913ed&v=4
+ url: https://github.com/kkinder
+kabirkhan:
+ login: kabirkhan
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/13891834?u=e0eabf792376443ac853e7dca6f550db4166fe35&v=4
+ url: https://github.com/kabirkhan
+zamiramir:
+ login: zamiramir
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/40475662?v=4
+ url: https://github.com/zamiramir
+trim21:
+ login: trim21
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/13553903?u=3cadf0f02095c9621aa29df6875f53a80ca4fbfb&v=4
+ url: https://github.com/trim21
+koxudaxi:
+ login: koxudaxi
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/630670?u=507d8577b4b3670546b449c4c2ccbc5af40d72f7&v=4
+ url: https://github.com/koxudaxi
+pablogamboa:
+ login: pablogamboa
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/12892536?u=326a57059ee0c40c4eb1b38413957236841c631b&v=4
+ url: https://github.com/pablogamboa
+dconathan:
+ login: dconathan
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/15098095?v=4
+ url: https://github.com/dconathan
+Jamim:
+ login: Jamim
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/5607572?u=9ce0b6a6d1a5124e28b3c04d8d26827ca328713a&v=4
+ url: https://github.com/Jamim
+svalouch:
+ login: svalouch
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/54674660?v=4
+ url: https://github.com/svalouch
+frankie567:
+ login: frankie567
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/1144727?u=f3e79acfe4ed207e15c2145161a8a9759925fcd2&v=4
+ url: https://github.com/frankie567
+marier-nico:
+ login: marier-nico
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/30477068?u=c7df6af853c8f4163d1517814f3e9a0715c82713&v=4
+ url: https://github.com/marier-nico
+Dustyposa:
+ login: Dustyposa
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/27180793?u=5cf2877f50b3eb2bc55086089a78a36f07042889&v=4
+ url: https://github.com/Dustyposa
+aviramha:
+ login: aviramha
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/41201924?u=6883cc4fc13a7b2e60d4deddd4be06f9c5287880&v=4
+ url: https://github.com/aviramha
+iwpnd:
+ login: iwpnd
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/6152183?u=ec59396e9437fff488791c5ecdf6d23f1f1ebf3a&v=4
+ url: https://github.com/iwpnd
+raphaelauv:
+ login: raphaelauv
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/10202690?u=e6f86f5c0c3026a15d6b51792fa3e532b12f1371&v=4
+ url: https://github.com/raphaelauv
+windson:
+ login: windson
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/1826682?u=8b28dcd716c46289f191f8828e01d74edd058bef&v=4
+ url: https://github.com/windson
+sm-Fifteen:
+ login: sm-Fifteen
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/516999?u=437c0c5038558c67e887ccd863c1ba0f846c03da&v=4
+ url: https://github.com/sm-Fifteen
+sattosan:
+ login: sattosan
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/20574756?u=b0d8474d2938189c6954423ae8d81d91013f80a8&v=4
+ url: https://github.com/sattosan
+michaeloliverx:
+ login: michaeloliverx
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/55017335?u=efb0cb6e261ff64d862fafb91ee80fc2e1f8a2ed&v=4
+ url: https://github.com/michaeloliverx
+voegtlel:
+ login: voegtlel
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/5764745?u=db8df3d70d427928ab6d7dbfc395a4a7109c1d1b&v=4
+ url: https://github.com/voegtlel
+HarshaLaxman:
+ login: HarshaLaxman
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/19939186?u=a112f38b0f6b4d4402dc8b51978b5a0b2e5c5970&v=4
+ url: https://github.com/HarshaLaxman
+RunningIkkyu:
+ login: RunningIkkyu
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/31848542?u=494ecc298e3f26197495bb357ad0f57cfd5f7a32&v=4
+ url: https://github.com/RunningIkkyu
+cassiobotaro:
+ login: cassiobotaro
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/3127847?u=a08022b191ddbd0a6159b2981d9d878b6d5bb71f&v=4
+ url: https://github.com/cassiobotaro
+chenl:
+ login: chenl
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/1677651?u=c618508eaad6d596cea36c8ea784b424288f6857&v=4
+ url: https://github.com/chenl
+retnikt:
+ login: retnikt
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/24581770?v=4
+ url: https://github.com/retnikt
+yankeexe:
+ login: yankeexe
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/13623913?u=f970e66421775a8d3cdab89c0c752eaead186f6d&v=4
+ url: https://github.com/yankeexe
+patrickkwang:
+ login: patrickkwang
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/1263870?u=4bf74020e15be490f19ef8322a76eec882220b96&v=4
+ url: https://github.com/patrickkwang
+victorphoenix3:
+ login: victorphoenix3
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/48182195?u=e4875bd088623cb4ddeb7be194ec54b453aff035&v=4
+ url: https://github.com/victorphoenix3
+davidefiocco:
+ login: davidefiocco
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/4547987?v=4
+ url: https://github.com/davidefiocco
+adriencaccia:
+ login: adriencaccia
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/19605940?u=9a59081f46bfc9d839886a49d5092cf572879049&v=4
+ url: https://github.com/adriencaccia
+jamescurtin:
+ login: jamescurtin
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/10189269?u=0b491fc600ca51f41cf1d95b49fa32a3eba1de57&v=4
+ url: https://github.com/jamescurtin
+jmriebold:
+ login: jmriebold
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/6983392?u=4efdc97bf2422dcc7e9ff65b9ff80087c8eb2a20&v=4
+ url: https://github.com/jmriebold
+nukopy:
+ login: nukopy
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/42367320?u=6061be0bd060506f6d564a8df3ae73fab048cdfe&v=4
+ url: https://github.com/nukopy
+imba-tjd:
+ login: imba-tjd
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/24759802?u=01e901a4fe004b4b126549d3ff1c4000fe3720b5&v=4
+ url: https://github.com/imba-tjd
+johnthagen:
+ login: johnthagen
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/10340167?u=47147fc4e4db1f573bee3fe428deeacb3197bc5f&v=4
+ url: https://github.com/johnthagen
+paxcodes:
+ login: paxcodes
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/13646646?u=e7429cc7ab11211ef762f4cd3efea7db6d9ef036&v=4
+ url: https://github.com/paxcodes
+kaustubhgupta:
+ login: kaustubhgupta
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/43691873?u=8dd738718ac7ffad4ef31e86b5d780a1141c695d&v=4
+ url: https://github.com/kaustubhgupta
+kinuax:
+ login: kinuax
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/13321374?u=22dc9873d6d9f2c7e4fc44c6480c3505efb1531f&v=4
+ url: https://github.com/kinuax
+wakabame:
+ login: wakabame
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/35513518?u=41ef6b0a55076e5c540620d68fb006e386c2ddb0&v=4
+ url: https://github.com/wakabame
+nzig:
+ login: nzig
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/7372858?u=e769add36ed73c778cdb136eb10bf96b1e119671&v=4
+ url: https://github.com/nzig
+yezz123:
+ login: yezz123
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/52716203?u=21b53ce4115062b1e20cb513e64ca0000c2ef127&v=4
+ url: https://github.com/yezz123
+softwarebloat:
+ login: softwarebloat
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/16540684?v=4
+ url: https://github.com/softwarebloat
+Lancetnik:
+ login: Lancetnik
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/44573917?u=6eaa0cdd35259fba40a76b82e4903440cba03fa9&v=4
+ url: https://github.com/Lancetnik
+joakimnordling:
+ login: joakimnordling
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/6637576?u=df5d99db9b899b399effd429f4358baaa6f7199c&v=4
+ url: https://github.com/joakimnordling
+yogabonito:
+ login: yogabonito
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/7026269?v=4
+ url: https://github.com/yogabonito
+s111d:
+ login: s111d
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/4954856?v=4
+ url: https://github.com/s111d
+estebanx64:
+ login: estebanx64
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/10840422?u=1900887aeed268699e5ea6f3fb7db614f7b77cd3&v=4
+ url: https://github.com/estebanx64
+ndimares:
+ login: ndimares
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/6267663?u=cfb27efde7a7212be8142abb6c058a1aeadb41b1&v=4
+ url: https://github.com/ndimares
+rabinlamadong:
+ login: rabinlamadong
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/170439781?v=4
+ url: https://github.com/rabinlamadong
+AyushSinghal1794:
+ login: AyushSinghal1794
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/89984761?v=4
+ url: https://github.com/AyushSinghal1794
+gsheni:
+ login: gsheni
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/8726321?u=ee3bd9ff6320f4715d1dd9671a3d55cccb65b984&v=4
+ url: https://github.com/gsheni
+chailandau:
+ login: chailandau
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/112015853?u=2e6aaf2b1647db43834aabeae8d8282b4ec01873&v=4
+ url: https://github.com/chailandau
+DanielKusyDev:
+ login: DanielKusyDev
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/36250676?u=2ea6114ff751fc48b55f231987a0e2582c6b1bd2&v=4
+ url: https://github.com/DanielKusyDev
+DanielYang59:
+ login: DanielYang59
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/80093591?u=63873f701c7c74aac83c906800a1dddc0bc8c92f&v=4
+ url: https://github.com/DanielYang59
+blueswen:
+ login: blueswen
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/1564148?u=6d6b8cc8f2b5cef715e68d6175154a8a94d518ee&v=4
+ url: https://github.com/blueswen
+Taoup:
+ login: Taoup
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/22348542?v=4
+ url: https://github.com/Taoup
diff --git a/docs/en/data/external_links.yml b/docs/en/data/external_links.yml
index 15f6169eef..b8a5fdb3a0 100644
--- a/docs/en/data/external_links.yml
+++ b/docs/en/data/external_links.yml
@@ -1,5 +1,13 @@
Articles:
English:
+ - author: Apitally
+ author_link: https://apitally.io
+ link: https://apitally.io/blog/getting-started-with-logging-in-fastapi
+ title: Getting started with logging in FastAPI
+ - author: Balthazar Rouberol
+ author_link: https://balthazar-rouberol.com
+ link: https://blog.balthazar-rouberol.com/how-to-profile-a-fastapi-asynchronous-request
+ title: How to profile a FastAPI asynchronous request
- author: Stephen Siegert - Neon
link: https://neon.tech/blog/deploy-a-serverless-fastapi-app-with-neon-postgres-and-aws-app-runner-at-any-scale
title: Deploy a Serverless FastAPI App with Neon Postgres and AWS App Runner at any scale
@@ -117,11 +125,11 @@ Articles:
link: https://valonjanuzaj.medium.com/deploy-a-dockerized-fastapi-application-to-aws-cc757830ba1b
title: Deploy a dockerized FastAPI application to AWS
- author: Amit Chaudhary
- author_link: https://twitter.com/amitness
+ author_link: https://x.com/amitness
link: https://amitness.com/2020/06/fastapi-vs-flask/
title: FastAPI for Flask Users
- author: Louis Guitton
- author_link: https://twitter.com/louis_guitton
+ author_link: https://x.com/louis_guitton
link: https://guitton.co/posts/fastapi-monitoring/
title: How to monitor your FastAPI service
- author: Precious Ndubueze
@@ -145,7 +153,7 @@ Articles:
link: https://netflixtechblog.com/introducing-dispatch-da4b8a2a8072
title: Introducing Dispatch
- author: Stavros Korokithakis
- author_link: https://twitter.com/Stavros
+ author_link: https://x.com/Stavros
link: https://www.stavros.io/posts/fastapi-with-django/
title: Using FastAPI with Django
- author: Twilio
@@ -153,11 +161,11 @@ Articles:
link: https://www.twilio.com/blog/build-secure-twilio-webhook-python-fastapi
title: Build a Secure Twilio Webhook with Python and FastAPI
- author: Sebastián Ramírez (tiangolo)
- author_link: https://twitter.com/tiangolo
+ author_link: https://x.com/tiangolo
link: https://dev.to/tiangolo/build-a-web-api-from-scratch-with-fastapi-the-workshop-2ehe
title: Build a web API from scratch with FastAPI - the workshop
- author: Paul Sec
- author_link: https://twitter.com/PaulWebSec
+ author_link: https://x.com/PaulWebSec
link: https://paulsec.github.io/posts/fastapi_plus_zeit_serverless_fu/
title: FastAPI + Zeit.co = 🚀
- author: cuongld2
@@ -165,7 +173,7 @@ Articles:
link: https://dev.to/cuongld2/build-simple-api-service-with-python-fastapi-part-1-581o
title: Build simple API service with Python FastAPI — Part 1
- author: Paurakh Sharma Humagain
- author_link: https://twitter.com/PaurakhSharma
+ author_link: https://x.com/PaurakhSharma
link: https://dev.to/paurakhsharma/microservice-in-python-using-fastapi-24cc
title: Microservice in Python using FastAPI
- author: Guillermo Cruz
@@ -177,7 +185,7 @@ Articles:
link: https://www.tutlinks.com/create-and-deploy-fastapi-app-to-heroku/
title: Create and Deploy FastAPI app to Heroku without using Docker
- author: Arthur Henrique
- author_link: https://twitter.com/arthurheinrique
+ author_link: https://x.com/arthurheinrique
link: https://medium.com/@arthur393/another-boilerplate-to-fastapi-azure-pipeline-ci-pytest-3c8d9a4be0bb
title: 'Another Boilerplate to FastAPI: Azure Pipeline CI + Pytest'
- author: Shane Soh
@@ -217,7 +225,7 @@ Articles:
link: https://towardsdatascience.com/how-to-deploy-a-machine-learning-model-dc51200fe8cf
title: How to Deploy a Machine Learning Model
- author: Johannes Gontrum
- author_link: https://twitter.com/gntrm
+ author_link: https://x.com/gntrm
link: https://medium.com/@gntrm/jwt-authentication-with-fastapi-and-aws-cognito-1333f7f2729e
title: JWT Authentication with FastAPI and AWS Cognito
- author: Ankush Thakur
@@ -253,7 +261,7 @@ Articles:
link: https://medium.com/@williamhayes/fastapi-starlette-debug-vs-prod-5f7561db3a59
title: FastAPI/Starlette debug vs prod
- author: Mukul Mantosh
- author_link: https://twitter.com/MantoshMukul
+ author_link: https://x.com/MantoshMukul
link: https://www.jetbrains.com/pycharm/guide/tutorials/fastapi-aws-kubernetes/
title: Developing FastAPI Application using K8s & AWS
- author: KrishNa
@@ -264,13 +272,21 @@ Articles:
author_link: https://devonray.com
link: https://devonray.com/blog/deploying-a-fastapi-project-using-aws-lambda-aurora-cdk
title: Deployment using Docker, Lambda, Aurora, CDK & GH Actions
+ - author: Shubhendra Kushwaha
+ author_link: https://www.linkedin.com/in/theshubhendra/
+ link: https://theshubhendra.medium.com/mastering-soft-delete-advanced-sqlalchemy-techniques-4678f4738947
+ title: 'Mastering Soft Delete: Advanced SQLAlchemy Techniques'
+ - author: Shubhendra Kushwaha
+ author_link: https://www.linkedin.com/in/theshubhendra/
+ link: https://theshubhendra.medium.com/role-based-row-filtering-advanced-sqlalchemy-techniques-733e6b1328f6
+ title: 'Role based row filtering: Advanced SQLAlchemy Techniques'
German:
- author: Marcel Sander (actidoo)
author_link: https://www.actidoo.com
link: https://www.actidoo.com/de/blog/python-fastapi-domain-driven-design
title: Domain-driven Design mit Python und FastAPI
- author: Nico Axtmann
- author_link: https://twitter.com/_nicoax
+ author_link: https://x.com/_nicoax
link: https://blog.codecentric.de/2019/08/inbetriebnahme-eines-scikit-learn-modells-mit-onnx-und-fastapi/
title: Inbetriebnahme eines scikit-learn-Modells mit ONNX und FastAPI
- author: Felix Schürmeyer
@@ -327,6 +343,10 @@ Articles:
link: https://qiita.com/mtitg/items/47770e9a562dd150631d
title: FastAPI|DB接続してCRUDするPython製APIサーバーを構築
Portuguese:
+ - author: Eduardo Mendes
+ author_link: https://bolha.us/@dunossauro
+ link: https://fastapidozero.dunossauro.com/
+ title: FastAPI do ZERO
- author: Jessica Temporal
author_link: https://jtemporal.com/socials
link: https://jtemporal.com/dicas-para-migrar-de-flask-para-fastapi-e-vice-versa/
@@ -384,14 +404,19 @@ Talks:
link: https://www.youtube.com/watch?v=uZdTe8_Z6BQ
title: 'PyCon AU 2023: Testing asynchronous applications with FastAPI and pytest'
- author: Sebastián Ramírez (tiangolo)
- author_link: https://twitter.com/tiangolo
+ author_link: https://x.com/tiangolo
link: https://www.youtube.com/watch?v=PnpTY1f4k2U
title: '[VIRTUAL] Py.Amsterdam''s flying Software Circus: Intro to FastAPI'
- author: Sebastián Ramírez (tiangolo)
- author_link: https://twitter.com/tiangolo
+ author_link: https://x.com/tiangolo
link: https://www.youtube.com/watch?v=z9K5pwb0rt8
title: 'PyConBY 2020: Serve ML models easily with FastAPI'
- author: Chris Withers
- author_link: https://twitter.com/chriswithers13
+ author_link: https://x.com/chriswithers13
link: https://www.youtube.com/watch?v=3DLwPcrE5mA
title: 'PyCon UK 2019: FastAPI from the ground up'
+ Taiwanese:
+ - author: Blueswen
+ author_link: https://github.com/blueswen
+ link: https://www.youtube.com/watch?v=y3sumuoDq4w
+ title: 'PyCon TW 2024: 全方位強化 Python 服務可觀測性:以 FastAPI 和 Grafana Stack 為例'
diff --git a/docs/en/data/github_sponsors.yml b/docs/en/data/github_sponsors.yml
index 5f0be61c22..7b34719b68 100644
--- a/docs/en/data/github_sponsors.yml
+++ b/docs/en/data/github_sponsors.yml
@@ -1,58 +1,58 @@
sponsors:
-- - login: bump-sh
- avatarUrl: https://avatars.githubusercontent.com/u/33217836?v=4
- url: https://github.com/bump-sh
- - login: porter-dev
- avatarUrl: https://avatars.githubusercontent.com/u/62078005?v=4
- url: https://github.com/porter-dev
+- - login: renderinc
+ avatarUrl: https://avatars.githubusercontent.com/u/36424661?v=4
+ url: https://github.com/renderinc
- login: andrew-propelauth
- avatarUrl: https://avatars.githubusercontent.com/u/89474256?u=1188c27cb744bbec36447a2cfd4453126b2ddb5c&v=4
+ avatarUrl: https://avatars.githubusercontent.com/u/89474256?u=c98993dec8553c09d424ede67bbe86e5c35f48c9&v=4
url: https://github.com/andrew-propelauth
- - login: zanfaruqui
- avatarUrl: https://avatars.githubusercontent.com/u/104461687?v=4
- url: https://github.com/zanfaruqui
- - login: Alek99
- avatarUrl: https://avatars.githubusercontent.com/u/38776361?u=bd6c163fe787c2de1a26c881598e54b67e2482dd&v=4
- url: https://github.com/Alek99
- - login: cryptapi
- avatarUrl: https://avatars.githubusercontent.com/u/44925437?u=61369138589bc7fee6c417f3fbd50fbd38286cc4&v=4
- url: https://github.com/cryptapi
- - login: Kong
- avatarUrl: https://avatars.githubusercontent.com/u/962416?v=4
- url: https://github.com/Kong
- - login: codacy
- avatarUrl: https://avatars.githubusercontent.com/u/1834093?v=4
- url: https://github.com/codacy
+ - login: blockbee-io
+ avatarUrl: https://avatars.githubusercontent.com/u/115143449?u=1b8620c2d6567c4df2111a371b85a51f448f9b85&v=4
+ url: https://github.com/blockbee-io
+ - login: zuplo
+ avatarUrl: https://avatars.githubusercontent.com/u/85497839?v=4
+ url: https://github.com/zuplo
+ - login: coderabbitai
+ avatarUrl: https://avatars.githubusercontent.com/u/132028505?v=4
+ url: https://github.com/coderabbitai
+ - login: subtotal
+ avatarUrl: https://avatars.githubusercontent.com/u/176449348?v=4
+ url: https://github.com/subtotal
+ - login: railwayapp
+ avatarUrl: https://avatars.githubusercontent.com/u/66716858?v=4
+ url: https://github.com/railwayapp
- login: scalar
avatarUrl: https://avatars.githubusercontent.com/u/301879?v=4
url: https://github.com/scalar
-- - login: ObliviousAI
- avatarUrl: https://avatars.githubusercontent.com/u/65656077?v=4
- url: https://github.com/ObliviousAI
-- - login: databento
- avatarUrl: https://avatars.githubusercontent.com/u/64141749?v=4
- url: https://github.com/databento
+- - login: dribia
+ avatarUrl: https://avatars.githubusercontent.com/u/41189616?v=4
+ url: https://github.com/dribia
- login: svix
avatarUrl: https://avatars.githubusercontent.com/u/80175132?v=4
url: https://github.com/svix
- - login: deepset-ai
- avatarUrl: https://avatars.githubusercontent.com/u/51827949?v=4
- url: https://github.com/deepset-ai
- - login: mikeckennedy
- avatarUrl: https://avatars.githubusercontent.com/u/2035561?u=ce6165b799ea3164cb6f5ff54ea08042057442af&v=4
- url: https://github.com/mikeckennedy
- - login: ndimares
- avatarUrl: https://avatars.githubusercontent.com/u/6267663?u=cfb27efde7a7212be8142abb6c058a1aeadb41b1&v=4
- url: https://github.com/ndimares
-- - login: takashi-yoneya
- avatarUrl: https://avatars.githubusercontent.com/u/33813153?u=2d0522bceba0b8b69adf1f2db866503bd96f944e&v=4
- url: https://github.com/takashi-yoneya
- - login: xoflare
- avatarUrl: https://avatars.githubusercontent.com/u/74335107?v=4
- url: https://github.com/xoflare
- - login: marvin-robot
+ - login: stainless-api
+ avatarUrl: https://avatars.githubusercontent.com/u/88061651?v=4
+ url: https://github.com/stainless-api
+ - login: speakeasy-api
+ avatarUrl: https://avatars.githubusercontent.com/u/91446104?v=4
+ url: https://github.com/speakeasy-api
+ - login: databento
+ avatarUrl: https://avatars.githubusercontent.com/u/64141749?v=4
+ url: https://github.com/databento
+ - login: permitio
+ avatarUrl: https://avatars.githubusercontent.com/u/71775833?v=4
+ url: https://github.com/permitio
+- - login: marvin-robot
avatarUrl: https://avatars.githubusercontent.com/u/41086007?u=b9fcab402d0cd0aec738b6574fe60855cb0cd36d&v=4
url: https://github.com/marvin-robot
+ - login: mercedes-benz
+ avatarUrl: https://avatars.githubusercontent.com/u/34240465?v=4
+ url: https://github.com/mercedes-benz
+ - login: Ponte-Energy-Partners
+ avatarUrl: https://avatars.githubusercontent.com/u/114745848?v=4
+ url: https://github.com/Ponte-Energy-Partners
+ - login: LambdaTest-Inc
+ avatarUrl: https://avatars.githubusercontent.com/u/171592363?u=96606606a45fa170427206199014f2a5a2a4920b&v=4
+ url: https://github.com/LambdaTest-Inc
- login: BoostryJP
avatarUrl: https://avatars.githubusercontent.com/u/57932412?v=4
url: https://github.com/BoostryJP
@@ -62,50 +62,50 @@ sponsors:
- - login: Trivie
avatarUrl: https://avatars.githubusercontent.com/u/8161763?v=4
url: https://github.com/Trivie
-- - login: americanair
- avatarUrl: https://avatars.githubusercontent.com/u/12281813?v=4
- url: https://github.com/americanair
- - login: CanoaPBC
- avatarUrl: https://avatars.githubusercontent.com/u/64223768?v=4
- url: https://github.com/CanoaPBC
- - login: mainframeindustries
+- - login: takashi-yoneya
+ avatarUrl: https://avatars.githubusercontent.com/u/33813153?u=2d0522bceba0b8b69adf1f2db866503bd96f944e&v=4
+ url: https://github.com/takashi-yoneya
+ - login: Doist
+ avatarUrl: https://avatars.githubusercontent.com/u/2565372?v=4
+ url: https://github.com/Doist
+- - login: mainframeindustries
avatarUrl: https://avatars.githubusercontent.com/u/55092103?v=4
url: https://github.com/mainframeindustries
- - login: mangualero
- avatarUrl: https://avatars.githubusercontent.com/u/3422968?u=c59272d7b5a912d6126fd6c6f17db71e20f506eb&v=4
- url: https://github.com/mangualero
- - login: birkjernstrom
- avatarUrl: https://avatars.githubusercontent.com/u/281715?u=4be14b43f76b4bd497b1941309bb390250b405e6&v=4
- url: https://github.com/birkjernstrom
- login: yasyf
avatarUrl: https://avatars.githubusercontent.com/u/709645?u=f36736b3c6a85f578886ecc42a740e7b436e7a01&v=4
url: https://github.com/yasyf
+- - login: alixlahuec
+ avatarUrl: https://avatars.githubusercontent.com/u/29543316?u=44357eb2a93bccf30fb9d389b8befe94a3d00985&v=4
+ url: https://github.com/alixlahuec
- - login: primer-io
avatarUrl: https://avatars.githubusercontent.com/u/62146168?v=4
url: https://github.com/primer-io
- - login: povilasb
- avatarUrl: https://avatars.githubusercontent.com/u/1213442?u=b11f58ed6ceea6e8297c9b310030478ebdac894d&v=4
- url: https://github.com/povilasb
-- - login: jhundman
- avatarUrl: https://avatars.githubusercontent.com/u/24263908?v=4
- url: https://github.com/jhundman
+- - login: nilslindemann
+ avatarUrl: https://avatars.githubusercontent.com/u/6892179?u=1dca6a22195d6cd1ab20737c0e19a4c55d639472&v=4
+ url: https://github.com/nilslindemann
- login: upciti
avatarUrl: https://avatars.githubusercontent.com/u/43346262?v=4
url: https://github.com/upciti
+ - login: thisisfixer
+ avatarUrl: https://avatars.githubusercontent.com/u/14433035?u=076d52a5b7891c764904af9f462bfb45428e25df&v=4
+ url: https://github.com/thisisfixer
- - login: samuelcolvin
avatarUrl: https://avatars.githubusercontent.com/u/4039449?u=42eb3b833047c8c4b4f647a031eaef148c16d93f&v=4
url: https://github.com/samuelcolvin
- - login: Kludex
- avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=62adc405ef418f4b6c8caa93d3eb8ab107bc4927&v=4
- url: https://github.com/Kludex
- - login: b-rad-c
- avatarUrl: https://avatars.githubusercontent.com/u/25362581?u=5bb10629f4015b62bec1f9a366675d5085551af9&v=4
- url: https://github.com/b-rad-c
+ - login: otosky
+ avatarUrl: https://avatars.githubusercontent.com/u/42260747?u=69d089387c743d89427aa4ad8740cfb34045a9e0&v=4
+ url: https://github.com/otosky
+ - login: ramonalmeidam
+ avatarUrl: https://avatars.githubusercontent.com/u/45269580?u=3358750b3a5854d7c3ed77aaca7dd20a0f529d32&v=4
+ url: https://github.com/ramonalmeidam
+ - login: roboflow
+ avatarUrl: https://avatars.githubusercontent.com/u/53104118?v=4
+ url: https://github.com/roboflow
- login: ehaca
avatarUrl: https://avatars.githubusercontent.com/u/25950317?u=cec1a3e0643b785288ae8260cc295a85ab344995&v=4
url: https://github.com/ehaca
- login: raphaellaude
- avatarUrl: https://avatars.githubusercontent.com/u/28026311?u=9ae4b158c0d2cb29ebd46df6b6edb7de08a67566&v=4
+ avatarUrl: https://avatars.githubusercontent.com/u/28026311?u=91e1c00d9ac4f8045527e13de8050d504531cbc0&v=4
url: https://github.com/raphaellaude
- login: timlrx
avatarUrl: https://avatars.githubusercontent.com/u/28362229?u=9a745ca31372ee324af682715ae88ce8522f9094&v=4
@@ -113,99 +113,39 @@ sponsors:
- login: Leay15
avatarUrl: https://avatars.githubusercontent.com/u/32212558?u=c4aa9c1737e515959382a5515381757b1fd86c53&v=4
url: https://github.com/Leay15
- - login: ygorpontelo
- avatarUrl: https://avatars.githubusercontent.com/u/32963605?u=35f7103f9c4c4c2589ae5737ee882e9375ef072e&v=4
- url: https://github.com/ygorpontelo
- - login: ProteinQure
- avatarUrl: https://avatars.githubusercontent.com/u/33707203?v=4
- url: https://github.com/ProteinQure
- - login: catherinenelson1
- avatarUrl: https://avatars.githubusercontent.com/u/11951946?u=e714b957185b8cf3d301cced7fc3ad2842122c6a&v=4
- url: https://github.com/catherinenelson1
- - login: jsoques
- avatarUrl: https://avatars.githubusercontent.com/u/12414216?u=620921d94196546cc8b9eae2cc4cbc3f95bab42f&v=4
- url: https://github.com/jsoques
- - login: joeds13
- avatarUrl: https://avatars.githubusercontent.com/u/13631604?u=628eb122e08bef43767b3738752b883e8e7f6259&v=4
- url: https://github.com/joeds13
- - login: dannywade
- avatarUrl: https://avatars.githubusercontent.com/u/13680237?u=418ee985bd41577b20fde81417fb2d901e875e8a&v=4
- url: https://github.com/dannywade
- - login: khadrawy
- avatarUrl: https://avatars.githubusercontent.com/u/13686061?u=59f25ef42ecf04c22657aac4238ce0e2d3d30304&v=4
- url: https://github.com/khadrawy
- - login: mjohnsey
- avatarUrl: https://avatars.githubusercontent.com/u/16784016?u=38fad2e6b411244560b3af99c5f5a4751bc81865&v=4
- url: https://github.com/mjohnsey
- - login: ashi-agrawal
- avatarUrl: https://avatars.githubusercontent.com/u/17105294?u=99c7a854035e5398d8e7b674f2d42baae6c957f8&v=4
- url: https://github.com/ashi-agrawal
- - login: sepsi77
- avatarUrl: https://avatars.githubusercontent.com/u/18682303?v=4
- url: https://github.com/sepsi77
- - login: wedwardbeck
- avatarUrl: https://avatars.githubusercontent.com/u/19333237?u=1de4ae2bf8d59eb4c013f21d863cbe0f2010575f&v=4
- url: https://github.com/wedwardbeck
- - login: RaamEEIL
- avatarUrl: https://avatars.githubusercontent.com/u/20320552?v=4
- url: https://github.com/RaamEEIL
- - login: anthonycepeda
- avatarUrl: https://avatars.githubusercontent.com/u/72019805?u=60bdf46240cff8fca482ff0fc07d963fd5e1a27c&v=4
- url: https://github.com/anthonycepeda
- - login: patricioperezv
- avatarUrl: https://avatars.githubusercontent.com/u/73832292?u=5f471f156e19ee7920e62ae0f4a47b95580e61cf&v=4
- url: https://github.com/patricioperezv
- login: kaoru0310
avatarUrl: https://avatars.githubusercontent.com/u/80977929?u=1b61d10142b490e56af932ddf08a390fae8ee94f&v=4
url: https://github.com/kaoru0310
- login: DelfinaCare
avatarUrl: https://avatars.githubusercontent.com/u/83734439?v=4
url: https://github.com/DelfinaCare
- - login: Eruditis
- avatarUrl: https://avatars.githubusercontent.com/u/95244703?v=4
- url: https://github.com/Eruditis
+ - login: Karine-Bauch
+ avatarUrl: https://avatars.githubusercontent.com/u/90465103?u=7feb1018abb1a5631cfd9a91fea723d1ceb5f49b&v=4
+ url: https://github.com/Karine-Bauch
- login: jugeeem
avatarUrl: https://avatars.githubusercontent.com/u/116043716?u=ae590d79c38ac79c91b9c5caa6887d061e865a3d&v=4
url: https://github.com/jugeeem
- - login: apitally
- avatarUrl: https://avatars.githubusercontent.com/u/138365043?v=4
- url: https://github.com/apitally
- - login: logic-automation
- avatarUrl: https://avatars.githubusercontent.com/u/144732884?v=4
- url: https://github.com/logic-automation
- - login: ddilidili
- avatarUrl: https://avatars.githubusercontent.com/u/42176885?u=c0a849dde06987434653197b5f638d3deb55fc6c&v=4
- url: https://github.com/ddilidili
- - login: ramonalmeidam
- avatarUrl: https://avatars.githubusercontent.com/u/45269580?u=3358750b3a5854d7c3ed77aaca7dd20a0f529d32&v=4
- url: https://github.com/ramonalmeidam
- login: dudikbender
avatarUrl: https://avatars.githubusercontent.com/u/53487583?u=3a57542938ebfd57579a0111db2b297e606d9681&v=4
url: https://github.com/dudikbender
- - login: prodhype
- avatarUrl: https://avatars.githubusercontent.com/u/60444672?u=3f278cff25ea37ead487d7861d4a984795de819e&v=4
- url: https://github.com/prodhype
- login: patsatsia
avatarUrl: https://avatars.githubusercontent.com/u/61111267?u=3271b85f7a37b479c8d0ae0a235182e83c166edf&v=4
url: https://github.com/patsatsia
- - login: tcsmith
- avatarUrl: https://avatars.githubusercontent.com/u/989034?u=7d8d741552b3279e8f4d3878679823a705a46f8f&v=4
- url: https://github.com/tcsmith
+ - login: anthonycepeda
+ avatarUrl: https://avatars.githubusercontent.com/u/72019805?u=60bdf46240cff8fca482ff0fc07d963fd5e1a27c&v=4
+ url: https://github.com/anthonycepeda
+ - login: patricioperezv
+ avatarUrl: https://avatars.githubusercontent.com/u/73832292?u=5f471f156e19ee7920e62ae0f4a47b95580e61cf&v=4
+ url: https://github.com/patricioperezv
+ - login: chickenandstats
+ avatarUrl: https://avatars.githubusercontent.com/u/79477966?u=ae2b894aa954070db1d7830dab99b49eba4e4567&v=4
+ url: https://github.com/chickenandstats
- login: dodo5522
avatarUrl: https://avatars.githubusercontent.com/u/1362607?u=9bf1e0e520cccc547c046610c468ce6115bbcf9f&v=4
url: https://github.com/dodo5522
- - login: nihpo
- avatarUrl: https://avatars.githubusercontent.com/u/1841030?u=0264956d7580f7e46687a762a7baa629f84cf97c&v=4
- url: https://github.com/nihpo
- login: knallgelb
avatarUrl: https://avatars.githubusercontent.com/u/2358812?u=c48cb6362b309d74cbf144bd6ad3aed3eb443e82&v=4
url: https://github.com/knallgelb
- - login: johannquerne
- avatarUrl: https://avatars.githubusercontent.com/u/2736484?u=9b3381546a25679913a2b08110e4373c98840821&v=4
- url: https://github.com/johannquerne
- - login: Shark009
- avatarUrl: https://avatars.githubusercontent.com/u/3163309?u=0c6f4091b0eda05c44c390466199826e6dc6e431&v=4
- url: https://github.com/Shark009
- login: dblackrun
avatarUrl: https://avatars.githubusercontent.com/u/3528486?v=4
url: https://github.com/dblackrun
@@ -215,27 +155,36 @@ sponsors:
- login: kennywakeland
avatarUrl: https://avatars.githubusercontent.com/u/3631417?u=7c8f743f1ae325dfadea7c62bbf1abd6a824fc55&v=4
url: https://github.com/kennywakeland
- - login: simw
- avatarUrl: https://avatars.githubusercontent.com/u/6322526?v=4
- url: https://github.com/simw
- - login: koconder
- avatarUrl: https://avatars.githubusercontent.com/u/25068?u=582657b23622aaa3dfe68bd028a780f272f456fa&v=4
- url: https://github.com/koconder
+ - login: aacayaco
+ avatarUrl: https://avatars.githubusercontent.com/u/3634801?u=eaadda178c964178fcb64886f6c732172c8f8219&v=4
+ url: https://github.com/aacayaco
+ - login: anomaly
+ avatarUrl: https://avatars.githubusercontent.com/u/3654837?v=4
+ url: https://github.com/anomaly
+ - login: mj0331
+ avatarUrl: https://avatars.githubusercontent.com/u/3890353?u=1c627ac1a024515b4871de5c3ebbfaa1a57f65d4&v=4
+ url: https://github.com/mj0331
+ - login: gorhack
+ avatarUrl: https://avatars.githubusercontent.com/u/4141690?u=ec119ebc4bdf00a7bc84657a71aa17834f4f27f3&v=4
+ url: https://github.com/gorhack
+ - login: Ryandaydev
+ avatarUrl: https://avatars.githubusercontent.com/u/4292423?u=679ff84cb7b988c5795a5fa583857f574a055763&v=4
+ url: https://github.com/Ryandaydev
+ - login: vincentkoc
+ avatarUrl: https://avatars.githubusercontent.com/u/25068?u=fbd5b2d51142daa4bdbc21e21953a3b8b8188a4a&v=4
+ url: https://github.com/vincentkoc
- login: jstanden
avatarUrl: https://avatars.githubusercontent.com/u/63288?u=c3658d57d2862c607a0e19c2101c3c51876e36ad&v=4
url: https://github.com/jstanden
- - login: andreaso
- avatarUrl: https://avatars.githubusercontent.com/u/285964?u=837265cc7562c0685f25b2d81cd9de0434fe107c&v=4
- url: https://github.com/andreaso
+ - login: paulcwatts
+ avatarUrl: https://avatars.githubusercontent.com/u/150269?u=1819e145d573b44f0ad74b87206d21cd60331d4e&v=4
+ url: https://github.com/paulcwatts
- login: robintw
avatarUrl: https://avatars.githubusercontent.com/u/296686?v=4
url: https://github.com/robintw
- login: pamelafox
avatarUrl: https://avatars.githubusercontent.com/u/297042?v=4
url: https://github.com/pamelafox
- - login: ericof
- avatarUrl: https://avatars.githubusercontent.com/u/306014?u=cf7c8733620397e6584a451505581c01c5d842d7&v=4
- url: https://github.com/ericof
- login: wshayes
avatarUrl: https://avatars.githubusercontent.com/u/365303?u=07ca03c5ee811eb0920e633cc3c3db73dbec1aa5&v=4
url: https://github.com/wshayes
@@ -248,39 +197,33 @@ sponsors:
- login: mintuhouse
avatarUrl: https://avatars.githubusercontent.com/u/769950?u=ecfbd79a97d33177e0d093ddb088283cf7fe8444&v=4
url: https://github.com/mintuhouse
- - login: Rehket
- avatarUrl: https://avatars.githubusercontent.com/u/7015688?u=3afb0ba200feebbc7f958950e92db34df2a3c172&v=4
- url: https://github.com/Rehket
- - login: hiancdtrsnm
- avatarUrl: https://avatars.githubusercontent.com/u/7343177?v=4
- url: https://github.com/hiancdtrsnm
- - login: TrevorBenson
- avatarUrl: https://avatars.githubusercontent.com/u/9167887?u=afdd1766fdb79e04e59094cc6a54cd011ee7f686&v=4
- url: https://github.com/TrevorBenson
- login: wdwinslow
- avatarUrl: https://avatars.githubusercontent.com/u/11562137?u=dc01daafb354135603a263729e3d26d939c0c452&v=4
+ avatarUrl: https://avatars.githubusercontent.com/u/11562137?u=371272f2c69e680e0559a7b0a57385e83a5dc728&v=4
url: https://github.com/wdwinslow
- - login: aacayaco
- avatarUrl: https://avatars.githubusercontent.com/u/3634801?u=eaadda178c964178fcb64886f6c732172c8f8219&v=4
- url: https://github.com/aacayaco
- - login: anomaly
- avatarUrl: https://avatars.githubusercontent.com/u/3654837?v=4
- url: https://github.com/anomaly
- - login: jgreys
- avatarUrl: https://avatars.githubusercontent.com/u/4136890?u=096820d1ef89877d57d0f68e669ead8b0fde84df&v=4
- url: https://github.com/jgreys
- - login: Ryandaydev
- avatarUrl: https://avatars.githubusercontent.com/u/4292423?u=48f68868db8886fce31a1d802c1003914c6cd7c6&v=4
- url: https://github.com/Ryandaydev
+ - login: jsoques
+ avatarUrl: https://avatars.githubusercontent.com/u/12414216?u=620921d94196546cc8b9eae2cc4cbc3f95bab42f&v=4
+ url: https://github.com/jsoques
+ - login: dannywade
+ avatarUrl: https://avatars.githubusercontent.com/u/13680237?u=418ee985bd41577b20fde81417fb2d901e875e8a&v=4
+ url: https://github.com/dannywade
+ - login: khadrawy
+ avatarUrl: https://avatars.githubusercontent.com/u/13686061?u=59f25ef42ecf04c22657aac4238ce0e2d3d30304&v=4
+ url: https://github.com/khadrawy
+ - login: mjohnsey
+ avatarUrl: https://avatars.githubusercontent.com/u/16784016?u=38fad2e6b411244560b3af99c5f5a4751bc81865&v=4
+ url: https://github.com/mjohnsey
+ - login: ashi-agrawal
+ avatarUrl: https://avatars.githubusercontent.com/u/17105294?u=99c7a854035e5398d8e7b674f2d42baae6c957f8&v=4
+ url: https://github.com/ashi-agrawal
+ - login: RaamEEIL
+ avatarUrl: https://avatars.githubusercontent.com/u/20320552?v=4
+ url: https://github.com/RaamEEIL
- login: jaredtrog
avatarUrl: https://avatars.githubusercontent.com/u/4381365?v=4
url: https://github.com/jaredtrog
- login: oliverxchen
avatarUrl: https://avatars.githubusercontent.com/u/4471774?u=534191f25e32eeaadda22dfab4b0a428733d5489&v=4
url: https://github.com/oliverxchen
- - login: ennui93
- avatarUrl: https://avatars.githubusercontent.com/u/5300907?u=5b5452725ddb391b2caaebf34e05aba873591c3a&v=4
- url: https://github.com/ennui93
- login: ternaus
avatarUrl: https://avatars.githubusercontent.com/u/5481618?u=513a26b02a39e7a28d587cd37c6cc877ea368e6e&v=4
url: https://github.com/ternaus
@@ -288,17 +231,35 @@ sponsors:
avatarUrl: https://avatars.githubusercontent.com/u/5920492?u=208d419cf667b8ac594c82a8db01932c7e50d057&v=4
url: https://github.com/eseglem
- login: FernandoCelmer
- avatarUrl: https://avatars.githubusercontent.com/u/6262214?u=d29fff3fd862fda4ca752079f13f32e84c762ea4&v=4
+ avatarUrl: https://avatars.githubusercontent.com/u/6262214?u=58ba6d5888fa7f355934e52db19f950e20b38162&v=4
url: https://github.com/FernandoCelmer
-- - login: getsentry
- avatarUrl: https://avatars.githubusercontent.com/u/1396951?v=4
- url: https://github.com/getsentry
+ - login: Rehket
+ avatarUrl: https://avatars.githubusercontent.com/u/7015688?u=3afb0ba200feebbc7f958950e92db34df2a3c172&v=4
+ url: https://github.com/Rehket
+ - login: hiancdtrsnm
+ avatarUrl: https://avatars.githubusercontent.com/u/7343177?v=4
+ url: https://github.com/hiancdtrsnm
+- - login: manoelpqueiroz
+ avatarUrl: https://avatars.githubusercontent.com/u/23669137?u=b12e84b28a84369ab5b30bd5a79e5788df5a0756&v=4
+ url: https://github.com/manoelpqueiroz
- - login: pawamoy
avatarUrl: https://avatars.githubusercontent.com/u/3999221?u=b030e4c89df2f3a36bc4710b925bdeb6745c9856&v=4
url: https://github.com/pawamoy
- - login: SebTota
- avatarUrl: https://avatars.githubusercontent.com/u/25122511?v=4
- url: https://github.com/SebTota
+ - login: siavashyj
+ avatarUrl: https://avatars.githubusercontent.com/u/43583410?u=562005ddc7901cd27a1219a118a2363817b14977&v=4
+ url: https://github.com/siavashyj
+ - login: mobyw
+ avatarUrl: https://avatars.githubusercontent.com/u/44370805?v=4
+ url: https://github.com/mobyw
+ - login: ArtyomVancyan
+ avatarUrl: https://avatars.githubusercontent.com/u/44609997?v=4
+ url: https://github.com/ArtyomVancyan
+ - login: caviri
+ avatarUrl: https://avatars.githubusercontent.com/u/45425937?u=5f3d66ea5edea94c028c51ebf1c0f3b37e6c3db5&v=4
+ url: https://github.com/caviri
+ - login: hgalytoby
+ avatarUrl: https://avatars.githubusercontent.com/u/50397689?u=6cc9028f3db63f8f60ad21c17b1ce4b88c4e2e60&v=4
+ url: https://github.com/hgalytoby
- login: nisutec
avatarUrl: https://avatars.githubusercontent.com/u/25281462?u=e562484c451fdfc59053163f64405f8eb262b8b0&v=4
url: https://github.com/nisutec
@@ -308,108 +269,42 @@ sponsors:
- login: joerambo
avatarUrl: https://avatars.githubusercontent.com/u/26282974?v=4
url: https://github.com/joerambo
- - login: rlnchow
- avatarUrl: https://avatars.githubusercontent.com/u/28018479?u=a93ca9cf1422b9ece155784a72d5f2fdbce7adff&v=4
- url: https://github.com/rlnchow
- login: engineerjoe440
avatarUrl: https://avatars.githubusercontent.com/u/33275230?u=eb223cad27017bb1e936ee9b429b450d092d0236&v=4
url: https://github.com/engineerjoe440
- login: bnkc
avatarUrl: https://avatars.githubusercontent.com/u/34930566?u=db5e6f4f87836cad26c2aa90ce390ce49041c5a9&v=4
url: https://github.com/bnkc
- - login: DevOpsKev
- avatarUrl: https://avatars.githubusercontent.com/u/36336550?u=6ccd5978fdaab06f37e22f2a14a7439341df7f67&v=4
- url: https://github.com/DevOpsKev
- login: petercool
- avatarUrl: https://avatars.githubusercontent.com/u/37613029?u=81c525232bb35780945a68e88afd96bb2cdad9c4&v=4
+ avatarUrl: https://avatars.githubusercontent.com/u/37613029?u=75aa8c6729e6e8f85a300561c4dbeef9d65c8797&v=4
url: https://github.com/petercool
- - login: JimFawkes
- avatarUrl: https://avatars.githubusercontent.com/u/12075115?u=dc58ecfd064d72887c34bf500ddfd52592509acd&v=4
- url: https://github.com/JimFawkes
- - login: artempronevskiy
- avatarUrl: https://avatars.githubusercontent.com/u/12235104?u=03df6e1e55c9c6fe5d230adabb8dd7d43d8bbe8f&v=4
- url: https://github.com/artempronevskiy
- - login: TheR1D
- avatarUrl: https://avatars.githubusercontent.com/u/16740832?u=b0dfdbdb27b79729430c71c6128962f77b7b53f7&v=4
- url: https://github.com/TheR1D
- - login: joshuatz
- avatarUrl: https://avatars.githubusercontent.com/u/17817563?u=f1bf05b690d1fc164218f0b420cdd3acb7913e21&v=4
- url: https://github.com/joshuatz
- - login: jangia
- avatarUrl: https://avatars.githubusercontent.com/u/17927101?u=9261b9bb0c3e3bb1ecba43e8915dc58d8c9a077e&v=4
- url: https://github.com/jangia
- - login: jackleeio
- avatarUrl: https://avatars.githubusercontent.com/u/20477587?u=c5184dab6d021733d10c8f975b20e391856303d6&v=4
- url: https://github.com/jackleeio
- - login: shuheng-liu
- avatarUrl: https://avatars.githubusercontent.com/u/22414322?u=813c45f30786c6b511b21a661def025d8f7b609e&v=4
- url: https://github.com/shuheng-liu
- - login: pers0n4
- avatarUrl: https://avatars.githubusercontent.com/u/24864600?u=f211a13a7b572cbbd7779b9c8d8cb428cc7ba07e&v=4
- url: https://github.com/pers0n4
- - login: curegit
- avatarUrl: https://avatars.githubusercontent.com/u/37978051?u=1733c322079118c0cdc573c03d92813f50a9faec&v=4
- url: https://github.com/curegit
- - login: fernandosmither
- avatarUrl: https://avatars.githubusercontent.com/u/66154723?u=f79753eb207d01cca5bbb91ac62db6123e7622d1&v=4
- url: https://github.com/fernandosmither
+ - login: JulioPeixoto
+ avatarUrl: https://avatars.githubusercontent.com/u/96303574?u=27d4614350cae33653f1be35cb47c92a12627ac9&v=4
+ url: https://github.com/JulioPeixoto
+ - login: johnl28
+ avatarUrl: https://avatars.githubusercontent.com/u/54412955?u=47dd06082d1c39caa90c752eb55566e4f3813957&v=4
+ url: https://github.com/johnl28
- login: PunRabbit
avatarUrl: https://avatars.githubusercontent.com/u/70463212?u=1a835cfbc99295a60c8282f6aa6199d1b42241a5&v=4
url: https://github.com/PunRabbit
- login: PelicanQ
avatarUrl: https://avatars.githubusercontent.com/u/77930606?v=4
url: https://github.com/PelicanQ
- - login: tahmarrrr23
- avatarUrl: https://avatars.githubusercontent.com/u/138208610?u=465a46b0ff72a74252d3e3a71ac7d2f1919cda28&v=4
- url: https://github.com/tahmarrrr23
- - login: zk-Call
- avatarUrl: https://avatars.githubusercontent.com/u/147117264?v=4
- url: https://github.com/zk-Call
- - login: kristiangronberg
- avatarUrl: https://avatars.githubusercontent.com/u/42678548?v=4
- url: https://github.com/kristiangronberg
- - login: leonardo-holguin
- avatarUrl: https://avatars.githubusercontent.com/u/43093055?u=b59013d52fb6c4e0954aaaabc0882bd844985b38&v=4
- url: https://github.com/leonardo-holguin
- - login: arrrrrmin
- avatarUrl: https://avatars.githubusercontent.com/u/43553423?u=36a3880a6eb29309c19e6cadbb173bafbe91deb1&v=4
- url: https://github.com/arrrrrmin
- - login: mobyw
- avatarUrl: https://avatars.githubusercontent.com/u/44370805?v=4
- url: https://github.com/mobyw
- - login: ArtyomVancyan
- avatarUrl: https://avatars.githubusercontent.com/u/44609997?v=4
- url: https://github.com/ArtyomVancyan
- - login: harol97
- avatarUrl: https://avatars.githubusercontent.com/u/49042862?u=2b18e115ab73f5f09a280be2850f93c58a12e3d2&v=4
- url: https://github.com/harol97
- - login: hgalytoby
- avatarUrl: https://avatars.githubusercontent.com/u/50397689?u=62c7ff3519858423579676cd0efbd7e3f1ffe63a&v=4
- url: https://github.com/hgalytoby
- - login: conservative-dude
- avatarUrl: https://avatars.githubusercontent.com/u/55538308?u=f250c44942ea6e73a6bd90739b381c470c192c11&v=4
- url: https://github.com/conservative-dude
- - login: Joaopcamposs
- avatarUrl: https://avatars.githubusercontent.com/u/57376574?u=699d5ba5ee66af1d089df6b5e532b97169e73650&v=4
- url: https://github.com/Joaopcamposs
- - login: browniebroke
- avatarUrl: https://avatars.githubusercontent.com/u/861044?u=5abfca5588f3e906b31583d7ee62f6de4b68aa24&v=4
- url: https://github.com/browniebroke
- login: miguelgr
avatarUrl: https://avatars.githubusercontent.com/u/1484589?u=54556072b8136efa12ae3b6902032ea2a39ace4b&v=4
url: https://github.com/miguelgr
- login: WillHogan
- avatarUrl: https://avatars.githubusercontent.com/u/1661551?u=7036c064cf29781470573865264ec8e60b6b809f&v=4
+ avatarUrl: https://avatars.githubusercontent.com/u/1661551?u=8a80356e3e7d5a417157aba7ea565dabc8678327&v=4
url: https://github.com/WillHogan
- login: my3
avatarUrl: https://avatars.githubusercontent.com/u/1825270?v=4
url: https://github.com/my3
- - login: leobiscassi
- avatarUrl: https://avatars.githubusercontent.com/u/1977418?u=f9f82445a847ab479bd7223debd677fcac6c49a0&v=4
- url: https://github.com/leobiscassi
- - login: cbonoz
- avatarUrl: https://avatars.githubusercontent.com/u/2351087?u=fd3e8030b2cc9fbfbb54a65e9890c548a016f58b&v=4
- url: https://github.com/cbonoz
+ - login: Alisa-lisa
+ avatarUrl: https://avatars.githubusercontent.com/u/4137964?u=e7e393504f554f4ff15863a1e01a5746863ef9ce&v=4
+ url: https://github.com/Alisa-lisa
+ - login: moonape1226
+ avatarUrl: https://avatars.githubusercontent.com/u/8532038?u=d9f8b855a429fff9397c3833c2ff83849ebf989d&v=4
+ url: https://github.com/moonape1226
- login: ddanier
avatarUrl: https://avatars.githubusercontent.com/u/113563?u=ed1dc79de72f93bd78581f88ebc6952b62f472da&v=4
url: https://github.com/ddanier
@@ -419,21 +314,48 @@ sponsors:
- 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
- - login: eteq
- avatarUrl: https://avatars.githubusercontent.com/u/346587?v=4
- url: https://github.com/eteq
- - login: dmig
- avatarUrl: https://avatars.githubusercontent.com/u/388564?v=4
- url: https://github.com/dmig
- - login: securancy
- avatarUrl: https://avatars.githubusercontent.com/u/606673?v=4
- url: https://github.com/securancy
+ - login: ceb10n
+ avatarUrl: https://avatars.githubusercontent.com/u/235213?u=edcce471814a1eba9f0cdaa4cd0de18921a940a6&v=4
+ url: https://github.com/ceb10n
- login: tochikuji
avatarUrl: https://avatars.githubusercontent.com/u/851759?v=4
url: https://github.com/tochikuji
+ - login: xncbf
+ avatarUrl: https://avatars.githubusercontent.com/u/9462045?u=a80a7bb349555b277645632ed66639ff43400614&v=4
+ url: https://github.com/xncbf
+ - login: DMantis
+ avatarUrl: https://avatars.githubusercontent.com/u/9536869?u=652dd0d49717803c0cbcbf44f7740e53cf2d4892&v=4
+ url: https://github.com/DMantis
+ - login: hard-coders
+ avatarUrl: https://avatars.githubusercontent.com/u/9651103?u=95db33927bbff1ed1c07efddeb97ac2ff33068ed&v=4
+ url: https://github.com/hard-coders
+ - login: supdann
+ avatarUrl: https://avatars.githubusercontent.com/u/9986994?u=9671810f4ae9504c063227fee34fd47567ff6954&v=4
+ url: https://github.com/supdann
+ - login: mntolia
+ avatarUrl: https://avatars.githubusercontent.com/u/10390224?v=4
+ url: https://github.com/mntolia
+ - login: Zuzah
+ avatarUrl: https://avatars.githubusercontent.com/u/10934846?u=1ef43e075ddc87bd1178372bf4d95ee6175cae27&v=4
+ url: https://github.com/Zuzah
+ - login: TheR1D
+ avatarUrl: https://avatars.githubusercontent.com/u/16740832?u=b0dfdbdb27b79729430c71c6128962f77b7b53f7&v=4
+ url: https://github.com/TheR1D
+ - login: joshuatz
+ avatarUrl: https://avatars.githubusercontent.com/u/17817563?u=f1bf05b690d1fc164218f0b420cdd3acb7913e21&v=4
+ url: https://github.com/joshuatz
+ - login: danielunderwood
+ avatarUrl: https://avatars.githubusercontent.com/u/4472301?v=4
+ url: https://github.com/danielunderwood
+ - login: rangulvers
+ avatarUrl: https://avatars.githubusercontent.com/u/5235430?u=e254d4af4ace5a05fa58372ae677c7d26f0d5a53&v=4
+ url: https://github.com/rangulvers
+ - login: sdevkota
+ avatarUrl: https://avatars.githubusercontent.com/u/5250987?u=4ed9a120c89805a8aefda1cbdc0cf6512e64d1b4&v=4
+ url: https://github.com/sdevkota
+ - 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
@@ -443,90 +365,48 @@ sponsors:
- login: harsh183
avatarUrl: https://avatars.githubusercontent.com/u/7780198?v=4
url: https://github.com/harsh183
- - login: hcristea
- avatarUrl: https://avatars.githubusercontent.com/u/7814406?u=61d7a4fcf846983a4606788eac25e1c6c1209ba8&v=4
- url: https://github.com/hcristea
- - login: moonape1226
- avatarUrl: https://avatars.githubusercontent.com/u/8532038?u=d9f8b855a429fff9397c3833c2ff83849ebf989d&v=4
- url: https://github.com/moonape1226
- - login: msehnout
- avatarUrl: https://avatars.githubusercontent.com/u/9369632?u=8c988f1b008a3f601385a3616f9327820f66e3a5&v=4
- url: https://github.com/msehnout
- - login: xncbf
- avatarUrl: https://avatars.githubusercontent.com/u/9462045?u=2ef1ede118a72c170805f50b9ad07341fd16a354&v=4
- url: https://github.com/xncbf
- - login: DMantis
- avatarUrl: https://avatars.githubusercontent.com/u/9536869?v=4
- url: https://github.com/DMantis
- - login: hard-coders
- avatarUrl: https://avatars.githubusercontent.com/u/9651103?u=95db33927bbff1ed1c07efddeb97ac2ff33068ed&v=4
- url: https://github.com/hard-coders
- - login: supdann
- avatarUrl: https://avatars.githubusercontent.com/u/9986994?u=9671810f4ae9504c063227fee34fd47567ff6954&v=4
- url: https://github.com/supdann
- - login: satwikkansal
- avatarUrl: https://avatars.githubusercontent.com/u/10217535?u=b12d6ef74ea297de9e46da6933b1a5b7ba9e6a61&v=4
- url: https://github.com/satwikkansal
- - login: mntolia
- avatarUrl: https://avatars.githubusercontent.com/u/10390224?v=4
- url: https://github.com/mntolia
- - login: pheanex
- avatarUrl: https://avatars.githubusercontent.com/u/10408624?u=5b6bab6ee174aa6e991333e06eb29f628741013d&v=4
- url: https://github.com/pheanex
- - login: dzoladz
- avatarUrl: https://avatars.githubusercontent.com/u/10561752?u=5ee314d54aa79592c18566827ad8914debd5630d&v=4
- url: https://github.com/dzoladz
- - login: Zuzah
- avatarUrl: https://avatars.githubusercontent.com/u/10934846?u=1ef43e075ddc87bd1178372bf4d95ee6175cae27&v=4
- url: https://github.com/Zuzah
- - login: Alisa-lisa
- avatarUrl: https://avatars.githubusercontent.com/u/4137964?u=e7e393504f554f4ff15863a1e01a5746863ef9ce&v=4
- url: https://github.com/Alisa-lisa
- - login: Graeme22
- avatarUrl: https://avatars.githubusercontent.com/u/4185684?u=498182a42300d7bcd4de1215190cb17eb501136c&v=4
- url: https://github.com/Graeme22
- - login: danielunderwood
- avatarUrl: https://avatars.githubusercontent.com/u/4472301?v=4
- url: https://github.com/danielunderwood
- - login: rangulvers
- avatarUrl: https://avatars.githubusercontent.com/u/5235430?v=4
- url: https://github.com/rangulvers
- - login: sdevkota
- avatarUrl: https://avatars.githubusercontent.com/u/5250987?u=4ed9a120c89805a8aefda1cbdc0cf6512e64d1b4&v=4
- url: https://github.com/sdevkota
- - login: brizzbuzz
- avatarUrl: https://avatars.githubusercontent.com/u/5607577?u=58d5aae33bc97e52f11f334d2702e8710314b5c1&v=4
- url: https://github.com/brizzbuzz
- - login: Baghdady92
- avatarUrl: https://avatars.githubusercontent.com/u/5708590?v=4
- url: https://github.com/Baghdady92
- - login: jakeecolution
- avatarUrl: https://avatars.githubusercontent.com/u/5884696?u=4a7c7883fb064b593b50cb6697b54687e6f7aafe&v=4
- url: https://github.com/jakeecolution
- - login: stephane-rbn
- avatarUrl: https://avatars.githubusercontent.com/u/5939522?u=eb7ffe768fa3bcbcd04de14fe4a47444cc00ec4c&v=4
- url: https://github.com/stephane-rbn
-- - login: danburonline
- avatarUrl: https://avatars.githubusercontent.com/u/34251194?u=94935cccfbec58083ab1e535212d54f1bf2c978a&v=4
- url: https://github.com/danburonline
- - login: AliYmn
- avatarUrl: https://avatars.githubusercontent.com/u/18416653?u=0de5a262e8b4dc0a08d065f30f7a39941e246530&v=4
- url: https://github.com/AliYmn
- - login: sadikkuzu
- avatarUrl: https://avatars.githubusercontent.com/u/23168063?u=d179c06bb9f65c4167fcab118526819f8e0dac17&v=4
- url: https://github.com/sadikkuzu
- - login: tran-hai-long
- avatarUrl: https://avatars.githubusercontent.com/u/119793901?u=3b173a845dcf099b275bdc9713a69cbbc36040ce&v=4
- url: https://github.com/tran-hai-long
+- - login: KOZ39
+ avatarUrl: https://avatars.githubusercontent.com/u/38822500?u=9dfc0a697df1c9628f08e20dc3fb17b1afc4e5a7&v=4
+ url: https://github.com/KOZ39
- login: rwxd
avatarUrl: https://avatars.githubusercontent.com/u/40308458?u=cd04a39e3655923be4f25c2ba8a5a07b3da3230a&v=4
url: https://github.com/rwxd
- - login: ssbarnea
- avatarUrl: https://avatars.githubusercontent.com/u/102495?u=c2efbf6fea2737e21dfc6b1113c4edc9644e9eaa&v=4
- url: https://github.com/ssbarnea
- - login: yuawn
- avatarUrl: https://avatars.githubusercontent.com/u/5111198?u=5315576f3fe1a70fd2d0f02181588f4eea5d353d&v=4
- url: https://github.com/yuawn
- - login: dongzhenye
- avatarUrl: https://avatars.githubusercontent.com/u/5765843?u=fe420c9a4c41e5b060faaf44029f5485616b470d&v=4
- url: https://github.com/dongzhenye
+ - login: morzan1001
+ avatarUrl: https://avatars.githubusercontent.com/u/47593005?u=c30ab7230f82a12a9b938dcb54f84a996931409a&v=4
+ url: https://github.com/morzan1001
+ - login: azharthegeek
+ avatarUrl: https://avatars.githubusercontent.com/u/51288109?u=0987b2a9f39c21ccb071b6bdce0fc60d8492f8e8&v=4
+ url: https://github.com/azharthegeek
+ - login: Olegt0rr
+ avatarUrl: https://avatars.githubusercontent.com/u/25399456?u=3e87b5239a2f4600975ba13be73054f8567c6060&v=4
+ url: https://github.com/Olegt0rr
+ - login: larsyngvelundin
+ avatarUrl: https://avatars.githubusercontent.com/u/34173819?u=74958599695bf83ac9f1addd935a51548a10c6b0&v=4
+ url: https://github.com/larsyngvelundin
+ - login: andrecorumba
+ avatarUrl: https://avatars.githubusercontent.com/u/37807517?u=9b9be3b41da9bda60957da9ef37b50dbf65baa61&v=4
+ url: https://github.com/andrecorumba
+ - login: ChenPu2002
+ avatarUrl: https://avatars.githubusercontent.com/u/113831763?v=4
+ url: https://github.com/ChenPu2002
+ - login: CoderDeltaLAN
+ avatarUrl: https://avatars.githubusercontent.com/u/152043745?u=4ff541efffb7d134e60c5fcf2dd1e343f90bb782&v=4
+ url: https://github.com/CoderDeltaLAN
+ - login: aghents
+ avatarUrl: https://avatars.githubusercontent.com/u/60949885?u=d8616ddf22cf998a712cdceefd6a0256a178fe9d&v=4
+ url: https://github.com/aghents
+ - login: 0ne-stone
+ avatarUrl: https://avatars.githubusercontent.com/u/62360849?u=746dd21c34e7e06eefb11b03e8bb01aaae3c2a4f&v=4
+ url: https://github.com/0ne-stone
+ - login: nayasinghania
+ avatarUrl: https://avatars.githubusercontent.com/u/74111380?u=752e99a5e139389fdc0a0677122adc08438eb076&v=4
+ url: https://github.com/nayasinghania
+ - login: Toothwitch
+ avatarUrl: https://avatars.githubusercontent.com/u/1710406?u=5eebb23b46cd26e48643b9e5179536cad491c17a&v=4
+ url: https://github.com/Toothwitch
+ - login: andreagrandi
+ avatarUrl: https://avatars.githubusercontent.com/u/636391?u=13d90cb8ec313593a5b71fbd4e33b78d6da736f5&v=4
+ url: https://github.com/andreagrandi
+ - login: msserpa
+ avatarUrl: https://avatars.githubusercontent.com/u/6334934?u=82c4489eb1559d88d2990d60001901b14f722bbb&v=4
+ url: https://github.com/msserpa
diff --git a/docs/en/data/members.yml b/docs/en/data/members.yml
index 0069f8c75c..7ec16e917e 100644
--- a/docs/en/data/members.yml
+++ b/docs/en/data/members.yml
@@ -11,9 +11,12 @@ members:
- login: svlandeg
avatar_url: https://avatars.githubusercontent.com/u/8796347
url: https://github.com/svlandeg
-- login: estebanx64
- avatar_url: https://avatars.githubusercontent.com/u/10840422
- url: https://github.com/estebanx64
+- login: YuriiMotov
+ avatar_url: https://avatars.githubusercontent.com/u/109919500
+ url: https://github.com/YuriiMotov
- login: patrick91
avatar_url: https://avatars.githubusercontent.com/u/667029
url: https://github.com/patrick91
+- login: luzzodev
+ avatar_url: https://avatars.githubusercontent.com/u/27291415
+ url: https://github.com/luzzodev
diff --git a/docs/en/data/people.yml b/docs/en/data/people.yml
index 02d1779e05..2fdb21a059 100644
--- a/docs/en/data/people.yml
+++ b/docs/en/data/people.yml
@@ -1,32 +1,43 @@
maintainers:
- login: tiangolo
- answers: 1885
- prs: 577
- avatarUrl: https://avatars.githubusercontent.com/u/1326112?u=740f11212a731f56798f558ceddb0bd07642afa7&v=4
+ answers: 1900
+ avatarUrl: https://avatars.githubusercontent.com/u/1326112?u=cb5d06e73a9e1998141b1641aa88e443c6717651&v=4
url: https://github.com/tiangolo
experts:
+- login: tiangolo
+ count: 1900
+ avatarUrl: https://avatars.githubusercontent.com/u/1326112?u=cb5d06e73a9e1998141b1641aa88e443c6717651&v=4
+ url: https://github.com/tiangolo
+- login: YuriiMotov
+ count: 971
+ avatarUrl: https://avatars.githubusercontent.com/u/109919500?u=b9b13d598dddfab529a52d264df80a900bfe7060&v=4
+ url: https://github.com/YuriiMotov
+- login: github-actions
+ count: 769
+ avatarUrl: https://avatars.githubusercontent.com/in/15368?v=4
+ url: https://github.com/apps/github-actions
- login: Kludex
- count: 608
- avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=62adc405ef418f4b6c8caa93d3eb8ab107bc4927&v=4
+ count: 654
+ avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=df8a3f06ba8f55ae1967a3e2d5ed882903a4e330&v=4
url: https://github.com/Kludex
-- login: dmontagu
- count: 241
- avatarUrl: https://avatars.githubusercontent.com/u/35119617?u=540f30c937a6450812628b9592a1dfe91bbe148e&v=4
- url: https://github.com/dmontagu
- login: jgould22
- count: 241
+ count: 263
avatarUrl: https://avatars.githubusercontent.com/u/4335847?u=ed77f67e0bb069084639b24d812dbb2a2b1dc554&v=4
url: https://github.com/jgould22
+- login: dmontagu
+ count: 240
+ avatarUrl: https://avatars.githubusercontent.com/u/35119617?u=540f30c937a6450812628b9592a1dfe91bbe148e&v=4
+ url: https://github.com/dmontagu
- login: Mause
- count: 220
+ count: 219
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=29682e4b6ac7d5293742ccf818188394b9a82972&v=4
+ count: 216
+ avatarUrl: https://avatars.githubusercontent.com/u/62724709?u=f1e7bae394a315da950912c92dc861a8eaf95d4c&v=4
url: https://github.com/ycd
- login: JarroVGIT
- count: 193
+ count: 190
avatarUrl: https://avatars.githubusercontent.com/u/13659033?u=e8bea32d07a5ef72f7dde3b2079ceb714923ca05&v=4
url: https://github.com/JarroVGIT
- login: euri10
@@ -35,16 +46,20 @@ experts:
url: https://github.com/euri10
- login: iudeen
count: 128
- avatarUrl: https://avatars.githubusercontent.com/u/10519440?u=2843b3303282bff8b212dcd4d9d6689452e4470c&v=4
+ avatarUrl: https://avatars.githubusercontent.com/u/10519440?u=f09cdd745e5bf16138f29b42732dd57c7f02bee1&v=4
url: https://github.com/iudeen
- login: phy25
count: 126
avatarUrl: https://avatars.githubusercontent.com/u/331403?v=4
url: https://github.com/phy25
-- login: YuriiMotov
- count: 104
- avatarUrl: https://avatars.githubusercontent.com/u/109919500?u=e83a39697a2d33ab2ec9bfbced794ee48bc29cec&v=4
- url: https://github.com/YuriiMotov
+- login: JavierSanchezCastro
+ count: 94
+ avatarUrl: https://avatars.githubusercontent.com/u/72013291?u=ae5679e6bd971d9d98cd5e76e8683f83642ba950&v=4
+ url: https://github.com/JavierSanchezCastro
+- login: luzzodev
+ count: 89
+ avatarUrl: https://avatars.githubusercontent.com/u/27291415?u=5607ae1ce75c5f54f09500ca854227f7bfd2033b&v=4
+ url: https://github.com/luzzodev
- login: raphaelauv
count: 83
avatarUrl: https://avatars.githubusercontent.com/u/10202690?u=e6f86f5c0c3026a15d6b51792fa3e532b12f1371&v=4
@@ -57,42 +72,38 @@ experts:
count: 71
avatarUrl: https://avatars.githubusercontent.com/u/23500353?u=e2e1d736f924d9be81e8bfc565b6d8836ba99773&v=4
url: https://github.com/ghandic
-- login: JavierSanchezCastro
- count: 64
- avatarUrl: https://avatars.githubusercontent.com/u/72013291?u=ae5679e6bd971d9d98cd5e76e8683f83642ba950&v=4
- url: https://github.com/JavierSanchezCastro
+- login: n8sty
+ count: 67
+ avatarUrl: https://avatars.githubusercontent.com/u/2964996?v=4
+ url: https://github.com/n8sty
- login: falkben
count: 59
avatarUrl: https://avatars.githubusercontent.com/u/653031?u=ad9838e089058c9e5a0bab94c0eec7cc181e0cd0&v=4
url: https://github.com/falkben
-- login: n8sty
- count: 56
- avatarUrl: https://avatars.githubusercontent.com/u/2964996?v=4
- url: https://github.com/n8sty
-- login: acidjunk
- count: 50
- avatarUrl: https://avatars.githubusercontent.com/u/685002?u=b5094ab4527fc84b006c0ac9ff54367bdebb2267&v=4
- url: https://github.com/acidjunk
- login: yinziyan1206
- count: 49
+ count: 54
avatarUrl: https://avatars.githubusercontent.com/u/37829370?u=da44ca53aefd5c23f346fab8e9fd2e108294c179&v=4
url: https://github.com/yinziyan1206
- login: sm-Fifteen
count: 49
avatarUrl: https://avatars.githubusercontent.com/u/516999?u=437c0c5038558c67e887ccd863c1ba0f846c03da&v=4
url: https://github.com/sm-Fifteen
-- login: insomnes
- count: 45
- avatarUrl: https://avatars.githubusercontent.com/u/16958893?u=f8be7088d5076d963984a21f95f44e559192d912&v=4
- url: https://github.com/insomnes
+- login: acidjunk
+ count: 49
+ avatarUrl: https://avatars.githubusercontent.com/u/685002?u=b5094ab4527fc84b006c0ac9ff54367bdebb2267&v=4
+ url: https://github.com/acidjunk
+- login: adriangb
+ count: 46
+ avatarUrl: https://avatars.githubusercontent.com/u/1755071?u=612704256e38d6ac9cbed24f10e4b6ac2da74ecb&v=4
+ url: https://github.com/adriangb
- login: Dustyposa
count: 45
avatarUrl: https://avatars.githubusercontent.com/u/27180793?u=5cf2877f50b3eb2bc55086089a78a36f07042889&v=4
url: https://github.com/Dustyposa
-- login: adriangb
+- login: insomnes
count: 45
- avatarUrl: https://avatars.githubusercontent.com/u/1755071?u=612704256e38d6ac9cbed24f10e4b6ac2da74ecb&v=4
- url: https://github.com/adriangb
+ avatarUrl: https://avatars.githubusercontent.com/u/16958893?u=f8be7088d5076d963984a21f95f44e559192d912&v=4
+ url: https://github.com/insomnes
- login: frankie567
count: 43
avatarUrl: https://avatars.githubusercontent.com/u/1144727?u=c159fe047727aedecbbeeaa96a1b03ceb9d39add&v=4
@@ -101,18 +112,22 @@ experts:
count: 43
avatarUrl: https://avatars.githubusercontent.com/u/87550035?u=241a71f6b7068738b81af3e57f45ffd723538401&v=4
url: https://github.com/odiseo0
+- login: sinisaos
+ count: 41
+ avatarUrl: https://avatars.githubusercontent.com/u/30960668?v=4
+ url: https://github.com/sinisaos
- login: includeamin
count: 40
avatarUrl: https://avatars.githubusercontent.com/u/11836741?u=8bd5ef7e62fe6a82055e33c4c0e0a7879ff8cfb6&v=4
url: https://github.com/includeamin
-- login: chbndrhnns
- count: 38
- avatarUrl: https://avatars.githubusercontent.com/u/7534547?v=4
- url: https://github.com/chbndrhnns
- login: STeveShary
count: 37
avatarUrl: https://avatars.githubusercontent.com/u/5167622?u=de8f597c81d6336fcebc37b32dfd61a3f877160c&v=4
url: https://github.com/STeveShary
+- login: chbndrhnns
+ count: 37
+ 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
@@ -123,28 +138,36 @@ experts:
url: https://github.com/panla
- login: prostomarkeloff
count: 28
- avatarUrl: https://avatars.githubusercontent.com/u/28061158?u=72309cc1f2e04e40fa38b29969cb4e9d3f722e7b&v=4
+ avatarUrl: https://avatars.githubusercontent.com/u/28061158?u=6918e39a1224194ba636e897461a02a20126d7ad&v=4
url: https://github.com/prostomarkeloff
- login: hasansezertasan
count: 27
avatarUrl: https://avatars.githubusercontent.com/u/13135006?u=99f0b0f0fc47e88e8abb337b4447357939ef93e7&v=4
url: https://github.com/hasansezertasan
+- login: alv2017
+ count: 26
+ avatarUrl: https://avatars.githubusercontent.com/u/31544722?v=4
+ url: https://github.com/alv2017
- login: dbanty
count: 26
- avatarUrl: https://avatars.githubusercontent.com/u/43723790?u=9bcce836bbce55835291c5b2ac93a4e311f4b3c3&v=4
+ avatarUrl: https://avatars.githubusercontent.com/u/43723790?u=9d726785d08e50b1e1cd96505800c8ea8405bce2&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: acnebs
- count: 23
- avatarUrl: https://avatars.githubusercontent.com/u/9054108?v=4
- url: https://github.com/acnebs
+- login: valentinDruzhinin
+ count: 24
+ avatarUrl: https://avatars.githubusercontent.com/u/12831905?u=aae1ebc675c91e8fa582df4fcc4fc4128106344d&v=4
+ url: https://github.com/valentinDruzhinin
- login: SirTelemak
count: 23
avatarUrl: https://avatars.githubusercontent.com/u/9435877?u=719327b7d2c4c62212456d771bfa7c6b8dbb9eac&v=4
url: https://github.com/SirTelemak
+- login: connebs
+ count: 22
+ avatarUrl: https://avatars.githubusercontent.com/u/9054108?u=e151d5f545a3395136d711c227c22032fda67cfa&v=4
+ url: https://github.com/connebs
- login: nymous
count: 22
avatarUrl: https://avatars.githubusercontent.com/u/4216559?u=360a36fb602cded27273cbfc0afc296eece90662&v=4
@@ -161,14 +184,22 @@ experts:
count: 20
avatarUrl: https://avatars.githubusercontent.com/u/22559461?u=a9cc3238217e21dc8796a1a500f01b722adb082c&v=4
url: https://github.com/nsidnev
-- login: ebottos94
- count: 20
- avatarUrl: https://avatars.githubusercontent.com/u/100039558?u=e2c672da5a7977fd24d87ce6ab35f8bf5b1ed9fa&v=4
- url: https://github.com/ebottos94
- login: chris-allnutt
count: 20
avatarUrl: https://avatars.githubusercontent.com/u/565544?v=4
url: https://github.com/chris-allnutt
+- login: ebottos94
+ count: 20
+ avatarUrl: https://avatars.githubusercontent.com/u/100039558?u=8b91053b3abe4a9209375e3651e1c1ef192d884b&v=4
+ url: https://github.com/ebottos94
+- login: estebanx64
+ count: 19
+ avatarUrl: https://avatars.githubusercontent.com/u/10840422?u=1900887aeed268699e5ea6f3fb7db614f7b77cd3&v=4
+ url: https://github.com/estebanx64
+- login: sehraramiz
+ count: 18
+ avatarUrl: https://avatars.githubusercontent.com/u/14166324?u=8fac65e84dfff24245d304a5b5b09f7b5bd69dc9&v=4
+ url: https://github.com/sehraramiz
- login: retnikt
count: 18
avatarUrl: https://avatars.githubusercontent.com/u/24581770?v=4
@@ -177,10 +208,10 @@ experts:
count: 18
avatarUrl: https://avatars.githubusercontent.com/u/22326718?u=31ba446ac290e23e56eea8e4f0c558aaf0b40779&v=4
url: https://github.com/zoliknemet
-- login: nkhitrov
+- login: caeser1996
count: 17
- avatarUrl: https://avatars.githubusercontent.com/u/28262306?u=66ee21316275ef356081c2efc4ed7a4572e690dc&v=4
- url: https://github.com/nkhitrov
+ avatarUrl: https://avatars.githubusercontent.com/u/16540232?u=05d2beb8e034d584d0a374b99d8826327bd7f614&v=4
+ url: https://github.com/caeser1996
- login: Hultner
count: 17
avatarUrl: https://avatars.githubusercontent.com/u/2669034?u=115e53df959309898ad8dc9443fbb35fee71df07&v=4
@@ -189,1170 +220,483 @@ experts:
count: 17
avatarUrl: https://avatars.githubusercontent.com/u/1765494?u=5b1ab7c582db4b4016fa31affe977d10af108ad4&v=4
url: https://github.com/harunyasar
-- login: caeser1996
+- login: nkhitrov
count: 17
- avatarUrl: https://avatars.githubusercontent.com/u/16540232?u=05d2beb8e034d584d0a374b99d8826327bd7f614&v=4
- url: https://github.com/caeser1996
+ avatarUrl: https://avatars.githubusercontent.com/u/28262306?u=e19427d8dc296d6950e9c424adacc92d37496fe9&v=4
+ url: https://github.com/nkhitrov
- login: dstlny
count: 16
avatarUrl: https://avatars.githubusercontent.com/u/41964673?u=9f2174f9d61c15c6e3a4c9e3aeee66f711ce311f&v=4
url: https://github.com/dstlny
-last_month_experts:
-- login: YuriiMotov
- count: 29
- avatarUrl: https://avatars.githubusercontent.com/u/109919500?u=e83a39697a2d33ab2ec9bfbced794ee48bc29cec&v=4
- url: https://github.com/YuriiMotov
-- login: killjoy1221
- count: 8
- avatarUrl: https://avatars.githubusercontent.com/u/3409962?u=723662989f2027755e67d200137c13c53ae154ac&v=4
- url: https://github.com/killjoy1221
-- login: Kludex
- count: 7
- avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=62adc405ef418f4b6c8caa93d3eb8ab107bc4927&v=4
- url: https://github.com/Kludex
-- login: JavierSanchezCastro
- count: 5
- avatarUrl: https://avatars.githubusercontent.com/u/72013291?u=ae5679e6bd971d9d98cd5e76e8683f83642ba950&v=4
- url: https://github.com/JavierSanchezCastro
-- login: hasansezertasan
- count: 5
- avatarUrl: https://avatars.githubusercontent.com/u/13135006?u=99f0b0f0fc47e88e8abb337b4447357939ef93e7&v=4
- url: https://github.com/hasansezertasan
-- login: PhysicallyActive
- count: 3
- avatarUrl: https://avatars.githubusercontent.com/u/160476156?u=7a8e44f4a43d3bba636f795bb7d9476c9233b4d8&v=4
- url: https://github.com/PhysicallyActive
-- login: n8sty
- count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/2964996?v=4
- url: https://github.com/n8sty
-- login: pedroconceicao
- count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/32837064?u=5a0e6559bc391442629a28b6923790b54deb4464&v=4
- url: https://github.com/pedroconceicao
-- login: PREPONDERANCE
- count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/112809059?u=30ab12dc9ddba2f94ab90e6ad4ad8bc5cfa7fccd&v=4
- url: https://github.com/PREPONDERANCE
-- login: aanchlia
- count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/2835374?u=3c3ed29aa8b09ccaf8d66def0ce82bc2f7e5aab6&v=4
- url: https://github.com/aanchlia
-- login: 0sahil
- count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/58521386?u=ac00b731c07c712d0baa57b8b70ac8422acf183c&v=4
- url: https://github.com/0sahil
-- login: jgould22
- count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/4335847?u=ed77f67e0bb069084639b24d812dbb2a2b1dc554&v=4
- url: https://github.com/jgould22
-three_months_experts:
-- login: YuriiMotov
- count: 101
- avatarUrl: https://avatars.githubusercontent.com/u/109919500?u=e83a39697a2d33ab2ec9bfbced794ee48bc29cec&v=4
- url: https://github.com/YuriiMotov
-- login: JavierSanchezCastro
- count: 20
- avatarUrl: https://avatars.githubusercontent.com/u/72013291?u=ae5679e6bd971d9d98cd5e76e8683f83642ba950&v=4
- url: https://github.com/JavierSanchezCastro
-- login: Kludex
- count: 17
- avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=62adc405ef418f4b6c8caa93d3eb8ab107bc4927&v=4
- url: https://github.com/Kludex
-- login: jgould22
- count: 14
- avatarUrl: https://avatars.githubusercontent.com/u/4335847?u=ed77f67e0bb069084639b24d812dbb2a2b1dc554&v=4
- url: https://github.com/jgould22
-- login: killjoy1221
- count: 8
- avatarUrl: https://avatars.githubusercontent.com/u/3409962?u=723662989f2027755e67d200137c13c53ae154ac&v=4
- url: https://github.com/killjoy1221
-- login: hasansezertasan
- count: 8
- avatarUrl: https://avatars.githubusercontent.com/u/13135006?u=99f0b0f0fc47e88e8abb337b4447357939ef93e7&v=4
- url: https://github.com/hasansezertasan
-- login: PhysicallyActive
- count: 6
- avatarUrl: https://avatars.githubusercontent.com/u/160476156?u=7a8e44f4a43d3bba636f795bb7d9476c9233b4d8&v=4
- url: https://github.com/PhysicallyActive
-- login: n8sty
- count: 5
- avatarUrl: https://avatars.githubusercontent.com/u/2964996?v=4
- url: https://github.com/n8sty
-- login: sehraramiz
- count: 4
- avatarUrl: https://avatars.githubusercontent.com/u/14166324?v=4
- url: https://github.com/sehraramiz
-- login: acidjunk
- count: 4
- avatarUrl: https://avatars.githubusercontent.com/u/685002?u=b5094ab4527fc84b006c0ac9ff54367bdebb2267&v=4
- url: https://github.com/acidjunk
-- login: estebanx64
- count: 3
- avatarUrl: https://avatars.githubusercontent.com/u/10840422?u=45f015f95e1c0f06df602be4ab688d4b854cc8a8&v=4
- url: https://github.com/estebanx64
-- login: PREPONDERANCE
- count: 3
- avatarUrl: https://avatars.githubusercontent.com/u/112809059?u=30ab12dc9ddba2f94ab90e6ad4ad8bc5cfa7fccd&v=4
- url: https://github.com/PREPONDERANCE
-- login: chrisK824
- count: 3
- avatarUrl: https://avatars.githubusercontent.com/u/79946379?u=03d85b22d696a58a9603e55fbbbe2de6b0f4face&v=4
- url: https://github.com/chrisK824
-- login: ryanisn
- count: 3
- avatarUrl: https://avatars.githubusercontent.com/u/53449841?v=4
- url: https://github.com/ryanisn
- login: pythonweb2
- count: 3
- avatarUrl: https://avatars.githubusercontent.com/u/32141163?v=4
- url: https://github.com/pythonweb2
-- login: omarcruzpantoja
- count: 3
- avatarUrl: https://avatars.githubusercontent.com/u/15116058?u=4b64c643fad49225d854e1aaecd1ffc6f9071a1b&v=4
- url: https://github.com/omarcruzpantoja
-- login: mskrip
- count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/17459600?u=10019d5c38ae3374dd4a6743b0223e56a78d4855&v=4
- url: https://github.com/mskrip
-- login: pedroconceicao
- count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/32837064?u=5a0e6559bc391442629a28b6923790b54deb4464&v=4
- url: https://github.com/pedroconceicao
-- login: Jackiexiao
- count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/18050469?u=a2003e21a7780477ba00bf87a9abef8af58e91d1&v=4
- url: https://github.com/Jackiexiao
-- login: aanchlia
- count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/2835374?u=3c3ed29aa8b09ccaf8d66def0ce82bc2f7e5aab6&v=4
- url: https://github.com/aanchlia
-- login: moreno-p
- count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/164261630?v=4
- url: https://github.com/moreno-p
-- login: 0sahil
- count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/58521386?u=ac00b731c07c712d0baa57b8b70ac8422acf183c&v=4
- url: https://github.com/0sahil
-- login: patrick91
- count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/667029?u=e35958a75ac1f99c81b4bc99e22db8cd665ae7f0&v=4
- url: https://github.com/patrick91
-- login: pprunty
- count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/58374462?u=5736576e586429abc97a803b8bcd4a6d828b8a2f&v=4
- url: https://github.com/pprunty
-- login: angely-dev
- count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/4362224?v=4
- url: https://github.com/angely-dev
-- login: mastizada
- count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/1975818?u=0751a06d7271c8bf17cb73b1b845644ab4d2c6dc&v=4
- url: https://github.com/mastizada
-- login: sm-Fifteen
- count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/516999?u=437c0c5038558c67e887ccd863c1ba0f846c03da&v=4
- url: https://github.com/sm-Fifteen
-- login: methane
- count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/199592?v=4
- url: https://github.com/methane
-- login: konstantinos1981
- count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/39465388?v=4
- url: https://github.com/konstantinos1981
-- login: druidance
- count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/160344534?v=4
- url: https://github.com/druidance
-- login: fabianfalon
- count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/3700760?u=95f69e31280b17ac22299cdcd345323b142fe0af&v=4
- url: https://github.com/fabianfalon
-- login: VatsalJagani
- count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/20964366?u=43552644be05c9107c029e26d5ab3be5a1920f45&v=4
- url: https://github.com/VatsalJagani
-- login: khaledadrani
- count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/45245894?u=49ed5056426a149a5af29d385d8bd3847101d3a4&v=4
- url: https://github.com/khaledadrani
-- login: ThirVondukr
- count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/50728601?u=167c0bd655e52817082e50979a86d2f98f95b1a3&v=4
- url: https://github.com/ThirVondukr
-six_months_experts:
-- login: YuriiMotov
- count: 104
- avatarUrl: https://avatars.githubusercontent.com/u/109919500?u=e83a39697a2d33ab2ec9bfbced794ee48bc29cec&v=4
- url: https://github.com/YuriiMotov
-- login: Kludex
- count: 104
- avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=62adc405ef418f4b6c8caa93d3eb8ab107bc4927&v=4
- url: https://github.com/Kludex
-- login: JavierSanchezCastro
- count: 40
- avatarUrl: https://avatars.githubusercontent.com/u/72013291?u=ae5679e6bd971d9d98cd5e76e8683f83642ba950&v=4
- url: https://github.com/JavierSanchezCastro
-- login: jgould22
- count: 40
- avatarUrl: https://avatars.githubusercontent.com/u/4335847?u=ed77f67e0bb069084639b24d812dbb2a2b1dc554&v=4
- url: https://github.com/jgould22
-- login: hasansezertasan
- count: 21
- avatarUrl: https://avatars.githubusercontent.com/u/13135006?u=99f0b0f0fc47e88e8abb337b4447357939ef93e7&v=4
- url: https://github.com/hasansezertasan
-- login: n8sty
- count: 19
- avatarUrl: https://avatars.githubusercontent.com/u/2964996?v=4
- url: https://github.com/n8sty
-- login: killjoy1221
- count: 9
- avatarUrl: https://avatars.githubusercontent.com/u/3409962?u=723662989f2027755e67d200137c13c53ae154ac&v=4
- url: https://github.com/killjoy1221
-- login: aanchlia
- count: 8
- avatarUrl: https://avatars.githubusercontent.com/u/2835374?u=3c3ed29aa8b09ccaf8d66def0ce82bc2f7e5aab6&v=4
- url: https://github.com/aanchlia
-- login: estebanx64
- count: 7
- avatarUrl: https://avatars.githubusercontent.com/u/10840422?u=45f015f95e1c0f06df602be4ab688d4b854cc8a8&v=4
- url: https://github.com/estebanx64
-- login: PhysicallyActive
- count: 6
- avatarUrl: https://avatars.githubusercontent.com/u/160476156?u=7a8e44f4a43d3bba636f795bb7d9476c9233b4d8&v=4
- url: https://github.com/PhysicallyActive
-- login: dolfinus
- count: 6
- avatarUrl: https://avatars.githubusercontent.com/u/4661021?u=a51b39001a2e5e7529b45826980becf786de2327&v=4
- url: https://github.com/dolfinus
-- login: Ventura94
- count: 6
- avatarUrl: https://avatars.githubusercontent.com/u/43103937?u=ccb837005aaf212a449c374618c4339089e2f733&v=4
- url: https://github.com/Ventura94
-- login: sehraramiz
- count: 5
- avatarUrl: https://avatars.githubusercontent.com/u/14166324?v=4
- url: https://github.com/sehraramiz
-- login: acidjunk
- count: 5
- avatarUrl: https://avatars.githubusercontent.com/u/685002?u=b5094ab4527fc84b006c0ac9ff54367bdebb2267&v=4
- url: https://github.com/acidjunk
-- login: shashstormer
- count: 5
- avatarUrl: https://avatars.githubusercontent.com/u/90090313?v=4
- url: https://github.com/shashstormer
-- login: GodMoonGoodman
- count: 4
- avatarUrl: https://avatars.githubusercontent.com/u/29688727?u=7b251da620d999644c37c1feeb292d033eed7ad6&v=4
- url: https://github.com/GodMoonGoodman
-- login: flo-at
- count: 4
- avatarUrl: https://avatars.githubusercontent.com/u/564288?v=4
- url: https://github.com/flo-at
-- login: PREPONDERANCE
- count: 3
- avatarUrl: https://avatars.githubusercontent.com/u/112809059?u=30ab12dc9ddba2f94ab90e6ad4ad8bc5cfa7fccd&v=4
- url: https://github.com/PREPONDERANCE
-- login: chrisK824
- count: 3
- avatarUrl: https://avatars.githubusercontent.com/u/79946379?u=03d85b22d696a58a9603e55fbbbe2de6b0f4face&v=4
- url: https://github.com/chrisK824
-- login: angely-dev
- count: 3
- avatarUrl: https://avatars.githubusercontent.com/u/4362224?v=4
- url: https://github.com/angely-dev
-- login: fmelihh
- count: 3
- avatarUrl: https://avatars.githubusercontent.com/u/99879453?u=671117dba9022db2237e3da7a39cbc2efc838db0&v=4
- url: https://github.com/fmelihh
-- login: ryanisn
- count: 3
- avatarUrl: https://avatars.githubusercontent.com/u/53449841?v=4
- url: https://github.com/ryanisn
-- login: JoshYuJump
- count: 3
- avatarUrl: https://avatars.githubusercontent.com/u/5901894?u=cdbca6296ac4cdcdf6945c112a1ce8d5342839ea&v=4
- url: https://github.com/JoshYuJump
-- login: pythonweb2
- count: 3
- avatarUrl: https://avatars.githubusercontent.com/u/32141163?v=4
- url: https://github.com/pythonweb2
-- login: omarcruzpantoja
- count: 3
- avatarUrl: https://avatars.githubusercontent.com/u/15116058?u=4b64c643fad49225d854e1aaecd1ffc6f9071a1b&v=4
- url: https://github.com/omarcruzpantoja
-- login: bogdan-coman-uv
- count: 3
- avatarUrl: https://avatars.githubusercontent.com/u/92912507?v=4
- url: https://github.com/bogdan-coman-uv
-- login: ahmedabdou14
- count: 3
- avatarUrl: https://avatars.githubusercontent.com/u/104530599?u=05365b155a1ff911532e8be316acfad2e0736f98&v=4
- url: https://github.com/ahmedabdou14
-- login: mskrip
- count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/17459600?u=10019d5c38ae3374dd4a6743b0223e56a78d4855&v=4
- url: https://github.com/mskrip
-- login: leonidktoto
- count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/159561986?v=4
- url: https://github.com/leonidktoto
-- login: pedroconceicao
- count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/32837064?u=5a0e6559bc391442629a28b6923790b54deb4464&v=4
- url: https://github.com/pedroconceicao
-- login: hwong557
- count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/460259?u=7d2f1b33ea5bda4d8e177ab3cb924a673d53087e&v=4
- url: https://github.com/hwong557
-- login: Jackiexiao
- count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/18050469?u=a2003e21a7780477ba00bf87a9abef8af58e91d1&v=4
- url: https://github.com/Jackiexiao
-- login: admo1
- count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/14835916?v=4
- url: https://github.com/admo1
-- login: binbjz
- count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/8213913?u=22b68b7a0d5bf5e09c02084c0f5f53d7503114cd&v=4
- url: https://github.com/binbjz
-- login: nameer
- count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/3931725?u=6199fb065df098fc13ac0a5e649f89672b586732&v=4
- url: https://github.com/nameer
-- login: moreno-p
- count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/164261630?v=4
- url: https://github.com/moreno-p
-- login: 0sahil
- count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/58521386?u=ac00b731c07c712d0baa57b8b70ac8422acf183c&v=4
- url: https://github.com/0sahil
-- login: nymous
- count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/4216559?u=360a36fb602cded27273cbfc0afc296eece90662&v=4
- url: https://github.com/nymous
-- login: patrick91
- count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/667029?u=e35958a75ac1f99c81b4bc99e22db8cd665ae7f0&v=4
- url: https://github.com/patrick91
-- login: pprunty
- count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/58374462?u=5736576e586429abc97a803b8bcd4a6d828b8a2f&v=4
- url: https://github.com/pprunty
-- login: JonnyBootsNpants
- count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/155071540?u=2d3a72b74a2c4c8eaacdb625c7ac850369579352&v=4
- url: https://github.com/JonnyBootsNpants
-- login: richin13
- count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/8370058?u=8e37a4cdbc78983a5f4b4847f6d1879fb39c851c&v=4
- url: https://github.com/richin13
-- login: mastizada
- count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/1975818?u=0751a06d7271c8bf17cb73b1b845644ab4d2c6dc&v=4
- url: https://github.com/mastizada
-- login: sm-Fifteen
- count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/516999?u=437c0c5038558c67e887ccd863c1ba0f846c03da&v=4
- url: https://github.com/sm-Fifteen
-- login: amacfie
- count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/889657?u=d70187989940b085bcbfa3bedad8dbc5f3ab1fe7&v=4
- url: https://github.com/amacfie
-- login: garg10may
- count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/8787120?u=7028d2b3a2a26534c1806eb76c7425a3fac9732f&v=4
- url: https://github.com/garg10may
-- login: methane
- count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/199592?v=4
- url: https://github.com/methane
-- login: konstantinos1981
- count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/39465388?v=4
- url: https://github.com/konstantinos1981
-- login: druidance
- count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/160344534?v=4
- url: https://github.com/druidance
-one_year_experts:
-- login: Kludex
- count: 207
- avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=62adc405ef418f4b6c8caa93d3eb8ab107bc4927&v=4
- url: https://github.com/Kludex
-- login: jgould22
- count: 118
- avatarUrl: https://avatars.githubusercontent.com/u/4335847?u=ed77f67e0bb069084639b24d812dbb2a2b1dc554&v=4
- url: https://github.com/jgould22
-- login: YuriiMotov
- count: 104
- avatarUrl: https://avatars.githubusercontent.com/u/109919500?u=e83a39697a2d33ab2ec9bfbced794ee48bc29cec&v=4
- url: https://github.com/YuriiMotov
-- login: JavierSanchezCastro
- count: 59
- avatarUrl: https://avatars.githubusercontent.com/u/72013291?u=ae5679e6bd971d9d98cd5e76e8683f83642ba950&v=4
- url: https://github.com/JavierSanchezCastro
-- login: n8sty
- count: 40
- avatarUrl: https://avatars.githubusercontent.com/u/2964996?v=4
- url: https://github.com/n8sty
-- login: hasansezertasan
- count: 27
- avatarUrl: https://avatars.githubusercontent.com/u/13135006?u=99f0b0f0fc47e88e8abb337b4447357939ef93e7&v=4
- url: https://github.com/hasansezertasan
-- login: chrisK824
count: 16
- avatarUrl: https://avatars.githubusercontent.com/u/79946379?u=03d85b22d696a58a9603e55fbbbe2de6b0f4face&v=4
- url: https://github.com/chrisK824
-- login: ahmedabdou14
- count: 13
- avatarUrl: https://avatars.githubusercontent.com/u/104530599?u=05365b155a1ff911532e8be316acfad2e0736f98&v=4
- url: https://github.com/ahmedabdou14
-- login: arjwilliams
- count: 12
- avatarUrl: https://avatars.githubusercontent.com/u/22227620?v=4
- url: https://github.com/arjwilliams
-- login: killjoy1221
- count: 10
- avatarUrl: https://avatars.githubusercontent.com/u/3409962?u=723662989f2027755e67d200137c13c53ae154ac&v=4
- url: https://github.com/killjoy1221
-- login: WilliamStam
- count: 10
- avatarUrl: https://avatars.githubusercontent.com/u/182800?v=4
- url: https://github.com/WilliamStam
-- login: iudeen
- count: 10
- avatarUrl: https://avatars.githubusercontent.com/u/10519440?u=2843b3303282bff8b212dcd4d9d6689452e4470c&v=4
- url: https://github.com/iudeen
-- login: nymous
- count: 9
- avatarUrl: https://avatars.githubusercontent.com/u/4216559?u=360a36fb602cded27273cbfc0afc296eece90662&v=4
- url: https://github.com/nymous
-- login: aanchlia
- count: 8
- avatarUrl: https://avatars.githubusercontent.com/u/2835374?u=3c3ed29aa8b09ccaf8d66def0ce82bc2f7e5aab6&v=4
- url: https://github.com/aanchlia
-- login: estebanx64
- count: 7
- avatarUrl: https://avatars.githubusercontent.com/u/10840422?u=45f015f95e1c0f06df602be4ab688d4b854cc8a8&v=4
- url: https://github.com/estebanx64
-- login: pythonweb2
- count: 7
avatarUrl: https://avatars.githubusercontent.com/u/32141163?v=4
url: https://github.com/pythonweb2
-- login: romabozhanovgithub
- count: 6
- avatarUrl: https://avatars.githubusercontent.com/u/67696229?u=e4b921eef096415300425aca249348f8abb78ad7&v=4
- url: https://github.com/romabozhanovgithub
-- login: PhysicallyActive
- count: 6
- avatarUrl: https://avatars.githubusercontent.com/u/160476156?u=7a8e44f4a43d3bba636f795bb7d9476c9233b4d8&v=4
- url: https://github.com/PhysicallyActive
-- login: mikeedjones
- count: 6
- avatarUrl: https://avatars.githubusercontent.com/u/4087139?u=cc4a242896ac2fcf88a53acfaf190d0fe0a1f0c9&v=4
- url: https://github.com/mikeedjones
-- login: dolfinus
- count: 6
- avatarUrl: https://avatars.githubusercontent.com/u/4661021?u=a51b39001a2e5e7529b45826980becf786de2327&v=4
- url: https://github.com/dolfinus
-- login: ebottos94
- count: 6
- avatarUrl: https://avatars.githubusercontent.com/u/100039558?u=e2c672da5a7977fd24d87ce6ab35f8bf5b1ed9fa&v=4
- url: https://github.com/ebottos94
-- login: Ventura94
- count: 6
- avatarUrl: https://avatars.githubusercontent.com/u/43103937?u=ccb837005aaf212a449c374618c4339089e2f733&v=4
- url: https://github.com/Ventura94
-- login: White-Mask
- count: 6
- avatarUrl: https://avatars.githubusercontent.com/u/31826970?u=8625355dc25ddf9c85a8b2b0b9932826c4c8f44c&v=4
- url: https://github.com/White-Mask
-- login: sehraramiz
- count: 5
- avatarUrl: https://avatars.githubusercontent.com/u/14166324?v=4
- url: https://github.com/sehraramiz
-- login: acidjunk
- count: 5
- avatarUrl: https://avatars.githubusercontent.com/u/685002?u=b5094ab4527fc84b006c0ac9ff54367bdebb2267&v=4
- url: https://github.com/acidjunk
-- login: JoshYuJump
- count: 5
- avatarUrl: https://avatars.githubusercontent.com/u/5901894?u=cdbca6296ac4cdcdf6945c112a1ce8d5342839ea&v=4
- url: https://github.com/JoshYuJump
-- login: alex-pobeditel-2004
- count: 5
- avatarUrl: https://avatars.githubusercontent.com/u/14791483?v=4
- url: https://github.com/alex-pobeditel-2004
-- login: shashstormer
- count: 5
- avatarUrl: https://avatars.githubusercontent.com/u/90090313?v=4
- url: https://github.com/shashstormer
-- login: wu-clan
- count: 5
- avatarUrl: https://avatars.githubusercontent.com/u/52145145?u=f8c9e5c8c259d248e1683fedf5027b4ee08a0967&v=4
- url: https://github.com/wu-clan
+- login: jonatasoli
+ count: 16
+ avatarUrl: https://avatars.githubusercontent.com/u/26334101?u=f601c3f111f2148bd9244c2cb3ebbd57b592e674&v=4
+ url: https://github.com/jonatasoli
+- login: ghost
+ count: 15
+ avatarUrl: https://avatars.githubusercontent.com/u/10137?u=b1951d34a583cf12ec0d3b0781ba19be97726318&v=4
+ url: https://github.com/ghost
- login: abhint
- count: 5
+ count: 15
avatarUrl: https://avatars.githubusercontent.com/u/25699289?u=b5d219277b4d001ac26fb8be357fddd88c29d51b&v=4
url: https://github.com/abhint
-- login: anthonycepeda
- count: 4
- avatarUrl: https://avatars.githubusercontent.com/u/72019805?u=60bdf46240cff8fca482ff0fc07d963fd5e1a27c&v=4
- url: https://github.com/anthonycepeda
-- login: GodMoonGoodman
- count: 4
- avatarUrl: https://avatars.githubusercontent.com/u/29688727?u=7b251da620d999644c37c1feeb292d033eed7ad6&v=4
- url: https://github.com/GodMoonGoodman
-- login: flo-at
- count: 4
- avatarUrl: https://avatars.githubusercontent.com/u/564288?v=4
- url: https://github.com/flo-at
+last_month_experts:
+- login: YuriiMotov
+ count: 17
+ avatarUrl: https://avatars.githubusercontent.com/u/109919500?u=b9b13d598dddfab529a52d264df80a900bfe7060&v=4
+ url: https://github.com/YuriiMotov
+- login: valentinDruzhinin
+ count: 5
+ avatarUrl: https://avatars.githubusercontent.com/u/12831905?u=aae1ebc675c91e8fa582df4fcc4fc4128106344d&v=4
+ url: https://github.com/valentinDruzhinin
- login: yinziyan1206
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/37829370?u=da44ca53aefd5c23f346fab8e9fd2e108294c179&v=4
url: https://github.com/yinziyan1206
-- login: amacfie
- count: 4
- avatarUrl: https://avatars.githubusercontent.com/u/889657?u=d70187989940b085bcbfa3bedad8dbc5f3ab1fe7&v=4
- url: https://github.com/amacfie
-- login: commonism
- count: 4
- avatarUrl: https://avatars.githubusercontent.com/u/164513?v=4
- url: https://github.com/commonism
-- login: dmontagu
- count: 4
- avatarUrl: https://avatars.githubusercontent.com/u/35119617?u=540f30c937a6450812628b9592a1dfe91bbe148e&v=4
- url: https://github.com/dmontagu
-- login: sanzoghenzo
- count: 4
- avatarUrl: https://avatars.githubusercontent.com/u/977953?v=4
- url: https://github.com/sanzoghenzo
-- login: lucasgadams
- count: 3
- avatarUrl: https://avatars.githubusercontent.com/u/36425095?v=4
- url: https://github.com/lucasgadams
-- login: NeilBotelho
- count: 3
- avatarUrl: https://avatars.githubusercontent.com/u/39030675?u=16fea2ff90a5c67b974744528a38832a6d1bb4f7&v=4
- url: https://github.com/NeilBotelho
-- login: hhartzer
- count: 3
- avatarUrl: https://avatars.githubusercontent.com/u/100533792?v=4
- url: https://github.com/hhartzer
-- login: binbjz
- count: 3
- avatarUrl: https://avatars.githubusercontent.com/u/8213913?u=22b68b7a0d5bf5e09c02084c0f5f53d7503114cd&v=4
- url: https://github.com/binbjz
-- login: PREPONDERANCE
- count: 3
- avatarUrl: https://avatars.githubusercontent.com/u/112809059?u=30ab12dc9ddba2f94ab90e6ad4ad8bc5cfa7fccd&v=4
- url: https://github.com/PREPONDERANCE
-- login: nameer
- count: 3
- avatarUrl: https://avatars.githubusercontent.com/u/3931725?u=6199fb065df098fc13ac0a5e649f89672b586732&v=4
- url: https://github.com/nameer
-- login: angely-dev
- count: 3
- avatarUrl: https://avatars.githubusercontent.com/u/4362224?v=4
- url: https://github.com/angely-dev
-- login: fmelihh
- count: 3
- avatarUrl: https://avatars.githubusercontent.com/u/99879453?u=671117dba9022db2237e3da7a39cbc2efc838db0&v=4
- url: https://github.com/fmelihh
-- login: ryanisn
- count: 3
- avatarUrl: https://avatars.githubusercontent.com/u/53449841?v=4
- url: https://github.com/ryanisn
-- login: theobouwman
- count: 3
- avatarUrl: https://avatars.githubusercontent.com/u/16098190?u=dc70db88a7a99b764c9a89a6e471e0b7ca478a35&v=4
- url: https://github.com/theobouwman
-- login: methane
- count: 3
- avatarUrl: https://avatars.githubusercontent.com/u/199592?v=4
- url: https://github.com/methane
-top_contributors:
-- login: nilslindemann
- count: 130
- avatarUrl: https://avatars.githubusercontent.com/u/6892179?u=1dca6a22195d6cd1ab20737c0e19a4c55d639472&v=4
- url: https://github.com/nilslindemann
-- login: jaystone776
- count: 49
- avatarUrl: https://avatars.githubusercontent.com/u/11191137?u=299205a95e9b6817a43144a48b643346a5aac5cc&v=4
- url: https://github.com/jaystone776
-- login: waynerv
- count: 25
- avatarUrl: https://avatars.githubusercontent.com/u/39515546?u=ec35139777597cdbbbddda29bf8b9d4396b429a9&v=4
- url: https://github.com/waynerv
-- login: tokusumi
- count: 24
- avatarUrl: https://avatars.githubusercontent.com/u/41147016?u=55010621aece725aa702270b54fed829b6a1fe60&v=4
- url: https://github.com/tokusumi
-- login: SwftAlpc
- count: 23
- avatarUrl: https://avatars.githubusercontent.com/u/52768429?u=6a3aa15277406520ad37f6236e89466ed44bc5b8&v=4
- url: https://github.com/SwftAlpc
-- login: Kludex
- count: 22
- avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=62adc405ef418f4b6c8caa93d3eb8ab107bc4927&v=4
- url: https://github.com/Kludex
-- login: hasansezertasan
- count: 22
- avatarUrl: https://avatars.githubusercontent.com/u/13135006?u=99f0b0f0fc47e88e8abb337b4447357939ef93e7&v=4
- url: https://github.com/hasansezertasan
-- login: dmontagu
- count: 17
- avatarUrl: https://avatars.githubusercontent.com/u/35119617?u=540f30c937a6450812628b9592a1dfe91bbe148e&v=4
- url: https://github.com/dmontagu
-- login: Xewus
- count: 14
- avatarUrl: https://avatars.githubusercontent.com/u/85196001?u=f8e2dc7e5104f109cef944af79050ea8d1b8f914&v=4
- url: https://github.com/Xewus
-- login: euri10
- count: 13
- avatarUrl: https://avatars.githubusercontent.com/u/1104190?u=321a2e953e6645a7d09b732786c7a8061e0f8a8b&v=4
- url: https://github.com/euri10
-- login: mariacamilagl
- count: 12
- avatarUrl: https://avatars.githubusercontent.com/u/11489395?u=4adb6986bf3debfc2b8216ae701f2bd47d73da7d&v=4
- url: https://github.com/mariacamilagl
-- login: AlertRED
- count: 12
- avatarUrl: https://avatars.githubusercontent.com/u/15695000?u=f5a4944c6df443030409c88da7d7fa0b7ead985c&v=4
- url: https://github.com/AlertRED
-- login: Smlep
- count: 11
- avatarUrl: https://avatars.githubusercontent.com/u/16785985?v=4
- url: https://github.com/Smlep
-- login: alejsdev
- count: 11
- avatarUrl: https://avatars.githubusercontent.com/u/90076947?u=9ca449ad5161af12766ddd1a22988e9b14315f5c&v=4
- url: https://github.com/alejsdev
-- login: hard-coders
- count: 10
- avatarUrl: https://avatars.githubusercontent.com/u/9651103?u=95db33927bbff1ed1c07efddeb97ac2ff33068ed&v=4
- url: https://github.com/hard-coders
-- login: KaniKim
- count: 10
- avatarUrl: https://avatars.githubusercontent.com/u/19832624?u=40f8f7f3f36d5f2365ba2ad0b40693e60958ce70&v=4
- url: https://github.com/KaniKim
-- login: xzmeng
- count: 9
- avatarUrl: https://avatars.githubusercontent.com/u/40202897?v=4
- url: https://github.com/xzmeng
-- login: Serrones
- count: 8
- avatarUrl: https://avatars.githubusercontent.com/u/22691749?u=4795b880e13ca33a73e52fc0ef7dc9c60c8fce47&v=4
- url: https://github.com/Serrones
-- login: rjNemo
- count: 8
- avatarUrl: https://avatars.githubusercontent.com/u/56785022?u=d5c3a02567c8649e146fcfc51b6060ccaf8adef8&v=4
- url: https://github.com/rjNemo
-- login: pablocm83
- count: 8
- avatarUrl: https://avatars.githubusercontent.com/u/28315068?u=3310fbb05bb8bfc50d2c48b6cb64ac9ee4a14549&v=4
- url: https://github.com/pablocm83
-- login: RunningIkkyu
- count: 7
- avatarUrl: https://avatars.githubusercontent.com/u/31848542?u=494ecc298e3f26197495bb357ad0f57cfd5f7a32&v=4
- url: https://github.com/RunningIkkyu
-- login: Alexandrhub
- count: 7
- avatarUrl: https://avatars.githubusercontent.com/u/119126536?u=9fc0d48f3307817bafecc5861eb2168401a6cb04&v=4
- url: https://github.com/Alexandrhub
-- login: NinaHwang
- count: 6
- avatarUrl: https://avatars.githubusercontent.com/u/79563565?u=eee6bfe9224c71193025ab7477f4f96ceaa05c62&v=4
- url: https://github.com/NinaHwang
-- 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
- url: https://github.com/wshayes
-- login: samuelcolvin
- count: 5
- avatarUrl: https://avatars.githubusercontent.com/u/4039449?u=42eb3b833047c8c4b4f647a031eaef148c16d93f&v=4
- url: https://github.com/samuelcolvin
-- login: Attsun1031
- count: 5
- avatarUrl: https://avatars.githubusercontent.com/u/1175560?v=4
- url: https://github.com/Attsun1031
-- login: ComicShrimp
- count: 5
- avatarUrl: https://avatars.githubusercontent.com/u/43503750?u=d2fbf412e7730183ce91686ca48d4147e1b7dc74&v=4
- url: https://github.com/ComicShrimp
-- login: rostik1410
- count: 5
- avatarUrl: https://avatars.githubusercontent.com/u/11443899?u=e26a635c2ba220467b308a326a579b8ccf4a8701&v=4
- url: https://github.com/rostik1410
-- login: tamtam-fitness
- count: 5
- avatarUrl: https://avatars.githubusercontent.com/u/62091034?u=8da19a6bd3d02f5d6ba30c7247d5b46c98dd1403&v=4
- url: https://github.com/tamtam-fitness
-- login: jekirl
- count: 4
- avatarUrl: https://avatars.githubusercontent.com/u/2546697?u=a027452387d85bd4a14834e19d716c99255fb3b7&v=4
- url: https://github.com/jekirl
-- login: jfunez
- count: 4
- avatarUrl: https://avatars.githubusercontent.com/u/805749?v=4
- url: https://github.com/jfunez
-- login: ycd
- count: 4
- avatarUrl: https://avatars.githubusercontent.com/u/62724709?u=29682e4b6ac7d5293742ccf818188394b9a82972&v=4
- url: https://github.com/ycd
-- login: komtaki
- count: 4
- avatarUrl: https://avatars.githubusercontent.com/u/39375566?u=260ad6b1a4b34c07dbfa728da5e586f16f6d1824&v=4
- url: https://github.com/komtaki
-- login: hitrust
- count: 4
- avatarUrl: https://avatars.githubusercontent.com/u/3360631?u=5fa1f475ad784d64eb9666bdd43cc4d285dcc773&v=4
- url: https://github.com/hitrust
-- login: JulianMaurin
- count: 4
- avatarUrl: https://avatars.githubusercontent.com/u/63545168?u=b7d15ac865268cbefc2d739e2f23d9aeeac1a622&v=4
- url: https://github.com/JulianMaurin
-- login: lsglucas
- count: 4
- avatarUrl: https://avatars.githubusercontent.com/u/61513630?u=320e43fe4dc7bc6efc64e9b8f325f8075634fd20&v=4
- url: https://github.com/lsglucas
-- login: BilalAlpaslan
- count: 4
- avatarUrl: https://avatars.githubusercontent.com/u/47563997?u=63ed66e304fe8d765762c70587d61d9196e5c82d&v=4
- url: https://github.com/BilalAlpaslan
-- login: adriangb
- count: 4
- avatarUrl: https://avatars.githubusercontent.com/u/1755071?u=612704256e38d6ac9cbed24f10e4b6ac2da74ecb&v=4
- url: https://github.com/adriangb
-- login: iudeen
- count: 4
- avatarUrl: https://avatars.githubusercontent.com/u/10519440?u=2843b3303282bff8b212dcd4d9d6689452e4470c&v=4
- url: https://github.com/iudeen
-- login: axel584
- count: 4
- avatarUrl: https://avatars.githubusercontent.com/u/1334088?u=9667041f5b15dc002b6f9665fda8c0412933ac04&v=4
- url: https://github.com/axel584
-- login: ivan-abc
- count: 4
- avatarUrl: https://avatars.githubusercontent.com/u/36765187?u=c6e0ba571c1ccb6db9d94e62e4b8b5eda811a870&v=4
- url: https://github.com/ivan-abc
-- login: divums
- count: 3
- avatarUrl: https://avatars.githubusercontent.com/u/1397556?v=4
- url: https://github.com/divums
-- login: prostomarkeloff
- count: 3
- avatarUrl: https://avatars.githubusercontent.com/u/28061158?u=72309cc1f2e04e40fa38b29969cb4e9d3f722e7b&v=4
- url: https://github.com/prostomarkeloff
-- login: nsidnev
- count: 3
- avatarUrl: https://avatars.githubusercontent.com/u/22559461?u=a9cc3238217e21dc8796a1a500f01b722adb082c&v=4
- url: https://github.com/nsidnev
-- login: pawamoy
- count: 3
- avatarUrl: https://avatars.githubusercontent.com/u/3999221?u=b030e4c89df2f3a36bc4710b925bdeb6745c9856&v=4
- url: https://github.com/pawamoy
-top_reviewers:
-- login: Kludex
- count: 158
- avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=62adc405ef418f4b6c8caa93d3eb8ab107bc4927&v=4
- url: https://github.com/Kludex
-- login: BilalAlpaslan
- count: 86
- avatarUrl: https://avatars.githubusercontent.com/u/47563997?u=63ed66e304fe8d765762c70587d61d9196e5c82d&v=4
- url: https://github.com/BilalAlpaslan
-- login: yezz123
- count: 85
- avatarUrl: https://avatars.githubusercontent.com/u/52716203?u=d7062cbc6eb7671d5dc9cc0e32a24ae335e0f225&v=4
- url: https://github.com/yezz123
-- login: iudeen
- count: 55
- avatarUrl: https://avatars.githubusercontent.com/u/10519440?u=2843b3303282bff8b212dcd4d9d6689452e4470c&v=4
- url: https://github.com/iudeen
-- login: tokusumi
- count: 51
- avatarUrl: https://avatars.githubusercontent.com/u/41147016?u=55010621aece725aa702270b54fed829b6a1fe60&v=4
- url: https://github.com/tokusumi
-- login: Xewus
- count: 50
- avatarUrl: https://avatars.githubusercontent.com/u/85196001?u=f8e2dc7e5104f109cef944af79050ea8d1b8f914&v=4
- url: https://github.com/Xewus
-- login: hasansezertasan
- count: 50
- avatarUrl: https://avatars.githubusercontent.com/u/13135006?u=99f0b0f0fc47e88e8abb337b4447357939ef93e7&v=4
- url: https://github.com/hasansezertasan
-- login: waynerv
- count: 47
- avatarUrl: https://avatars.githubusercontent.com/u/39515546?u=ec35139777597cdbbbddda29bf8b9d4396b429a9&v=4
- url: https://github.com/waynerv
-- login: Laineyzhang55
- count: 47
- avatarUrl: https://avatars.githubusercontent.com/u/59285379?v=4
- url: https://github.com/Laineyzhang55
-- login: ycd
- count: 45
- avatarUrl: https://avatars.githubusercontent.com/u/62724709?u=29682e4b6ac7d5293742ccf818188394b9a82972&v=4
- url: https://github.com/ycd
-- login: cikay
- count: 41
- avatarUrl: https://avatars.githubusercontent.com/u/24587499?u=e772190a051ab0eaa9c8542fcff1892471638f2b&v=4
- url: https://github.com/cikay
-- login: alejsdev
- count: 38
- avatarUrl: https://avatars.githubusercontent.com/u/90076947?u=9ca449ad5161af12766ddd1a22988e9b14315f5c&v=4
- url: https://github.com/alejsdev
-- login: JarroVGIT
- count: 34
- avatarUrl: https://avatars.githubusercontent.com/u/13659033?u=e8bea32d07a5ef72f7dde3b2079ceb714923ca05&v=4
- url: https://github.com/JarroVGIT
-- login: AdrianDeAnda
- count: 33
- avatarUrl: https://avatars.githubusercontent.com/u/1024932?u=b2ea249c6b41ddf98679c8d110d0f67d4a3ebf93&v=4
- url: https://github.com/AdrianDeAnda
-- login: ArcLightSlavik
- count: 31
- avatarUrl: https://avatars.githubusercontent.com/u/31127044?u=b0f2c37142f4b762e41ad65dc49581813422bd71&v=4
- url: https://github.com/ArcLightSlavik
-- login: cassiobotaro
- count: 28
- avatarUrl: https://avatars.githubusercontent.com/u/3127847?u=a08022b191ddbd0a6159b2981d9d878b6d5bb71f&v=4
- url: https://github.com/cassiobotaro
-- login: lsglucas
- count: 27
- avatarUrl: https://avatars.githubusercontent.com/u/61513630?u=320e43fe4dc7bc6efc64e9b8f325f8075634fd20&v=4
- url: https://github.com/lsglucas
-- login: komtaki
- count: 27
- avatarUrl: https://avatars.githubusercontent.com/u/39375566?u=260ad6b1a4b34c07dbfa728da5e586f16f6d1824&v=4
- url: https://github.com/komtaki
+- login: tiangolo
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/1326112?u=cb5d06e73a9e1998141b1641aa88e443c6717651&v=4
+ url: https://github.com/tiangolo
+- login: luzzodev
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/27291415?u=5607ae1ce75c5f54f09500ca854227f7bfd2033b&v=4
+ url: https://github.com/luzzodev
+three_months_experts:
- login: YuriiMotov
- count: 25
- avatarUrl: https://avatars.githubusercontent.com/u/109919500?u=e83a39697a2d33ab2ec9bfbced794ee48bc29cec&v=4
+ count: 397
+ avatarUrl: https://avatars.githubusercontent.com/u/109919500?u=b9b13d598dddfab529a52d264df80a900bfe7060&v=4
url: https://github.com/YuriiMotov
-- login: Ryandaydev
- count: 25
- avatarUrl: https://avatars.githubusercontent.com/u/4292423?u=48f68868db8886fce31a1d802c1003914c6cd7c6&v=4
- url: https://github.com/Ryandaydev
-- login: LorhanSohaky
+- login: valentinDruzhinin
count: 24
- avatarUrl: https://avatars.githubusercontent.com/u/16273730?u=095b66f243a2cd6a0aadba9a095009f8aaf18393&v=4
- url: https://github.com/LorhanSohaky
-- login: dmontagu
- count: 23
- avatarUrl: https://avatars.githubusercontent.com/u/35119617?u=540f30c937a6450812628b9592a1dfe91bbe148e&v=4
- url: https://github.com/dmontagu
-- login: nilslindemann
- count: 23
- avatarUrl: https://avatars.githubusercontent.com/u/6892179?u=1dca6a22195d6cd1ab20737c0e19a4c55d639472&v=4
- url: https://github.com/nilslindemann
-- login: hard-coders
- count: 23
- avatarUrl: https://avatars.githubusercontent.com/u/9651103?u=95db33927bbff1ed1c07efddeb97ac2ff33068ed&v=4
- url: https://github.com/hard-coders
-- login: rjNemo
- count: 21
- avatarUrl: https://avatars.githubusercontent.com/u/56785022?u=d5c3a02567c8649e146fcfc51b6060ccaf8adef8&v=4
- url: https://github.com/rjNemo
-- login: odiseo0
- count: 20
- avatarUrl: https://avatars.githubusercontent.com/u/87550035?u=241a71f6b7068738b81af3e57f45ffd723538401&v=4
- url: https://github.com/odiseo0
-- login: 0417taehyun
- count: 19
- avatarUrl: https://avatars.githubusercontent.com/u/63915557?u=47debaa860fd52c9b98c97ef357ddcec3b3fb399&v=4
- url: https://github.com/0417taehyun
+ avatarUrl: https://avatars.githubusercontent.com/u/12831905?u=aae1ebc675c91e8fa582df4fcc4fc4128106344d&v=4
+ url: https://github.com/valentinDruzhinin
+- login: luzzodev
+ count: 17
+ avatarUrl: https://avatars.githubusercontent.com/u/27291415?u=5607ae1ce75c5f54f09500ca854227f7bfd2033b&v=4
+ url: https://github.com/luzzodev
+- login: raceychan
+ count: 6
+ avatarUrl: https://avatars.githubusercontent.com/u/75417963?u=060c62870ec5a791765e63ac20d8885d11143786&v=4
+ url: https://github.com/raceychan
+- login: yinziyan1206
+ count: 5
+ avatarUrl: https://avatars.githubusercontent.com/u/37829370?u=da44ca53aefd5c23f346fab8e9fd2e108294c179&v=4
+ url: https://github.com/yinziyan1206
+- login: DoctorJohn
+ count: 5
+ avatarUrl: https://avatars.githubusercontent.com/u/14076775?u=2913e70a6142772847e91e2aaa5b9152391715e9&v=4
+ url: https://github.com/DoctorJohn
+- login: tiangolo
+ count: 4
+ avatarUrl: https://avatars.githubusercontent.com/u/1326112?u=cb5d06e73a9e1998141b1641aa88e443c6717651&v=4
+ url: https://github.com/tiangolo
+- login: sachinh35
+ count: 4
+ avatarUrl: https://avatars.githubusercontent.com/u/21972708?u=8560b97b8b41e175f476270b56de8a493b84f302&v=4
+ url: https://github.com/sachinh35
+- login: eqsdxr
+ count: 4
+ avatarUrl: https://avatars.githubusercontent.com/u/157279130?u=58fddf77ed76966eaa8c73eea9bea4bb0c53b673&v=4
+ url: https://github.com/eqsdxr
+- login: Jelle-tenB
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/210023470?u=c25d66addf36a747bd9fab773c4a6e7b238f45d4&v=4
+ url: https://github.com/Jelle-tenB
+- login: pythonweb2
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/32141163?v=4
+ url: https://github.com/pythonweb2
+- login: WilliamDEdwards
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/12184311?u=9b29d5d1d71f5f1a7ef9e439963ad3529e3b33a4&v=4
+ url: https://github.com/WilliamDEdwards
+- login: Brikas
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/80290187?v=4
+ url: https://github.com/Brikas
+- login: purepani
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/7587353?v=4
+ url: https://github.com/purepani
- login: JavierSanchezCastro
- count: 19
+ count: 2
avatarUrl: https://avatars.githubusercontent.com/u/72013291?u=ae5679e6bd971d9d98cd5e76e8683f83642ba950&v=4
url: https://github.com/JavierSanchezCastro
-- login: Smlep
- count: 17
- avatarUrl: https://avatars.githubusercontent.com/u/16785985?v=4
- url: https://github.com/Smlep
-- login: zy7y
- count: 17
- avatarUrl: https://avatars.githubusercontent.com/u/67154681?u=5d634834cc514028ea3f9115f7030b99a1f4d5a4&v=4
- url: https://github.com/zy7y
-- login: junah201
- count: 17
- avatarUrl: https://avatars.githubusercontent.com/u/75025529?u=2451c256e888fa2a06bcfc0646d09b87ddb6a945&v=4
- url: https://github.com/junah201
-- login: peidrao
- count: 17
- avatarUrl: https://avatars.githubusercontent.com/u/32584628?u=a66902b40c13647d0ed0e573d598128240a4dd04&v=4
- url: https://github.com/peidrao
-- login: yanever
- count: 16
- avatarUrl: https://avatars.githubusercontent.com/u/21978760?v=4
- url: https://github.com/yanever
-- login: SwftAlpc
- count: 16
- avatarUrl: https://avatars.githubusercontent.com/u/52768429?u=6a3aa15277406520ad37f6236e89466ed44bc5b8&v=4
- url: https://github.com/SwftAlpc
-- login: axel584
- count: 16
- avatarUrl: https://avatars.githubusercontent.com/u/1334088?u=9667041f5b15dc002b6f9665fda8c0412933ac04&v=4
- url: https://github.com/axel584
-- login: codespearhead
- count: 16
- avatarUrl: https://avatars.githubusercontent.com/u/72931357?u=0fce6b82219b604d58adb614a761556425579cb5&v=4
- url: https://github.com/codespearhead
-- login: Alexandrhub
- count: 16
- avatarUrl: https://avatars.githubusercontent.com/u/119126536?u=9fc0d48f3307817bafecc5861eb2168401a6cb04&v=4
- url: https://github.com/Alexandrhub
-- login: DevDae
- count: 16
- avatarUrl: https://avatars.githubusercontent.com/u/87962045?u=08e10fa516e844934f4b3fc7c38b33c61697e4a1&v=4
- url: https://github.com/DevDae
-- login: Aruelius
- count: 16
- avatarUrl: https://avatars.githubusercontent.com/u/25380989?u=574f8cfcda3ea77a3f81884f6b26a97068e36a9d&v=4
- url: https://github.com/Aruelius
-- login: OzgunCaglarArslan
- count: 16
- avatarUrl: https://avatars.githubusercontent.com/u/86166426?v=4
- url: https://github.com/OzgunCaglarArslan
-- login: pedabraham
- count: 15
- avatarUrl: https://avatars.githubusercontent.com/u/16860088?u=abf922a7b920bf8fdb7867d8b43e091f1e796178&v=4
- url: https://github.com/pedabraham
-- login: delhi09
- count: 15
- avatarUrl: https://avatars.githubusercontent.com/u/63476957?u=6c86e59b48e0394d4db230f37fc9ad4d7e2c27c7&v=4
- url: https://github.com/delhi09
-- login: wdh99
- count: 14
- avatarUrl: https://avatars.githubusercontent.com/u/108172295?u=8a8fb95d5afe3e0fa33257b2aecae88d436249eb&v=4
- url: https://github.com/wdh99
-- login: sh0nk
- count: 13
- avatarUrl: https://avatars.githubusercontent.com/u/6478810?u=af15d724875cec682ed8088a86d36b2798f981c0&v=4
- url: https://github.com/sh0nk
-- login: r0b2g1t
- count: 13
- avatarUrl: https://avatars.githubusercontent.com/u/5357541?u=6428442d875d5d71aaa1bb38bb11c4be1a526bc2&v=4
- url: https://github.com/r0b2g1t
-- login: RunningIkkyu
- count: 12
- avatarUrl: https://avatars.githubusercontent.com/u/31848542?u=494ecc298e3f26197495bb357ad0f57cfd5f7a32&v=4
- url: https://github.com/RunningIkkyu
-- login: ivan-abc
- count: 12
- avatarUrl: https://avatars.githubusercontent.com/u/36765187?u=c6e0ba571c1ccb6db9d94e62e4b8b5eda811a870&v=4
- url: https://github.com/ivan-abc
-- login: AlertRED
- count: 12
- avatarUrl: https://avatars.githubusercontent.com/u/15695000?u=f5a4944c6df443030409c88da7d7fa0b7ead985c&v=4
- url: https://github.com/AlertRED
-- login: solomein-sv
- count: 11
- avatarUrl: https://avatars.githubusercontent.com/u/46193920?u=789927ee09cfabd752d3bd554fa6baf4850d2777&v=4
- url: https://github.com/solomein-sv
-top_translations_reviewers:
-- login: s111d
- count: 146
- avatarUrl: https://avatars.githubusercontent.com/u/4954856?v=4
- url: https://github.com/s111d
-- login: Xewus
- count: 128
- avatarUrl: https://avatars.githubusercontent.com/u/85196001?u=f8e2dc7e5104f109cef944af79050ea8d1b8f914&v=4
- url: https://github.com/Xewus
-- login: tokusumi
- count: 104
- avatarUrl: https://avatars.githubusercontent.com/u/41147016?u=55010621aece725aa702270b54fed829b6a1fe60&v=4
- url: https://github.com/tokusumi
-- login: hasansezertasan
- count: 91
- avatarUrl: https://avatars.githubusercontent.com/u/13135006?u=99f0b0f0fc47e88e8abb337b4447357939ef93e7&v=4
- url: https://github.com/hasansezertasan
-- login: AlertRED
- count: 70
- avatarUrl: https://avatars.githubusercontent.com/u/15695000?u=f5a4944c6df443030409c88da7d7fa0b7ead985c&v=4
- url: https://github.com/AlertRED
-- login: Alexandrhub
- count: 68
- avatarUrl: https://avatars.githubusercontent.com/u/119126536?u=9fc0d48f3307817bafecc5861eb2168401a6cb04&v=4
- url: https://github.com/Alexandrhub
-- login: waynerv
- count: 63
- avatarUrl: https://avatars.githubusercontent.com/u/39515546?u=ec35139777597cdbbbddda29bf8b9d4396b429a9&v=4
- url: https://github.com/waynerv
-- login: hard-coders
- count: 53
- avatarUrl: https://avatars.githubusercontent.com/u/9651103?u=95db33927bbff1ed1c07efddeb97ac2ff33068ed&v=4
- url: https://github.com/hard-coders
-- login: Laineyzhang55
- count: 48
- avatarUrl: https://avatars.githubusercontent.com/u/59285379?v=4
- url: https://github.com/Laineyzhang55
-- login: Kludex
- count: 46
- avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=62adc405ef418f4b6c8caa93d3eb8ab107bc4927&v=4
- url: https://github.com/Kludex
-- login: komtaki
+- login: TaigoFr
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/17792131?u=372b27056ec82f1ae03d8b3f37ef55b04a7cfdd1&v=4
+ url: https://github.com/TaigoFr
+- login: Garrett-R
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/6614695?u=c128fd775002882f6e391bda5a89d1bdc5bdf45f&v=4
+ url: https://github.com/Garrett-R
+- login: jymchng
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/27895426?u=fb88c47775147d62a395fdb895d1af4148c7b566&v=4
+ url: https://github.com/jymchng
+- login: davidhuser
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/4357648?u=6ed702f8f6d49a8b2a0ed33cbd8ab59c2d7db7f7&v=4
+ url: https://github.com/davidhuser
+six_months_experts:
+- login: YuriiMotov
+ count: 763
+ avatarUrl: https://avatars.githubusercontent.com/u/109919500?u=b9b13d598dddfab529a52d264df80a900bfe7060&v=4
+ url: https://github.com/YuriiMotov
+- login: luzzodev
count: 45
- avatarUrl: https://avatars.githubusercontent.com/u/39375566?u=260ad6b1a4b34c07dbfa728da5e586f16f6d1824&v=4
- url: https://github.com/komtaki
-- login: alperiox
- count: 42
- avatarUrl: https://avatars.githubusercontent.com/u/34214152?u=2c5acad3461d4dbc2d48371ba86cac56ae9b25cc&v=4
- url: https://github.com/alperiox
-- login: Winand
- count: 40
- avatarUrl: https://avatars.githubusercontent.com/u/53390?u=bb0e71a2fc3910a8e0ee66da67c33de40ea695f8&v=4
- url: https://github.com/Winand
-- login: solomein-sv
- count: 38
- avatarUrl: https://avatars.githubusercontent.com/u/46193920?u=789927ee09cfabd752d3bd554fa6baf4850d2777&v=4
- url: https://github.com/solomein-sv
-- login: lsglucas
- count: 36
- avatarUrl: https://avatars.githubusercontent.com/u/61513630?u=320e43fe4dc7bc6efc64e9b8f325f8075634fd20&v=4
- url: https://github.com/lsglucas
-- login: SwftAlpc
- count: 36
- avatarUrl: https://avatars.githubusercontent.com/u/52768429?u=6a3aa15277406520ad37f6236e89466ed44bc5b8&v=4
- url: https://github.com/SwftAlpc
-- login: nilslindemann
- count: 35
- avatarUrl: https://avatars.githubusercontent.com/u/6892179?u=1dca6a22195d6cd1ab20737c0e19a4c55d639472&v=4
- url: https://github.com/nilslindemann
-- login: rjNemo
- count: 34
- avatarUrl: https://avatars.githubusercontent.com/u/56785022?u=d5c3a02567c8649e146fcfc51b6060ccaf8adef8&v=4
- url: https://github.com/rjNemo
-- login: akarev0
- count: 33
- avatarUrl: https://avatars.githubusercontent.com/u/53393089?u=6e528bb4789d56af887ce6fe237bea4010885406&v=4
- url: https://github.com/akarev0
-- login: romashevchenko
- count: 32
- avatarUrl: https://avatars.githubusercontent.com/u/132477732?v=4
- url: https://github.com/romashevchenko
-- login: wdh99
- count: 31
- avatarUrl: https://avatars.githubusercontent.com/u/108172295?u=8a8fb95d5afe3e0fa33257b2aecae88d436249eb&v=4
- url: https://github.com/wdh99
-- login: LorhanSohaky
- count: 30
- avatarUrl: https://avatars.githubusercontent.com/u/16273730?u=095b66f243a2cd6a0aadba9a095009f8aaf18393&v=4
- url: https://github.com/LorhanSohaky
-- login: cassiobotaro
- count: 29
- avatarUrl: https://avatars.githubusercontent.com/u/3127847?u=a08022b191ddbd0a6159b2981d9d878b6d5bb71f&v=4
- url: https://github.com/cassiobotaro
-- login: pedabraham
- count: 28
- avatarUrl: https://avatars.githubusercontent.com/u/16860088?u=abf922a7b920bf8fdb7867d8b43e091f1e796178&v=4
- url: https://github.com/pedabraham
-- login: Smlep
- count: 28
- avatarUrl: https://avatars.githubusercontent.com/u/16785985?v=4
- url: https://github.com/Smlep
-- login: dedkot01
- count: 28
- avatarUrl: https://avatars.githubusercontent.com/u/26196675?u=e2966887124e67932853df4f10f86cb526edc7b0&v=4
- url: https://github.com/dedkot01
-- login: hsuanchi
- count: 28
- avatarUrl: https://avatars.githubusercontent.com/u/24913710?u=0b094ae292292fee093818e37ceb645c114d2bff&v=4
- url: https://github.com/hsuanchi
-- login: dpinezich
- count: 28
- avatarUrl: https://avatars.githubusercontent.com/u/3204540?u=a2e1465e3ee10d537614d513589607eddefde09f&v=4
- url: https://github.com/dpinezich
-- login: maoyibo
- count: 27
- avatarUrl: https://avatars.githubusercontent.com/u/7887703?v=4
- url: https://github.com/maoyibo
-- login: 0417taehyun
- count: 27
- avatarUrl: https://avatars.githubusercontent.com/u/63915557?u=47debaa860fd52c9b98c97ef357ddcec3b3fb399&v=4
- url: https://github.com/0417taehyun
-- login: BilalAlpaslan
- count: 26
- avatarUrl: https://avatars.githubusercontent.com/u/47563997?u=63ed66e304fe8d765762c70587d61d9196e5c82d&v=4
- url: https://github.com/BilalAlpaslan
-- login: zy7y
- count: 25
- avatarUrl: https://avatars.githubusercontent.com/u/67154681?u=5d634834cc514028ea3f9115f7030b99a1f4d5a4&v=4
- url: https://github.com/zy7y
-- login: mycaule
- count: 25
- avatarUrl: https://avatars.githubusercontent.com/u/6161385?u=e3cec75bd6d938a0d73fae0dc5534d1ab2ed1b0e&v=4
- url: https://github.com/mycaule
-- login: sh0nk
- count: 23
- avatarUrl: https://avatars.githubusercontent.com/u/6478810?u=af15d724875cec682ed8088a86d36b2798f981c0&v=4
- url: https://github.com/sh0nk
-- login: axel584
- count: 23
- avatarUrl: https://avatars.githubusercontent.com/u/1334088?u=9667041f5b15dc002b6f9665fda8c0412933ac04&v=4
- url: https://github.com/axel584
-- login: AGolicyn
- count: 21
- avatarUrl: https://avatars.githubusercontent.com/u/86262613?u=3c21606ab8d210a061a1673decff1e7d5592b380&v=4
- url: https://github.com/AGolicyn
-- login: OzgunCaglarArslan
- count: 21
- avatarUrl: https://avatars.githubusercontent.com/u/86166426?v=4
- url: https://github.com/OzgunCaglarArslan
-- login: Attsun1031
- count: 20
- avatarUrl: https://avatars.githubusercontent.com/u/1175560?v=4
- url: https://github.com/Attsun1031
-- login: ycd
- count: 20
- avatarUrl: https://avatars.githubusercontent.com/u/62724709?u=29682e4b6ac7d5293742ccf818188394b9a82972&v=4
- url: https://github.com/ycd
-- login: delhi09
- count: 20
- avatarUrl: https://avatars.githubusercontent.com/u/63476957?u=6c86e59b48e0394d4db230f37fc9ad4d7e2c27c7&v=4
- url: https://github.com/delhi09
-- login: rogerbrinkmann
- count: 20
- avatarUrl: https://avatars.githubusercontent.com/u/5690226?v=4
- url: https://github.com/rogerbrinkmann
-- login: DevDae
- count: 20
- avatarUrl: https://avatars.githubusercontent.com/u/87962045?u=08e10fa516e844934f4b3fc7c38b33c61697e4a1&v=4
- url: https://github.com/DevDae
-- login: sattosan
- count: 19
- avatarUrl: https://avatars.githubusercontent.com/u/20574756?u=b0d8474d2938189c6954423ae8d81d91013f80a8&v=4
- url: https://github.com/sattosan
-- login: ComicShrimp
- count: 18
- avatarUrl: https://avatars.githubusercontent.com/u/43503750?u=d2fbf412e7730183ce91686ca48d4147e1b7dc74&v=4
- url: https://github.com/ComicShrimp
-- login: junah201
- count: 18
- avatarUrl: https://avatars.githubusercontent.com/u/75025529?u=2451c256e888fa2a06bcfc0646d09b87ddb6a945&v=4
- url: https://github.com/junah201
-- login: simatheone
- count: 18
- avatarUrl: https://avatars.githubusercontent.com/u/78508673?u=1b9658d9ee0bde33f56130dd52275493ddd38690&v=4
- url: https://github.com/simatheone
-- login: ivan-abc
- count: 18
- avatarUrl: https://avatars.githubusercontent.com/u/36765187?u=c6e0ba571c1ccb6db9d94e62e4b8b5eda811a870&v=4
- url: https://github.com/ivan-abc
+ avatarUrl: https://avatars.githubusercontent.com/u/27291415?u=5607ae1ce75c5f54f09500ca854227f7bfd2033b&v=4
+ url: https://github.com/luzzodev
+- login: valentinDruzhinin
+ count: 24
+ avatarUrl: https://avatars.githubusercontent.com/u/12831905?u=aae1ebc675c91e8fa582df4fcc4fc4128106344d&v=4
+ url: https://github.com/valentinDruzhinin
+- login: alv2017
+ count: 16
+ avatarUrl: https://avatars.githubusercontent.com/u/31544722?v=4
+ url: https://github.com/alv2017
+- login: sachinh35
+ count: 9
+ avatarUrl: https://avatars.githubusercontent.com/u/21972708?u=8560b97b8b41e175f476270b56de8a493b84f302&v=4
+ url: https://github.com/sachinh35
+- login: yauhen-sobaleu
+ count: 9
+ avatarUrl: https://avatars.githubusercontent.com/u/51629535?u=fc1817060daf2df438bfca86c44f33da5cd667db&v=4
+ url: https://github.com/yauhen-sobaleu
+- login: tiangolo
+ count: 6
+ avatarUrl: https://avatars.githubusercontent.com/u/1326112?u=cb5d06e73a9e1998141b1641aa88e443c6717651&v=4
+ url: https://github.com/tiangolo
- login: JavierSanchezCastro
- count: 18
+ count: 6
avatarUrl: https://avatars.githubusercontent.com/u/72013291?u=ae5679e6bd971d9d98cd5e76e8683f83642ba950&v=4
url: https://github.com/JavierSanchezCastro
-- login: bezaca
+- login: raceychan
+ count: 6
+ avatarUrl: https://avatars.githubusercontent.com/u/75417963?u=060c62870ec5a791765e63ac20d8885d11143786&v=4
+ url: https://github.com/raceychan
+- login: yinziyan1206
+ count: 5
+ avatarUrl: https://avatars.githubusercontent.com/u/37829370?u=da44ca53aefd5c23f346fab8e9fd2e108294c179&v=4
+ url: https://github.com/yinziyan1206
+- login: DoctorJohn
+ count: 5
+ avatarUrl: https://avatars.githubusercontent.com/u/14076775?u=2913e70a6142772847e91e2aaa5b9152391715e9&v=4
+ url: https://github.com/DoctorJohn
+- login: eqsdxr
+ count: 4
+ avatarUrl: https://avatars.githubusercontent.com/u/157279130?u=58fddf77ed76966eaa8c73eea9bea4bb0c53b673&v=4
+ url: https://github.com/eqsdxr
+- login: Kludex
+ count: 4
+ avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=df8a3f06ba8f55ae1967a3e2d5ed882903a4e330&v=4
+ url: https://github.com/Kludex
+- login: Jelle-tenB
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/210023470?u=c25d66addf36a747bd9fab773c4a6e7b238f45d4&v=4
+ url: https://github.com/Jelle-tenB
+- login: adsouza
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/275832?u=f90f110cfafeafed2f14339e840941c2c328c186&v=4
+ url: https://github.com/adsouza
+- login: pythonweb2
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/32141163?v=4
+ url: https://github.com/pythonweb2
+- login: WilliamDEdwards
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/12184311?u=9b29d5d1d71f5f1a7ef9e439963ad3529e3b33a4&v=4
+ url: https://github.com/WilliamDEdwards
+- login: Brikas
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/80290187?v=4
+ url: https://github.com/Brikas
+- login: purepani
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/7587353?v=4
+ url: https://github.com/purepani
+- login: TaigoFr
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/17792131?u=372b27056ec82f1ae03d8b3f37ef55b04a7cfdd1&v=4
+ url: https://github.com/TaigoFr
+- login: Garrett-R
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/6614695?u=c128fd775002882f6e391bda5a89d1bdc5bdf45f&v=4
+ url: https://github.com/Garrett-R
+- login: EverStarck
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/51029456?u=343409b7cb6b3ea6a59359f4e8370d9c3f140ecd&v=4
+ url: https://github.com/EverStarck
+- login: henrymcl
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/26480299?v=4
+ url: https://github.com/henrymcl
+- login: jymchng
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/27895426?u=fb88c47775147d62a395fdb895d1af4148c7b566&v=4
+ url: https://github.com/jymchng
+- login: davidhuser
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/4357648?u=6ed702f8f6d49a8b2a0ed33cbd8ab59c2d7db7f7&v=4
+ url: https://github.com/davidhuser
+- login: PidgeyBE
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/19860056?u=47b584eb1c1ab45e31c1b474109a962d7e82be49&v=4
+ url: https://github.com/PidgeyBE
+- login: KianAnbarestani
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/145364424?u=dcc3d8fb4ca07d36fb52a17f38b6650565de40be&v=4
+ url: https://github.com/KianAnbarestani
+- login: jgould22
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/4335847?u=ed77f67e0bb069084639b24d812dbb2a2b1dc554&v=4
+ url: https://github.com/jgould22
+- login: marsboy02
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/86903678?u=04cc319d6605f8d1ba3a0bed9f4f55a582719ae6&v=4
+ url: https://github.com/marsboy02
+one_year_experts:
+- login: YuriiMotov
+ count: 824
+ avatarUrl: https://avatars.githubusercontent.com/u/109919500?u=b9b13d598dddfab529a52d264df80a900bfe7060&v=4
+ url: https://github.com/YuriiMotov
+- login: luzzodev
+ count: 89
+ avatarUrl: https://avatars.githubusercontent.com/u/27291415?u=5607ae1ce75c5f54f09500ca854227f7bfd2033b&v=4
+ url: https://github.com/luzzodev
+- login: Kludex
+ count: 50
+ avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=df8a3f06ba8f55ae1967a3e2d5ed882903a4e330&v=4
+ url: https://github.com/Kludex
+- login: sinisaos
+ count: 33
+ avatarUrl: https://avatars.githubusercontent.com/u/30960668?v=4
+ url: https://github.com/sinisaos
+- login: alv2017
+ count: 26
+ avatarUrl: https://avatars.githubusercontent.com/u/31544722?v=4
+ url: https://github.com/alv2017
+- login: valentinDruzhinin
+ count: 24
+ avatarUrl: https://avatars.githubusercontent.com/u/12831905?u=aae1ebc675c91e8fa582df4fcc4fc4128106344d&v=4
+ url: https://github.com/valentinDruzhinin
+- login: JavierSanchezCastro
+ count: 24
+ avatarUrl: https://avatars.githubusercontent.com/u/72013291?u=ae5679e6bd971d9d98cd5e76e8683f83642ba950&v=4
+ url: https://github.com/JavierSanchezCastro
+- login: jgould22
count: 17
- avatarUrl: https://avatars.githubusercontent.com/u/69092910?u=4ac58eab99bd37d663f3d23551df96d4fbdbf760&v=4
- url: https://github.com/bezaca
+ avatarUrl: https://avatars.githubusercontent.com/u/4335847?u=ed77f67e0bb069084639b24d812dbb2a2b1dc554&v=4
+ url: https://github.com/jgould22
+- login: tiangolo
+ count: 14
+ avatarUrl: https://avatars.githubusercontent.com/u/1326112?u=cb5d06e73a9e1998141b1641aa88e443c6717651&v=4
+ url: https://github.com/tiangolo
+- login: Kfir-G
+ count: 13
+ avatarUrl: https://avatars.githubusercontent.com/u/57500876?u=a3bf923ab27bce3d1b13779a8dd22eb7675017fd&v=4
+ url: https://github.com/Kfir-G
+- login: sehraramiz
+ count: 11
+ avatarUrl: https://avatars.githubusercontent.com/u/14166324?u=8fac65e84dfff24245d304a5b5b09f7b5bd69dc9&v=4
+ url: https://github.com/sehraramiz
+- login: sachinh35
+ count: 9
+ avatarUrl: https://avatars.githubusercontent.com/u/21972708?u=8560b97b8b41e175f476270b56de8a493b84f302&v=4
+ url: https://github.com/sachinh35
+- login: yauhen-sobaleu
+ count: 9
+ avatarUrl: https://avatars.githubusercontent.com/u/51629535?u=fc1817060daf2df438bfca86c44f33da5cd667db&v=4
+ url: https://github.com/yauhen-sobaleu
+- login: estebanx64
+ count: 7
+ avatarUrl: https://avatars.githubusercontent.com/u/10840422?u=1900887aeed268699e5ea6f3fb7db614f7b77cd3&v=4
+ url: https://github.com/estebanx64
+- login: ceb10n
+ count: 7
+ avatarUrl: https://avatars.githubusercontent.com/u/235213?u=edcce471814a1eba9f0cdaa4cd0de18921a940a6&v=4
+ url: https://github.com/ceb10n
+- login: yvallois
+ count: 7
+ avatarUrl: https://avatars.githubusercontent.com/u/36999744?v=4
+ url: https://github.com/yvallois
+- login: raceychan
+ count: 6
+ avatarUrl: https://avatars.githubusercontent.com/u/75417963?u=060c62870ec5a791765e63ac20d8885d11143786&v=4
+ url: https://github.com/raceychan
+- login: yinziyan1206
+ count: 5
+ avatarUrl: https://avatars.githubusercontent.com/u/37829370?u=da44ca53aefd5c23f346fab8e9fd2e108294c179&v=4
+ url: https://github.com/yinziyan1206
+- login: DoctorJohn
+ count: 5
+ avatarUrl: https://avatars.githubusercontent.com/u/14076775?u=2913e70a6142772847e91e2aaa5b9152391715e9&v=4
+ url: https://github.com/DoctorJohn
+- login: n8sty
+ count: 5
+ avatarUrl: https://avatars.githubusercontent.com/u/2964996?v=4
+ url: https://github.com/n8sty
+- login: pythonweb2
+ count: 4
+ avatarUrl: https://avatars.githubusercontent.com/u/32141163?v=4
+ url: https://github.com/pythonweb2
+- login: eqsdxr
+ count: 4
+ avatarUrl: https://avatars.githubusercontent.com/u/157279130?u=58fddf77ed76966eaa8c73eea9bea4bb0c53b673&v=4
+ url: https://github.com/eqsdxr
+- login: yokwejuste
+ count: 4
+ avatarUrl: https://avatars.githubusercontent.com/u/71908316?u=4ba43bd63c169b5c015137d8916752a44001445a&v=4
+ url: https://github.com/yokwejuste
+- login: WilliamDEdwards
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/12184311?u=9b29d5d1d71f5f1a7ef9e439963ad3529e3b33a4&v=4
+ url: https://github.com/WilliamDEdwards
+- login: mattmess1221
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/3409962?u=d22ea18aa8ea688af25a45df306134d593621a44&v=4
+ url: https://github.com/mattmess1221
+- login: Jelle-tenB
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/210023470?u=c25d66addf36a747bd9fab773c4a6e7b238f45d4&v=4
+ url: https://github.com/Jelle-tenB
+- login: viniciusCalcantara
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/108818737?u=80f3ec7427fa6a41d5896984d0c526432f2299fa&v=4
+ url: https://github.com/viniciusCalcantara
+- login: davidhuser
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/4357648?u=6ed702f8f6d49a8b2a0ed33cbd8ab59c2d7db7f7&v=4
+ url: https://github.com/davidhuser
+- login: dbfreem
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/9778569?u=f2f1e9135b5e4f1b0c6821a548b17f97572720fc&v=4
+ url: https://github.com/dbfreem
+- login: SobikXexe
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/87701130?v=4
+ url: https://github.com/SobikXexe
+- login: pawelad
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/7062874?u=d27dc220545a8401ad21840590a97d474d7101e6&v=4
+ url: https://github.com/pawelad
+- login: Isuxiz
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/48672727?u=34d7b4ade252687d22a27cf53037b735b244bfc1&v=4
+ url: https://github.com/Isuxiz
+- login: Minibrams
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/8108085?u=b028dbc308fa8485e0e2e9402b3d03d8deb22bf9&v=4
+ url: https://github.com/Minibrams
+- login: adsouza
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/275832?u=f90f110cfafeafed2f14339e840941c2c328c186&v=4
+ url: https://github.com/adsouza
+- login: Synrom
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/30272537?v=4
+ url: https://github.com/Synrom
+- login: gaby
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/835733?u=8c72dec16fa560bdc81113354f2ffd79ad062bde&v=4
+ url: https://github.com/gaby
+- login: Ale-Cas
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/64859146?u=d52a6ecf8d83d2927e2ae270bdfcc83495dba8c9&v=4
+ url: https://github.com/Ale-Cas
+- login: CharlesPerrotMinotHCHB
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/112571330?u=e3a666718ff5ad1d1c49d6c31358a9f80c841b30&v=4
+ url: https://github.com/CharlesPerrotMinotHCHB
+- login: yanggeorge
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/2434407?v=4
+ url: https://github.com/yanggeorge
+- login: Brikas
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/80290187?v=4
+ url: https://github.com/Brikas
+- login: dolfinus
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/4661021?u=ed5ddadcf36d9b943ebe61febe0b96ee34e5425d&v=4
+ url: https://github.com/dolfinus
+- login: slafs
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/210173?v=4
+ url: https://github.com/slafs
+- login: purepani
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/7587353?v=4
+ url: https://github.com/purepani
+- login: ddahan
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/1933516?u=1d200a620e8d6841df017e9f2bb7efb58b580f40&v=4
+ url: https://github.com/ddahan
+- login: TaigoFr
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/17792131?u=372b27056ec82f1ae03d8b3f37ef55b04a7cfdd1&v=4
+ url: https://github.com/TaigoFr
+- login: Garrett-R
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/6614695?u=c128fd775002882f6e391bda5a89d1bdc5bdf45f&v=4
+ url: https://github.com/Garrett-R
+- login: jd-solanki
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/47495003?u=6e225cb42c688d0cd70e65c6baedb9f5922b1178&v=4
+ url: https://github.com/jd-solanki
+- login: EverStarck
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/51029456?u=343409b7cb6b3ea6a59359f4e8370d9c3f140ecd&v=4
+ url: https://github.com/EverStarck
+- login: henrymcl
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/26480299?v=4
+ url: https://github.com/henrymcl
+- login: jymchng
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/27895426?u=fb88c47775147d62a395fdb895d1af4148c7b566&v=4
+ url: https://github.com/jymchng
+- login: christiansicari
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/29756552?v=4
+ url: https://github.com/christiansicari
+- login: JacobHayes
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/2555532?u=354a525847a276bbb4426b0c95791a8ba5970f9b&v=4
+ url: https://github.com/JacobHayes
+- login: iloveitaly
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/150855?v=4
+ url: https://github.com/iloveitaly
+- login: iiotsrc
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/131771119?u=bcaf2559ef6266af70b151b7fda31a1ee3dbecb3&v=4
+ url: https://github.com/iiotsrc
+- login: PidgeyBE
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/19860056?u=47b584eb1c1ab45e31c1b474109a962d7e82be49&v=4
+ url: https://github.com/PidgeyBE
+- login: KianAnbarestani
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/145364424?u=dcc3d8fb4ca07d36fb52a17f38b6650565de40be&v=4
+ url: https://github.com/KianAnbarestani
+- login: ykaiqx
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/56395004?u=1eebf5ce25a8067f7bfa6251a24f667be492d9d6&v=4
+ url: https://github.com/ykaiqx
+- login: AliYmn
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/18416653?u=a77e2605e3ce6aaf6fef8ad4a7b0d32954fba47a&v=4
+ url: https://github.com/AliYmn
+- login: gelezo43
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/40732698?u=611f39d3c1d2f4207a590937a78c1f10eed6232c&v=4
+ url: https://github.com/gelezo43
+- login: jfeaver
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/1091338?u=0bcba366447d8fadad63f6705a52d128da4c7ec2&v=4
+ url: https://github.com/jfeaver
diff --git a/docs/en/data/skip_users.yml b/docs/en/data/skip_users.yml
new file mode 100644
index 0000000000..cf24003af8
--- /dev/null
+++ b/docs/en/data/skip_users.yml
@@ -0,0 +1,5 @@
+- tiangolo
+- codecov
+- github-actions
+- pre-commit-ci
+- dependabot
diff --git a/docs/en/data/sponsors.yml b/docs/en/data/sponsors.yml
index 8c0956ac52..7a015e4048 100644
--- a/docs/en/data/sponsors.yml
+++ b/docs/en/data/sponsors.yml
@@ -1,63 +1,63 @@
gold:
- - url: https://cryptapi.io/
- title: "CryptAPI: Your easy to use, secure and privacy oriented payment gateway."
- img: https://fastapi.tiangolo.com/img/sponsors/cryptapi.svg
- - url: https://platform.sh/try-it-now/?utm_source=fastapi-signup&utm_medium=banner&utm_campaign=FastAPI-signup-June-2023
- title: "Build, run and scale your apps on a modern, reliable, and secure PaaS."
- img: https://fastapi.tiangolo.com/img/sponsors/platform-sh.png
- - url: https://www.porter.run
- title: Deploy FastAPI on AWS with a few clicks
- img: https://fastapi.tiangolo.com/img/sponsors/porter.png
- - url: https://bump.sh/fastapi?utm_source=fastapi&utm_medium=referral&utm_campaign=sponsor
- title: Automate FastAPI documentation generation with Bump.sh
- img: https://fastapi.tiangolo.com/img/sponsors/bump-sh.svg
+ - url: https://blockbee.io?ref=fastapi
+ title: BlockBee Cryptocurrency Payment Gateway
+ img: https://fastapi.tiangolo.com/img/sponsors/blockbee.png
- url: https://github.com/scalar/scalar/?utm_source=fastapi&utm_medium=website&utm_campaign=main-badge
title: "Scalar: Beautiful Open-Source API References from Swagger/OpenAPI files"
img: https://fastapi.tiangolo.com/img/sponsors/scalar.svg
- url: https://www.propelauth.com/?utm_source=fastapi&utm_campaign=1223&utm_medium=mainbadge
title: Auth, user management and more for your B2B product
img: https://fastapi.tiangolo.com/img/sponsors/propelauth.png
- - url: https://docs.withcoherence.com/configuration/frameworks/?utm_medium=advertising&utm_source=fastapi&utm_campaign=docs#fastapi-example
- title: Coherence
- img: https://fastapi.tiangolo.com/img/sponsors/coherence.png
- - url: https://www.mongodb.com/developer/languages/python/python-quickstart-fastapi/?utm_campaign=fastapi_framework&utm_source=fastapi_sponsorship&utm_medium=web_referral
- title: Simplify Full Stack Development with FastAPI & MongoDB
- img: https://fastapi.tiangolo.com/img/sponsors/mongodb.png
- - url: https://konghq.com/products/kong-konnect?utm_medium=referral&utm_source=github&utm_campaign=platform&utm_content=fast-api
- title: Kong Konnect - API management platform
- img: https://fastapi.tiangolo.com/img/sponsors/kong.png
- url: https://zuplo.link/fastapi-gh
- title: 'Zuplo: Scale, Protect, Document, and Monetize your FastAPI'
+ title: 'Zuplo: Deploy, Secure, Document, and Monetize your FastAPI'
img: https://fastapi.tiangolo.com/img/sponsors/zuplo.png
- - url: https://fine.dev?ref=fastapibadge
- title: "Fine's AI FastAPI Workflow: Effortlessly Deploy and Integrate FastAPI into Your Project"
- img: https://fastapi.tiangolo.com/img/sponsors/fine.png
- url: https://liblab.com?utm_source=fastapi
title: liblab - Generate SDKs from FastAPI
img: https://fastapi.tiangolo.com/img/sponsors/liblab.png
+ - url: https://docs.render.com/deploy-fastapi?utm_source=deploydoc&utm_medium=referral&utm_campaign=fastapi
+ title: Deploy & scale any full-stack web app on Render. Focus on building apps, not infra.
+ img: https://fastapi.tiangolo.com/img/sponsors/render.svg
+ - url: https://www.coderabbit.ai/?utm_source=fastapi&utm_medium=badge&utm_campaign=fastapi
+ title: Cut Code Review Time & Bugs in Half with CodeRabbit
+ img: https://fastapi.tiangolo.com/img/sponsors/coderabbit.png
+ - url: https://subtotal.com/?utm_source=fastapi&utm_medium=sponsorship&utm_campaign=open-source
+ title: The Gold Standard in Retail Account Linking
+ img: https://fastapi.tiangolo.com/img/sponsors/subtotal.svg
+ - url: https://docs.railway.com/guides/fastapi?utm_medium=integration&utm_source=docs&utm_campaign=fastapi
+ title: Deploy enterprise applications at startup speed
+ img: https://fastapi.tiangolo.com/img/sponsors/railway.png
silver:
- - url: https://github.com/deepset-ai/haystack/
- title: Build powerful search from composable, open source building blocks
- img: https://fastapi.tiangolo.com/img/sponsors/haystack-fastapi.svg
- - url: https://databento.com/
+ - url: https://databento.com/?utm_source=fastapi&utm_medium=sponsor&utm_content=display
title: Pay as you go for market data
img: https://fastapi.tiangolo.com/img/sponsors/databento.svg
- - url: https://speakeasy.com?utm_source=fastapi+repo&utm_medium=github+sponsorship
+ - url: https://speakeasy.com/editor?utm_source=fastapi+repo&utm_medium=github+sponsorship
title: SDKs for your API | Speakeasy
img: https://fastapi.tiangolo.com/img/sponsors/speakeasy.png
- url: https://www.svix.com/
title: Svix - Webhooks as a service
img: https://fastapi.tiangolo.com/img/sponsors/svix.svg
- - url: https://www.codacy.com/?utm_source=github&utm_medium=sponsors&utm_id=pioneers
- title: Take code reviews from hours to minutes
- img: https://fastapi.tiangolo.com/img/sponsors/codacy.png
- url: https://www.stainlessapi.com/?utm_source=fastapi&utm_medium=referral
title: Stainless | Generate best-in-class SDKs
img: https://fastapi.tiangolo.com/img/sponsors/stainless.png
+ - url: https://www.permit.io/blog/implement-authorization-in-fastapi?utm_source=github&utm_medium=referral&utm_campaign=fastapi
+ title: Fine-Grained Authorization for FastAPI
+ img: https://fastapi.tiangolo.com/img/sponsors/permit.png
+ - url: https://www.interviewpal.com/?utm_source=fastapi&utm_medium=open-source&utm_campaign=dev-hiring
+ title: InterviewPal - AI Interview Coach for Engineers and Devs
+ img: https://fastapi.tiangolo.com/img/sponsors/interviewpal.png
+ - url: https://dribia.com/en/
+ title: Dribia - Data Science within your reach
+ img: https://fastapi.tiangolo.com/img/sponsors/dribia.png
bronze:
- url: https://www.exoflare.com/open-source/?utm_source=FastAPI&utm_campaign=open_source
title: Biosecurity risk assessments made easy.
img: https://fastapi.tiangolo.com/img/sponsors/exoflare.png
- - url: https://testdriven.io/courses/tdd-fastapi/
- title: Learn to build high-quality web apps with best practices
- img: https://fastapi.tiangolo.com/img/sponsors/testdriven.svg
+ # - url: https://testdriven.io/courses/tdd-fastapi/
+ # title: Learn to build high-quality web apps with best practices
+ # img: https://fastapi.tiangolo.com/img/sponsors/testdriven.svg
+ - url: https://lambdatest.com/?utm_source=fastapi&utm_medium=partner&utm_campaign=sponsor&utm_term=opensource&utm_content=webpage
+ title: LambdaTest, AI-Powered Cloud-based Test Orchestration Platform
+ img: https://fastapi.tiangolo.com/img/sponsors/lambdatest.png
+ - url: https://requestly.com/fastapi
+ title: All-in-one platform to Test, Mock and Intercept APIs. Built for speed, privacy and offline support.
+ img: https://fastapi.tiangolo.com/img/sponsors/requestly.png
diff --git a/docs/en/data/sponsors_badge.yml b/docs/en/data/sponsors_badge.yml
index d8a41fbcb7..14f55805c6 100644
--- a/docs/en/data/sponsors_badge.yml
+++ b/docs/en/data/sponsors_badge.yml
@@ -29,4 +29,21 @@ logins:
- andrew-propelauth
- svix
- zuplo-oss
+ - zuplo
- Kong
+ - speakeasy-api
+ - jess-render
+ - blockbee-io
+ - liblaber
+ - render-sponsorships
+ - renderinc
+ - stainless-api
+ - snapit-cypher
+ - coderabbitai
+ - permitio
+ - LambdaTest-Inc
+ - dribia
+ - madisonredtfeldt
+ - railwayapp
+ - subtotal
+ - requestly
diff --git a/docs/en/data/topic_repos.yml b/docs/en/data/topic_repos.yml
new file mode 100644
index 0000000000..9d95fb8b1f
--- /dev/null
+++ b/docs/en/data/topic_repos.yml
@@ -0,0 +1,495 @@
+- name: full-stack-fastapi-template
+ html_url: https://github.com/fastapi/full-stack-fastapi-template
+ stars: 38085
+ owner_login: fastapi
+ owner_html_url: https://github.com/fastapi
+- name: Hello-Python
+ html_url: https://github.com/mouredev/Hello-Python
+ stars: 32243
+ owner_login: mouredev
+ owner_html_url: https://github.com/mouredev
+- name: serve
+ html_url: https://github.com/jina-ai/serve
+ stars: 21754
+ owner_login: jina-ai
+ owner_html_url: https://github.com/jina-ai
+- name: HivisionIDPhotos
+ html_url: https://github.com/Zeyi-Lin/HivisionIDPhotos
+ stars: 19400
+ owner_login: Zeyi-Lin
+ owner_html_url: https://github.com/Zeyi-Lin
+- name: sqlmodel
+ html_url: https://github.com/fastapi/sqlmodel
+ stars: 16859
+ owner_login: fastapi
+ owner_html_url: https://github.com/fastapi
+- name: Douyin_TikTok_Download_API
+ html_url: https://github.com/Evil0ctal/Douyin_TikTok_Download_API
+ stars: 14452
+ owner_login: Evil0ctal
+ owner_html_url: https://github.com/Evil0ctal
+- name: fastapi-best-practices
+ html_url: https://github.com/zhanymkanov/fastapi-best-practices
+ stars: 13613
+ owner_login: zhanymkanov
+ owner_html_url: https://github.com/zhanymkanov
+- name: fastapi_mcp
+ html_url: https://github.com/tadata-org/fastapi_mcp
+ stars: 10624
+ owner_login: tadata-org
+ owner_html_url: https://github.com/tadata-org
+- name: awesome-fastapi
+ html_url: https://github.com/mjhea0/awesome-fastapi
+ stars: 10415
+ owner_login: mjhea0
+ owner_html_url: https://github.com/mjhea0
+- name: FastUI
+ html_url: https://github.com/pydantic/FastUI
+ stars: 8879
+ owner_login: pydantic
+ owner_html_url: https://github.com/pydantic
+- name: XHS-Downloader
+ html_url: https://github.com/JoeanAmier/XHS-Downloader
+ stars: 8824
+ owner_login: JoeanAmier
+ owner_html_url: https://github.com/JoeanAmier
+- name: SurfSense
+ html_url: https://github.com/MODSetter/SurfSense
+ stars: 8257
+ owner_login: MODSetter
+ owner_html_url: https://github.com/MODSetter
+- name: FileCodeBox
+ html_url: https://github.com/vastsa/FileCodeBox
+ stars: 7367
+ owner_login: vastsa
+ owner_html_url: https://github.com/vastsa
+- name: polar
+ html_url: https://github.com/polarsource/polar
+ stars: 7291
+ owner_login: polarsource
+ owner_html_url: https://github.com/polarsource
+- name: nonebot2
+ html_url: https://github.com/nonebot/nonebot2
+ stars: 7065
+ owner_login: nonebot
+ owner_html_url: https://github.com/nonebot
+- name: hatchet
+ html_url: https://github.com/hatchet-dev/hatchet
+ stars: 6070
+ owner_login: hatchet-dev
+ owner_html_url: https://github.com/hatchet-dev
+- name: serge
+ html_url: https://github.com/serge-chat/serge
+ stars: 5754
+ owner_login: serge-chat
+ owner_html_url: https://github.com/serge-chat
+- name: fastapi-users
+ html_url: https://github.com/fastapi-users/fastapi-users
+ stars: 5599
+ owner_login: fastapi-users
+ owner_html_url: https://github.com/fastapi-users
+- name: strawberry
+ html_url: https://github.com/strawberry-graphql/strawberry
+ stars: 4422
+ owner_login: strawberry-graphql
+ owner_html_url: https://github.com/strawberry-graphql
+- name: chatgpt-web-share
+ html_url: https://github.com/chatpire/chatgpt-web-share
+ stars: 4301
+ owner_login: chatpire
+ owner_html_url: https://github.com/chatpire
+- name: poem
+ html_url: https://github.com/poem-web/poem
+ stars: 4197
+ owner_login: poem-web
+ owner_html_url: https://github.com/poem-web
+- name: dynaconf
+ html_url: https://github.com/dynaconf/dynaconf
+ stars: 4144
+ owner_login: dynaconf
+ owner_html_url: https://github.com/dynaconf
+- name: atrilabs-engine
+ html_url: https://github.com/Atri-Labs/atrilabs-engine
+ stars: 4094
+ owner_login: Atri-Labs
+ owner_html_url: https://github.com/Atri-Labs
+- name: Kokoro-FastAPI
+ html_url: https://github.com/remsky/Kokoro-FastAPI
+ stars: 3739
+ owner_login: remsky
+ owner_html_url: https://github.com/remsky
+- name: logfire
+ html_url: https://github.com/pydantic/logfire
+ stars: 3614
+ owner_login: pydantic
+ owner_html_url: https://github.com/pydantic
+- name: LitServe
+ html_url: https://github.com/Lightning-AI/LitServe
+ stars: 3578
+ owner_login: Lightning-AI
+ owner_html_url: https://github.com/Lightning-AI
+- name: datamodel-code-generator
+ html_url: https://github.com/koxudaxi/datamodel-code-generator
+ stars: 3496
+ owner_login: koxudaxi
+ owner_html_url: https://github.com/koxudaxi
+- name: farfalle
+ html_url: https://github.com/rashadphz/farfalle
+ stars: 3459
+ owner_login: rashadphz
+ owner_html_url: https://github.com/rashadphz
+- name: fastapi-admin
+ html_url: https://github.com/fastapi-admin/fastapi-admin
+ stars: 3456
+ owner_login: fastapi-admin
+ owner_html_url: https://github.com/fastapi-admin
+- name: huma
+ html_url: https://github.com/danielgtaylor/huma
+ stars: 3447
+ owner_login: danielgtaylor
+ owner_html_url: https://github.com/danielgtaylor
+- name: tracecat
+ html_url: https://github.com/TracecatHQ/tracecat
+ stars: 3254
+ owner_login: TracecatHQ
+ owner_html_url: https://github.com/TracecatHQ
+- name: opyrator
+ html_url: https://github.com/ml-tooling/opyrator
+ stars: 3134
+ owner_login: ml-tooling
+ owner_html_url: https://github.com/ml-tooling
+- name: docarray
+ html_url: https://github.com/docarray/docarray
+ stars: 3107
+ owner_login: docarray
+ owner_html_url: https://github.com/docarray
+- name: fastapi-realworld-example-app
+ html_url: https://github.com/nsidnev/fastapi-realworld-example-app
+ stars: 2936
+ owner_login: nsidnev
+ owner_html_url: https://github.com/nsidnev
+- name: uvicorn-gunicorn-fastapi-docker
+ html_url: https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker
+ stars: 2804
+ owner_login: tiangolo
+ owner_html_url: https://github.com/tiangolo
+- name: best-of-web-python
+ html_url: https://github.com/ml-tooling/best-of-web-python
+ stars: 2610
+ owner_login: ml-tooling
+ owner_html_url: https://github.com/ml-tooling
+- name: mcp-context-forge
+ html_url: https://github.com/IBM/mcp-context-forge
+ stars: 2572
+ owner_login: IBM
+ owner_html_url: https://github.com/IBM
+- name: fastapi-react
+ html_url: https://github.com/Buuntu/fastapi-react
+ stars: 2451
+ owner_login: Buuntu
+ owner_html_url: https://github.com/Buuntu
+- name: RasaGPT
+ html_url: https://github.com/paulpierre/RasaGPT
+ stars: 2441
+ owner_login: paulpierre
+ owner_html_url: https://github.com/paulpierre
+- name: FastAPI-template
+ html_url: https://github.com/s3rius/FastAPI-template
+ stars: 2424
+ owner_login: s3rius
+ owner_html_url: https://github.com/s3rius
+- name: sqladmin
+ html_url: https://github.com/aminalaee/sqladmin
+ stars: 2357
+ owner_login: aminalaee
+ owner_html_url: https://github.com/aminalaee
+- name: nextpy
+ html_url: https://github.com/dot-agent/nextpy
+ stars: 2324
+ owner_login: dot-agent
+ owner_html_url: https://github.com/dot-agent
+- name: supabase-py
+ html_url: https://github.com/supabase/supabase-py
+ stars: 2236
+ owner_login: supabase
+ owner_html_url: https://github.com/supabase
+- name: 30-Days-of-Python
+ html_url: https://github.com/codingforentrepreneurs/30-Days-of-Python
+ stars: 2210
+ owner_login: codingforentrepreneurs
+ owner_html_url: https://github.com/codingforentrepreneurs
+- name: langserve
+ html_url: https://github.com/langchain-ai/langserve
+ stars: 2171
+ owner_login: langchain-ai
+ owner_html_url: https://github.com/langchain-ai
+- name: fastapi-utils
+ html_url: https://github.com/fastapiutils/fastapi-utils
+ stars: 2164
+ owner_login: fastapiutils
+ owner_html_url: https://github.com/fastapiutils
+- name: solara
+ html_url: https://github.com/widgetti/solara
+ stars: 2102
+ owner_login: widgetti
+ owner_html_url: https://github.com/widgetti
+- name: Yuxi-Know
+ html_url: https://github.com/xerrors/Yuxi-Know
+ stars: 1995
+ owner_login: xerrors
+ owner_html_url: https://github.com/xerrors
+- name: mangum
+ html_url: https://github.com/Kludex/mangum
+ stars: 1989
+ owner_login: Kludex
+ owner_html_url: https://github.com/Kludex
+- name: python-week-2022
+ html_url: https://github.com/rochacbruno/python-week-2022
+ stars: 1816
+ owner_login: rochacbruno
+ owner_html_url: https://github.com/rochacbruno
+- name: agentkit
+ html_url: https://github.com/BCG-X-Official/agentkit
+ stars: 1789
+ owner_login: BCG-X-Official
+ owner_html_url: https://github.com/BCG-X-Official
+- name: manage-fastapi
+ html_url: https://github.com/ycd/manage-fastapi
+ stars: 1780
+ owner_login: ycd
+ owner_html_url: https://github.com/ycd
+- name: ormar
+ html_url: https://github.com/collerek/ormar
+ stars: 1777
+ owner_login: collerek
+ owner_html_url: https://github.com/collerek
+- name: openapi-python-client
+ html_url: https://github.com/openapi-generators/openapi-python-client
+ stars: 1707
+ owner_login: openapi-generators
+ owner_html_url: https://github.com/openapi-generators
+- name: piccolo
+ html_url: https://github.com/piccolo-orm/piccolo
+ stars: 1695
+ owner_login: piccolo-orm
+ owner_html_url: https://github.com/piccolo-orm
+- name: vue-fastapi-admin
+ html_url: https://github.com/mizhexiaoxiao/vue-fastapi-admin
+ stars: 1695
+ owner_login: mizhexiaoxiao
+ owner_html_url: https://github.com/mizhexiaoxiao
+- name: fastapi-cache
+ html_url: https://github.com/long2ice/fastapi-cache
+ stars: 1653
+ owner_login: long2ice
+ owner_html_url: https://github.com/long2ice
+- name: langchain-serve
+ html_url: https://github.com/jina-ai/langchain-serve
+ stars: 1635
+ owner_login: jina-ai
+ owner_html_url: https://github.com/jina-ai
+- name: termpair
+ html_url: https://github.com/cs01/termpair
+ stars: 1624
+ owner_login: cs01
+ owner_html_url: https://github.com/cs01
+- name: slowapi
+ html_url: https://github.com/laurentS/slowapi
+ stars: 1620
+ owner_login: laurentS
+ owner_html_url: https://github.com/laurentS
+- name: coronavirus-tracker-api
+ html_url: https://github.com/ExpDev07/coronavirus-tracker-api
+ stars: 1576
+ owner_login: ExpDev07
+ owner_html_url: https://github.com/ExpDev07
+- name: fastapi-crudrouter
+ html_url: https://github.com/awtkns/fastapi-crudrouter
+ stars: 1546
+ owner_login: awtkns
+ owner_html_url: https://github.com/awtkns
+- name: FastAPI-boilerplate
+ html_url: https://github.com/benavlabs/FastAPI-boilerplate
+ stars: 1516
+ owner_login: benavlabs
+ owner_html_url: https://github.com/benavlabs
+- name: awesome-fastapi-projects
+ html_url: https://github.com/Kludex/awesome-fastapi-projects
+ stars: 1481
+ owner_login: Kludex
+ owner_html_url: https://github.com/Kludex
+- name: fastapi-pagination
+ html_url: https://github.com/uriyyo/fastapi-pagination
+ stars: 1453
+ owner_login: uriyyo
+ owner_html_url: https://github.com/uriyyo
+- name: bracket
+ html_url: https://github.com/evroon/bracket
+ stars: 1415
+ owner_login: evroon
+ owner_html_url: https://github.com/evroon
+- name: awesome-python-resources
+ html_url: https://github.com/DjangoEx/awesome-python-resources
+ stars: 1413
+ owner_login: DjangoEx
+ owner_html_url: https://github.com/DjangoEx
+- name: fastapi-boilerplate
+ html_url: https://github.com/teamhide/fastapi-boilerplate
+ stars: 1406
+ owner_login: teamhide
+ owner_html_url: https://github.com/teamhide
+- name: budgetml
+ html_url: https://github.com/ebhy/budgetml
+ stars: 1346
+ owner_login: ebhy
+ owner_html_url: https://github.com/ebhy
+- name: fastapi-amis-admin
+ html_url: https://github.com/amisadmin/fastapi-amis-admin
+ stars: 1342
+ owner_login: amisadmin
+ owner_html_url: https://github.com/amisadmin
+- name: fastapi-langgraph-agent-production-ready-template
+ html_url: https://github.com/wassim249/fastapi-langgraph-agent-production-ready-template
+ stars: 1334
+ owner_login: wassim249
+ owner_html_url: https://github.com/wassim249
+- name: fastapi-tutorial
+ html_url: https://github.com/liaogx/fastapi-tutorial
+ stars: 1303
+ owner_login: liaogx
+ owner_html_url: https://github.com/liaogx
+- name: fastapi_best_architecture
+ html_url: https://github.com/fastapi-practices/fastapi_best_architecture
+ stars: 1276
+ owner_login: fastapi-practices
+ owner_html_url: https://github.com/fastapi-practices
+- name: fastcrud
+ html_url: https://github.com/benavlabs/fastcrud
+ stars: 1272
+ owner_login: benavlabs
+ owner_html_url: https://github.com/benavlabs
+- name: fastapi-code-generator
+ html_url: https://github.com/koxudaxi/fastapi-code-generator
+ stars: 1253
+ owner_login: koxudaxi
+ owner_html_url: https://github.com/koxudaxi
+- name: prometheus-fastapi-instrumentator
+ html_url: https://github.com/trallnag/prometheus-fastapi-instrumentator
+ stars: 1246
+ owner_login: trallnag
+ owner_html_url: https://github.com/trallnag
+- name: bolt-python
+ html_url: https://github.com/slackapi/bolt-python
+ stars: 1221
+ owner_login: slackapi
+ owner_html_url: https://github.com/slackapi
+- name: bedrock-chat
+ html_url: https://github.com/aws-samples/bedrock-chat
+ stars: 1220
+ owner_login: aws-samples
+ owner_html_url: https://github.com/aws-samples
+- name: fastapi_production_template
+ html_url: https://github.com/zhanymkanov/fastapi_production_template
+ stars: 1202
+ owner_login: zhanymkanov
+ owner_html_url: https://github.com/zhanymkanov
+- name: fastapi-scaff
+ html_url: https://github.com/atpuxiner/fastapi-scaff
+ stars: 1193
+ owner_login: atpuxiner
+ owner_html_url: https://github.com/atpuxiner
+- name: langchain-extract
+ html_url: https://github.com/langchain-ai/langchain-extract
+ stars: 1164
+ owner_login: langchain-ai
+ owner_html_url: https://github.com/langchain-ai
+- name: fastapi-alembic-sqlmodel-async
+ html_url: https://github.com/jonra1993/fastapi-alembic-sqlmodel-async
+ stars: 1149
+ owner_login: jonra1993
+ owner_html_url: https://github.com/jonra1993
+- name: odmantic
+ html_url: https://github.com/art049/odmantic
+ stars: 1133
+ owner_login: art049
+ owner_html_url: https://github.com/art049
+- name: restish
+ html_url: https://github.com/rest-sh/restish
+ stars: 1122
+ owner_login: rest-sh
+ owner_html_url: https://github.com/rest-sh
+- name: runhouse
+ html_url: https://github.com/run-house/runhouse
+ stars: 1047
+ owner_login: run-house
+ owner_html_url: https://github.com/run-house
+- name: flock
+ html_url: https://github.com/Onelevenvy/flock
+ stars: 1027
+ owner_login: Onelevenvy
+ owner_html_url: https://github.com/Onelevenvy
+- name: authx
+ html_url: https://github.com/yezz123/authx
+ stars: 999
+ owner_login: yezz123
+ owner_html_url: https://github.com/yezz123
+- name: autollm
+ html_url: https://github.com/viddexa/autollm
+ stars: 999
+ owner_login: viddexa
+ owner_html_url: https://github.com/viddexa
+- name: lanarky
+ html_url: https://github.com/ajndkr/lanarky
+ stars: 995
+ owner_login: ajndkr
+ owner_html_url: https://github.com/ajndkr
+- name: titiler
+ html_url: https://github.com/developmentseed/titiler
+ stars: 952
+ owner_login: developmentseed
+ owner_html_url: https://github.com/developmentseed
+- name: energy-forecasting
+ html_url: https://github.com/iusztinpaul/energy-forecasting
+ stars: 946
+ owner_login: iusztinpaul
+ owner_html_url: https://github.com/iusztinpaul
+- name: secure
+ html_url: https://github.com/TypeError/secure
+ stars: 944
+ owner_login: TypeError
+ owner_html_url: https://github.com/TypeError
+- name: langcorn
+ html_url: https://github.com/msoedov/langcorn
+ stars: 934
+ owner_login: msoedov
+ owner_html_url: https://github.com/msoedov
+- name: RuoYi-Vue3-FastAPI
+ html_url: https://github.com/insistence/RuoYi-Vue3-FastAPI
+ stars: 930
+ owner_login: insistence
+ owner_html_url: https://github.com/insistence
+- name: aktools
+ html_url: https://github.com/akfamily/aktools
+ stars: 916
+ owner_login: akfamily
+ owner_html_url: https://github.com/akfamily
+- name: every-pdf
+ html_url: https://github.com/DDULDDUCK/every-pdf
+ stars: 907
+ owner_login: DDULDDUCK
+ owner_html_url: https://github.com/DDULDDUCK
+- name: marker-api
+ html_url: https://github.com/adithya-s-k/marker-api
+ stars: 903
+ owner_login: adithya-s-k
+ owner_html_url: https://github.com/adithya-s-k
+- name: fastapi-observability
+ html_url: https://github.com/blueswen/fastapi-observability
+ stars: 902
+ owner_login: blueswen
+ owner_html_url: https://github.com/blueswen
+- name: fastapi-do-zero
+ html_url: https://github.com/dunossauro/fastapi-do-zero
+ stars: 900
+ owner_login: dunossauro
+ owner_html_url: https://github.com/dunossauro
diff --git a/docs/en/data/translation_reviewers.yml b/docs/en/data/translation_reviewers.yml
new file mode 100644
index 0000000000..68ef01f6d5
--- /dev/null
+++ b/docs/en/data/translation_reviewers.yml
@@ -0,0 +1,1850 @@
+s111d:
+ login: s111d
+ count: 147
+ avatarUrl: https://avatars.githubusercontent.com/u/4954856?v=4
+ url: https://github.com/s111d
+Xewus:
+ login: Xewus
+ count: 140
+ avatarUrl: https://avatars.githubusercontent.com/u/85196001?u=f8e2dc7e5104f109cef944af79050ea8d1b8f914&v=4
+ url: https://github.com/Xewus
+sodaMelon:
+ login: sodaMelon
+ count: 127
+ avatarUrl: https://avatars.githubusercontent.com/u/66295123?u=be939db90f1119efee9e6110cc05066ff1f40f00&v=4
+ url: https://github.com/sodaMelon
+ceb10n:
+ login: ceb10n
+ count: 116
+ avatarUrl: https://avatars.githubusercontent.com/u/235213?u=edcce471814a1eba9f0cdaa4cd0de18921a940a6&v=4
+ url: https://github.com/ceb10n
+tokusumi:
+ login: tokusumi
+ count: 104
+ avatarUrl: https://avatars.githubusercontent.com/u/41147016?u=55010621aece725aa702270b54fed829b6a1fe60&v=4
+ url: https://github.com/tokusumi
+hasansezertasan:
+ login: hasansezertasan
+ count: 95
+ avatarUrl: https://avatars.githubusercontent.com/u/13135006?u=99f0b0f0fc47e88e8abb337b4447357939ef93e7&v=4
+ url: https://github.com/hasansezertasan
+hard-coders:
+ login: hard-coders
+ count: 93
+ avatarUrl: https://avatars.githubusercontent.com/u/9651103?u=95db33927bbff1ed1c07efddeb97ac2ff33068ed&v=4
+ url: https://github.com/hard-coders
+alv2017:
+ login: alv2017
+ count: 88
+ avatarUrl: https://avatars.githubusercontent.com/u/31544722?v=4
+ url: https://github.com/alv2017
+nazarepiedady:
+ login: nazarepiedady
+ count: 86
+ avatarUrl: https://avatars.githubusercontent.com/u/31008635?u=f69ddc4ea8bda3bdfac7aa0e2ea38de282e6ee2d&v=4
+ url: https://github.com/nazarepiedady
+AlertRED:
+ login: AlertRED
+ count: 81
+ avatarUrl: https://avatars.githubusercontent.com/u/15695000?u=f5a4944c6df443030409c88da7d7fa0b7ead985c&v=4
+ url: https://github.com/AlertRED
+Alexandrhub:
+ login: Alexandrhub
+ count: 68
+ avatarUrl: https://avatars.githubusercontent.com/u/119126536?u=9fc0d48f3307817bafecc5861eb2168401a6cb04&v=4
+ url: https://github.com/Alexandrhub
+waynerv:
+ login: waynerv
+ count: 63
+ avatarUrl: https://avatars.githubusercontent.com/u/39515546?u=ec35139777597cdbbbddda29bf8b9d4396b429a9&v=4
+ url: https://github.com/waynerv
+cassiobotaro:
+ login: cassiobotaro
+ count: 62
+ avatarUrl: https://avatars.githubusercontent.com/u/3127847?u=a08022b191ddbd0a6159b2981d9d878b6d5bb71f&v=4
+ url: https://github.com/cassiobotaro
+nilslindemann:
+ login: nilslindemann
+ count: 59
+ avatarUrl: https://avatars.githubusercontent.com/u/6892179?u=1dca6a22195d6cd1ab20737c0e19a4c55d639472&v=4
+ url: https://github.com/nilslindemann
+mattwang44:
+ login: mattwang44
+ count: 59
+ avatarUrl: https://avatars.githubusercontent.com/u/24987826?u=58e37fb3927b9124b458945ac4c97aa0f1062d85&v=4
+ url: https://github.com/mattwang44
+tiangolo:
+ login: tiangolo
+ count: 55
+ avatarUrl: https://avatars.githubusercontent.com/u/1326112?u=cb5d06e73a9e1998141b1641aa88e443c6717651&v=4
+ url: https://github.com/tiangolo
+Laineyzhang55:
+ login: Laineyzhang55
+ count: 48
+ avatarUrl: https://avatars.githubusercontent.com/u/59285379?v=4
+ url: https://github.com/Laineyzhang55
+Kludex:
+ login: Kludex
+ count: 47
+ avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=df8a3f06ba8f55ae1967a3e2d5ed882903a4e330&v=4
+ url: https://github.com/Kludex
+komtaki:
+ login: komtaki
+ count: 45
+ avatarUrl: https://avatars.githubusercontent.com/u/39375566?u=260ad6b1a4b34c07dbfa728da5e586f16f6d1824&v=4
+ url: https://github.com/komtaki
+rostik1410:
+ login: rostik1410
+ count: 42
+ avatarUrl: https://avatars.githubusercontent.com/u/11443899?u=e26a635c2ba220467b308a326a579b8ccf4a8701&v=4
+ url: https://github.com/rostik1410
+svlandeg:
+ login: svlandeg
+ count: 42
+ avatarUrl: https://avatars.githubusercontent.com/u/8796347?u=556c97650c27021911b0b9447ec55e75987b0e8a&v=4
+ url: https://github.com/svlandeg
+alperiox:
+ login: alperiox
+ count: 42
+ avatarUrl: https://avatars.githubusercontent.com/u/34214152?u=2c5acad3461d4dbc2d48371ba86cac56ae9b25cc&v=4
+ url: https://github.com/alperiox
+Rishat-F:
+ login: Rishat-F
+ count: 42
+ avatarUrl: https://avatars.githubusercontent.com/u/66554797?v=4
+ url: https://github.com/Rishat-F
+Winand:
+ login: Winand
+ count: 40
+ avatarUrl: https://avatars.githubusercontent.com/u/53390?u=bb0e71a2fc3910a8e0ee66da67c33de40ea695f8&v=4
+ url: https://github.com/Winand
+YuriiMotov:
+ login: YuriiMotov
+ count: 40
+ avatarUrl: https://avatars.githubusercontent.com/u/109919500?u=b9b13d598dddfab529a52d264df80a900bfe7060&v=4
+ url: https://github.com/YuriiMotov
+solomein-sv:
+ login: solomein-sv
+ count: 38
+ avatarUrl: https://avatars.githubusercontent.com/u/46193920?u=789927ee09cfabd752d3bd554fa6baf4850d2777&v=4
+ url: https://github.com/solomein-sv
+JavierSanchezCastro:
+ login: JavierSanchezCastro
+ count: 38
+ avatarUrl: https://avatars.githubusercontent.com/u/72013291?u=ae5679e6bd971d9d98cd5e76e8683f83642ba950&v=4
+ url: https://github.com/JavierSanchezCastro
+alejsdev:
+ login: alejsdev
+ count: 37
+ avatarUrl: https://avatars.githubusercontent.com/u/90076947?u=447d12a1b347f466b35378bee4c7104cc9b2c571&v=4
+ url: https://github.com/alejsdev
+stlucasgarcia:
+ login: stlucasgarcia
+ count: 36
+ avatarUrl: https://avatars.githubusercontent.com/u/61513630?u=c22d8850e9dc396a8820766a59837f967e14f9a0&v=4
+ url: https://github.com/stlucasgarcia
+SwftAlpc:
+ login: SwftAlpc
+ count: 36
+ avatarUrl: https://avatars.githubusercontent.com/u/52768429?u=6a3aa15277406520ad37f6236e89466ed44bc5b8&v=4
+ url: https://github.com/SwftAlpc
+timothy-jeong:
+ login: timothy-jeong
+ count: 36
+ avatarUrl: https://avatars.githubusercontent.com/u/53824764?u=db3d0cea2f5fab64d810113c5039a369699a2774&v=4
+ url: https://github.com/timothy-jeong
+mezgoodle:
+ login: mezgoodle
+ count: 35
+ avatarUrl: https://avatars.githubusercontent.com/u/41520940?u=4a9c765af688389d54296845d18b8f6cd6ddf09a&v=4
+ url: https://github.com/mezgoodle
+rjNemo:
+ login: rjNemo
+ count: 34
+ avatarUrl: https://avatars.githubusercontent.com/u/56785022?u=d5c3a02567c8649e146fcfc51b6060ccaf8adef8&v=4
+ url: https://github.com/rjNemo
+codingjenny:
+ login: codingjenny
+ count: 34
+ avatarUrl: https://avatars.githubusercontent.com/u/103817302?u=3a042740dc0ff58615da0d8679230966fd7693e8&v=4
+ url: https://github.com/codingjenny
+akarev0:
+ login: akarev0
+ count: 33
+ avatarUrl: https://avatars.githubusercontent.com/u/53393089?u=6e528bb4789d56af887ce6fe237bea4010885406&v=4
+ url: https://github.com/akarev0
+romashevchenko:
+ login: romashevchenko
+ count: 32
+ avatarUrl: https://avatars.githubusercontent.com/u/132477732?v=4
+ url: https://github.com/romashevchenko
+LorhanSohaky:
+ login: LorhanSohaky
+ count: 30
+ avatarUrl: https://avatars.githubusercontent.com/u/16273730?u=095b66f243a2cd6a0aadba9a095009f8aaf18393&v=4
+ url: https://github.com/LorhanSohaky
+Vincy1230:
+ login: Vincy1230
+ count: 30
+ avatarUrl: https://avatars.githubusercontent.com/u/81342412?u=ab5e256a4077a4a91f3f9cd2115ba80780454cbe&v=4
+ url: https://github.com/Vincy1230
+black-redoc:
+ login: black-redoc
+ count: 29
+ avatarUrl: https://avatars.githubusercontent.com/u/18581590?u=7b6336166d0797fbbd44ea70c1c3ecadfc89af9e&v=4
+ url: https://github.com/black-redoc
+pedabraham:
+ login: pedabraham
+ count: 28
+ avatarUrl: https://avatars.githubusercontent.com/u/16860088?u=abf922a7b920bf8fdb7867d8b43e091f1e796178&v=4
+ url: https://github.com/pedabraham
+Smlep:
+ login: Smlep
+ count: 28
+ avatarUrl: https://avatars.githubusercontent.com/u/16785985?u=ffe99fa954c8e774ef1117e58d34aece92051e27&v=4
+ url: https://github.com/Smlep
+dedkot01:
+ login: dedkot01
+ count: 28
+ avatarUrl: https://avatars.githubusercontent.com/u/26196675?u=e2966887124e67932853df4f10f86cb526edc7b0&v=4
+ url: https://github.com/dedkot01
+hsuanchi:
+ login: hsuanchi
+ count: 28
+ avatarUrl: https://avatars.githubusercontent.com/u/24913710?u=7d25a398e478b6e63503bf6f26c54efa9e0da07b&v=4
+ url: https://github.com/hsuanchi
+dpinezich:
+ login: dpinezich
+ count: 28
+ avatarUrl: https://avatars.githubusercontent.com/u/3204540?u=a2e1465e3ee10d537614d513589607eddefde09f&v=4
+ url: https://github.com/dpinezich
+maoyibo:
+ login: maoyibo
+ count: 27
+ avatarUrl: https://avatars.githubusercontent.com/u/7887703?v=4
+ url: https://github.com/maoyibo
+0417taehyun:
+ login: 0417taehyun
+ count: 27
+ avatarUrl: https://avatars.githubusercontent.com/u/63915557?u=47debaa860fd52c9b98c97ef357ddcec3b3fb399&v=4
+ url: https://github.com/0417taehyun
+BilalAlpaslan:
+ login: BilalAlpaslan
+ count: 26
+ avatarUrl: https://avatars.githubusercontent.com/u/47563997?u=63ed66e304fe8d765762c70587d61d9196e5c82d&v=4
+ url: https://github.com/BilalAlpaslan
+junah201:
+ login: junah201
+ count: 26
+ avatarUrl: https://avatars.githubusercontent.com/u/75025529?u=2451c256e888fa2a06bcfc0646d09b87ddb6a945&v=4
+ url: https://github.com/junah201
+zy7y:
+ login: zy7y
+ count: 25
+ avatarUrl: https://avatars.githubusercontent.com/u/67154681?u=5d634834cc514028ea3f9115f7030b99a1f4d5a4&v=4
+ url: https://github.com/zy7y
+mycaule:
+ login: mycaule
+ count: 25
+ avatarUrl: https://avatars.githubusercontent.com/u/6161385?u=e3cec75bd6d938a0d73fae0dc5534d1ab2ed1b0e&v=4
+ url: https://github.com/mycaule
+Aruelius:
+ login: Aruelius
+ count: 24
+ avatarUrl: https://avatars.githubusercontent.com/u/25380989?u=574f8cfcda3ea77a3f81884f6b26a97068e36a9d&v=4
+ url: https://github.com/Aruelius
+wisderfin:
+ login: wisderfin
+ count: 24
+ avatarUrl: https://avatars.githubusercontent.com/u/77553770?u=9a23740d520d65dc0051cdc1ecd87f31cb900313&v=4
+ url: https://github.com/wisderfin
+OzgunCaglarArslan:
+ login: OzgunCaglarArslan
+ count: 24
+ avatarUrl: https://avatars.githubusercontent.com/u/86166426?v=4
+ url: https://github.com/OzgunCaglarArslan
+sh0nk:
+ login: sh0nk
+ count: 23
+ avatarUrl: https://avatars.githubusercontent.com/u/6478810?u=af15d724875cec682ed8088a86d36b2798f981c0&v=4
+ url: https://github.com/sh0nk
+axel584:
+ login: axel584
+ count: 23
+ avatarUrl: https://avatars.githubusercontent.com/u/1334088?u=9667041f5b15dc002b6f9665fda8c0412933ac04&v=4
+ url: https://github.com/axel584
+DianaTrufanova:
+ login: DianaTrufanova
+ count: 23
+ avatarUrl: https://avatars.githubusercontent.com/u/119067607?u=1cd55f841b68b4a187fa6d06a7dafa5f070195aa&v=4
+ url: https://github.com/DianaTrufanova
+AGolicyn:
+ login: AGolicyn
+ count: 21
+ avatarUrl: https://avatars.githubusercontent.com/u/86262613?u=3c21606ab8d210a061a1673decff1e7d5592b380&v=4
+ url: https://github.com/AGolicyn
+Attsun1031:
+ login: Attsun1031
+ count: 20
+ avatarUrl: https://avatars.githubusercontent.com/u/1175560?v=4
+ url: https://github.com/Attsun1031
+ycd:
+ login: ycd
+ count: 20
+ avatarUrl: https://avatars.githubusercontent.com/u/62724709?u=f1e7bae394a315da950912c92dc861a8eaf95d4c&v=4
+ url: https://github.com/ycd
+delhi09:
+ login: delhi09
+ count: 20
+ avatarUrl: https://avatars.githubusercontent.com/u/63476957?u=6c86e59b48e0394d4db230f37fc9ad4d7e2c27c7&v=4
+ url: https://github.com/delhi09
+rogerbrinkmann:
+ login: rogerbrinkmann
+ count: 20
+ avatarUrl: https://avatars.githubusercontent.com/u/5690226?v=4
+ url: https://github.com/rogerbrinkmann
+DevDae:
+ login: DevDae
+ count: 20
+ avatarUrl: https://avatars.githubusercontent.com/u/87962045?u=08e10fa516e844934f4b3fc7c38b33c61697e4a1&v=4
+ url: https://github.com/DevDae
+sattosan:
+ login: sattosan
+ count: 19
+ avatarUrl: https://avatars.githubusercontent.com/u/20574756?u=b0d8474d2938189c6954423ae8d81d91013f80a8&v=4
+ url: https://github.com/sattosan
+yes0ng:
+ login: yes0ng
+ count: 19
+ avatarUrl: https://avatars.githubusercontent.com/u/25501794?u=3aed18b0d491e0220a167a1e9e58bea3638c6707&v=4
+ url: https://github.com/yes0ng
+ComicShrimp:
+ login: ComicShrimp
+ count: 18
+ avatarUrl: https://avatars.githubusercontent.com/u/43503750?u=d2fbf412e7730183ce91686ca48d4147e1b7dc74&v=4
+ url: https://github.com/ComicShrimp
+simatheone:
+ login: simatheone
+ count: 18
+ avatarUrl: https://avatars.githubusercontent.com/u/78508673?u=1b9658d9ee0bde33f56130dd52275493ddd38690&v=4
+ url: https://github.com/simatheone
+ivan-abc:
+ login: ivan-abc
+ count: 18
+ avatarUrl: https://avatars.githubusercontent.com/u/36765187?u=c6e0ba571c1ccb6db9d94e62e4b8b5eda811a870&v=4
+ url: https://github.com/ivan-abc
+Limsunoh:
+ login: Limsunoh
+ count: 18
+ avatarUrl: https://avatars.githubusercontent.com/u/90311848?u=f456e0c5709fd50c8cd2898b551558eda14e5f21&v=4
+ url: https://github.com/Limsunoh
+SofiiaTrufanova:
+ login: SofiiaTrufanova
+ count: 18
+ avatarUrl: https://avatars.githubusercontent.com/u/63260929?u=483e0b64fabc76343b3be39b7e1dcb930a95e1bb&v=4
+ url: https://github.com/SofiiaTrufanova
+bezaca:
+ login: bezaca
+ count: 17
+ avatarUrl: https://avatars.githubusercontent.com/u/69092910?u=4ac58eab99bd37d663f3d23551df96d4fbdbf760&v=4
+ url: https://github.com/bezaca
+lbmendes:
+ login: lbmendes
+ count: 17
+ avatarUrl: https://avatars.githubusercontent.com/u/80999926?u=646619e2f07ac5a7c3f65fe7834197461a4fff9f&v=4
+ url: https://github.com/lbmendes
+spacesphere:
+ login: spacesphere
+ count: 17
+ avatarUrl: https://avatars.githubusercontent.com/u/34628304?u=cde91f6002dd33156e1bf8005f11a7a3ed76b790&v=4
+ url: https://github.com/spacesphere
+panko:
+ login: panko
+ count: 17
+ avatarUrl: https://avatars.githubusercontent.com/u/1569515?u=a84a5d255621ed82f8e1ca052f5f2eeb75997da2&v=4
+ url: https://github.com/panko
+jeison-araya:
+ login: jeison-araya
+ count: 17
+ avatarUrl: https://avatars.githubusercontent.com/u/57369279?u=17001e68af7d8e5b8c343e5e9df4050f419998d5&v=4
+ url: https://github.com/jeison-araya
+yanever:
+ login: yanever
+ count: 16
+ avatarUrl: https://avatars.githubusercontent.com/u/21978760?v=4
+ url: https://github.com/yanever
+mastizada:
+ login: mastizada
+ count: 16
+ avatarUrl: https://avatars.githubusercontent.com/u/1975818?u=0751a06d7271c8bf17cb73b1b845644ab4d2c6dc&v=4
+ url: https://github.com/mastizada
+Joao-Pedro-P-Holanda:
+ login: Joao-Pedro-P-Holanda
+ count: 16
+ avatarUrl: https://avatars.githubusercontent.com/u/110267046?u=331bd016326dac4cf3df4848f6db2dbbf8b5f978&v=4
+ url: https://github.com/Joao-Pedro-P-Holanda
+JaeHyuckSa:
+ login: JaeHyuckSa
+ count: 16
+ avatarUrl: https://avatars.githubusercontent.com/u/104830931?u=f3b4a2baea550f428a4c602a30ebee6721c1e3df&v=4
+ url: https://github.com/JaeHyuckSa
+Jedore:
+ login: Jedore
+ count: 15
+ avatarUrl: https://avatars.githubusercontent.com/u/17944025?u=81d503e1c800eb666b3861ca47a3a773bbc3f539&v=4
+ url: https://github.com/Jedore
+kim-sangah:
+ login: kim-sangah
+ count: 15
+ avatarUrl: https://avatars.githubusercontent.com/u/173775778?v=4
+ url: https://github.com/kim-sangah
+PandaHun:
+ login: PandaHun
+ count: 14
+ avatarUrl: https://avatars.githubusercontent.com/u/13096845?u=646eba44db720e37d0dbe8e98e77ab534ea78a20&v=4
+ url: https://github.com/PandaHun
+dukkee:
+ login: dukkee
+ count: 14
+ avatarUrl: https://avatars.githubusercontent.com/u/36825394?u=ccfd86e6a4f2d093dad6f7544cc875af67fa2df8&v=4
+ url: https://github.com/dukkee
+BORA040126:
+ login: BORA040126
+ count: 14
+ avatarUrl: https://avatars.githubusercontent.com/u/88664069?u=98e382727a485971e04aaa7c873d9a75a17ee3be&v=4
+ url: https://github.com/BORA040126
+mattkoehne:
+ login: mattkoehne
+ count: 14
+ avatarUrl: https://avatars.githubusercontent.com/u/80362153?v=4
+ url: https://github.com/mattkoehne
+jovicon:
+ login: jovicon
+ count: 13
+ avatarUrl: https://avatars.githubusercontent.com/u/21287303?u=b049eac3e51a4c0473c2efe66b4d28a7d8f2b572&v=4
+ url: https://github.com/jovicon
+izaguerreiro:
+ login: izaguerreiro
+ count: 13
+ avatarUrl: https://avatars.githubusercontent.com/u/2241504?v=4
+ url: https://github.com/izaguerreiro
+jburckel:
+ login: jburckel
+ count: 13
+ avatarUrl: https://avatars.githubusercontent.com/u/11768758?u=044462e4130e086a0621f4abb45f0d7a289ab7fa&v=4
+ url: https://github.com/jburckel
+peidrao:
+ login: peidrao
+ count: 13
+ avatarUrl: https://avatars.githubusercontent.com/u/32584628?u=64c634bb10381905038ff7faf3c8c3df47fb799a&v=4
+ url: https://github.com/peidrao
+impocode:
+ login: impocode
+ count: 13
+ avatarUrl: https://avatars.githubusercontent.com/u/109408819?u=9cdfc5ccb31a2094c520f41b6087012fa9048982&v=4
+ url: https://github.com/impocode
+wesinalves:
+ login: wesinalves
+ count: 13
+ avatarUrl: https://avatars.githubusercontent.com/u/13563128?u=9eb17ed50645dd684bfec47e75dba4e9772ec9c1&v=4
+ url: https://github.com/wesinalves
+NastasiaSaby:
+ login: NastasiaSaby
+ count: 12
+ avatarUrl: https://avatars.githubusercontent.com/u/8245071?u=b3afd005f9e4bf080c219ef61a592b3a8004b764&v=4
+ url: https://github.com/NastasiaSaby
+oandersonmagalhaes:
+ login: oandersonmagalhaes
+ count: 12
+ avatarUrl: https://avatars.githubusercontent.com/u/83456692?v=4
+ url: https://github.com/oandersonmagalhaes
+mkdir700:
+ login: mkdir700
+ count: 12
+ avatarUrl: https://avatars.githubusercontent.com/u/56359329?u=3d6ea8714f5000829b60dcf7b13a75b1e73aaf47&v=4
+ url: https://github.com/mkdir700
+batlopes:
+ login: batlopes
+ count: 12
+ avatarUrl: https://avatars.githubusercontent.com/u/33462923?u=0fb3d7acb316764616f11e4947faf080e49ad8d9&v=4
+ url: https://github.com/batlopes
+joonas-yoon:
+ login: joonas-yoon
+ count: 12
+ avatarUrl: https://avatars.githubusercontent.com/u/9527681?u=0166d22ef4749e617c6516e79f833cd8d73f1949&v=4
+ url: https://github.com/joonas-yoon
+baseplate-admin:
+ login: baseplate-admin
+ count: 12
+ avatarUrl: https://avatars.githubusercontent.com/u/61817579?u=ce4c268fa949ae9a0290996e7949195302055812&v=4
+ url: https://github.com/baseplate-admin
+KaniKim:
+ login: KaniKim
+ count: 12
+ avatarUrl: https://avatars.githubusercontent.com/u/19832624?u=296dbdd490e0eb96e3d45a2608c065603b17dc31&v=4
+ url: https://github.com/KaniKim
+andersonrocha0:
+ login: andersonrocha0
+ count: 12
+ avatarUrl: https://avatars.githubusercontent.com/u/22346169?u=93a1359c8c5461d894802c0cc65bcd09217e7a02&v=4
+ url: https://github.com/andersonrocha0
+gitgernit:
+ login: gitgernit
+ count: 12
+ avatarUrl: https://avatars.githubusercontent.com/u/129539613?u=d04f10143ab32c93f563ea14bf242d1d2bc991b0&v=4
+ url: https://github.com/gitgernit
+kwang1215:
+ login: kwang1215
+ count: 12
+ avatarUrl: https://avatars.githubusercontent.com/u/74170199?u=2a63ff6692119dde3f5e5693365b9fcd6f977b08&v=4
+ url: https://github.com/kwang1215
+AdrianDeAnda:
+ login: AdrianDeAnda
+ count: 11
+ avatarUrl: https://avatars.githubusercontent.com/u/1024932?u=b2ea249c6b41ddf98679c8d110d0f67d4a3ebf93&v=4
+ url: https://github.com/AdrianDeAnda
+blt232018:
+ login: blt232018
+ count: 11
+ avatarUrl: https://avatars.githubusercontent.com/u/43393471?u=172b0e0391db1aa6c1706498d6dfcb003c8a4857&v=4
+ url: https://github.com/blt232018
+NinaHwang:
+ login: NinaHwang
+ count: 11
+ avatarUrl: https://avatars.githubusercontent.com/u/79563565?u=241f2cb6d38a2d379536608a8ea5a22ed4b1a3ea&v=4
+ url: https://github.com/NinaHwang
+glsglsgls:
+ login: glsglsgls
+ count: 11
+ avatarUrl: https://avatars.githubusercontent.com/u/76133879?v=4
+ url: https://github.com/glsglsgls
+k94-ishi:
+ login: k94-ishi
+ count: 11
+ avatarUrl: https://avatars.githubusercontent.com/u/32672580?u=bc7c5c07af0656be9fe4f1784a444af8d81ded89&v=4
+ url: https://github.com/k94-ishi
+codespearhead:
+ login: codespearhead
+ count: 11
+ avatarUrl: https://avatars.githubusercontent.com/u/72931357?u=0fce6b82219b604d58adb614a761556425579cb5&v=4
+ url: https://github.com/codespearhead
+emrhnsyts:
+ login: emrhnsyts
+ count: 11
+ avatarUrl: https://avatars.githubusercontent.com/u/42899027?u=ad26798e3f8feed2041c5dd5f87e58933d6c3283&v=4
+ url: https://github.com/emrhnsyts
+Lufa1u:
+ login: Lufa1u
+ count: 11
+ avatarUrl: https://avatars.githubusercontent.com/u/112495876?u=087658920ed9e74311597bdd921d8d2de939d276&v=4
+ url: https://github.com/Lufa1u
+waketzheng:
+ login: waketzheng
+ count: 11
+ avatarUrl: https://avatars.githubusercontent.com/u/35413830?u=df19e4fd5bb928e7d086e053ef26a46aad23bf84&v=4
+ url: https://github.com/waketzheng
+KNChiu:
+ login: KNChiu
+ count: 11
+ avatarUrl: https://avatars.githubusercontent.com/u/36751646?v=4
+ url: https://github.com/KNChiu
+maru0123-2004:
+ login: maru0123-2004
+ count: 11
+ avatarUrl: https://avatars.githubusercontent.com/u/43961566?u=16ed8603a4d6a4665cb6c53a7aece6f31379b769&v=4
+ url: https://github.com/maru0123-2004
+mariacamilagl:
+ login: mariacamilagl
+ count: 10
+ avatarUrl: https://avatars.githubusercontent.com/u/11489395?u=4adb6986bf3debfc2b8216ae701f2bd47d73da7d&v=4
+ url: https://github.com/mariacamilagl
+ryuckel:
+ login: ryuckel
+ count: 10
+ avatarUrl: https://avatars.githubusercontent.com/u/36391432?u=094eec0cfddd5013f76f31e55e56147d78b19553&v=4
+ url: https://github.com/ryuckel
+umitkaanusta:
+ login: umitkaanusta
+ count: 10
+ avatarUrl: https://avatars.githubusercontent.com/u/53405015?v=4
+ url: https://github.com/umitkaanusta
+kty4119:
+ login: kty4119
+ count: 10
+ avatarUrl: https://avatars.githubusercontent.com/u/49435654?v=4
+ url: https://github.com/kty4119
+RobotToI:
+ login: RobotToI
+ count: 10
+ avatarUrl: https://avatars.githubusercontent.com/u/44951382?u=e41dbc19191ce7abed86694b1a44ea0523e1c60e&v=4
+ url: https://github.com/RobotToI
+vitumenezes:
+ login: vitumenezes
+ count: 10
+ avatarUrl: https://avatars.githubusercontent.com/u/9680878?u=05fd25cfafdc09382bf8907c37293a696c205754&v=4
+ url: https://github.com/vitumenezes
+fcrozetta:
+ login: fcrozetta
+ count: 10
+ avatarUrl: https://avatars.githubusercontent.com/u/8006246?u=fa2a743e803de2c3a84d3ed8042faefed16c5e43&v=4
+ url: https://github.com/fcrozetta
+sUeharaE4:
+ login: sUeharaE4
+ count: 10
+ avatarUrl: https://avatars.githubusercontent.com/u/44468359?v=4
+ url: https://github.com/sUeharaE4
+Ernilia:
+ login: Ernilia
+ count: 10
+ avatarUrl: https://avatars.githubusercontent.com/u/125735800?u=13bfaac417a53fd5b5cf992efea363ca72598813&v=4
+ url: https://github.com/Ernilia
+socket-socket:
+ login: socket-socket
+ count: 10
+ avatarUrl: https://avatars.githubusercontent.com/u/121552599?u=104df6503242e8d762fe293e7036f7260f245d49&v=4
+ url: https://github.com/socket-socket
+nick-cjyx9:
+ login: nick-cjyx9
+ count: 10
+ avatarUrl: https://avatars.githubusercontent.com/u/119087246?u=7227a2de948c68fb8396d5beff1ee5b0e057c42e&v=4
+ url: https://github.com/nick-cjyx9
+lucasbalieiro:
+ login: lucasbalieiro
+ count: 10
+ avatarUrl: https://avatars.githubusercontent.com/u/37416577?u=dad91601ee4f40458d691774ec439aff308344d7&v=4
+ url: https://github.com/lucasbalieiro
+Zhongheng-Cheng:
+ login: Zhongheng-Cheng
+ count: 10
+ avatarUrl: https://avatars.githubusercontent.com/u/95612344?u=a0f7730a3cc7486827965e01a119ad610bda4b0a&v=4
+ url: https://github.com/Zhongheng-Cheng
+RunningIkkyu:
+ login: RunningIkkyu
+ count: 9
+ avatarUrl: https://avatars.githubusercontent.com/u/31848542?u=494ecc298e3f26197495bb357ad0f57cfd5f7a32&v=4
+ url: https://github.com/RunningIkkyu
+JulianMaurin:
+ login: JulianMaurin
+ count: 9
+ avatarUrl: https://avatars.githubusercontent.com/u/63545168?u=b7d15ac865268cbefc2d739e2f23d9aeeac1a622&v=4
+ url: https://github.com/JulianMaurin
+JeongHyeongKim:
+ login: JeongHyeongKim
+ count: 9
+ avatarUrl: https://avatars.githubusercontent.com/u/26577800?u=77f060f4686f32c248907b81b16ee2b3177ca44c&v=4
+ url: https://github.com/JeongHyeongKim
+arthurio:
+ login: arthurio
+ count: 9
+ avatarUrl: https://avatars.githubusercontent.com/u/950449?u=76b997138273ce5e1990b971c4f27c9aff979fd5&v=4
+ url: https://github.com/arthurio
+Lenclove:
+ login: Lenclove
+ count: 9
+ avatarUrl: https://avatars.githubusercontent.com/u/32355298?u=d0065e01650c63c2b2413f42d983634b2ea85481&v=4
+ url: https://github.com/Lenclove
+eVery1337:
+ login: eVery1337
+ count: 9
+ avatarUrl: https://avatars.githubusercontent.com/u/84917945?u=7af243f05ecfba59191199a70d8ba365c1327768&v=4
+ url: https://github.com/eVery1337
+aykhans:
+ login: aykhans
+ count: 9
+ avatarUrl: https://avatars.githubusercontent.com/u/88669260?u=798da457cc3276d3c6dd7fd628d0005ad8b298cc&v=4
+ url: https://github.com/aykhans
+riroan:
+ login: riroan
+ count: 9
+ avatarUrl: https://avatars.githubusercontent.com/u/33053284?u=2d18e3771506ee874b66d6aa2b3b1107fd95c38f&v=4
+ url: https://github.com/riroan
+MinLee0210:
+ login: MinLee0210
+ count: 9
+ avatarUrl: https://avatars.githubusercontent.com/u/57653278?u=8ca05a7efbc76048183da00da87d148b755a3ba8&v=4
+ url: https://github.com/MinLee0210
+yodai-yodai:
+ login: yodai-yodai
+ count: 9
+ avatarUrl: https://avatars.githubusercontent.com/u/7031039?u=4f3593f5931892b931a745cfab846eff6e9332e7&v=4
+ url: https://github.com/yodai-yodai
+marcelomarkus:
+ login: marcelomarkus
+ count: 9
+ avatarUrl: https://avatars.githubusercontent.com/u/20115018?u=dda090ce9160ef0cd2ff69b1e5ea741283425cba&v=4
+ url: https://github.com/marcelomarkus
+JoaoGustavoRogel:
+ login: JoaoGustavoRogel
+ count: 9
+ avatarUrl: https://avatars.githubusercontent.com/u/29525510?u=a0a91251f5e43e132608d55d28ccb8645c5ea405&v=4
+ url: https://github.com/JoaoGustavoRogel
+Yarous:
+ login: Yarous
+ count: 9
+ avatarUrl: https://avatars.githubusercontent.com/u/61277193?u=5b462347458a373b2d599c6f416d2b75eddbffad&v=4
+ url: https://github.com/Yarous
+dimaqq:
+ login: dimaqq
+ count: 8
+ avatarUrl: https://avatars.githubusercontent.com/u/662249?u=15313dec91bae789685e4abb3c2152251de41948&v=4
+ url: https://github.com/dimaqq
+julianofischer:
+ login: julianofischer
+ count: 8
+ avatarUrl: https://avatars.githubusercontent.com/u/158303?u=d91662eb949d4cc7368831cf37a5cdfd90b7010c&v=4
+ url: https://github.com/julianofischer
+bnzone:
+ login: bnzone
+ count: 8
+ avatarUrl: https://avatars.githubusercontent.com/u/39371503?u=c16f00c41d88479fa2d57b0d7d233b758eacce2d&v=4
+ url: https://github.com/bnzone
+shamosishen:
+ login: shamosishen
+ count: 8
+ avatarUrl: https://avatars.githubusercontent.com/u/9498321?u=c83c20c79e019a0b555a125adf20fc4fb7a882c8&v=4
+ url: https://github.com/shamosishen
+mertssmnoglu:
+ login: mertssmnoglu
+ count: 8
+ avatarUrl: https://avatars.githubusercontent.com/u/61623638?u=59dd885b68ff1832f9ab3b4a4446896358c23442&v=4
+ url: https://github.com/mertssmnoglu
+mahone3297:
+ login: mahone3297
+ count: 8
+ avatarUrl: https://avatars.githubusercontent.com/u/1701379?u=20588ff0e456d13e8017333eb237595d11410234&v=4
+ url: https://github.com/mahone3297
+KimJoonSeo:
+ login: KimJoonSeo
+ count: 8
+ avatarUrl: https://avatars.githubusercontent.com/u/17760162?u=a58cdc77ae1c069a64166f7ecc4d42eecfd9a468&v=4
+ url: https://github.com/KimJoonSeo
+camigomezdev:
+ login: camigomezdev
+ count: 8
+ avatarUrl: https://avatars.githubusercontent.com/u/16061815?u=25b5ebc042fff53fa03dc107ded10e36b1b7a5b9&v=4
+ url: https://github.com/camigomezdev
+minaton-ru:
+ login: minaton-ru
+ count: 8
+ avatarUrl: https://avatars.githubusercontent.com/u/53541518?u=67336ca11a85493f75031508aade588dad3b9910&v=4
+ url: https://github.com/minaton-ru
+sungchan1:
+ login: sungchan1
+ count: 8
+ avatarUrl: https://avatars.githubusercontent.com/u/28076127?u=a816d86ef3e60450a7225f128caf9a394c9320f9&v=4
+ url: https://github.com/sungchan1
+Serrones:
+ login: Serrones
+ count: 7
+ avatarUrl: https://avatars.githubusercontent.com/u/22691749?u=4795b880e13ca33a73e52fc0ef7dc9c60c8fce47&v=4
+ url: https://github.com/Serrones
+israteneda:
+ login: israteneda
+ count: 7
+ avatarUrl: https://avatars.githubusercontent.com/u/20668624?u=67574648f89019d1c73b16a6a009da659557f9e5&v=4
+ url: https://github.com/israteneda
+krocdort:
+ login: krocdort
+ count: 7
+ avatarUrl: https://avatars.githubusercontent.com/u/34248814?v=4
+ url: https://github.com/krocdort
+anthonycepeda:
+ login: anthonycepeda
+ count: 7
+ avatarUrl: https://avatars.githubusercontent.com/u/72019805?u=60bdf46240cff8fca482ff0fc07d963fd5e1a27c&v=4
+ url: https://github.com/anthonycepeda
+fabioueno:
+ login: fabioueno
+ count: 7
+ avatarUrl: https://avatars.githubusercontent.com/u/14273852?u=edd700982b16317ac6ebfd24c47bc0029b21d360&v=4
+ url: https://github.com/fabioueno
+cfraboulet:
+ login: cfraboulet
+ count: 7
+ avatarUrl: https://avatars.githubusercontent.com/u/62244267?u=ed0e286ba48fa1dafd64a08e50f3364b8e12df34&v=4
+ url: https://github.com/cfraboulet
+HiemalBeryl:
+ login: HiemalBeryl
+ count: 7
+ avatarUrl: https://avatars.githubusercontent.com/u/63165207?u=276f4af2829baf28b912c718675852bfccb0e7b4&v=4
+ url: https://github.com/HiemalBeryl
+pablocm83:
+ login: pablocm83
+ count: 7
+ avatarUrl: https://avatars.githubusercontent.com/u/28315068?u=3310fbb05bb8bfc50d2c48b6cb64ac9ee4a14549&v=4
+ url: https://github.com/pablocm83
+d2a-raudenaerde:
+ login: d2a-raudenaerde
+ count: 7
+ avatarUrl: https://avatars.githubusercontent.com/u/5213150?v=4
+ url: https://github.com/d2a-raudenaerde
+valentinDruzhinin:
+ login: valentinDruzhinin
+ count: 7
+ avatarUrl: https://avatars.githubusercontent.com/u/12831905?u=aae1ebc675c91e8fa582df4fcc4fc4128106344d&v=4
+ url: https://github.com/valentinDruzhinin
+Zerohertz:
+ login: Zerohertz
+ count: 7
+ avatarUrl: https://avatars.githubusercontent.com/u/42334717?u=5ebf4d33e73b1ad373154f6cdee44f7cab4d05ba&v=4
+ url: https://github.com/Zerohertz
+deniscapeto:
+ login: deniscapeto
+ count: 6
+ avatarUrl: https://avatars.githubusercontent.com/u/12864353?u=20c5b2300b264a585a8381acf3cef44bcfcc1ead&v=4
+ url: https://github.com/deniscapeto
+bsab:
+ login: bsab
+ count: 6
+ avatarUrl: https://avatars.githubusercontent.com/u/9799799?u=c4a09b1abb794cd8280c4793d43d0e2eb963ecda&v=4
+ url: https://github.com/bsab
+ArcLightSlavik:
+ login: ArcLightSlavik
+ count: 6
+ avatarUrl: https://avatars.githubusercontent.com/u/31127044?u=b0f2c37142f4b762e41ad65dc49581813422bd71&v=4
+ url: https://github.com/ArcLightSlavik
+Cajuteq:
+ login: Cajuteq
+ count: 6
+ avatarUrl: https://avatars.githubusercontent.com/u/26676532?u=8ee0422981810e51480855de1c0d67b6b79cd3f2&v=4
+ url: https://github.com/Cajuteq
+emmrichard:
+ login: emmrichard
+ count: 6
+ avatarUrl: https://avatars.githubusercontent.com/u/1328018?u=8114d8fc0e8e42a092e4283013a1c54b792c466b&v=4
+ url: https://github.com/emmrichard
+wakabame:
+ login: wakabame
+ count: 6
+ avatarUrl: https://avatars.githubusercontent.com/u/35513518?u=41ef6b0a55076e5c540620d68fb006e386c2ddb0&v=4
+ url: https://github.com/wakabame
+mawassk:
+ login: mawassk
+ count: 6
+ avatarUrl: https://avatars.githubusercontent.com/u/84179197?v=4
+ url: https://github.com/mawassk
+diogoduartec:
+ login: diogoduartec
+ count: 6
+ avatarUrl: https://avatars.githubusercontent.com/u/31852339?u=7514a5f05fcbeccc62f8c5dc25879efeb1ef9335&v=4
+ url: https://github.com/diogoduartec
+aqcool:
+ login: aqcool
+ count: 6
+ avatarUrl: https://avatars.githubusercontent.com/u/52229895?v=4
+ url: https://github.com/aqcool
+'1320555911':
+ login: '1320555911'
+ count: 6
+ avatarUrl: https://avatars.githubusercontent.com/u/58590086?u=6d8f4fbf08d5ac72c1c895892c461c5e0b013dc3&v=4
+ url: https://github.com/1320555911
+mcthesw:
+ login: mcthesw
+ count: 6
+ avatarUrl: https://avatars.githubusercontent.com/u/61224072?u=82a1b106298348f060c3f4f39817e0cae5ce2b7c&v=4
+ url: https://github.com/mcthesw
+xzmeng:
+ login: xzmeng
+ count: 6
+ avatarUrl: https://avatars.githubusercontent.com/u/40202897?v=4
+ url: https://github.com/xzmeng
+negadive:
+ login: negadive
+ count: 6
+ avatarUrl: https://avatars.githubusercontent.com/u/47322392?u=c1be2e9b9b346b4a77d9157da2a5739ab25ce0f8&v=4
+ url: https://github.com/negadive
+mbroton:
+ login: mbroton
+ count: 6
+ avatarUrl: https://avatars.githubusercontent.com/u/50829834?u=a48610bf1bffaa9c75d03228926e2eb08a2e24ee&v=4
+ url: https://github.com/mbroton
+Kirilex:
+ login: Kirilex
+ count: 6
+ avatarUrl: https://avatars.githubusercontent.com/u/100281552?v=4
+ url: https://github.com/Kirilex
+arunppsg:
+ login: arunppsg
+ count: 6
+ avatarUrl: https://avatars.githubusercontent.com/u/26398753?v=4
+ url: https://github.com/arunppsg
+dimastbk:
+ login: dimastbk
+ count: 6
+ avatarUrl: https://avatars.githubusercontent.com/u/3132181?u=66587398d43466a1dc75c238df5f048e0afc77ed&v=4
+ url: https://github.com/dimastbk
+dudyaosuplayer:
+ login: dudyaosuplayer
+ count: 6
+ avatarUrl: https://avatars.githubusercontent.com/u/62661898?u=7864cc5f01b1c845ae8ad49acf45dec6faca0c57&v=4
+ url: https://github.com/dudyaosuplayer
+talhaumer:
+ login: talhaumer
+ count: 6
+ avatarUrl: https://avatars.githubusercontent.com/u/46643702?u=5d1fd7057ea9534fb3221931b809a3d750157212&v=4
+ url: https://github.com/talhaumer
+bankofsardine:
+ login: bankofsardine
+ count: 6
+ avatarUrl: https://avatars.githubusercontent.com/u/44944207?u=0368e1b698ffab6bf29e202f9fd2dddd352429f1&v=4
+ url: https://github.com/bankofsardine
+Rekl0w:
+ login: Rekl0w
+ count: 6
+ avatarUrl: https://avatars.githubusercontent.com/u/91488737?u=3b62b04a3e6699eab9b1eea4e88c09a39b753a17&v=4
+ url: https://github.com/Rekl0w
+rsip22:
+ login: rsip22
+ count: 5
+ avatarUrl: https://avatars.githubusercontent.com/u/16676222?v=4
+ url: https://github.com/rsip22
+jessicapaz:
+ login: jessicapaz
+ count: 5
+ avatarUrl: https://avatars.githubusercontent.com/u/20428941?u=6ffdaab5a85bf77a2d8870dade5e53555f34577b&v=4
+ url: https://github.com/jessicapaz
+mohsen-mahmoodi:
+ login: mohsen-mahmoodi
+ count: 5
+ avatarUrl: https://avatars.githubusercontent.com/u/2872586?u=3a9fc1aa16a3a0ab93a1f8550de82a940592857d&v=4
+ url: https://github.com/mohsen-mahmoodi
+jeesang7:
+ login: jeesang7
+ count: 5
+ avatarUrl: https://avatars.githubusercontent.com/u/30719956?u=35fc8bca04d32d3c4ce085956f0636b959ba30f6&v=4
+ url: https://github.com/jeesang7
+TemaSpb:
+ login: TemaSpb
+ count: 5
+ avatarUrl: https://avatars.githubusercontent.com/u/20205738?u=d7dce0718720a7107803a573d628d8dd3d5c2fb4&v=4
+ url: https://github.com/TemaSpb
+BugLight:
+ login: BugLight
+ count: 5
+ avatarUrl: https://avatars.githubusercontent.com/u/13618366?u=7d733749f80e5f7e66a434cf42aedcfc60340f43&v=4
+ url: https://github.com/BugLight
+0x4Dark:
+ login: 0x4Dark
+ count: 5
+ avatarUrl: https://avatars.githubusercontent.com/u/7569289?v=4
+ url: https://github.com/0x4Dark
+Wuerike:
+ login: Wuerike
+ count: 5
+ avatarUrl: https://avatars.githubusercontent.com/u/35462243?u=80c753dedf4a78db12ef66316dbdebbe6d84a2b9&v=4
+ url: https://github.com/Wuerike
+jvmazagao:
+ login: jvmazagao
+ count: 5
+ avatarUrl: https://avatars.githubusercontent.com/u/22477816?u=2b57addf5830906bf6ae5f25cd4c8c2fa5c2d68e&v=4
+ url: https://github.com/jvmazagao
+cun3yt:
+ login: cun3yt
+ count: 5
+ avatarUrl: https://avatars.githubusercontent.com/u/24409240?u=06abfd77786db859b0602d5369d2ae18c932c17c&v=4
+ url: https://github.com/cun3yt
+Mordson:
+ login: Mordson
+ count: 5
+ avatarUrl: https://avatars.githubusercontent.com/u/39025897?u=b94ea96ef35bbe43bc85359cfb31d28ac16d470c&v=4
+ url: https://github.com/Mordson
+aminkhani:
+ login: aminkhani
+ count: 5
+ avatarUrl: https://avatars.githubusercontent.com/u/51851950?u=051896c4933816bc61d11091d887f6e8dfd1d27b&v=4
+ url: https://github.com/aminkhani
+nifadyev:
+ login: nifadyev
+ count: 5
+ avatarUrl: https://avatars.githubusercontent.com/u/36514612?u=e101da8641d5a09901d2155255a93f8ab3d9c468&v=4
+ url: https://github.com/nifadyev
+LaurEars:
+ login: LaurEars
+ count: 5
+ avatarUrl: https://avatars.githubusercontent.com/u/4914725?v=4
+ url: https://github.com/LaurEars
+Chushine:
+ login: Chushine
+ count: 5
+ avatarUrl: https://avatars.githubusercontent.com/u/135534400?v=4
+ url: https://github.com/Chushine
+ChuyuChoyeon:
+ login: ChuyuChoyeon
+ count: 5
+ avatarUrl: https://avatars.githubusercontent.com/u/129537877?u=f0c76f3327817a8b86b422d62e04a34bf2827f2b&v=4
+ url: https://github.com/ChuyuChoyeon
+frwl404:
+ login: frwl404
+ count: 5
+ avatarUrl: https://avatars.githubusercontent.com/u/42642656?u=8395a3d991d9fac86901277d76f0f70857b56ec5&v=4
+ url: https://github.com/frwl404
+esrefzeki:
+ login: esrefzeki
+ count: 5
+ avatarUrl: https://avatars.githubusercontent.com/u/54935247?u=193cf5a169ca05fc54995a4dceabc82c7dc6e5ea&v=4
+ url: https://github.com/esrefzeki
+dtleal:
+ login: dtleal
+ count: 5
+ avatarUrl: https://avatars.githubusercontent.com/u/31096951?u=704664ec74ab655485e5c909b25de3fa09a922ba&v=4
+ url: https://github.com/dtleal
+art3xa:
+ login: art3xa
+ count: 5
+ avatarUrl: https://avatars.githubusercontent.com/u/92092049?v=4
+ url: https://github.com/art3xa
+SamuelBFavarin:
+ login: SamuelBFavarin
+ count: 5
+ avatarUrl: https://avatars.githubusercontent.com/u/17628602?u=5aac13ae492fa9a86e397a70803ac723dba2efe7&v=4
+ url: https://github.com/SamuelBFavarin
+takacs:
+ login: takacs
+ count: 5
+ avatarUrl: https://avatars.githubusercontent.com/u/44911031?u=f6c6b70b3ba86ceb93b0f9bcab609bf9328b2305&v=4
+ url: https://github.com/takacs
+anton2yakovlev:
+ login: anton2yakovlev
+ count: 5
+ avatarUrl: https://avatars.githubusercontent.com/u/44229180?u=bdd445ba99074b378e7298d23c4bf6d707d2c282&v=4
+ url: https://github.com/anton2yakovlev
+ILoveSorasakiHina:
+ login: ILoveSorasakiHina
+ count: 5
+ avatarUrl: https://avatars.githubusercontent.com/u/114038930?u=3d3ed8dc3bf57e641d1b26badee5bc79ef34f25b&v=4
+ url: https://github.com/ILoveSorasakiHina
+devluisrodrigues:
+ login: devluisrodrigues
+ count: 5
+ avatarUrl: https://avatars.githubusercontent.com/u/103431660?u=d9674a3249edc4601d2c712cdebf899918503c3a&v=4
+ url: https://github.com/devluisrodrigues
+11kkw:
+ login: 11kkw
+ count: 5
+ avatarUrl: https://avatars.githubusercontent.com/u/21125286?v=4
+ url: https://github.com/11kkw
+lpdswing:
+ login: lpdswing
+ count: 4
+ avatarUrl: https://avatars.githubusercontent.com/u/20874036?u=7a4fc3e4d0719e37b305deb7af234a7b63200787&v=4
+ url: https://github.com/lpdswing
+SepehrRasouli:
+ login: SepehrRasouli
+ count: 4
+ avatarUrl: https://avatars.githubusercontent.com/u/81516241?u=3987e880c77d653dd85963302150e07bb7c0ef99&v=4
+ url: https://github.com/SepehrRasouli
+Zxilly:
+ login: Zxilly
+ count: 4
+ avatarUrl: https://avatars.githubusercontent.com/u/31370133?u=c5359b8d9d80a7cdc23d5295d179ed90174996c8&v=4
+ url: https://github.com/Zxilly
+eavv:
+ login: eavv
+ count: 4
+ avatarUrl: https://avatars.githubusercontent.com/u/18273429?u=c05e8b4ea62810ee7889ca049e510cdd0a66fd26&v=4
+ url: https://github.com/eavv
+AlexandreBiguet:
+ login: AlexandreBiguet
+ count: 4
+ avatarUrl: https://avatars.githubusercontent.com/u/1483079?u=ff926455cd4cab03c6c49441aa5dc2b21df3e266&v=4
+ url: https://github.com/AlexandreBiguet
+FelipeSilva93:
+ login: FelipeSilva93
+ count: 4
+ avatarUrl: https://avatars.githubusercontent.com/u/66804965?u=e7cb4b580e46f2e04ecb4cd4d7a12acdddd3c6c1&v=4
+ url: https://github.com/FelipeSilva93
+peacekimjapan:
+ login: peacekimjapan
+ count: 4
+ avatarUrl: https://avatars.githubusercontent.com/u/33534175?u=e4219bcebc3773a7068cc34c3eb268ef77cec31b&v=4
+ url: https://github.com/peacekimjapan
+bas-baskara:
+ login: bas-baskara
+ count: 4
+ avatarUrl: https://avatars.githubusercontent.com/u/41407847?u=cdabfaff7481c3323f24a76d9350393b964f2b89&v=4
+ url: https://github.com/bas-baskara
+odiseo0:
+ login: odiseo0
+ count: 4
+ avatarUrl: https://avatars.githubusercontent.com/u/87550035?u=241a71f6b7068738b81af3e57f45ffd723538401&v=4
+ url: https://github.com/odiseo0
+eryknn:
+ login: eryknn
+ count: 4
+ avatarUrl: https://avatars.githubusercontent.com/u/87120651?v=4
+ url: https://github.com/eryknn
+personage-hub:
+ login: personage-hub
+ count: 4
+ avatarUrl: https://avatars.githubusercontent.com/u/76659786?v=4
+ url: https://github.com/personage-hub
+aminalaee:
+ login: aminalaee
+ count: 4
+ avatarUrl: https://avatars.githubusercontent.com/u/19784933?v=4
+ url: https://github.com/aminalaee
+erfan-rfmhr:
+ login: erfan-rfmhr
+ count: 4
+ avatarUrl: https://avatars.githubusercontent.com/u/98986056?u=0acda1ff1df0989f3f3eb79977baa35da4cb6c8c&v=4
+ url: https://github.com/erfan-rfmhr
+Scorpionchiques:
+ login: Scorpionchiques
+ count: 4
+ avatarUrl: https://avatars.githubusercontent.com/u/15703294?v=4
+ url: https://github.com/Scorpionchiques
+lordqyxz:
+ login: lordqyxz
+ count: 4
+ avatarUrl: https://avatars.githubusercontent.com/u/31722468?u=974553c0ba53526d9be7e9876544283291be3b0d&v=4
+ url: https://github.com/lordqyxz
+heysaeid:
+ login: heysaeid
+ count: 4
+ avatarUrl: https://avatars.githubusercontent.com/u/63112273?u=5397ead391319a147a18b70cc04d1a334f235ef3&v=4
+ url: https://github.com/heysaeid
+Yois4101:
+ login: Yois4101
+ count: 4
+ avatarUrl: https://avatars.githubusercontent.com/u/119609381?v=4
+ url: https://github.com/Yois4101
+tamtam-fitness:
+ login: tamtam-fitness
+ count: 4
+ avatarUrl: https://avatars.githubusercontent.com/u/62091034?u=8da19a6bd3d02f5d6ba30c7247d5b46c98dd1403&v=4
+ url: https://github.com/tamtam-fitness
+mpmeleshko:
+ login: mpmeleshko
+ count: 4
+ avatarUrl: https://avatars.githubusercontent.com/u/34425664?v=4
+ url: https://github.com/mpmeleshko
+SonnyYou:
+ login: SonnyYou
+ count: 4
+ avatarUrl: https://avatars.githubusercontent.com/u/18657569?v=4
+ url: https://github.com/SonnyYou
+matiasbertani:
+ login: matiasbertani
+ count: 4
+ avatarUrl: https://avatars.githubusercontent.com/u/65260383?u=d5edd86a6e2ab4fb1aab7751931fe045a963afd7&v=4
+ url: https://github.com/matiasbertani
+thiennc254:
+ login: thiennc254
+ count: 4
+ avatarUrl: https://avatars.githubusercontent.com/u/97406628?u=1b2860679694b9a552764d0fa81dbd7a016322ec&v=4
+ url: https://github.com/thiennc254
+javillegasna:
+ login: javillegasna
+ count: 4
+ avatarUrl: https://avatars.githubusercontent.com/u/38879192?u=df9ab0d628f8c1f1c849db7b3c0939337f42c3f1&v=4
+ url: https://github.com/javillegasna
+9zimin9:
+ login: 9zimin9
+ count: 4
+ avatarUrl: https://avatars.githubusercontent.com/u/174453744?v=4
+ url: https://github.com/9zimin9
+ilhamfadillah:
+ login: ilhamfadillah
+ count: 4
+ avatarUrl: https://avatars.githubusercontent.com/u/20577838?u=c56192cf99b55affcaad408b240259c62e633450&v=4
+ url: https://github.com/ilhamfadillah
+gerry-sabar:
+ login: gerry-sabar
+ count: 4
+ avatarUrl: https://avatars.githubusercontent.com/u/1120123?v=4
+ url: https://github.com/gerry-sabar
+cookie-byte217:
+ login: cookie-byte217
+ count: 4
+ avatarUrl: https://avatars.githubusercontent.com/u/57880178?v=4
+ url: https://github.com/cookie-byte217
+AbolfazlKameli:
+ login: AbolfazlKameli
+ count: 4
+ avatarUrl: https://avatars.githubusercontent.com/u/120686133?u=e41743da3c1820efafc59c5870cacd4f4425334c&v=4
+ url: https://github.com/AbolfazlKameli
+tyronedamasceno:
+ login: tyronedamasceno
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/12273721?u=913bca6bab96d9416ad8c9874c80de0833782050&v=4
+ url: https://github.com/tyronedamasceno
+LikoIlya:
+ login: LikoIlya
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/15039930?v=4
+ url: https://github.com/LikoIlya
+ss-o-furda:
+ login: ss-o-furda
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/56111536?u=d2326baa464a3778c280ed85fd14c00f87eb1080&v=4
+ url: https://github.com/ss-o-furda
+Frans06:
+ login: Frans06
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/5842109?u=77529d5517ae80438249b1a45f2d59372a31a212&v=4
+ url: https://github.com/Frans06
+Jefidev:
+ login: Jefidev
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/9964497?u=1da6eee587a8b425ca4afbfdfc6c3a639fe85d02&v=4
+ url: https://github.com/Jefidev
+Xaraxx:
+ login: Xaraxx
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/29824698?u=dde2e233e22bb5ca1f8bb0c6e353ccd0d06e6066&v=4
+ url: https://github.com/Xaraxx
+Suyoung789:
+ login: Suyoung789
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/31277231?u=744bd3e641413e19bfad6b06a90bb0887c3f9332&v=4
+ url: https://github.com/Suyoung789
+akagaeng:
+ login: akagaeng
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/17076841?u=9ada2eb6a33dc705ba96d58f802c787dea3859b8&v=4
+ url: https://github.com/akagaeng
+phamquanganh31101998:
+ login: phamquanganh31101998
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/43257497?u=36fa4ee689415d869a98453083a7c4213d2136ee&v=4
+ url: https://github.com/phamquanganh31101998
+peebbv6364:
+ login: peebbv6364
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/26784747?u=75583df215ee01a5cd2dc646aecb81e7dbd33d06&v=4
+ url: https://github.com/peebbv6364
+mrparalon:
+ login: mrparalon
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/19637629?u=6339508ceb665717cae862a4d33816ac874cbb8f&v=4
+ url: https://github.com/mrparalon
+creyD:
+ login: creyD
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/15138480?u=51cd2873cd93807beb578af8e23975856fdbc945&v=4
+ url: https://github.com/creyD
+zhoonit:
+ login: zhoonit
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/17230883?u=698cb26dcce4770374b592aad3b7489e91c07fc6&v=4
+ url: https://github.com/zhoonit
+Sefank:
+ login: Sefank
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/12670778?u=ca16995c68a82cabc7435c54ac0564930f62dd59&v=4
+ url: https://github.com/Sefank
+RuslanTer:
+ login: RuslanTer
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/48125303?v=4
+ url: https://github.com/RuslanTer
+FedorGN:
+ login: FedorGN
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/66411909?u=22382380e7d66ee57ffbfc2ae6bd5efd0cdb672e&v=4
+ url: https://github.com/FedorGN
+rafsaf:
+ login: rafsaf
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/51059348?u=5fe59a56e1f2f9ccd8005d71752a8276f133ae1a&v=4
+ url: https://github.com/rafsaf
+frnsimoes:
+ login: frnsimoes
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/66239468?u=fd8d408946633acc4bea057c207e6c0833871527&v=4
+ url: https://github.com/frnsimoes
+lieryan:
+ login: lieryan
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/1006989?v=4
+ url: https://github.com/lieryan
+ValeryVal:
+ login: ValeryVal
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/85856176?v=4
+ url: https://github.com/ValeryVal
+chesstrian:
+ login: chesstrian
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/3923412?u=8ea9bea6cfb5e6c64dc81be65ac2a9aaf23c5d47&v=4
+ url: https://github.com/chesstrian
+PabloEmidio:
+ login: PabloEmidio
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/69937719?u=f4d04cb78da68bb93a641f0b793ff665162e712a&v=4
+ url: https://github.com/PabloEmidio
+PraveenNanda124:
+ login: PraveenNanda124
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/116082827?u=b40c4f23c191692e88f676dc3bf33fc7f315edd4&v=4
+ url: https://github.com/PraveenNanda124
+guites:
+ login: guites
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/71985299?u=5dab5eb82b0a67fe709fc893f47a423df4de5d46&v=4
+ url: https://github.com/guites
+Junhyung21:
+ login: Junhyung21
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/138214497?u=66377988eaad4f57004decb183f396560407a73f&v=4
+ url: https://github.com/Junhyung21
+rinaatt:
+ login: rinaatt
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/6111202?u=9f62ebd2a72879db54d0b51c07c1d1e7203a4813&v=4
+ url: https://github.com/rinaatt
+Slijeff:
+ login: Slijeff
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/31459252?u=083776331690bbcf427766071e33ac28bb8d271d&v=4
+ url: https://github.com/Slijeff
+GeorchW:
+ login: GeorchW
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/8687777?u=ae4160f1d88f32692760003f3be9b5fc40a6e00d&v=4
+ url: https://github.com/GeorchW
+Vlad0395:
+ login: Vlad0395
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/37487589?u=57dc6660b9904cc0bc59b73569bbfb1ac871a4a1&v=4
+ url: https://github.com/Vlad0395
+bisibuka:
+ login: bisibuka
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/221887?v=4
+ url: https://github.com/bisibuka
+aimasheraz1:
+ login: aimasheraz1
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/132935019?v=4
+ url: https://github.com/aimasheraz1
+whysage:
+ login: whysage
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/67018871?u=a05d63a1b315dcf56a4c0dda3c0ca84ce3d6c87f&v=4
+ url: https://github.com/whysage
+Chake9928:
+ login: Chake9928
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/62596047?u=7aa2c0aad46911934ce3d22f83a895d05fa54e09&v=4
+ url: https://github.com/Chake9928
+qaerial:
+ login: qaerial
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/41595550?v=4
+ url: https://github.com/qaerial
+bluefish6:
+ login: bluefish6
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/3324881?u=d107f6d0017927191644829fb845a8ceb8ac20ee&v=4
+ url: https://github.com/bluefish6
+Sion99:
+ login: Sion99
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/82511301?v=4
+ url: https://github.com/Sion99
+nymous:
+ login: nymous
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/4216559?u=360a36fb602cded27273cbfc0afc296eece90662&v=4
+ url: https://github.com/nymous
+EpsilonRationes:
+ login: EpsilonRationes
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/148639079?v=4
+ url: https://github.com/EpsilonRationes
+SametEmin:
+ login: SametEmin
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/115692383?u=bda9052f698e50b0df6657fb9436d07e8496fe2f&v=4
+ url: https://github.com/SametEmin
+fhabers21:
+ login: fhabers21
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/58401847?v=4
+ url: https://github.com/fhabers21
+kohiry:
+ login: kohiry
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/57669492?u=f6ab0a062740261e882879269a41a47788c84043&v=4
+ url: https://github.com/kohiry
+ptt3199:
+ login: ptt3199
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/51350651?u=2c3d947a80283e32bf616d4c3af139a6be69680f&v=4
+ url: https://github.com/ptt3199
+arynoot:
+ login: arynoot
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/73756088?v=4
+ url: https://github.com/arynoot
+GDemay:
+ login: GDemay
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/7033942?u=bbdcb4e2a67df4ec9caa2440362d8cebc44d65e8&v=4
+ url: https://github.com/GDemay
+maxscheijen:
+ login: maxscheijen
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/47034840?v=4
+ url: https://github.com/maxscheijen
+celestywang:
+ login: celestywang
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/184830753?v=4
+ url: https://github.com/celestywang
+RyaWcksn:
+ login: RyaWcksn
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/42831964?u=0cb4265faf3e3425a89e59b6fddd3eb2de180af0&v=4
+ url: https://github.com/RyaWcksn
+tienduong-21:
+ login: tienduong-21
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/80129618?v=4
+ url: https://github.com/tienduong-21
+zbellos:
+ login: zbellos
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/204500646?v=4
+ url: https://github.com/zbellos
+Mohammad222PR:
+ login: Mohammad222PR
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/116789737?u=25810a5fe049d2f1618e2e7417cea011cc353ce4&v=4
+ url: https://github.com/Mohammad222PR
+EdmilsonRodrigues:
+ login: EdmilsonRodrigues
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/62777025?u=217d6f3cd6cc750bb8818a3af7726c8d74eb7c2d&v=4
+ url: https://github.com/EdmilsonRodrigues
+blaisep:
+ login: blaisep
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/254456?u=97d584b7c0a6faf583aa59975df4f993f671d121&v=4
+ url: https://github.com/blaisep
+SirTelemak:
+ login: SirTelemak
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/9435877?u=719327b7d2c4c62212456d771bfa7c6b8dbb9eac&v=4
+ url: https://github.com/SirTelemak
+ovezovs:
+ login: ovezovs
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/44060682?u=9cb4d738b15e64157cb65afbe2e31bd0c8f3f6e6&v=4
+ url: https://github.com/ovezovs
+neatek:
+ login: neatek
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/3075678?u=3001e778e4aa0bf6d3142d09f0b9d13b2c55066f&v=4
+ url: https://github.com/neatek
+sprytnyk:
+ login: sprytnyk
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/16718258?u=4893ea96bfebfbdbde8abd9e06851eca12b01bc9&v=4
+ url: https://github.com/sprytnyk
+wfpinedar:
+ login: wfpinedar
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/5309214?u=4af7b6b3907b015699a9994d0808137dd68f7658&v=4
+ url: https://github.com/wfpinedar
+italopenaforte:
+ login: italopenaforte
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/7786881?u=e64a8f24b1ba95eb82f283be8ab90892e40c5465&v=4
+ url: https://github.com/italopenaforte
+hackerneocom:
+ login: hackerneocom
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/67042948?u=ca365045bd261cec5a64059aa23cf80065148c3c&v=4
+ url: https://github.com/hackerneocom
+dmas-at-wiris:
+ login: dmas-at-wiris
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/24917162?u=0df147936a375b4b64232c650de31a227a6b59a0&v=4
+ url: https://github.com/dmas-at-wiris
+TorhamDev:
+ login: TorhamDev
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/87639984?u=07e5429fbd9c5d63c5ca55a0f31ef541216f0ce6&v=4
+ url: https://github.com/TorhamDev
+jaystone776:
+ login: jaystone776
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/11191137?u=299205a95e9b6817a43144a48b643346a5aac5cc&v=4
+ url: https://github.com/jaystone776
+AaronDewes:
+ login: AaronDewes
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/67546953?v=4
+ url: https://github.com/AaronDewes
+kunansy:
+ login: kunansy
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/20476946?u=d8321cd00787d5ee29bfdd8ff6fde23ad783a581&v=4
+ url: https://github.com/kunansy
+TimorChow:
+ login: TimorChow
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/18365403?u=bcbb357be0a447bc682a161932eab5032cede4af&v=4
+ url: https://github.com/TimorChow
+ataberkciftlikli:
+ login: ataberkciftlikli
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/64265169?u=ca7c1348242559f70bc1dc027a4be277c464676f&v=4
+ url: https://github.com/ataberkciftlikli
+leandrodesouzadev:
+ login: leandrodesouzadev
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/85115541?u=4eb25f43f1fe23727d61e986cf83b73b86e2a95a&v=4
+ url: https://github.com/leandrodesouzadev
+dutkiewicz:
+ login: dutkiewicz
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/6649846?u=e941be6e1ab2ffdf41cea227a73f0ffbef20628f&v=4
+ url: https://github.com/dutkiewicz
+mirusu400:
+ login: mirusu400
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/25397908?u=deda776115e4ee6f76fa526bb5127bd1a6c4b231&v=4
+ url: https://github.com/mirusu400
+its0x08:
+ login: its0x08
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/15280042?u=d7c2058f29d4e8fbdae09b194e04c5e410350211&v=4
+ url: https://github.com/its0x08
+linsein:
+ login: linsein
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/23748021?u=4db169ce262b69aa7292f82b785436544f69fb88&v=4
+ url: https://github.com/linsein
+0xflotus:
+ login: 0xflotus
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/26602940?u=3c52ce6393bb547c97e6380ccdee03e0c64152c6&v=4
+ url: https://github.com/0xflotus
+jonatasoli:
+ login: jonatasoli
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/26334101?u=f601c3f111f2148bd9244c2cb3ebbd57b592e674&v=4
+ url: https://github.com/jonatasoli
+tyzh-dev:
+ login: tyzh-dev
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/51972581?u=ba3882da7c009918a8e2d6b9ead31c89f09c922d&v=4
+ url: https://github.com/tyzh-dev
+yurkevich-dev:
+ login: yurkevich-dev
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/45145188?u=db2de8c186073d95693279dcf085fcebffab57d0&v=4
+ url: https://github.com/yurkevich-dev
+emp7yhead:
+ login: emp7yhead
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/20521260?u=9494c74cb9e1601d734b1f2726e292e257777d98&v=4
+ url: https://github.com/emp7yhead
+BartoszCki:
+ login: BartoszCki
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/17833351?u=40025e1182c32a9664834baec268dadad127703d&v=4
+ url: https://github.com/BartoszCki
+hakancelikdev:
+ login: hakancelikdev
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/19157033?u=095ea8e0af1de642edd92e5f806c70359e00c977&v=4
+ url: https://github.com/hakancelikdev
+KaterinaSolovyeva:
+ login: KaterinaSolovyeva
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/85114725?u=1fe81463cb6b1fd01ac047172fa4895e2a3cecaa&v=4
+ url: https://github.com/KaterinaSolovyeva
+zhanymkanov:
+ login: zhanymkanov
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/22341602?u=aa1c47285a4f5692d165ccb2a441c5553f23ef83&v=4
+ url: https://github.com/zhanymkanov
+felipebpl:
+ login: felipebpl
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/62957465?u=3c05f0f358b9575503c03122daefb115b6ac1414&v=4
+ url: https://github.com/felipebpl
+iudeen:
+ login: iudeen
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/10519440?u=f09cdd745e5bf16138f29b42732dd57c7f02bee1&v=4
+ url: https://github.com/iudeen
+dwisulfahnur:
+ login: dwisulfahnur
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/12431528?v=4
+ url: https://github.com/dwisulfahnur
+ayr-ton:
+ login: ayr-ton
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/1090517?u=5cf70a0e0f0dbf084e074e494aa94d7c91a46ba6&v=4
+ url: https://github.com/ayr-ton
+raphaelauv:
+ login: raphaelauv
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/10202690?u=e6f86f5c0c3026a15d6b51792fa3e532b12f1371&v=4
+ url: https://github.com/raphaelauv
+Fahad-Md-Kamal:
+ login: Fahad-Md-Kamal
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/34704464?u=141086368c5557d5a1a533fe291f21f9fc584458&v=4
+ url: https://github.com/Fahad-Md-Kamal
+zxcq544:
+ login: zxcq544
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/5781268?u=25959ea03803742c3b28220b27fc07923a491dcb&v=4
+ url: https://github.com/zxcq544
+AlexandrMaltsevYDX:
+ login: AlexandrMaltsevYDX
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/109986802?u=ed275d72bfcdb4d15abdd54e7be026adbb9ca098&v=4
+ url: https://github.com/AlexandrMaltsevYDX
+realFranco:
+ login: realFranco
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/45880759?u=22fea3007d3e2d4c8c82d6ccfbde71454c4c6dd8&v=4
+ url: https://github.com/realFranco
+piaria:
+ login: piaria
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/110835535?u=5af3d56254faa05bbca4258a46c5723489480f90&v=4
+ url: https://github.com/piaria
+mojtabapaso:
+ login: mojtabapaso
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/121169359?u=ced1d5ad673bcd9e949ebf967a4ab50185637443&v=4
+ url: https://github.com/mojtabapaso
+eghbalpoorMH:
+ login: eghbalpoorMH
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/36267498?v=4
+ url: https://github.com/eghbalpoorMH
+Tiazen:
+ login: Tiazen
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/16170159?u=0ce5e32f76e3f10733c8f25d97db9e31b753838c&v=4
+ url: https://github.com/Tiazen
+jfunez:
+ login: jfunez
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/805749?v=4
+ url: https://github.com/jfunez
+s-rigaud:
+ login: s-rigaud
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/46346622?u=eee0adaa9fdff9e312d52526fbd4020dd6860c27&v=4
+ url: https://github.com/s-rigaud
+Artem4es:
+ login: Artem4es
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/110793967?u=0f9d4e80e055adc1aa8b548e951f6b4989fa2e78&v=4
+ url: https://github.com/Artem4es
+sulemanhelp:
+ login: sulemanhelp
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/22400366?u=3e8e68750655c7f5b2e0ba1d54f5779ee526707d&v=4
+ url: https://github.com/sulemanhelp
+theRealNonso:
+ login: theRealNonso
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/29557286?u=6f062680edccfeb4c802daf3b1d8b2a9e21ae013&v=4
+ url: https://github.com/theRealNonso
+AhsanSheraz:
+ login: AhsanSheraz
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/51913596?u=08e31cacb3048be30722c94010ddd028f3fdbec4&v=4
+ url: https://github.com/AhsanSheraz
+HealerNguyen:
+ login: HealerNguyen
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/29653304?u=6ab095689054c63b1f4ceb26dd66847450225c87&v=4
+ url: https://github.com/HealerNguyen
+isulim:
+ login: isulim
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/30448496?u=44c47838defa48a16606b895dce08890fca8482f&v=4
+ url: https://github.com/isulim
+siavashyj:
+ login: siavashyj
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/43583410?u=562005ddc7901cd27a1219a118a2363817b14977&v=4
+ url: https://github.com/siavashyj
+Ramin-RX7:
+ login: Ramin-RX7
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/52785580?u=b3678f779ad0ee9cd9dca9e50ccb804b5eb990a5&v=4
+ url: https://github.com/Ramin-RX7
+DevSpace88:
+ login: DevSpace88
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/102557040?u=6b356e3e1b9b6bc6a208b363988d4089ef94193f&v=4
+ url: https://github.com/DevSpace88
+Yum-git:
+ login: Yum-git
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/56100888?u=7c6ae21af081488b5fb703ab096fb1926025fd50&v=4
+ url: https://github.com/Yum-git
+oubush:
+ login: oubush
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/7489099?u=c86448bc61f5e7f03a1f14a768beeb09c33899d4&v=4
+ url: https://github.com/oubush
+KAZAMA-DREAM:
+ login: KAZAMA-DREAM
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/73453137?u=5108c757a3842733a448d9a16cdc65d82899eee1&v=4
+ url: https://github.com/KAZAMA-DREAM
+aprilcoskun:
+ login: aprilcoskun
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/17393603?u=18177d5bdba3a4567b8664587c882fb734e5fa09&v=4
+ url: https://github.com/aprilcoskun
+zhiquanchi:
+ login: zhiquanchi
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/29973289?u=744c74bc2635f839235ec32a0a934c5cef9a156d&v=4
+ url: https://github.com/zhiquanchi
+Jamim:
+ login: Jamim
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/5607572?u=9ce0b6a6d1a5124e28b3c04d8d26827ca328713a&v=4
+ url: https://github.com/Jamim
+alvinkhalil:
+ login: alvinkhalil
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/84583022?u=ab0eeb9ce6ffe93fd9bb23daf782b9867b864149&v=4
+ url: https://github.com/alvinkhalil
+leylaeminova:
+ login: leylaeminova
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/100516839?u=0b0dab9e31742076b22812b14a39b4e6d8f6de4a&v=4
+ url: https://github.com/leylaeminova
+UN-9BOT:
+ login: UN-9BOT
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/111110804?u=39e158937ed795972c2d0400fc521c50e9bfb9e7&v=4
+ url: https://github.com/UN-9BOT
+flasonme:
+ login: flasonme
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/30571019?v=4
+ url: https://github.com/flasonme
+gustavoprezoto:
+ login: gustavoprezoto
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/62812585?u=2e936a0c6a2f11ecf3a735ebd33386100bcfebf8&v=4
+ url: https://github.com/gustavoprezoto
+johnny630:
+ login: johnny630
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/2870590?v=4
+ url: https://github.com/johnny630
+JCTrapero:
+ login: JCTrapero
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/109148166?u=bea607a04058176c4c2ae0d7c2e9ec647ccef002&v=4
+ url: https://github.com/JCTrapero
+ZhibangYue:
+ login: ZhibangYue
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/93324586?u=20fb23e3718e0364bb217966470d35e0637dd4fe&v=4
+ url: https://github.com/ZhibangYue
+saeye:
+ login: saeye
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/62229734?u=312d619db2588b60d5d5bde65260a2f44fdc6c76&v=4
+ url: https://github.com/saeye
+Heumhub:
+ login: Heumhub
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/173761521?v=4
+ url: https://github.com/Heumhub
+manumolina:
+ login: manumolina
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/2404208?u=fdc5502910f8dec814b2477f89587b9e45fac846&v=4
+ url: https://github.com/manumolina
+logan2d5:
+ login: logan2d5
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/146642263?u=dbd6621f8b0330d6919f6a7131277b92e26fbe87&v=4
+ url: https://github.com/logan2d5
+guspan-tanadi:
+ login: guspan-tanadi
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/36249910?v=4
+ url: https://github.com/guspan-tanadi
+tiaggo16:
+ login: tiaggo16
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/62227573?u=359f4e2c51a4b13c8553ac5af405d635b07bb61f&v=4
+ url: https://github.com/tiaggo16
+kiharito:
+ login: kiharito
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/38311245?v=4
+ url: https://github.com/kiharito
+t4f1d:
+ login: t4f1d
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/4054172?u=463d5ce0ec8ad8582f6e9351bb8c9a5105b39bb7&v=4
+ url: https://github.com/t4f1d
+J-Fuji:
+ login: J-Fuji
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/101452903?v=4
+ url: https://github.com/J-Fuji
+MrL8199:
+ login: MrL8199
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/39489075?u=3fc4f89c86973e40b5970d838c801bdbc13ac828&v=4
+ url: https://github.com/MrL8199
+ivintoiu:
+ login: ivintoiu
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/1853336?u=5e3d0977f44661fb9712fa297cc8f7608ea6ce48&v=4
+ url: https://github.com/ivintoiu
+TechnoService2:
+ login: TechnoService2
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/142113388?v=4
+ url: https://github.com/TechnoService2
+EgorOnishchuk:
+ login: EgorOnishchuk
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/120256301?v=4
+ url: https://github.com/EgorOnishchuk
+iamantonreznik:
+ login: iamantonreznik
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/112612414?u=bf6de9a1ab17326fe14de0709719fff3826526d0&v=4
+ url: https://github.com/iamantonreznik
+Azazul123:
+ login: Azazul123
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/102759111?u=b48ce6e30a81a23467cc30e0c011bcc57f0326ab&v=4
+ url: https://github.com/Azazul123
+ykertytsky:
+ login: ykertytsky
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/83857001?u=1172902656ee604cf37f5e36abe938cd34a97a32&v=4
+ url: https://github.com/ykertytsky
+NavesSapnis:
+ login: NavesSapnis
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/79222417?u=b5b10291b8e9130ca84fd20f0a641e04ed94b6b1&v=4
+ url: https://github.com/NavesSapnis
+eqsdxr:
+ login: eqsdxr
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/157279130?u=58fddf77ed76966eaa8c73eea9bea4bb0c53b673&v=4
+ url: https://github.com/eqsdxr
+syedasamina56:
+ login: syedasamina56
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/183273097?v=4
+ url: https://github.com/syedasamina56
diff --git a/docs/en/data/translators.yml b/docs/en/data/translators.yml
new file mode 100644
index 0000000000..cf61eee8ea
--- /dev/null
+++ b/docs/en/data/translators.yml
@@ -0,0 +1,550 @@
+nilslindemann:
+ login: nilslindemann
+ count: 122
+ avatarUrl: https://avatars.githubusercontent.com/u/6892179?u=1dca6a22195d6cd1ab20737c0e19a4c55d639472&v=4
+ url: https://github.com/nilslindemann
+jaystone776:
+ login: jaystone776
+ count: 46
+ avatarUrl: https://avatars.githubusercontent.com/u/11191137?u=299205a95e9b6817a43144a48b643346a5aac5cc&v=4
+ url: https://github.com/jaystone776
+valentinDruzhinin:
+ login: valentinDruzhinin
+ count: 29
+ avatarUrl: https://avatars.githubusercontent.com/u/12831905?u=aae1ebc675c91e8fa582df4fcc4fc4128106344d&v=4
+ url: https://github.com/valentinDruzhinin
+ceb10n:
+ login: ceb10n
+ count: 27
+ avatarUrl: https://avatars.githubusercontent.com/u/235213?u=edcce471814a1eba9f0cdaa4cd0de18921a940a6&v=4
+ url: https://github.com/ceb10n
+tokusumi:
+ login: tokusumi
+ count: 23
+ avatarUrl: https://avatars.githubusercontent.com/u/41147016?u=55010621aece725aa702270b54fed829b6a1fe60&v=4
+ url: https://github.com/tokusumi
+SwftAlpc:
+ login: SwftAlpc
+ count: 23
+ avatarUrl: https://avatars.githubusercontent.com/u/52768429?u=6a3aa15277406520ad37f6236e89466ed44bc5b8&v=4
+ url: https://github.com/SwftAlpc
+hasansezertasan:
+ login: hasansezertasan
+ count: 22
+ avatarUrl: https://avatars.githubusercontent.com/u/13135006?u=99f0b0f0fc47e88e8abb337b4447357939ef93e7&v=4
+ url: https://github.com/hasansezertasan
+waynerv:
+ login: waynerv
+ count: 20
+ avatarUrl: https://avatars.githubusercontent.com/u/39515546?u=ec35139777597cdbbbddda29bf8b9d4396b429a9&v=4
+ url: https://github.com/waynerv
+AlertRED:
+ login: AlertRED
+ count: 16
+ avatarUrl: https://avatars.githubusercontent.com/u/15695000?u=f5a4944c6df443030409c88da7d7fa0b7ead985c&v=4
+ url: https://github.com/AlertRED
+hard-coders:
+ login: hard-coders
+ count: 15
+ avatarUrl: https://avatars.githubusercontent.com/u/9651103?u=95db33927bbff1ed1c07efddeb97ac2ff33068ed&v=4
+ url: https://github.com/hard-coders
+Joao-Pedro-P-Holanda:
+ login: Joao-Pedro-P-Holanda
+ count: 14
+ avatarUrl: https://avatars.githubusercontent.com/u/110267046?u=331bd016326dac4cf3df4848f6db2dbbf8b5f978&v=4
+ url: https://github.com/Joao-Pedro-P-Holanda
+codingjenny:
+ login: codingjenny
+ count: 14
+ avatarUrl: https://avatars.githubusercontent.com/u/103817302?u=3a042740dc0ff58615da0d8679230966fd7693e8&v=4
+ url: https://github.com/codingjenny
+Xewus:
+ login: Xewus
+ count: 13
+ avatarUrl: https://avatars.githubusercontent.com/u/85196001?u=f8e2dc7e5104f109cef944af79050ea8d1b8f914&v=4
+ url: https://github.com/Xewus
+Zhongheng-Cheng:
+ login: Zhongheng-Cheng
+ count: 13
+ avatarUrl: https://avatars.githubusercontent.com/u/95612344?u=a0f7730a3cc7486827965e01a119ad610bda4b0a&v=4
+ url: https://github.com/Zhongheng-Cheng
+Smlep:
+ login: Smlep
+ count: 11
+ avatarUrl: https://avatars.githubusercontent.com/u/16785985?u=ffe99fa954c8e774ef1117e58d34aece92051e27&v=4
+ url: https://github.com/Smlep
+marcelomarkus:
+ login: marcelomarkus
+ count: 11
+ avatarUrl: https://avatars.githubusercontent.com/u/20115018?u=dda090ce9160ef0cd2ff69b1e5ea741283425cba&v=4
+ url: https://github.com/marcelomarkus
+KaniKim:
+ login: KaniKim
+ count: 10
+ avatarUrl: https://avatars.githubusercontent.com/u/19832624?u=296dbdd490e0eb96e3d45a2608c065603b17dc31&v=4
+ url: https://github.com/KaniKim
+Vincy1230:
+ login: Vincy1230
+ count: 9
+ avatarUrl: https://avatars.githubusercontent.com/u/81342412?u=ab5e256a4077a4a91f3f9cd2115ba80780454cbe&v=4
+ url: https://github.com/Vincy1230
+rjNemo:
+ login: rjNemo
+ count: 8
+ avatarUrl: https://avatars.githubusercontent.com/u/56785022?u=d5c3a02567c8649e146fcfc51b6060ccaf8adef8&v=4
+ url: https://github.com/rjNemo
+xzmeng:
+ login: xzmeng
+ count: 8
+ avatarUrl: https://avatars.githubusercontent.com/u/40202897?v=4
+ url: https://github.com/xzmeng
+pablocm83:
+ login: pablocm83
+ count: 8
+ avatarUrl: https://avatars.githubusercontent.com/u/28315068?u=3310fbb05bb8bfc50d2c48b6cb64ac9ee4a14549&v=4
+ url: https://github.com/pablocm83
+ptt3199:
+ login: ptt3199
+ count: 7
+ avatarUrl: https://avatars.githubusercontent.com/u/51350651?u=2c3d947a80283e32bf616d4c3af139a6be69680f&v=4
+ url: https://github.com/ptt3199
+NinaHwang:
+ login: NinaHwang
+ count: 6
+ avatarUrl: https://avatars.githubusercontent.com/u/79563565?u=241f2cb6d38a2d379536608a8ea5a22ed4b1a3ea&v=4
+ url: https://github.com/NinaHwang
+batlopes:
+ login: batlopes
+ count: 6
+ avatarUrl: https://avatars.githubusercontent.com/u/33462923?u=0fb3d7acb316764616f11e4947faf080e49ad8d9&v=4
+ url: https://github.com/batlopes
+tiangolo:
+ login: tiangolo
+ count: 6
+ avatarUrl: https://avatars.githubusercontent.com/u/1326112?u=cb5d06e73a9e1998141b1641aa88e443c6717651&v=4
+ url: https://github.com/tiangolo
+lucasbalieiro:
+ login: lucasbalieiro
+ count: 6
+ avatarUrl: https://avatars.githubusercontent.com/u/37416577?u=dad91601ee4f40458d691774ec439aff308344d7&v=4
+ url: https://github.com/lucasbalieiro
+Alexandrhub:
+ login: Alexandrhub
+ count: 6
+ avatarUrl: https://avatars.githubusercontent.com/u/119126536?u=9fc0d48f3307817bafecc5861eb2168401a6cb04&v=4
+ url: https://github.com/Alexandrhub
+Serrones:
+ login: Serrones
+ count: 5
+ avatarUrl: https://avatars.githubusercontent.com/u/22691749?u=4795b880e13ca33a73e52fc0ef7dc9c60c8fce47&v=4
+ url: https://github.com/Serrones
+RunningIkkyu:
+ login: RunningIkkyu
+ count: 5
+ avatarUrl: https://avatars.githubusercontent.com/u/31848542?u=494ecc298e3f26197495bb357ad0f57cfd5f7a32&v=4
+ url: https://github.com/RunningIkkyu
+Attsun1031:
+ login: Attsun1031
+ count: 5
+ avatarUrl: https://avatars.githubusercontent.com/u/1175560?v=4
+ url: https://github.com/Attsun1031
+rostik1410:
+ login: rostik1410
+ count: 5
+ avatarUrl: https://avatars.githubusercontent.com/u/11443899?u=e26a635c2ba220467b308a326a579b8ccf4a8701&v=4
+ url: https://github.com/rostik1410
+alv2017:
+ login: alv2017
+ count: 5
+ avatarUrl: https://avatars.githubusercontent.com/u/31544722?v=4
+ url: https://github.com/alv2017
+komtaki:
+ login: komtaki
+ count: 4
+ avatarUrl: https://avatars.githubusercontent.com/u/39375566?u=260ad6b1a4b34c07dbfa728da5e586f16f6d1824&v=4
+ url: https://github.com/komtaki
+JulianMaurin:
+ login: JulianMaurin
+ count: 4
+ avatarUrl: https://avatars.githubusercontent.com/u/63545168?u=b7d15ac865268cbefc2d739e2f23d9aeeac1a622&v=4
+ url: https://github.com/JulianMaurin
+stlucasgarcia:
+ login: stlucasgarcia
+ count: 4
+ avatarUrl: https://avatars.githubusercontent.com/u/61513630?u=c22d8850e9dc396a8820766a59837f967e14f9a0&v=4
+ url: https://github.com/stlucasgarcia
+ComicShrimp:
+ login: ComicShrimp
+ count: 4
+ avatarUrl: https://avatars.githubusercontent.com/u/43503750?u=d2fbf412e7730183ce91686ca48d4147e1b7dc74&v=4
+ url: https://github.com/ComicShrimp
+BilalAlpaslan:
+ login: BilalAlpaslan
+ count: 4
+ avatarUrl: https://avatars.githubusercontent.com/u/47563997?u=63ed66e304fe8d765762c70587d61d9196e5c82d&v=4
+ url: https://github.com/BilalAlpaslan
+axel584:
+ login: axel584
+ count: 4
+ avatarUrl: https://avatars.githubusercontent.com/u/1334088?u=9667041f5b15dc002b6f9665fda8c0412933ac04&v=4
+ url: https://github.com/axel584
+tamtam-fitness:
+ login: tamtam-fitness
+ count: 4
+ avatarUrl: https://avatars.githubusercontent.com/u/62091034?u=8da19a6bd3d02f5d6ba30c7247d5b46c98dd1403&v=4
+ url: https://github.com/tamtam-fitness
+Limsunoh:
+ login: Limsunoh
+ count: 4
+ avatarUrl: https://avatars.githubusercontent.com/u/90311848?u=f456e0c5709fd50c8cd2898b551558eda14e5f21&v=4
+ url: https://github.com/Limsunoh
+kwang1215:
+ login: kwang1215
+ count: 4
+ avatarUrl: https://avatars.githubusercontent.com/u/74170199?u=2a63ff6692119dde3f5e5693365b9fcd6f977b08&v=4
+ url: https://github.com/kwang1215
+k94-ishi:
+ login: k94-ishi
+ count: 4
+ avatarUrl: https://avatars.githubusercontent.com/u/32672580?u=bc7c5c07af0656be9fe4f1784a444af8d81ded89&v=4
+ url: https://github.com/k94-ishi
+Mohammad222PR:
+ login: Mohammad222PR
+ count: 4
+ avatarUrl: https://avatars.githubusercontent.com/u/116789737?u=25810a5fe049d2f1618e2e7417cea011cc353ce4&v=4
+ url: https://github.com/Mohammad222PR
+NavesSapnis:
+ login: NavesSapnis
+ count: 4
+ avatarUrl: https://avatars.githubusercontent.com/u/79222417?u=b5b10291b8e9130ca84fd20f0a641e04ed94b6b1&v=4
+ url: https://github.com/NavesSapnis
+jfunez:
+ login: jfunez
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/805749?v=4
+ url: https://github.com/jfunez
+ycd:
+ login: ycd
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/62724709?u=f1e7bae394a315da950912c92dc861a8eaf95d4c&v=4
+ url: https://github.com/ycd
+mariacamilagl:
+ login: mariacamilagl
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/11489395?u=4adb6986bf3debfc2b8216ae701f2bd47d73da7d&v=4
+ url: https://github.com/mariacamilagl
+maoyibo:
+ login: maoyibo
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/7887703?v=4
+ url: https://github.com/maoyibo
+blt232018:
+ login: blt232018
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/43393471?u=172b0e0391db1aa6c1706498d6dfcb003c8a4857&v=4
+ url: https://github.com/blt232018
+magiskboy:
+ login: magiskboy
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/13352088?u=18b6d672523f9e9d98401f31dd50e28bb27d826f&v=4
+ url: https://github.com/magiskboy
+luccasmmg:
+ login: luccasmmg
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/11317382?u=65099a5a0d492b89119471f8a7014637cc2e04da&v=4
+ url: https://github.com/luccasmmg
+lbmendes:
+ login: lbmendes
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/80999926?u=646619e2f07ac5a7c3f65fe7834197461a4fff9f&v=4
+ url: https://github.com/lbmendes
+Zssaer:
+ login: Zssaer
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/45691504?u=4c0c195f25cb5ac6af32acfb0ab35427682938d2&v=4
+ url: https://github.com/Zssaer
+ChuyuChoyeon:
+ login: ChuyuChoyeon
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/129537877?u=f0c76f3327817a8b86b422d62e04a34bf2827f2b&v=4
+ url: https://github.com/ChuyuChoyeon
+ivan-abc:
+ login: ivan-abc
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/36765187?u=c6e0ba571c1ccb6db9d94e62e4b8b5eda811a870&v=4
+ url: https://github.com/ivan-abc
+mojtabapaso:
+ login: mojtabapaso
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/121169359?u=ced1d5ad673bcd9e949ebf967a4ab50185637443&v=4
+ url: https://github.com/mojtabapaso
+hsuanchi:
+ login: hsuanchi
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/24913710?u=7d25a398e478b6e63503bf6f26c54efa9e0da07b&v=4
+ url: https://github.com/hsuanchi
+alejsdev:
+ login: alejsdev
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/90076947?u=447d12a1b347f466b35378bee4c7104cc9b2c571&v=4
+ url: https://github.com/alejsdev
+riroan:
+ login: riroan
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/33053284?u=2d18e3771506ee874b66d6aa2b3b1107fd95c38f&v=4
+ url: https://github.com/riroan
+nayeonkinn:
+ login: nayeonkinn
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/98254573?u=64a75ac99b320d4935eff8d1fceea9680fa07473&v=4
+ url: https://github.com/nayeonkinn
+pe-brian:
+ login: pe-brian
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/1783138?u=7e6242eb9e85bcf673fa88bbac9dd6dc3f03b1b5&v=4
+ url: https://github.com/pe-brian
+maxscheijen:
+ login: maxscheijen
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/47034840?v=4
+ url: https://github.com/maxscheijen
+ilacftemp:
+ login: ilacftemp
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/159066669?v=4
+ url: https://github.com/ilacftemp
+devluisrodrigues:
+ login: devluisrodrigues
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/103431660?u=d9674a3249edc4601d2c712cdebf899918503c3a&v=4
+ url: https://github.com/devluisrodrigues
+devfernandoa:
+ login: devfernandoa
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/28360583?u=c4308abd62e8847c9e572e1bb9fe6b9dc9ef8e50&v=4
+ url: https://github.com/devfernandoa
+kim-sangah:
+ login: kim-sangah
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/173775778?v=4
+ url: https://github.com/kim-sangah
+9zimin9:
+ login: 9zimin9
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/174453744?v=4
+ url: https://github.com/9zimin9
+nahyunkeem:
+ login: nahyunkeem
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/174440096?u=e12401d492eee58570f8914d0872b52e421a776e&v=4
+ url: https://github.com/nahyunkeem
+timothy-jeong:
+ login: timothy-jeong
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/53824764?u=db3d0cea2f5fab64d810113c5039a369699a2774&v=4
+ url: https://github.com/timothy-jeong
+gerry-sabar:
+ login: gerry-sabar
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/1120123?v=4
+ url: https://github.com/gerry-sabar
+Rishat-F:
+ login: Rishat-F
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/66554797?v=4
+ url: https://github.com/Rishat-F
+ruzia:
+ login: ruzia
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/24503?v=4
+ url: https://github.com/ruzia
+izaguerreiro:
+ login: izaguerreiro
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/2241504?v=4
+ url: https://github.com/izaguerreiro
+Xaraxx:
+ login: Xaraxx
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/29824698?u=dde2e233e22bb5ca1f8bb0c6e353ccd0d06e6066&v=4
+ url: https://github.com/Xaraxx
+sh0nk:
+ login: sh0nk
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/6478810?u=af15d724875cec682ed8088a86d36b2798f981c0&v=4
+ url: https://github.com/sh0nk
+dukkee:
+ login: dukkee
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/36825394?u=ccfd86e6a4f2d093dad6f7544cc875af67fa2df8&v=4
+ url: https://github.com/dukkee
+oandersonmagalhaes:
+ login: oandersonmagalhaes
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/83456692?v=4
+ url: https://github.com/oandersonmagalhaes
+leandrodesouzadev:
+ login: leandrodesouzadev
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/85115541?u=4eb25f43f1fe23727d61e986cf83b73b86e2a95a&v=4
+ url: https://github.com/leandrodesouzadev
+kty4119:
+ login: kty4119
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/49435654?v=4
+ url: https://github.com/kty4119
+ASpathfinder:
+ login: ASpathfinder
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/31813636?u=2090bd1b7abb65cfeff0c618f99f11afa82c0548&v=4
+ url: https://github.com/ASpathfinder
+jujumilk3:
+ login: jujumilk3
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/41659814?u=538f7dfef03b59f25e43f10d59a31c19ef538a0c&v=4
+ url: https://github.com/jujumilk3
+ayr-ton:
+ login: ayr-ton
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/1090517?u=5cf70a0e0f0dbf084e074e494aa94d7c91a46ba6&v=4
+ url: https://github.com/ayr-ton
+KdHyeon0661:
+ login: KdHyeon0661
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/20253352?u=5ae1aae34b091a39f22cbe60a02b79dcbdbea031&v=4
+ url: https://github.com/KdHyeon0661
+LorhanSohaky:
+ login: LorhanSohaky
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/16273730?u=095b66f243a2cd6a0aadba9a095009f8aaf18393&v=4
+ url: https://github.com/LorhanSohaky
+cfraboulet:
+ login: cfraboulet
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/62244267?u=ed0e286ba48fa1dafd64a08e50f3364b8e12df34&v=4
+ url: https://github.com/cfraboulet
+dedkot01:
+ login: dedkot01
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/26196675?u=e2966887124e67932853df4f10f86cb526edc7b0&v=4
+ url: https://github.com/dedkot01
+AGolicyn:
+ login: AGolicyn
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/86262613?u=3c21606ab8d210a061a1673decff1e7d5592b380&v=4
+ url: https://github.com/AGolicyn
+fhabers21:
+ login: fhabers21
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/58401847?v=4
+ url: https://github.com/fhabers21
+TabarakoAkula:
+ login: TabarakoAkula
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/113298631?u=add801e370dbc502cd94ce6d3484760d7fef5406&v=4
+ url: https://github.com/TabarakoAkula
+AhsanSheraz:
+ login: AhsanSheraz
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/51913596?u=08e31cacb3048be30722c94010ddd028f3fdbec4&v=4
+ url: https://github.com/AhsanSheraz
+ArtemKhymenko:
+ login: ArtemKhymenko
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/14346625?u=f2fa553d9e5ec5e0f05d66bd649f7be347169631&v=4
+ url: https://github.com/ArtemKhymenko
+hasnatsajid:
+ login: hasnatsajid
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/86589885?u=6668823c3b029bfecf10a8918ed3af1aafb8b15e&v=4
+ url: https://github.com/hasnatsajid
+alperiox:
+ login: alperiox
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/34214152?u=2c5acad3461d4dbc2d48371ba86cac56ae9b25cc&v=4
+ url: https://github.com/alperiox
+emrhnsyts:
+ login: emrhnsyts
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/42899027?u=ad26798e3f8feed2041c5dd5f87e58933d6c3283&v=4
+ url: https://github.com/emrhnsyts
+vusallyv:
+ login: vusallyv
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/85983771?u=6fb8e2f876bca06e9f846606423c8f18fb46ad06&v=4
+ url: https://github.com/vusallyv
+jackleeio:
+ login: jackleeio
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/20477587?u=c5184dab6d021733d10c8f975b20e391856303d6&v=4
+ url: https://github.com/jackleeio
+choi-haram:
+ login: choi-haram
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/62204475?v=4
+ url: https://github.com/choi-haram
+imtiaz101325:
+ login: imtiaz101325
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/54007087?u=194d972b501b9ea9d2ddeaed757c492936e0121a&v=4
+ url: https://github.com/imtiaz101325
+fabianfalon:
+ login: fabianfalon
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/3700760?u=95f69e31280b17ac22299cdcd345323b142fe0af&v=4
+ url: https://github.com/fabianfalon
+waketzheng:
+ login: waketzheng
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/35413830?u=df19e4fd5bb928e7d086e053ef26a46aad23bf84&v=4
+ url: https://github.com/waketzheng
+billzhong:
+ login: billzhong
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/1644011?v=4
+ url: https://github.com/billzhong
+chaoless:
+ login: chaoless
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/64477804?v=4
+ url: https://github.com/chaoless
+logan2d5:
+ login: logan2d5
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/146642263?u=dbd6621f8b0330d6919f6a7131277b92e26fbe87&v=4
+ url: https://github.com/logan2d5
+andersonrocha0:
+ login: andersonrocha0
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/22346169?u=93a1359c8c5461d894802c0cc65bcd09217e7a02&v=4
+ url: https://github.com/andersonrocha0
+saeye:
+ login: saeye
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/62229734?u=312d619db2588b60d5d5bde65260a2f44fdc6c76&v=4
+ url: https://github.com/saeye
+11kkw:
+ login: 11kkw
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/21125286?v=4
+ url: https://github.com/11kkw
+yes0ng:
+ login: yes0ng
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/25501794?u=3aed18b0d491e0220a167a1e9e58bea3638c6707&v=4
+ url: https://github.com/yes0ng
+EgorOnishchuk:
+ login: EgorOnishchuk
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/120256301?v=4
+ url: https://github.com/EgorOnishchuk
+EdmilsonRodrigues:
+ login: EdmilsonRodrigues
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/62777025?u=217d6f3cd6cc750bb8818a3af7726c8d74eb7c2d&v=4
+ url: https://github.com/EdmilsonRodrigues
+YuriiMotov:
+ login: YuriiMotov
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/109919500?u=b9b13d598dddfab529a52d264df80a900bfe7060&v=4
+ url: https://github.com/YuriiMotov
diff --git a/docs/en/docs/_llm-test.md b/docs/en/docs/_llm-test.md
new file mode 100644
index 0000000000..e72450b916
--- /dev/null
+++ b/docs/en/docs/_llm-test.md
@@ -0,0 +1,503 @@
+# LLM test file { #llm-test-file }
+
+This document tests if the LLM, which translates the documentation, understands the `general_prompt` in `scripts/translate.py` and the language specific prompt in `docs/{language code}/llm-prompt.md`. The language specific prompt is appended to `general_prompt`.
+
+Tests added here will be seen by all designers of language specific prompts.
+
+Use as follows:
+
+* Have a language specific prompt – `docs/{language code}/llm-prompt.md`.
+* Do a fresh translation of this document into your desired target language (see e.g. the `translate-page` command of the `translate.py`). This will create the translation under `docs/{language code}/docs/_llm-test.md`.
+* Check if things are okay in the translation.
+* If necessary, improve your language specific prompt, the general prompt, or the English document.
+* Then manually fix the remaining issues in the translation, so that it is a good translation.
+* Retranslate, having the good translation in place. The ideal result would be that the LLM makes no changes anymore to the translation. That means that the general prompt and your language specific prompt are as good as they can be (It will sometimes make a few seemingly random changes, the reason is that LLMs are not deterministic algorithms).
+
+The tests:
+
+## Code snippets { #code-snippets}
+
+//// tab | Test
+
+This is a code snippet: `foo`. And this is another code snippet: `bar`. And another one: `baz quux`.
+
+////
+
+//// tab | Info
+
+Content of code snippets should be left as is.
+
+See section `### Content of code snippets` in the general prompt in `scripts/translate.py`.
+
+////
+
+## Quotes { #quotes }
+
+//// tab | Test
+
+Yesterday, my friend wrote: "If you spell incorrectly correctly, you have spelled it incorrectly". To which I answered: "Correct, but 'incorrectly' is incorrectly not '"incorrectly"'".
+
+/// note
+
+The LLM will probably translate this wrong. Interesting is only if it keeps the fixed translation when retranslating.
+
+///
+
+////
+
+//// tab | Info
+
+The prompt designer may choose if they want to convert neutral quotes to typographic quotes. It is okay to leave them as is.
+
+See for example section `### Quotes` in `docs/de/llm-prompt.md`.
+
+////
+
+## Quotes in code snippets { #quotes-in-code-snippets}
+
+//// tab | Test
+
+`pip install "foo[bar]"`
+
+Examples for string literals in code snippets: `"this"`, `'that'`.
+
+A difficult example for string literals in code snippets: `f"I like {'oranges' if orange else "apples"}"`
+
+Hardcore: `Yesterday, my friend wrote: "If you spell incorrectly correctly, you have spelled it incorrectly". To which I answered: "Correct, but 'incorrectly' is incorrectly not '"incorrectly"'"`
+
+////
+
+//// tab | Info
+
+... However, quotes inside code snippets must stay as is.
+
+////
+
+## code blocks { #code-blocks }
+
+//// tab | Test
+
+A Bash code example...
+
+```bash
+# Print a greeting to the universe
+echo "Hello universe"
+```
+
+...and a console code example...
+
+```console
+$ fastapi run main.py
+ FastAPI Starting server
+ Searching for package file structure
+```
+
+...and another console code example...
+
+```console
+// Create a directory "Code"
+$ mkdir code
+// Switch into that directory
+$ cd code
+```
+
+...and a Python code example...
+
+```Python
+wont_work() # This won't work 😱
+works(foo="bar") # This works 🎉
+```
+
+...and that's it.
+
+////
+
+//// tab | Info
+
+Code in code blocks should not be modified, with the exception of comments.
+
+See section `### Content of code blocks` in the general prompt in `scripts/translate.py`.
+
+////
+
+## Tabs and colored boxes { #tabs-and-colored-boxes }
+
+//// tab | Test
+
+/// info
+Some text
+///
+
+/// note
+Some text
+///
+
+/// note | Technical details
+Some text
+///
+
+/// check
+Some text
+///
+
+/// tip
+Some text
+///
+
+/// warning
+Some text
+///
+
+/// danger
+Some text
+///
+
+////
+
+//// tab | Info
+
+Tabs and `Info`/`Note`/`Warning`/etc. blocks should have the translation of their title added after a vertical bar (`|`).
+
+See sections `### Special blocks` and `### Tab blocks` in the general prompt in `scripts/translate.py`.
+
+////
+
+## Web- and internal links { #web-and-internal-links }
+
+//// tab | Test
+
+The link text should get translated, the link address should remain unchaged:
+
+* [Link to heading above](#code-snippets)
+* [Internal link](index.md#installation){.internal-link target=_blank}
+* External link
+* Link to a style
+* Link to a script
+* Link to an image
+
+The link text should get translated, the link address should point to the translation:
+
+* FastAPI link
+
+////
+
+//// tab | Info
+
+Links should be translated, but their address shall remain unchanged. An exception are absolute links to pages of the FastAPI documentation. In that case it should link to the translation.
+
+See section `### Links` in the general prompt in `scripts/translate.py`.
+
+////
+
+## HTML "abbr" elements { #html-abbr-elements }
+
+//// tab | Test
+
+Here some things wrapped in HTML "abbr" elements (Some are invented):
+
+### The abbr gives a full phrase { #the-abbr-gives-a-full-phrase }
+
+* GTD
+* lt
+* XWT
+* PSGI
+
+### The abbr gives an explanation { #the-abbr-gives-an-explanation }
+
+* cluster
+* Deep Learning
+
+### The abbr gives a full phrase and an explanation { #the-abbr-gives-a-full-phrase-and-an-explanation }
+
+* MDN
+* I/O.
+
+////
+
+//// tab | Info
+
+"title" attributes of "abbr" elements are translated following some specific instructions.
+
+Translations can add their own "abbr" elements which the LLM should not remove. E.g. to explain English words.
+
+See section `### HTML abbr elements` in the general prompt in `scripts/translate.py`.
+
+////
+
+## Headings { #headings }
+
+//// tab | Test
+
+### Develop a webapp - a tutorial { #develop-a-webapp-a-tutorial }
+
+Hello.
+
+### Type hints and -annotations { #type-hints-and-annotations }
+
+Hello again.
+
+### Super- and subclasses { #super-and-subclasses }
+
+Hello again.
+
+////
+
+//// tab | Info
+
+The only hard rule for headings is that the LLM leaves the hash part inside curly brackets unchanged, which ensures that links do not break.
+
+See section `### Headings` in the general prompt in `scripts/translate.py`.
+
+For some language specific instructions, see e.g. section `### Headings` in `docs/de/llm-prompt.md`.
+
+////
+
+## Terms used in the docs { #terms-used-in-the-docs }
+
+//// tab | Test
+
+* you
+* your
+
+* e.g.
+* etc.
+
+* `foo` as an `int`
+* `bar` as a `str`
+* `baz` as a `list`
+
+* the Tutorial - User guide
+* the Advanced User Guide
+* the SQLModel docs
+* the API docs
+* the automatic docs
+
+* Data Science
+* Deep Learning
+* Machine Learning
+* Dependency Injection
+* HTTP Basic authentication
+* HTTP Digest
+* ISO format
+* the JSON Schema standard
+* the JSON schema
+* the schema definition
+* Password Flow
+* Mobile
+
+* deprecated
+* designed
+* invalid
+* on the fly
+* standard
+* default
+* case-sensitive
+* case-insensitive
+
+* to serve the application
+* to serve the page
+
+* the app
+* the application
+
+* the request
+* the response
+* the error response
+
+* the path operation
+* the path operation decorator
+* the path operation function
+
+* the body
+* the request body
+* the response body
+* the JSON body
+* the form body
+* the file body
+* the function body
+
+* the parameter
+* the body parameter
+* the path parameter
+* the query parameter
+* the cookie parameter
+* the header parameter
+* the form parameter
+* the function parameter
+
+* the event
+* the startup event
+* the startup of the server
+* the shutdown event
+* the lifespan event
+
+* the handler
+* the event handler
+* the exception handler
+* to handle
+
+* the model
+* the Pydantic model
+* the data model
+* the database model
+* the form model
+* the model object
+
+* the class
+* the base class
+* the parent class
+* the subclass
+* the child class
+* the sibling class
+* the class method
+
+* the header
+* the headers
+* the authorization header
+* the `Authorization` header
+* the forwarded header
+
+* the dependency injection system
+* the dependency
+* the dependable
+* the dependant
+
+* I/O bound
+* CPU bound
+* concurrency
+* parallelism
+* multiprocessing
+
+* the env var
+* the environment variable
+* the `PATH`
+* the `PATH` variable
+
+* the authentication
+* the authentication provider
+* the authorization
+* the authorization form
+* the authorization provider
+* the user authenticates
+* the system authenticates the user
+
+* the CLI
+* the command line interface
+
+* the server
+* the client
+
+* the cloud provider
+* the cloud service
+
+* the development
+* the development stages
+
+* the dict
+* the dictionary
+* the enumeration
+* the enum
+* the enum member
+
+* the encoder
+* the decoder
+* to encode
+* to decode
+
+* the exception
+* to raise
+
+* the expression
+* the statement
+
+* the frontend
+* the backend
+
+* the GitHub discussion
+* the GitHub issue
+
+* the performance
+* the performance optimization
+
+* the return type
+* the return value
+
+* the security
+* the security scheme
+
+* the task
+* the background task
+* the task function
+
+* the template
+* the template engine
+
+* the type annotation
+* the type hint
+
+* the server worker
+* the Uvicorn worker
+* the Gunicorn Worker
+* the worker process
+* the worker class
+* the workload
+
+* the deployment
+* to deploy
+
+* the SDK
+* the software development kit
+
+* the `APIRouter`
+* the `requirements.txt`
+* the Bearer Token
+* the breaking change
+* the bug
+* the button
+* the callable
+* the code
+* the commit
+* the context manager
+* the coroutine
+* the database session
+* the disk
+* the domain
+* the engine
+* the fake X
+* the HTTP GET method
+* the item
+* the library
+* the lifespan
+* the lock
+* the middleware
+* the mobile application
+* the module
+* the mounting
+* the network
+* the origin
+* the override
+* the payload
+* the processor
+* the property
+* the proxy
+* the pull request
+* the query
+* the RAM
+* the remote machine
+* the status code
+* the string
+* the tag
+* the web framework
+* the wildcard
+* to return
+* to validate
+
+////
+
+//// tab | Info
+
+This is a not complete and not normative list of (mostly) technical terms seen in the docs. It may be helpful for the prompt designer to figure out for which terms the LLM needs a helping hand. For example when it keeps reverting a good translation to a suboptimal translation. Or when it has problems conjugating/declinating a term in your language.
+
+See e.g. section `### List of English terms and their preferred German translations` in `docs/de/llm-prompt.md`.
+
+////
diff --git a/docs/en/docs/about/index.md b/docs/en/docs/about/index.md
index 27b78696b5..d178dfec75 100644
--- a/docs/en/docs/about/index.md
+++ b/docs/en/docs/about/index.md
@@ -1,3 +1,3 @@
-# About
+# About { #about }
About FastAPI, its design, inspiration and more. 🤓
diff --git a/docs/en/docs/advanced/additional-responses.md b/docs/en/docs/advanced/additional-responses.md
index 674f0672cb..799532c5b2 100644
--- a/docs/en/docs/advanced/additional-responses.md
+++ b/docs/en/docs/advanced/additional-responses.md
@@ -1,4 +1,4 @@
-# Additional Responses in OpenAPI
+# Additional Responses in OpenAPI { #additional-responses-in-openapi }
/// warning
@@ -14,11 +14,11 @@ Those additional responses will be included in the OpenAPI schema, so they will
But for those additional responses you have to make sure you return a `Response` like `JSONResponse` directly, with your status code and content.
-## Additional Response with `model`
+## Additional Response with `model` { #additional-response-with-model }
You can pass to your *path operation decorators* a parameter `responses`.
-It receives a `dict`, the keys are status codes for each response, like `200`, and the values are other `dict`s with the information for each of them.
+It receives a `dict`: the keys are status codes for each response (like `200`), and the values are other `dict`s with the information for each of them.
Each of those response `dict`s can have a key `model`, containing a Pydantic model, just like `response_model`.
@@ -26,9 +26,7 @@ Each of those response `dict`s can have a key `model`, containing a Pydantic mod
For example, to declare another response with a status code `404` and a Pydantic model `Message`, you can write:
-```Python hl_lines="18 22"
-{!../../../docs_src/additional_responses/tutorial001.py!}
-```
+{* ../../docs_src/additional_responses/tutorial001.py hl[18,22] *}
/// note
@@ -171,15 +169,13 @@ The schemas are referenced to another place inside the OpenAPI schema:
}
```
-## Additional media types for the main response
+## Additional media types for the main response { #additional-media-types-for-the-main-response }
You can use this same `responses` parameter to add different media types for the same main response.
For example, you can add an additional media type of `image/png`, declaring that your *path operation* can return a JSON object (with media type `application/json`) or a PNG image:
-```Python hl_lines="19-24 28"
-{!../../../docs_src/additional_responses/tutorial002.py!}
-```
+{* ../../docs_src/additional_responses/tutorial002.py hl[19:24,28] *}
/// note
@@ -195,7 +191,7 @@ But if you have specified a custom response class with `None` as its media type,
///
-## Combining information
+## Combining information { #combining-information }
You can also combine response information from multiple places, including the `response_model`, `status_code`, and `responses` parameters.
@@ -207,15 +203,13 @@ For example, you can declare a response with a status code `404` that uses a Pyd
And a response with a status code `200` that uses your `response_model`, but includes a custom `example`:
-```Python hl_lines="20-31"
-{!../../../docs_src/additional_responses/tutorial003.py!}
-```
+{* ../../docs_src/additional_responses/tutorial003.py hl[20:31] *}
It will all be combined and included in your OpenAPI, and shown in the API docs:
-## Combine predefined responses and custom ones
+## Combine predefined responses and custom ones { #combine-predefined-responses-and-custom-ones }
You might want to have some predefined responses that apply to many *path operations*, but you want to combine them with custom responses needed by each *path operation*.
@@ -243,11 +237,9 @@ You can use that technique to reuse some predefined responses in your *path oper
For example:
-```Python hl_lines="13-17 26"
-{!../../../docs_src/additional_responses/tutorial004.py!}
-```
+{* ../../docs_src/additional_responses/tutorial004.py hl[13:17,26] *}
-## More information about OpenAPI responses
+## More information about OpenAPI responses { #more-information-about-openapi-responses }
To see what exactly you can include in the responses, you can check these sections in the OpenAPI specification:
diff --git a/docs/en/docs/advanced/additional-status-codes.md b/docs/en/docs/advanced/additional-status-codes.md
index 99ad72b536..23bcd13c32 100644
--- a/docs/en/docs/advanced/additional-status-codes.md
+++ b/docs/en/docs/advanced/additional-status-codes.md
@@ -1,10 +1,10 @@
-# Additional Status Codes
+# Additional Status Codes { #additional-status-codes }
By default, **FastAPI** will return the responses using a `JSONResponse`, putting the content you return from your *path operation* inside of that `JSONResponse`.
It will use the default status code or the one you set in your *path operation*.
-## Additional status codes
+## Additional status codes { #additional-status-codes_1 }
If you want to return additional status codes apart from the main one, you can do that by returning a `Response` directly, like a `JSONResponse`, and set the additional status code directly.
@@ -14,57 +14,7 @@ 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:
-//// tab | Python 3.10+
-
-```Python hl_lines="4 25"
-{!> ../../../docs_src/additional_status_codes/tutorial001_an_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="4 25"
-{!> ../../../docs_src/additional_status_codes/tutorial001_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="4 26"
-{!> ../../../docs_src/additional_status_codes/tutorial001_an.py!}
-```
-
-////
-
-//// tab | 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!}
-```
-
-////
-
-//// tab | Python 3.8+ non-Annotated
-
-/// tip
-
-Prefer to use the `Annotated` version if possible.
-
-///
-
-```Python hl_lines="4 25"
-{!> ../../../docs_src/additional_status_codes/tutorial001.py!}
-```
-
-////
+{* ../../docs_src/additional_status_codes/tutorial001_an_py310.py hl[4,25] *}
/// warning
@@ -76,7 +26,7 @@ Make sure it has the data you want it to have, and that the values are valid JSO
///
-/// note | "Technical Details"
+/// note | Technical Details
You could also use `from starlette.responses import JSONResponse`.
@@ -84,7 +34,7 @@ You could also use `from starlette.responses import JSONResponse`.
///
-## OpenAPI and API docs
+## OpenAPI and API docs { #openapi-and-api-docs }
If you return additional status codes and responses directly, they won't be included in the OpenAPI schema (the API docs), because FastAPI doesn't have a way to know beforehand what you are going to return.
diff --git a/docs/en/docs/advanced/advanced-dependencies.md b/docs/en/docs/advanced/advanced-dependencies.md
index f65e1b1809..e0404b389f 100644
--- a/docs/en/docs/advanced/advanced-dependencies.md
+++ b/docs/en/docs/advanced/advanced-dependencies.md
@@ -1,6 +1,6 @@
-# Advanced Dependencies
+# Advanced Dependencies { #advanced-dependencies }
-## Parameterized dependencies
+## Parameterized dependencies { #parameterized-dependencies }
All the dependencies we have seen are a fixed function or class.
@@ -10,7 +10,7 @@ Let's imagine that we want to have a dependency that checks if the query paramet
But we want to be able to parameterize that fixed content.
-## A "callable" instance
+## A "callable" instance { #a-callable-instance }
In Python there's a way to make an instance of a class a "callable".
@@ -18,111 +18,27 @@ Not the class itself (which is already a callable), but an instance of that clas
To do that, we declare a method `__call__`:
-//// tab | Python 3.9+
-
-```Python hl_lines="12"
-{!> ../../../docs_src/dependencies/tutorial011_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="11"
-{!> ../../../docs_src/dependencies/tutorial011_an.py!}
-```
-
-////
-
-//// tab | Python 3.8+ non-Annotated
-
-/// tip
-
-Prefer to use the `Annotated` version if possible.
-
-///
-
-```Python hl_lines="10"
-{!> ../../../docs_src/dependencies/tutorial011.py!}
-```
-
-////
+{* ../../docs_src/dependencies/tutorial011_an_py39.py hl[12] *}
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.
-## Parameterize the instance
+## Parameterize the instance { #parameterize-the-instance }
And now, we can use `__init__` to declare the parameters of the instance that we can use to "parameterize" the dependency:
-//// tab | Python 3.9+
-
-```Python hl_lines="9"
-{!> ../../../docs_src/dependencies/tutorial011_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="8"
-{!> ../../../docs_src/dependencies/tutorial011_an.py!}
-```
-
-////
-
-//// tab | Python 3.8+ non-Annotated
-
-/// tip
-
-Prefer to use the `Annotated` version if possible.
-
-///
-
-```Python hl_lines="7"
-{!> ../../../docs_src/dependencies/tutorial011.py!}
-```
-
-////
+{* ../../docs_src/dependencies/tutorial011_an_py39.py hl[9] *}
In this case, **FastAPI** won't ever touch or care about `__init__`, we will use it directly in our code.
-## Create an instance
+## Create an instance { #create-an-instance }
We could create an instance of this class with:
-//// tab | Python 3.9+
-
-```Python hl_lines="18"
-{!> ../../../docs_src/dependencies/tutorial011_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="17"
-{!> ../../../docs_src/dependencies/tutorial011_an.py!}
-```
-
-////
-
-//// tab | Python 3.8+ non-Annotated
-
-/// tip
-
-Prefer to use the `Annotated` version if possible.
-
-///
-
-```Python hl_lines="16"
-{!> ../../../docs_src/dependencies/tutorial011.py!}
-```
-
-////
+{* ../../docs_src/dependencies/tutorial011_an_py39.py hl[18] *}
And that way we are able to "parameterize" our dependency, that now has `"bar"` inside of it, as the attribute `checker.fixed_content`.
-## Use the instance as a dependency
+## Use the instance as a dependency { #use-the-instance-as-a-dependency }
Then, we could use this `checker` in a `Depends(checker)`, instead of `Depends(FixedContentQueryChecker)`, because the dependency is the instance, `checker`, not the class itself.
@@ -134,35 +50,7 @@ checker(q="somequery")
...and pass whatever that returns as the value of the dependency in our *path operation function* as the parameter `fixed_content_included`:
-//// tab | Python 3.9+
-
-```Python hl_lines="22"
-{!> ../../../docs_src/dependencies/tutorial011_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="21"
-{!> ../../../docs_src/dependencies/tutorial011_an.py!}
-```
-
-////
-
-//// tab | Python 3.8+ non-Annotated
-
-/// tip
-
-Prefer to use the `Annotated` version if possible.
-
-///
-
-```Python hl_lines="20"
-{!> ../../../docs_src/dependencies/tutorial011.py!}
-```
-
-////
+{* ../../docs_src/dependencies/tutorial011_an_py39.py hl[22] *}
/// tip
@@ -175,3 +63,91 @@ In the chapters about security, there are utility functions that are implemented
If you understood all this, you already know how those utility tools for security work underneath.
///
+
+## Dependencies with `yield`, `HTTPException`, `except` and Background Tasks { #dependencies-with-yield-httpexception-except-and-background-tasks }
+
+/// warning
+
+You most probably don't need these technical details.
+
+These details are useful mainly if you had a FastAPI application older than 0.118.0 and you are facing issues with dependencies with `yield`.
+
+///
+
+Dependencies with `yield` have evolved over time to account for the different use cases and to fix some issues, here's a summary of what has changed.
+
+### Dependencies with `yield` and `StreamingResponse`, Technical Details { #dependencies-with-yield-and-streamingresponse-technical-details }
+
+Before FastAPI 0.118.0, if you used a dependency with `yield`, it would run the exit code after the *path operation function* returned but right before sending the response.
+
+The intention was to avoid holding resources for longer than necessary, waiting for the response to travel through the network.
+
+This change also meant that if you returned a `StreamingResponse`, the exit code of the dependency with `yield` would have been already run.
+
+For example, if you had a database session in a dependency with `yield`, the `StreamingResponse` would not be able to use that session while streaming data because the session would have already been closed in the exit code after `yield`.
+
+This behavior was reverted in 0.118.0, to make the exit code after `yield` be executed after the response is sent.
+
+/// info
+
+As you will see below, this is very similar to the behavior before version 0.106.0, but with several improvements and bug fixes for corner cases.
+
+///
+
+#### Use Cases with Early Exit Code { #use-cases-with-early-exit-code }
+
+There are some use cases with specific conditions that could benefit from the old behavior of running the exit code of dependencies with `yield` before sending the response.
+
+For example, imagine you have code that uses a database session in a dependency with `yield` only to verify a user, but the database session is never used again in the *path operation function*, only in the dependency, **and** the response takes a long time to be sent, like a `StreamingResponse` that sends data slowly, but for some reason doesn't use the database.
+
+In this case, the database session would be held until the response is finished being sent, but if you don't use it, then it wouldn't be necessary to hold it.
+
+Here's how it could look like:
+
+{* ../../docs_src/dependencies/tutorial013_an_py310.py *}
+
+The exit code, the automatic closing of the `Session` in:
+
+{* ../../docs_src/dependencies/tutorial013_an_py310.py ln[19:21] *}
+
+...would be run after the the response finishes sending the slow data:
+
+{* ../../docs_src/dependencies/tutorial013_an_py310.py ln[30:38] hl[31:33] *}
+
+But as `generate_stream()` doesn't use the database session, it is not really necessary to keep the session open while sending the response.
+
+If you have this specific use case using SQLModel (or SQLAlchemy), you could explicitly close the session after you don't need it anymore:
+
+{* ../../docs_src/dependencies/tutorial014_an_py310.py ln[24:28] hl[28] *}
+
+That way the session would release the database connection, so other requests could use it.
+
+If you have a different use case that needs to exit early from a dependency with `yield`, please create a GitHub Discussion Question with your specific use case and why you would benefit from having early closing for dependencies with `yield`.
+
+If there are compelling use cases for early closing in dependencies with `yield`, I would consider adding a new way to opt in to early closing.
+
+### Dependencies with `yield` and `except`, Technical Details { #dependencies-with-yield-and-except-technical-details }
+
+Before FastAPI 0.110.0, if you used a dependency with `yield`, and then you captured an exception with `except` in that dependency, and you didn't raise the exception again, the exception would be automatically raised/forwarded to any exception handlers or the internal server error handler.
+
+This was changed in version 0.110.0 to fix unhandled memory consumption from forwarded exceptions without a handler (internal server errors), and to make it consistent with the behavior of regular Python code.
+
+### Background Tasks and Dependencies with `yield`, Technical Details { #background-tasks-and-dependencies-with-yield-technical-details }
+
+Before FastAPI 0.106.0, raising exceptions after `yield` was not possible, the exit code in dependencies with `yield` was executed *after* the response was sent, so [Exception Handlers](../handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank} would have already run.
+
+This was designed this way mainly to allow using the same objects "yielded" by dependencies inside of background tasks, because the exit code would be executed after the background tasks were finished.
+
+This was changed in FastAPI 0.106.0 with the intention to not hold resources while waiting for the response to travel through the network.
+
+/// tip
+
+Additionally, a background task is normally an independent set of logic that should be handled separately, with its own resources (e.g. its own database connection).
+
+So, this way you will probably have cleaner code.
+
+///
+
+If you used to rely on this behavior, now you should create the resources for background tasks inside the background task itself, and use internally only data that doesn't depend on the resources of dependencies with `yield`.
+
+For example, instead of using the same database session, you would create a new database session inside of the background task, and you would obtain the objects from the database using this new session. And then instead of passing the object from the database as a parameter to the background task function, you would pass the ID of that object and then obtain the object again inside the background task function.
diff --git a/docs/en/docs/advanced/async-tests.md b/docs/en/docs/advanced/async-tests.md
index ac459ff0c8..e920e22c3c 100644
--- a/docs/en/docs/advanced/async-tests.md
+++ b/docs/en/docs/advanced/async-tests.md
@@ -1,4 +1,4 @@
-# Async Tests
+# Async Tests { #async-tests }
You have already seen how to test your **FastAPI** applications using the provided `TestClient`. Up to now, you have only seen how to write synchronous tests, without using `async` functions.
@@ -6,11 +6,11 @@ Being able to use asynchronous functions in your tests could be useful, for exam
Let's look at how we can make that work.
-## pytest.mark.anyio
+## pytest.mark.anyio { #pytest-mark-anyio }
If we want to call asynchronous functions in our tests, our test functions have to be asynchronous. AnyIO provides a neat plugin for this, that allows us to specify that some test functions are to be called asynchronously.
-## HTTPX
+## HTTPX { #httpx }
Even if your **FastAPI** application uses normal `def` functions instead of `async def`, it is still an `async` application underneath.
@@ -18,7 +18,7 @@ The `TestClient` does some magic inside to call the asynchronous FastAPI applica
The `TestClient` is based on HTTPX, and luckily, we can use it directly to test the API.
-## Example
+## Example { #example }
For a simple example, let's consider a file structure similar to the one described in [Bigger Applications](../tutorial/bigger-applications.md){.internal-link target=_blank} and [Testing](../tutorial/testing.md){.internal-link target=_blank}:
@@ -32,17 +32,13 @@ For a simple example, let's consider a file structure similar to the one describ
The file `main.py` would have:
-```Python
-{!../../../docs_src/async_tests/main.py!}
-```
+{* ../../docs_src/async_tests/main.py *}
The file `test_main.py` would have the tests for `main.py`, it could look like this now:
-```Python
-{!../../../docs_src/async_tests/test_main.py!}
-```
+{* ../../docs_src/async_tests/test_main.py *}
-## Run it
+## Run it { #run-it }
You can run your tests as usual via:
@@ -56,13 +52,11 @@ $ pytest
-## Available responses
+## Available responses { #available-responses }
Here are some of the available responses.
Keep in mind that you can use `Response` to return anything else, or even create a custom sub-class.
-/// note | "Technical Details"
+/// note | Technical Details
You could also use `from starlette.responses import HTMLResponse`.
@@ -129,7 +121,7 @@ You could also use `from starlette.responses import HTMLResponse`.
///
-### `Response`
+### `Response` { #response }
The main `Response` class, all the other responses inherit from it.
@@ -142,31 +134,27 @@ It accepts the following parameters:
* `headers` - A `dict` of strings.
* `media_type` - A `str` giving the media type. E.g. `"text/html"`.
-FastAPI (actually Starlette) will automatically include a Content-Length header. It will also include a Content-Type header, based on the media_type and appending a charset for text types.
+FastAPI (actually Starlette) will automatically include a Content-Length header. It will also include a Content-Type header, based on the `media_type` and appending a charset for text types.
-```Python hl_lines="1 18"
-{!../../../docs_src/response_directly/tutorial002.py!}
-```
+{* ../../docs_src/response_directly/tutorial002.py hl[1,18] *}
-### `HTMLResponse`
+### `HTMLResponse` { #htmlresponse }
Takes some text or bytes and returns an HTML response, as you read above.
-### `PlainTextResponse`
+### `PlainTextResponse` { #plaintextresponse }
-Takes some text or bytes and returns an plain text response.
+Takes some text or bytes and returns a plain text response.
-```Python hl_lines="2 7 9"
-{!../../../docs_src/custom_response/tutorial005.py!}
-```
+{* ../../docs_src/custom_response/tutorial005.py hl[2,7,9] *}
-### `JSONResponse`
+### `JSONResponse` { #jsonresponse }
Takes some data and returns an `application/json` encoded response.
This is the default response used in **FastAPI**, as you read above.
-### `ORJSONResponse`
+### `ORJSONResponse` { #orjsonresponse }
A fast alternative JSON response using `orjson`, as you read above.
@@ -176,7 +164,7 @@ This requires installing `orjson` for example with `pip install orjson`.
///
-### `UJSONResponse`
+### `UJSONResponse` { #ujsonresponse }
An alternative JSON response using `ujson`.
@@ -192,9 +180,7 @@ This requires installing `ujson` for example with `pip install ujson`.
///
-```Python hl_lines="2 7"
-{!../../../docs_src/custom_response/tutorial001.py!}
-```
+{* ../../docs_src/custom_response/tutorial001.py hl[2,7] *}
/// tip
@@ -202,24 +188,20 @@ It's possible that `ORJSONResponse` might be a faster alternative.
///
-### `RedirectResponse`
+### `RedirectResponse` { #redirectresponse }
Returns an HTTP redirect. Uses a 307 status code (Temporary Redirect) by default.
You can return a `RedirectResponse` directly:
-```Python hl_lines="2 9"
-{!../../../docs_src/custom_response/tutorial006.py!}
-```
+{* ../../docs_src/custom_response/tutorial006.py hl[2,9] *}
---
Or you can use it in the `response_class` parameter:
-```Python hl_lines="2 7 9"
-{!../../../docs_src/custom_response/tutorial006b.py!}
-```
+{* ../../docs_src/custom_response/tutorial006b.py hl[2,7,9] *}
If you do that, then you can return the URL directly from your *path operation* function.
@@ -229,29 +211,23 @@ In this case, the `status_code` used will be the default one for the `RedirectRe
You can also use the `status_code` parameter combined with the `response_class` parameter:
-```Python hl_lines="2 7 9"
-{!../../../docs_src/custom_response/tutorial006c.py!}
-```
+{* ../../docs_src/custom_response/tutorial006c.py hl[2,7,9] *}
-### `StreamingResponse`
+### `StreamingResponse` { #streamingresponse }
Takes an async generator or a normal generator/iterator and streams the response body.
-```Python hl_lines="2 14"
-{!../../../docs_src/custom_response/tutorial007.py!}
-```
+{* ../../docs_src/custom_response/tutorial007.py hl[2,14] *}
-#### Using `StreamingResponse` with file-like objects
+#### Using `StreamingResponse` with file-like objects { #using-streamingresponse-with-file-like-objects }
-If you have a file-like object (e.g. the object returned by `open()`), you can create a generator function to iterate over that file-like object.
+If you have a file-like object (e.g. the object returned by `open()`), you can create a generator function to iterate over that file-like object.
That way, you don't have to read it all first in memory, and you can pass that generator function to the `StreamingResponse`, and return it.
This includes many libraries to interact with cloud storage, video processing, and others.
-```{ .python .annotate hl_lines="2 10-12 14" }
-{!../../../docs_src/custom_response/tutorial008.py!}
-```
+{* ../../docs_src/custom_response/tutorial008.py hl[2,10:12,14] *}
1. This is the generator function. It's a "generator function" because it contains `yield` statements inside.
2. By using a `with` block, we make sure that the file-like object is closed after the generator function is done. So, after it finishes sending the response.
@@ -267,32 +243,28 @@ Notice that here as we are using standard `open()` that doesn't support `async`
///
-### `FileResponse`
+### `FileResponse` { #fileresponse }
Asynchronously streams a file as the response.
Takes a different set of arguments to instantiate than the other response types:
-* `path` - The filepath to the file to stream.
+* `path` - The file path to the file to stream.
* `headers` - Any custom headers to include, as a dictionary.
* `media_type` - A string giving the media type. If unset, the filename or path will be used to infer a media type.
* `filename` - If set, this will be included in the response `Content-Disposition`.
File responses will include appropriate `Content-Length`, `Last-Modified` and `ETag` headers.
-```Python hl_lines="2 10"
-{!../../../docs_src/custom_response/tutorial009.py!}
-```
+{* ../../docs_src/custom_response/tutorial009.py hl[2,10] *}
You can also use the `response_class` parameter:
-```Python hl_lines="2 8 10"
-{!../../../docs_src/custom_response/tutorial009b.py!}
-```
+{* ../../docs_src/custom_response/tutorial009b.py hl[2,8,10] *}
In this case, you can return the file path directly from your *path operation* function.
-## Custom response class
+## Custom response class { #custom-response-class }
You can create your own custom response class, inheriting from `Response` and using it.
@@ -302,9 +274,7 @@ Let's say you want it to return indented and formatted JSON, so you want to use
You could create a `CustomORJSONResponse`. The main thing you have to do is create a `Response.render(content)` method that returns the content as `bytes`:
-```Python hl_lines="9-14 17"
-{!../../../docs_src/custom_response/tutorial009c.py!}
-```
+{* ../../docs_src/custom_response/tutorial009c.py hl[9:14,17] *}
Now instead of returning:
@@ -322,7 +292,7 @@ Now instead of returning:
Of course, you will probably find much better ways to take advantage of this than formatting JSON. 😉
-## Default response class
+## Default response class { #default-response-class }
When creating a **FastAPI** class instance or an `APIRouter` you can specify which response class to use by default.
@@ -330,9 +300,7 @@ The parameter that defines this is `default_response_class`.
In the example below, **FastAPI** will use `ORJSONResponse` by default, in all *path operations*, instead of `JSONResponse`.
-```Python hl_lines="2 4"
-{!../../../docs_src/custom_response/tutorial010.py!}
-```
+{* ../../docs_src/custom_response/tutorial010.py hl[2,4] *}
/// tip
@@ -340,6 +308,6 @@ You can still override `response_class` in *path operations* as before.
///
-## Additional documentation
+## Additional documentation { #additional-documentation }
You can also declare the media type and many other details in OpenAPI using `responses`: [Additional Responses in OpenAPI](additional-responses.md){.internal-link target=_blank}.
diff --git a/docs/en/docs/advanced/dataclasses.md b/docs/en/docs/advanced/dataclasses.md
index 252ab6fa5e..b7b9b65c52 100644
--- a/docs/en/docs/advanced/dataclasses.md
+++ b/docs/en/docs/advanced/dataclasses.md
@@ -1,12 +1,10 @@
-# Using Dataclasses
+# Using Dataclasses { #using-dataclasses }
FastAPI is built on top of **Pydantic**, and I have been showing you how to use Pydantic models to declare requests and responses.
But FastAPI also supports using `dataclasses` the same way:
-```Python hl_lines="1 7-12 19-20"
-{!../../../docs_src/dataclasses/tutorial001.py!}
-```
+{* ../../docs_src/dataclasses/tutorial001.py hl[1,7:12,19:20] *}
This is still supported thanks to **Pydantic**, as it has internal support for `dataclasses`.
@@ -30,13 +28,11 @@ But if you have a bunch of dataclasses laying around, this is a nice trick to us
///
-## Dataclasses in `response_model`
+## Dataclasses in `response_model` { #dataclasses-in-response-model }
You can also use `dataclasses` in the `response_model` parameter:
-```Python hl_lines="1 7-13 19"
-{!../../../docs_src/dataclasses/tutorial002.py!}
-```
+{* ../../docs_src/dataclasses/tutorial002.py hl[1,7:13,19] *}
The dataclass will be automatically converted to a Pydantic dataclass.
@@ -44,7 +40,7 @@ This way, its schema will show up in the API docs user interface:
-## Dataclasses in Nested Data Structures
+## Dataclasses in Nested Data Structures { #dataclasses-in-nested-data-structures }
You can also combine `dataclasses` with other type annotations to make nested data structures.
@@ -52,9 +48,7 @@ In some cases, you might still have to use Pydantic's version of `dataclasses`.
In that case, you can simply swap the standard `dataclasses` with `pydantic.dataclasses`, which is a drop-in replacement:
-```{ .python .annotate hl_lines="1 5 8-11 14-17 23-25 28" }
-{!../../../docs_src/dataclasses/tutorial003.py!}
-```
+{* ../../docs_src/dataclasses/tutorial003.py hl[1,5,8:11,14:17,23:25,28] *}
1. We still import `field` from standard `dataclasses`.
@@ -90,12 +84,12 @@ You can combine `dataclasses` with other type annotations in many different comb
Check the in-code annotation tips above to see more specific details.
-## Learn More
+## Learn More { #learn-more }
You can also combine `dataclasses` with other Pydantic models, inherit from them, include them in your own models, etc.
To learn more, check the Pydantic docs about dataclasses.
-## Version
+## Version { #version }
This is available since FastAPI version `0.67.0`. 🔖
diff --git a/docs/en/docs/advanced/events.md b/docs/en/docs/advanced/events.md
index 7fd9343446..d9e3cb52e7 100644
--- a/docs/en/docs/advanced/events.md
+++ b/docs/en/docs/advanced/events.md
@@ -1,4 +1,4 @@
-# Lifespan Events
+# Lifespan Events { #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**.
@@ -8,7 +8,7 @@ Because this code is executed before the application **starts** taking requests,
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
+## Use Case { #use-case }
Let's start with an example **use case** and then see how to solve it with this.
@@ -22,7 +22,7 @@ You could load it at the top level of the module/file, but that would also mean
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
+## Lifespan { #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).
@@ -30,9 +30,7 @@ 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!}
-```
+{* ../../docs_src/events/tutorial003.py hl[16,19] *}
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*.
@@ -46,27 +44,23 @@ Maybe you need to start a new version, or you just got tired of running it. 🤷
///
-### Lifespan function
+### Lifespan function { #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!}
-```
+{* ../../docs_src/events/tutorial003.py hl[14:19] *}
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
+### Async Context Manager { #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!}
-```
+{* ../../docs_src/events/tutorial003.py hl[1,13] *}
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:
@@ -88,11 +82,9 @@ In our code example above, we don't use it directly, but we pass it to FastAPI f
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!}
-```
+{* ../../docs_src/events/tutorial003.py hl[22] *}
-## Alternative Events (deprecated)
+## Alternative Events (deprecated) { #alternative-events-deprecated }
/// warning
@@ -108,13 +100,11 @@ You can define event handlers (functions) that need to be executed before the ap
These functions can be declared with `async def` or normal `def`.
-### `startup` event
+### `startup` event { #startup-event }
To add a function that should be run before the application starts, declare it with the event `"startup"`:
-```Python hl_lines="8"
-{!../../../docs_src/events/tutorial001.py!}
-```
+{* ../../docs_src/events/tutorial001.py hl[8] *}
In this case, the `startup` event handler function will initialize the items "database" (just a `dict`) with some values.
@@ -122,13 +112,11 @@ 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 { #shutdown-event }
To add a function that should be run when the application is shutting down, declare it with the event `"shutdown"`:
-```Python hl_lines="6"
-{!../../../docs_src/events/tutorial002.py!}
-```
+{* ../../docs_src/events/tutorial002.py hl[6] *}
Here, the `shutdown` event handler function will write a text line `"Application shutdown"` to a file `log.txt`.
@@ -150,7 +138,7 @@ So, we declare the event handler function with standard `def` instead of `async
///
-### `startup` and `shutdown` together
+### `startup` and `shutdown` together { #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.
@@ -158,7 +146,7 @@ Doing that in separated functions that don't share logic or variables together i
Because of that, it's now recommended to instead use the `lifespan` as explained above.
-## Technical Details
+## Technical Details { #technical-details }
Just a technical detail for the curious nerds. 🤓
@@ -166,12 +154,12 @@ Underneath, in the ASGI technical specification, this is part of the Starlette's Lifespan' docs.
+You can read more about the Starlette `lifespan` handlers in Starlette's Lifespan' docs.
Including how to handle lifespan state that can be used in other areas of your code.
///
-## Sub Applications
+## Sub Applications { #sub-applications }
🚨 Keep 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}.
diff --git a/docs/en/docs/advanced/generate-clients.md b/docs/en/docs/advanced/generate-clients.md
index faa7c323f5..897c308086 100644
--- a/docs/en/docs/advanced/generate-clients.md
+++ b/docs/en/docs/advanced/generate-clients.md
@@ -1,129 +1,76 @@
-# Generate Clients
+# Generating SDKs { #generating-sdks }
-As **FastAPI** is based on the OpenAPI specification, you get automatic compatibility with many tools, including the automatic API docs (provided by Swagger UI).
+Because **FastAPI** is based on the **OpenAPI** specification, its APIs can be described in a standard format that many tools understand.
-One particular advantage that is not necessarily obvious is that you can **generate clients** (sometimes called **SDKs** ) for your API, for many different **programming languages**.
+This makes it easy to generate up-to-date **documentation**, client libraries (**SDKs**) in multiple languages, and **testing** or **automation workflows** that stay in sync with your code.
-## OpenAPI Client Generators
+In this guide, you'll learn how to generate a **TypeScript SDK** for your FastAPI backend.
-There are many tools to generate clients from **OpenAPI**.
+## Open Source SDK Generators { #open-source-sdk-generators }
-A common tool is OpenAPI Generator.
+A versatile option is the OpenAPI Generator, which supports **many programming languages** and can generate SDKs from your OpenAPI specification.
-If you are building a **frontend**, a very interesting alternative is openapi-ts.
+For **TypeScript clients**, Hey API is a purpose-built solution, providing an optimized experience for the TypeScript ecosystem.
-## Client and SDK Generators - Sponsor
+You can discover more SDK generators on OpenAPI.Tools.
-There are also some **company-backed** Client and SDK generators based on OpenAPI (FastAPI), in some cases they can offer you **additional features** on top of high-quality generated SDKs/clients.
+/// tip
-Some of them also ✨ [**sponsor FastAPI**](../help-fastapi.md#sponsor-the-author){.internal-link target=_blank} ✨, this ensures the continued and healthy **development** of FastAPI and its **ecosystem**.
+FastAPI automatically generates **OpenAPI 3.1** specifications, so any tool you use must support this version.
-And it shows their true commitment to FastAPI and its **community** (you), as they not only want to provide you a **good service** but also want to make sure you have a **good and healthy framework**, FastAPI. 🙇
+///
+
+## SDK Generators from FastAPI Sponsors { #sdk-generators-from-fastapi-sponsors }
+
+This section highlights **venture-backed** and **company-supported** solutions from companies that sponsor FastAPI. These products provide **additional features** and **integrations** on top of high-quality generated SDKs.
+
+By ✨ [**sponsoring FastAPI**](../help-fastapi.md#sponsor-the-author){.internal-link target=_blank} ✨, these companies help ensure the framework and its **ecosystem** remain healthy and **sustainable**.
+
+Their sponsorship also demonstrates a strong commitment to the FastAPI **community** (you), showing that they care not only about offering a **great service** but also about supporting a **robust and thriving framework**, FastAPI. 🙇
For example, you might want to try:
-* Speakeasy
-* Stainless
-* liblab
+* Speakeasy
+* Stainless
+* liblab
-There are also several other companies offering similar services that you can search and find online. 🤓
+Some of these solutions may also be open source or offer free tiers, so you can try them without a financial commitment. Other commercial SDK generators are available and can be found online. 🤓
-## Generate a TypeScript Frontend Client
+## Create a TypeScript SDK { #create-a-typescript-sdk }
Let's start with a simple FastAPI application:
-//// tab | Python 3.9+
-
-```Python hl_lines="7-9 12-13 16-17 21"
-{!> ../../../docs_src/generate_clients/tutorial001_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="9-11 14-15 18 19 23"
-{!> ../../../docs_src/generate_clients/tutorial001.py!}
-```
-
-////
+{* ../../docs_src/generate_clients/tutorial001_py39.py hl[7:9,12:13,16:17,21] *}
Notice that the *path operations* define the models they use for request payload and response payload, using the models `Item` and `ResponseMessage`.
-### API Docs
+### API Docs { #api-docs }
-If you go to the API docs, you will see that it has the **schemas** for the data to be sent in requests and received in responses:
+If you go to `/docs`, you will see that it has the **schemas** for the data to be sent in requests and received in responses:
You can see those schemas because they were declared with the models in the app.
-That information is available in the app's **OpenAPI schema**, and then shown in the API docs (by Swagger UI).
+That information is available in the app's **OpenAPI schema**, and then shown in the API docs.
-And that same information from the models that is included in OpenAPI is what can be used to **generate the client code**.
+That same information from the models that is included in OpenAPI is what can be used to **generate the client code**.
-### Generate a TypeScript Client
+### Hey API { #hey-api }
-Now that we have the app with the models, we can generate the client code for the frontend.
+Once we have a FastAPI app with the models, we can use Hey API to generate a TypeScript client. The fastest way to do that is via npx.
-#### Install `openapi-ts`
-
-You can install `openapi-ts` in your frontend code with:
-
-
@@ -145,44 +92,30 @@ The response object will also have autocompletion:
-## FastAPI App with Tags
+## FastAPI App with Tags { #fastapi-app-with-tags }
-In many cases your FastAPI app will be bigger, and you will probably use tags to separate different groups of *path operations*.
+In many cases, your FastAPI app will be bigger, and you will probably use tags to separate different groups of *path operations*.
For example, you could have a section for **items** and another section for **users**, and they could be separated by tags:
-//// tab | Python 3.9+
+{* ../../docs_src/generate_clients/tutorial002_py39.py hl[21,26,34] *}
-```Python hl_lines="21 26 34"
-{!> ../../../docs_src/generate_clients/tutorial002_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="23 28 36"
-{!> ../../../docs_src/generate_clients/tutorial002.py!}
-```
-
-////
-
-### Generate a TypeScript Client with Tags
+### Generate a TypeScript Client with Tags { #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.
-This way you will be able to have things ordered and grouped correctly for the client code:
+This way, you will be able to have things ordered and grouped correctly for the client code:
-In this case you have:
+In this case, you have:
* `ItemsService`
* `UsersService`
-### Client Method Names
+### Client Method Names { #client-method-names }
-Right now the generated method names like `createItemItemsPost` don't look very clean:
+Right now, the generated method names like `createItemItemsPost` don't look very clean:
```TypeScript
ItemsService.createItemItemsPost({name: "Plumbus", price: 5})
@@ -194,17 +127,17 @@ OpenAPI requires that each operation ID is unique across all the *path operation
But I'll show you how to improve that next. 🤓
-## Custom Operation IDs and Better Method Names
+## Custom Operation IDs and Better Method Names { #custom-operation-ids-and-better-method-names }
You can **modify** the way these operation IDs are **generated** to make them simpler and have **simpler method names** in the clients.
-In this case you will have to ensure that each operation ID is **unique** in some other way.
+In this case, you will have to ensure that each operation ID is **unique** in some other way.
For example, you could make sure that each *path operation* has a tag, and then generate the operation ID based on the **tag** and the *path operation* **name** (the function name).
-### Custom Generate Unique ID Function
+### Custom Generate Unique ID Function { #custom-generate-unique-id-function }
-FastAPI uses a **unique ID** for each *path operation*, it is used for the **operation ID** and also for the names of any needed custom models, for requests or responses.
+FastAPI uses a **unique ID** for each *path operation*, which is used for the **operation ID** and also for the names of any needed custom models, for requests or responses.
You can customize that function. It takes an `APIRoute` and outputs a string.
@@ -212,31 +145,17 @@ 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:
-//// tab | Python 3.9+
+{* ../../docs_src/generate_clients/tutorial003_py39.py hl[6:7,10] *}
-```Python hl_lines="6-7 10"
-{!> ../../../docs_src/generate_clients/tutorial003_py39.py!}
-```
+### Generate a TypeScript Client with Custom Operation IDs { #generate-a-typescript-client-with-custom-operation-ids }
-////
-
-//// tab | Python 3.8+
-
-```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:
+Now, if you generate the client again, you will see that it has the improved method names:
As you see, the method names now have the tag and then the function name, now they don't include information from the URL path and the HTTP operation.
-### Preprocess the OpenAPI Specification for the Client Generator
+### Preprocess the OpenAPI Specification for the Client Generator { #preprocess-the-openapi-specification-for-the-client-generator }
The generated code still has some **duplicated information**.
@@ -244,57 +163,37 @@ We already know that this method is related to the **items** because that word i
We will probably still want to keep it for OpenAPI in general, as that will ensure that the operation IDs are **unique**.
-But for the generated client we could **modify** the OpenAPI operation IDs right before generating the clients, just to make those method names nicer and **cleaner**.
+But for the generated client, we could **modify** the OpenAPI operation IDs right before generating the clients, just to make those method names nicer and **cleaner**.
We could download the OpenAPI JSON to a file `openapi.json` and then we could **remove that prefixed tag** with a script like this:
-//// tab | Python
-
-```Python
-{!> ../../../docs_src/generate_clients/tutorial004.py!}
-```
-
-////
+{* ../../docs_src/generate_clients/tutorial004.py *}
//// tab | Node.js
```Javascript
-{!> ../../../docs_src/generate_clients/tutorial004.js!}
+{!> ../../docs_src/generate_clients/tutorial004.js!}
```
////
With that, the operation IDs would be renamed from things like `items-get_items` to just `get_items`, that way the client generator can generate simpler method names.
-### Generate a TypeScript Client with the Preprocessed OpenAPI
+### Generate a TypeScript Client with the Preprocessed OpenAPI { #generate-a-typescript-client-with-the-preprocessed-openapi }
-Now as the end result is in a file `openapi.json`, you would modify the `package.json` to use that local file, for example:
+Since the end result is now in an `openapi.json` file, you need to update your input location:
-```JSON hl_lines="7"
-{
- "name": "frontend-app",
- "version": "1.0.0",
- "description": "",
- "main": "index.js",
- "scripts": {
- "generate-client": "openapi-ts --input ./openapi.json --output ./src/client --client axios"
- },
- "author": "",
- "license": "",
- "devDependencies": {
- "@hey-api/openapi-ts": "^0.27.38",
- "typescript": "^4.6.2"
- }
-}
+```sh
+npx @hey-api/openapi-ts -i ./openapi.json -o src/client
```
After generating the new client, you would now have **clean method names**, with all the **autocompletion**, **inline errors**, etc:
-## Benefits
+## Benefits { #benefits }
-When using the automatically generated clients you would get **autocompletion** for:
+When using the automatically generated clients, you would get **autocompletion** for:
* Methods.
* Request payloads in the body, query parameters, etc.
@@ -304,6 +203,6 @@ You would also have **inline errors** for everything.
And whenever you update the backend code, and **regenerate** the frontend, it would have any new *path operations* available as methods, the old ones removed, and any other change would be reflected on the generated code. 🤓
-This also means that if something changed it will be **reflected** on the client code automatically. And if you **build** the client it will error out if you have any **mismatch** in the data used.
+This also means that if something changed, it will be **reflected** on the client code automatically. And if you **build** the client, it will error out if you have any **mismatch** in the data used.
So, you would **detect many errors** very early in the development cycle instead of having to wait for the errors to show up to your final users in production and then trying to debug where the problem is. ✨
diff --git a/docs/en/docs/advanced/index.md b/docs/en/docs/advanced/index.md
index 36f0720c0e..9355516fb4 100644
--- a/docs/en/docs/advanced/index.md
+++ b/docs/en/docs/advanced/index.md
@@ -1,6 +1,6 @@
-# Advanced User Guide
+# Advanced User Guide { #advanced-user-guide }
-## Additional Features
+## Additional Features { #additional-features }
The main [Tutorial - User Guide](../tutorial/index.md){.internal-link target=_blank} should be enough to give you a tour through all the main features of **FastAPI**.
@@ -14,23 +14,8 @@ And it's possible that for your use case, the solution is in one of them.
///
-## Read the Tutorial first
+## Read the Tutorial first { #read-the-tutorial-first }
You could still use most of the features in **FastAPI** with the knowledge from the main [Tutorial - User Guide](../tutorial/index.md){.internal-link target=_blank}.
And the next sections assume you already read it, and assume that you know those main ideas.
-
-## External Courses
-
-Although the [Tutorial - User Guide](../tutorial/index.md){.internal-link target=_blank} and this **Advanced User Guide** are written as a guided tutorial (like a book) and should be enough for you to **learn FastAPI**, you might want to complement it with additional courses.
-
-Or it might be the case that you just prefer to take other courses because they adapt better to your learning style.
-
-Some course providers ✨ [**sponsor FastAPI**](../help-fastapi.md#sponsor-the-author){.internal-link target=_blank} ✨, this ensures the continued and healthy **development** of FastAPI and its **ecosystem**.
-
-And it shows their true commitment to FastAPI and its **community** (you), as they not only want to provide you a **good learning experience** but also want to make sure you have a **good and healthy framework**, FastAPI. 🙇
-
-You might want to try their courses:
-
-* Talk Python Training
-* Test-Driven Development
diff --git a/docs/en/docs/advanced/middleware.md b/docs/en/docs/advanced/middleware.md
index 70415adca3..8deb0d917d 100644
--- a/docs/en/docs/advanced/middleware.md
+++ b/docs/en/docs/advanced/middleware.md
@@ -1,4 +1,4 @@
-# Advanced Middleware
+# Advanced Middleware { #advanced-middleware }
In the main tutorial you read how to add [Custom Middleware](../tutorial/middleware.md){.internal-link target=_blank} to your application.
@@ -6,7 +6,7 @@ And then you also read how to handle [CORS with the `CORSMiddleware`](../tutoria
In this section we'll see how to use other middlewares.
-## Adding ASGI middlewares
+## Adding ASGI middlewares { #adding-asgi-middlewares }
As **FastAPI** is based on Starlette and implements the ASGI specification, you can use any ASGI middleware.
@@ -24,7 +24,7 @@ app = SomeASGIApp()
new_app = UnicornMiddleware(app, some_config="rainbow")
```
-But FastAPI (actually Starlette) provides a simpler way to do it that makes sure that the internal middlewares to handle server errors and custom exception handlers work properly.
+But FastAPI (actually Starlette) provides a simpler way to do it that makes sure that the internal middlewares handle server errors and custom exception handlers work properly.
For that, you use `app.add_middleware()` (as in the example for CORS).
@@ -39,11 +39,11 @@ app.add_middleware(UnicornMiddleware, some_config="rainbow")
`app.add_middleware()` receives a middleware class as the first argument and any additional arguments to be passed to the middleware.
-## Integrated middlewares
+## Integrated middlewares { #integrated-middlewares }
**FastAPI** includes several middlewares for common use cases, we'll see next how to use them.
-/// note | "Technical Details"
+/// note | Technical Details
For the next examples, you could also use `from starlette.middleware.something import SomethingMiddleware`.
@@ -51,46 +51,41 @@ For the next examples, you could also use `from starlette.middleware.something i
///
-## `HTTPSRedirectMiddleware`
+## `HTTPSRedirectMiddleware` { #httpsredirectmiddleware }
Enforces that all incoming requests must either be `https` or `wss`.
-Any incoming requests to `http` or `ws` will be redirected to the secure scheme instead.
+Any incoming request to `http` or `ws` will be redirected to the secure scheme instead.
-```Python hl_lines="2 6"
-{!../../../docs_src/advanced_middleware/tutorial001.py!}
-```
+{* ../../docs_src/advanced_middleware/tutorial001.py hl[2,6] *}
-## `TrustedHostMiddleware`
+## `TrustedHostMiddleware` { #trustedhostmiddleware }
Enforces that all incoming requests have a correctly set `Host` header, in order to guard against HTTP Host Header attacks.
-```Python hl_lines="2 6-8"
-{!../../../docs_src/advanced_middleware/tutorial002.py!}
-```
+{* ../../docs_src/advanced_middleware/tutorial002.py hl[2,6:8] *}
The following arguments are supported:
* `allowed_hosts` - A list of domain names that should be allowed as hostnames. Wildcard domains such as `*.example.com` are supported for matching subdomains. To allow any hostname either use `allowed_hosts=["*"]` or omit the middleware.
+* `www_redirect` - If set to True, requests to non-www versions of the allowed hosts will be redirected to their www counterparts. Defaults to `True`.
If an incoming request does not validate correctly then a `400` response will be sent.
-## `GZipMiddleware`
+## `GZipMiddleware` { #gzipmiddleware }
Handles GZip responses for any request that includes `"gzip"` in the `Accept-Encoding` header.
The middleware will handle both standard and streaming responses.
-```Python hl_lines="2 6"
-{!../../../docs_src/advanced_middleware/tutorial003.py!}
-```
+{* ../../docs_src/advanced_middleware/tutorial003.py hl[2,6] *}
The following arguments are supported:
* `minimum_size` - Do not GZip responses that are smaller than this minimum size in bytes. Defaults to `500`.
* `compresslevel` - Used during GZip compression. It is an integer ranging from 1 to 9. Defaults to `9`. Lower value results in faster compression but larger file sizes, while higher value results in slower compression but smaller file sizes.
-## Other middlewares
+## Other middlewares { #other-middlewares }
There are many other ASGI middlewares.
@@ -99,4 +94,4 @@ For example:
* Uvicorn's `ProxyHeadersMiddleware`
* MessagePack
-To see other available middlewares check Starlette's Middleware docs and the ASGI Awesome List.
+To see other available middlewares check Starlette's Middleware docs and the ASGI Awesome List.
diff --git a/docs/en/docs/advanced/openapi-callbacks.md b/docs/en/docs/advanced/openapi-callbacks.md
index 7fead2ed9f..059d893c26 100644
--- a/docs/en/docs/advanced/openapi-callbacks.md
+++ b/docs/en/docs/advanced/openapi-callbacks.md
@@ -1,4 +1,4 @@
-# OpenAPI Callbacks
+# OpenAPI Callbacks { #openapi-callbacks }
You could create an API with a *path operation* that could trigger a request to an *external API* created by someone else (probably the same developer that would be *using* your API).
@@ -6,7 +6,7 @@ The process that happens when your API app calls the *external API* is named a "
In this case, you could want to document how that external API *should* look like. What *path operation* it should have, what body it should expect, what response it should return, etc.
-## An app with callbacks
+## An app with callbacks { #an-app-with-callbacks }
Let's see all this with an example.
@@ -23,7 +23,7 @@ Then your API will (let's imagine):
* Send a notification back to the API user (the external developer).
* This will be done by sending a POST request (from *your API*) to some *external API* provided by that external developer (this is the "callback").
-## The normal **FastAPI** app
+## The normal **FastAPI** app { #the-normal-fastapi-app }
Let's first see how the normal API app would look like before adding the callback.
@@ -31,9 +31,7 @@ It will have a *path operation* that will receive an `Invoice` body, and a query
This part is pretty normal, most of the code is probably already familiar to you:
-```Python hl_lines="9-13 36-53"
-{!../../../docs_src/openapi_callbacks/tutorial001.py!}
-```
+{* ../../docs_src/openapi_callbacks/tutorial001.py hl[9:13,36:53] *}
/// tip
@@ -43,7 +41,7 @@ The `callback_url` query parameter uses a Pydantic OpenAPI 3 expression (see more below) where it can use variables with parameters and parts of the original request sent to *your API*.
-### The callback path expression
+### The callback path expression { #the-callback-path-expression }
The callback *path* can have an OpenAPI 3 expression that can contain parts of the original request sent to *your API*.
@@ -169,15 +163,13 @@ Notice how the callback URL used contains the URL received as a query parameter
///
-### Add the callback router
+### Add the callback router { #add-the-callback-router }
At this point you have the *callback path operation(s)* needed (the one(s) that the *external developer* should implement in the *external API*) in the callback router you created above.
Now use the parameter `callbacks` in *your API's path operation decorator* to pass the attribute `.routes` (that's actually just a `list` of routes/*path operations*) from that callback router:
-```Python hl_lines="35"
-{!../../../docs_src/openapi_callbacks/tutorial001.py!}
-```
+{* ../../docs_src/openapi_callbacks/tutorial001.py hl[35] *}
/// tip
@@ -185,7 +177,7 @@ Notice that you are not passing the router itself (`invoices_callback_router`) t
///
-### Check the docs
+### Check the docs { #check-the-docs }
Now you can start your app and go to http://127.0.0.1:8000/docs.
diff --git a/docs/en/docs/advanced/openapi-webhooks.md b/docs/en/docs/advanced/openapi-webhooks.md
index 5ee321e2ae..416cf4b75b 100644
--- a/docs/en/docs/advanced/openapi-webhooks.md
+++ b/docs/en/docs/advanced/openapi-webhooks.md
@@ -1,4 +1,4 @@
-# OpenAPI Webhooks
+# OpenAPI Webhooks { #openapi-webhooks }
There are cases where you want to tell your API **users** that your app could call *their* app (sending a request) with some data, normally to **notify** of some type of **event**.
@@ -6,7 +6,7 @@ This means that instead of the normal process of your users sending requests to
This is normally called a **webhook**.
-## Webhooks steps
+## Webhooks steps { #webhooks-steps }
The process normally is that **you define** in your code what is the message that you will send, the **body of the request**.
@@ -16,7 +16,7 @@ And **your users** define in some way (for example in a web dashboard somewhere)
All the **logic** about how to register the URLs for webhooks and the code to actually send those requests is up to you. You write it however you want to in **your own code**.
-## Documenting webhooks with **FastAPI** and OpenAPI
+## Documenting webhooks with **FastAPI** and OpenAPI { #documenting-webhooks-with-fastapi-and-openapi }
With **FastAPI**, using OpenAPI, you can define the names of these webhooks, the types of HTTP operations that your app can send (e.g. `POST`, `PUT`, etc.) and the request **bodies** that your app would send.
@@ -28,13 +28,11 @@ Webhooks are available in OpenAPI 3.1.0 and above, supported by FastAPI `0.99.0`
///
-## An app with webhooks
+## An app with webhooks { #an-app-with-webhooks }
When you create a **FastAPI** application, there is a `webhooks` attribute that you can use to define *webhooks*, the same way you would define *path operations*, for example with `@app.webhooks.post()`.
-```Python hl_lines="9-13 36-53"
-{!../../../docs_src/openapi_webhooks/tutorial001.py!}
-```
+{* ../../docs_src/openapi_webhooks/tutorial001.py hl[9:13,36:53] *}
The webhooks that you define will end up in the **OpenAPI** schema and the automatic **docs UI**.
@@ -48,7 +46,7 @@ Notice that with webhooks you are actually not declaring a *path* (like `/items/
This is because it is expected that **your users** would define the actual **URL path** where they want to receive the webhook request in some other way (e.g. a web dashboard).
-### Check the docs
+### Check the docs { #check-the-docs }
Now you can start your app and go to http://127.0.0.1:8000/docs.
diff --git a/docs/en/docs/advanced/path-operation-advanced-configuration.md b/docs/en/docs/advanced/path-operation-advanced-configuration.md
index c8874bad90..b9961f9f38 100644
--- a/docs/en/docs/advanced/path-operation-advanced-configuration.md
+++ b/docs/en/docs/advanced/path-operation-advanced-configuration.md
@@ -1,6 +1,6 @@
-# Path Operation Advanced Configuration
+# Path Operation Advanced Configuration { #path-operation-advanced-configuration }
-## OpenAPI operationId
+## OpenAPI operationId { #openapi-operationid }
/// warning
@@ -12,19 +12,15 @@ You can set the OpenAPI `operationId` to be used in your *path operation* with t
You would have to make sure that it is unique for each operation.
-```Python hl_lines="6"
-{!../../../docs_src/path_operation_advanced_configuration/tutorial001.py!}
-```
+{* ../../docs_src/path_operation_advanced_configuration/tutorial001.py hl[6] *}
-### Using the *path operation function* name as the operationId
+### Using the *path operation function* name as the operationId { #using-the-path-operation-function-name-as-the-operationid }
If you want to use your APIs' function names as `operationId`s, you can iterate over all of them and override each *path operation's* `operation_id` using their `APIRoute.name`.
You should do it after adding all your *path operations*.
-```Python hl_lines="2 12-21 24"
-{!../../../docs_src/path_operation_advanced_configuration/tutorial002.py!}
-```
+{* ../../docs_src/path_operation_advanced_configuration/tutorial002.py hl[2, 12:21, 24] *}
/// tip
@@ -40,15 +36,13 @@ Even if they are in different modules (Python files).
///
-## Exclude from OpenAPI
+## Exclude from OpenAPI { #exclude-from-openapi }
To exclude a *path operation* from the generated OpenAPI schema (and thus, from the automatic documentation systems), use the parameter `include_in_schema` and set it to `False`:
-```Python hl_lines="6"
-{!../../../docs_src/path_operation_advanced_configuration/tutorial003.py!}
-```
+{* ../../docs_src/path_operation_advanced_configuration/tutorial003.py hl[6] *}
-## Advanced description from docstring
+## Advanced description from docstring { #advanced-description-from-docstring }
You can limit the lines used from the docstring of a *path operation function* for OpenAPI.
@@ -56,11 +50,9 @@ Adding an `\f` (an escaped "form feed" character) causes **FastAPI** to truncate
It won't show up in the documentation, but other tools (such as Sphinx) will be able to use the rest.
-```Python hl_lines="19-29"
-{!../../../docs_src/path_operation_advanced_configuration/tutorial004.py!}
-```
+{* ../../docs_src/path_operation_advanced_configuration/tutorial004.py hl[19:29] *}
-## Additional Responses
+## Additional Responses { #additional-responses }
You probably have seen how to declare the `response_model` and `status_code` for a *path operation*.
@@ -70,11 +62,11 @@ You can also declare additional responses with their models, status codes, etc.
There's a whole chapter here in the documentation about it, you can read it at [Additional Responses in OpenAPI](additional-responses.md){.internal-link target=_blank}.
-## OpenAPI Extra
+## OpenAPI Extra { #openapi-extra }
When you declare a *path operation* in your application, **FastAPI** automatically generates the relevant metadata about that *path operation* to be included in the OpenAPI schema.
-/// note | "Technical details"
+/// note | Technical details
In the OpenAPI specification it is called the Operation Object.
@@ -96,13 +88,11 @@ If you only need to declare additional responses, a more convenient way to do it
You can extend the OpenAPI schema for a *path operation* using the parameter `openapi_extra`.
-### OpenAPI Extensions
+### OpenAPI Extensions { #openapi-extensions }
This `openapi_extra` can be helpful, for example, to declare [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!}
-```
+{* ../../docs_src/path_operation_advanced_configuration/tutorial005.py hl[6] *}
If you open the automatic API docs, your extension will show up at the bottom of the specific *path operation*.
@@ -139,7 +129,7 @@ And if you see the resulting OpenAPI (at `/openapi.json` in your API), you will
}
```
-### Custom OpenAPI *path operation* schema
+### Custom OpenAPI *path operation* schema { #custom-openapi-path-operation-schema }
The dictionary in `openapi_extra` will be deeply merged with the automatically generated OpenAPI schema for the *path operation*.
@@ -149,15 +139,13 @@ For example, you could decide to read and validate the request with your own cod
You could do that with `openapi_extra`:
-```Python hl_lines="20-37 39-40"
-{!../../../docs_src/path_operation_advanced_configuration/tutorial006.py!}
-```
+{* ../../docs_src/path_operation_advanced_configuration/tutorial006.py hl[19:36, 39:40] *}
In this example, we didn't declare any Pydantic model. In fact, the request body is not even parsed as JSON, it is read directly as `bytes`, and the function `magic_data_reader()` would be in charge of parsing it in some way.
Nevertheless, we can declare the expected schema for the request body.
-### Custom OpenAPI content type
+### Custom OpenAPI content type { #custom-openapi-content-type }
Using this same trick, you could use a Pydantic model to define the JSON Schema that is then included in the custom OpenAPI schema section for the *path operation*.
@@ -167,17 +155,13 @@ For example, in this application we don't use FastAPI's integrated functionality
//// tab | Pydantic v2
-```Python hl_lines="17-22 24"
-{!> ../../../docs_src/path_operation_advanced_configuration/tutorial007.py!}
-```
+{* ../../docs_src/path_operation_advanced_configuration/tutorial007.py hl[17:22, 24] *}
////
//// tab | Pydantic v1
-```Python hl_lines="17-22 24"
-{!> ../../../docs_src/path_operation_advanced_configuration/tutorial007_pv1.py!}
-```
+{* ../../docs_src/path_operation_advanced_configuration/tutorial007_pv1.py hl[17:22, 24] *}
////
@@ -195,17 +179,13 @@ And then in our code, we parse that YAML content directly, and then we are again
//// tab | Pydantic v2
-```Python hl_lines="26-33"
-{!> ../../../docs_src/path_operation_advanced_configuration/tutorial007.py!}
-```
+{* ../../docs_src/path_operation_advanced_configuration/tutorial007.py hl[26:33] *}
////
//// tab | Pydantic v1
-```Python hl_lines="26-33"
-{!> ../../../docs_src/path_operation_advanced_configuration/tutorial007_pv1.py!}
-```
+{* ../../docs_src/path_operation_advanced_configuration/tutorial007_pv1.py hl[26:33] *}
////
diff --git a/docs/en/docs/advanced/response-change-status-code.md b/docs/en/docs/advanced/response-change-status-code.md
index b88d74a8af..912ed0f1a1 100644
--- a/docs/en/docs/advanced/response-change-status-code.md
+++ b/docs/en/docs/advanced/response-change-status-code.md
@@ -1,10 +1,10 @@
-# Response - Change Status Code
+# Response - Change Status Code { #response-change-status-code }
You probably read before that you can set a default [Response Status Code](../tutorial/response-status-code.md){.internal-link target=_blank}.
But in some cases you need to return a different status code than the default.
-## Use case
+## Use case { #use-case }
For example, imagine that you want to return an HTTP status code of "OK" `200` by default.
@@ -14,15 +14,13 @@ But you still want to be able to filter and convert the data you return with a `
For those cases, you can use a `Response` parameter.
-## Use a `Response` parameter
+## Use a `Response` parameter { #use-a-response-parameter }
You can declare a parameter of type `Response` in your *path operation function* (as you can do for cookies and headers).
And then you can set the `status_code` in that *temporal* response object.
-```Python hl_lines="1 9 12"
-{!../../../docs_src/response_change_status_code/tutorial001.py!}
-```
+{* ../../docs_src/response_change_status_code/tutorial001.py hl[1,9,12] *}
And then you can return any object you need, as you normally would (a `dict`, a database model, etc).
diff --git a/docs/en/docs/advanced/response-cookies.md b/docs/en/docs/advanced/response-cookies.md
index 85e423f42a..1f41d84b7c 100644
--- a/docs/en/docs/advanced/response-cookies.md
+++ b/docs/en/docs/advanced/response-cookies.md
@@ -1,14 +1,12 @@
-# Response Cookies
+# Response Cookies { #response-cookies }
-## Use a `Response` parameter
+## Use a `Response` parameter { #use-a-response-parameter }
You can declare a parameter of type `Response` in your *path operation function*.
And then you can set cookies in that *temporal* response object.
-```Python hl_lines="1 8-9"
-{!../../../docs_src/response_cookies/tutorial002.py!}
-```
+{* ../../docs_src/response_cookies/tutorial002.py hl[1, 8:9] *}
And then you can return any object you need, as you normally would (a `dict`, a database model, etc).
@@ -18,7 +16,7 @@ And if you declared a `response_model`, it will still be used to filter and conv
You can also declare the `Response` parameter in dependencies, and set cookies (and headers) in them.
-## Return a `Response` directly
+## Return a `Response` directly { #return-a-response-directly }
You can also create cookies when returning a `Response` directly in your code.
@@ -26,9 +24,7 @@ To do that, you can create a response as described in [Return a Response Directl
Then set Cookies in it, and then return it:
-```Python hl_lines="10-12"
-{!../../../docs_src/response_cookies/tutorial001.py!}
-```
+{* ../../docs_src/response_cookies/tutorial001.py hl[10:12] *}
/// tip
@@ -40,9 +36,9 @@ And also that you are not sending any data that should have been filtered by a `
///
-### More info
+### More info { #more-info }
-/// note | "Technical Details"
+/// note | Technical Details
You could also use `from starlette.responses import Response` or `from starlette.responses import JSONResponse`.
@@ -52,4 +48,4 @@ And as the `Response` can be used frequently to set headers and cookies, **FastA
///
-To see all the available parameters and options, check the documentation in Starlette.
+To see all the available parameters and options, check the documentation in Starlette.
diff --git a/docs/en/docs/advanced/response-directly.md b/docs/en/docs/advanced/response-directly.md
index 2251659c55..3197e1bd46 100644
--- a/docs/en/docs/advanced/response-directly.md
+++ b/docs/en/docs/advanced/response-directly.md
@@ -1,4 +1,4 @@
-# Return a Response Directly
+# Return a Response Directly { #return-a-response-directly }
When you create a **FastAPI** *path operation* you can normally return any data from it: a `dict`, a `list`, a Pydantic model, a database model, etc.
@@ -10,7 +10,7 @@ But you can return a `JSONResponse` directly from your *path operations*.
It might be useful, for example, to return custom headers or cookies.
-## Return a `Response`
+## Return a `Response` { #return-a-response }
In fact, you can return any `Response` or any sub-class of it.
@@ -26,19 +26,17 @@ It won't do any data conversion with Pydantic models, it won't convert the conte
This gives you a lot of flexibility. You can return any data type, override any data declaration or validation, etc.
-## Using the `jsonable_encoder` in a `Response`
+## Using the `jsonable_encoder` in a `Response` { #using-the-jsonable-encoder-in-a-response }
-Because **FastAPI** doesn't do any change to a `Response` you return, you have to make sure its contents are ready for it.
+Because **FastAPI** doesn't make any changes to a `Response` you return, you have to make sure its contents are ready for it.
For example, you cannot put a Pydantic model in a `JSONResponse` without first converting it to a `dict` with all the data types (like `datetime`, `UUID`, etc) converted to JSON-compatible types.
For those cases, you can use the `jsonable_encoder` to convert your data before passing it to a response:
-```Python hl_lines="6-7 21-22"
-{!../../../docs_src/response_directly/tutorial001.py!}
-```
+{* ../../docs_src/response_directly/tutorial001.py hl[6:7,21:22] *}
-/// note | "Technical Details"
+/// note | Technical Details
You could also use `from starlette.responses import JSONResponse`.
@@ -46,7 +44,7 @@ You could also use `from starlette.responses import JSONResponse`.
///
-## Returning a custom `Response`
+## Returning a custom `Response` { #returning-a-custom-response }
The example above shows all the parts you need, but it's not very useful yet, as you could have just returned the `item` directly, and **FastAPI** would put it in a `JSONResponse` for you, converting it to a `dict`, etc. All that by default.
@@ -56,13 +54,11 @@ Let's say that you want to return an using the 'X-' prefix.
+Keep in mind that custom proprietary headers can be added using the `X-` prefix.
-But if you have custom headers that you want a client in a browser to be able to see, you need to add them to your CORS configurations (read more in [CORS (Cross-Origin Resource Sharing)](../tutorial/cors.md){.internal-link target=_blank}), using the parameter `expose_headers` documented in Starlette's CORS docs.
+But if you have custom headers that you want a client in a browser to be able to see, you need to add them to your CORS configurations (read more in [CORS (Cross-Origin Resource Sharing)](../tutorial/cors.md){.internal-link target=_blank}), using the parameter `expose_headers` documented in Starlette's CORS docs.
diff --git a/docs/en/docs/advanced/security/http-basic-auth.md b/docs/en/docs/advanced/security/http-basic-auth.md
index c302bf8dcb..01b98eeff3 100644
--- a/docs/en/docs/advanced/security/http-basic-auth.md
+++ b/docs/en/docs/advanced/security/http-basic-auth.md
@@ -1,4 +1,4 @@
-# HTTP Basic Auth
+# HTTP Basic Auth { #http-basic-auth }
For the simplest cases, you can use HTTP Basic Auth.
@@ -12,7 +12,7 @@ That tells the browser to show the integrated prompt for a username and password
Then, when you type that username and password, the browser sends them in the header automatically.
-## Simple HTTP Basic Auth
+## Simple HTTP Basic Auth { #simple-http-basic-auth }
* Import `HTTPBasic` and `HTTPBasicCredentials`.
* Create a "`security` scheme" using `HTTPBasic`.
@@ -20,41 +20,13 @@ 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.
-//// tab | Python 3.9+
-
-```Python hl_lines="4 8 12"
-{!> ../../../docs_src/security/tutorial006_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="2 7 11"
-{!> ../../../docs_src/security/tutorial006_an.py!}
-```
-
-////
-
-//// tab | Python 3.8+ non-Annotated
-
-/// tip
-
-Prefer to use the `Annotated` version if possible.
-
-///
-
-```Python hl_lines="2 6 10"
-{!> ../../../docs_src/security/tutorial006.py!}
-```
-
-////
+{* ../../docs_src/security/tutorial006_an_py39.py hl[4,8,12] *}
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:
-## Check the username
+## Check the username { #check-the-username }
Here's a more complete example.
@@ -68,35 +40,7 @@ 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"`.
-//// tab | Python 3.9+
-
-```Python hl_lines="1 12-24"
-{!> ../../../docs_src/security/tutorial007_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="1 12-24"
-{!> ../../../docs_src/security/tutorial007_an.py!}
-```
-
-////
-
-//// tab | Python 3.8+ non-Annotated
-
-/// tip
-
-Prefer to use the `Annotated` version if possible.
-
-///
-
-```Python hl_lines="1 11-21"
-{!> ../../../docs_src/security/tutorial007.py!}
-```
-
-////
+{* ../../docs_src/security/tutorial007_an_py39.py hl[1,12:24] *}
This would be similar to:
@@ -108,7 +52,7 @@ if not (credentials.username == "stanleyjobson") or not (credentials.password ==
But by using the `secrets.compare_digest()` it will be secure against a type of attacks called "timing attacks".
-### Timing Attacks
+### Timing Attacks { #timing-attacks }
But what's a "timing attack"?
@@ -136,19 +80,19 @@ if "stanleyjobsox" == "stanleyjobson" and "love123" == "swordfish":
Python will have to compare the whole `stanleyjobso` in both `stanleyjobsox` and `stanleyjobson` before realizing that both strings are not the same. So it will take some extra microseconds to reply back "Incorrect username or password".
-#### The time to answer helps the attackers
+#### The time to answer helps the attackers { #the-time-to-answer-helps-the-attackers }
At that point, by noticing that the server took some microseconds longer to send the "Incorrect username or password" response, the attackers will know that they got _something_ right, some of the initial letters were right.
And then they can try again knowing that it's probably something more similar to `stanleyjobsox` than to `johndoe`.
-#### A "professional" attack
+#### A "professional" attack { #a-professional-attack }
-Of course, the attackers would not try all this by hand, they would write a program to do it, possibly with thousands or millions of tests per second. And would get just one extra correct letter at a time.
+Of course, the attackers would not try all this by hand, they would write a program to do it, possibly with thousands or millions of tests per second. And they would get just one extra correct letter at a time.
But doing that, in some minutes or hours the attackers would have guessed the correct username and password, with the "help" of our application, just using the time taken to answer.
-#### Fix it with `secrets.compare_digest()`
+#### Fix it with `secrets.compare_digest()` { #fix-it-with-secrets-compare-digest }
But in our code we are actually using `secrets.compare_digest()`.
@@ -156,36 +100,8 @@ In short, it will take the same time to compare `stanleyjobsox` to `stanleyjobso
That way, using `secrets.compare_digest()` in your application code, it will be safe against this whole range of security attacks.
-### Return the error
+### Return the error { #return-the-error }
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:
-//// tab | Python 3.9+
-
-```Python hl_lines="26-30"
-{!> ../../../docs_src/security/tutorial007_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="26-30"
-{!> ../../../docs_src/security/tutorial007_an.py!}
-```
-
-////
-
-//// tab | Python 3.8+ non-Annotated
-
-/// tip
-
-Prefer to use the `Annotated` version if possible.
-
-///
-
-```Python hl_lines="23-27"
-{!> ../../../docs_src/security/tutorial007.py!}
-```
-
-////
+{* ../../docs_src/security/tutorial007_an_py39.py hl[26:30] *}
diff --git a/docs/en/docs/advanced/security/index.md b/docs/en/docs/advanced/security/index.md
index edb42132e6..996d716b4c 100644
--- a/docs/en/docs/advanced/security/index.md
+++ b/docs/en/docs/advanced/security/index.md
@@ -1,6 +1,6 @@
-# Advanced Security
+# Advanced Security { #advanced-security }
-## Additional Features
+## Additional Features { #additional-features }
There are some extra features to handle security apart from the ones covered in the [Tutorial - User Guide: Security](../../tutorial/security/index.md){.internal-link target=_blank}.
@@ -12,7 +12,7 @@ And it's possible that for your use case, the solution is in one of them.
///
-## Read the Tutorial first
+## Read the Tutorial first { #read-the-tutorial-first }
The next sections assume you already read the main [Tutorial - User Guide: Security](../../tutorial/security/index.md){.internal-link target=_blank}.
diff --git a/docs/en/docs/advanced/security/oauth2-scopes.md b/docs/en/docs/advanced/security/oauth2-scopes.md
index ff52d7bb80..67c927cd08 100644
--- a/docs/en/docs/advanced/security/oauth2-scopes.md
+++ b/docs/en/docs/advanced/security/oauth2-scopes.md
@@ -1,12 +1,12 @@
-# OAuth2 scopes
+# OAuth2 scopes { #oauth2-scopes }
You can use OAuth2 scopes directly with **FastAPI**, they are integrated to work seamlessly.
This would allow you to have a more fine-grained permission system, following the OAuth2 standard, integrated into your OpenAPI application (and the API docs).
-OAuth2 with scopes is the mechanism used by many big authentication providers, like Facebook, Google, GitHub, Microsoft, Twitter, etc. They use it to provide specific permissions to users and applications.
+OAuth2 with scopes is the mechanism used by many big authentication providers, like Facebook, Google, GitHub, Microsoft, X (Twitter), etc. They use it to provide specific permissions to users and applications.
-Every time you "log in with" Facebook, Google, GitHub, Microsoft, Twitter, that application is using OAuth2 with scopes.
+Every time you "log in with" Facebook, Google, GitHub, Microsoft, X (Twitter), that application is using OAuth2 with scopes.
In this section you will see how to manage authentication and authorization with the same OAuth2 with scopes in your **FastAPI** application.
@@ -26,7 +26,7 @@ But if you know you need it, or you are curious, keep reading.
///
-## OAuth2 scopes and OpenAPI
+## OAuth2 scopes and OpenAPI { #oauth2-scopes-and-openapi }
The OAuth2 specification defines "scopes" as a list of strings separated by spaces.
@@ -58,149 +58,21 @@ For OAuth2 they are just strings.
///
-## Global view
+## Global view { #global-view }
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:
-//// tab | Python 3.10+
-
-```Python hl_lines="5 9 13 47 65 106 108-116 122-125 129-135 140 156"
-{!> ../../../docs_src/security/tutorial005_an_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="2 5 9 13 47 65 106 108-116 122-125 129-135 140 156"
-{!> ../../../docs_src/security/tutorial005_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="2 5 9 13 48 66 107 109-117 123-126 130-136 141 157"
-{!> ../../../docs_src/security/tutorial005_an.py!}
-```
-
-////
-
-//// tab | Python 3.10+ non-Annotated
-
-/// tip
-
-Prefer to use the `Annotated` version if possible.
-
-///
-
-```Python hl_lines="4 8 12 46 64 105 107-115 121-124 128-134 139 155"
-{!> ../../../docs_src/security/tutorial005_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+ non-Annotated
-
-/// tip
-
-Prefer to use the `Annotated` version if possible.
-
-///
-
-```Python hl_lines="2 5 9 13 47 65 106 108-116 122-125 129-135 140 156"
-{!> ../../../docs_src/security/tutorial005_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+ non-Annotated
-
-/// tip
-
-Prefer to use the `Annotated` version if possible.
-
-///
-
-```Python hl_lines="2 5 9 13 47 65 106 108-116 122-125 129-135 140 156"
-{!> ../../../docs_src/security/tutorial005.py!}
-```
-
-////
+{* ../../docs_src/security/tutorial005_an_py310.py hl[5,9,13,47,65,106,108:116,122:126,130:136,141,157] *}
Now let's review those changes step by step.
-## OAuth2 Security scheme
+## OAuth2 Security scheme { #oauth2-security-scheme }
The first change is that now we are declaring the OAuth2 security scheme with two available scopes, `me` and `items`.
The `scopes` parameter receives a `dict` with each scope as a key and the description as the value:
-//// tab | Python 3.10+
-
-```Python hl_lines="63-66"
-{!> ../../../docs_src/security/tutorial005_an_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="63-66"
-{!> ../../../docs_src/security/tutorial005_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="64-67"
-{!> ../../../docs_src/security/tutorial005_an.py!}
-```
-
-////
-
-//// tab | Python 3.10+ non-Annotated
-
-/// tip
-
-Prefer to use the `Annotated` version if possible.
-
-///
-
-```Python hl_lines="62-65"
-{!> ../../../docs_src/security/tutorial005_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+ non-Annotated
-
-/// tip
-
-Prefer to use the `Annotated` version if possible.
-
-///
-
-```Python hl_lines="63-66"
-{!> ../../../docs_src/security/tutorial005_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+ non-Annotated
-
-/// tip
-
-Prefer to use the `Annotated` version if possible.
-
-///
-
-```Python hl_lines="63-66"
-{!> ../../../docs_src/security/tutorial005.py!}
-```
-
-////
+{* ../../docs_src/security/tutorial005_an_py310.py hl[63:66] *}
Because we are now declaring those scopes, they will show up in the API docs when you log-in/authorize.
@@ -210,7 +82,7 @@ This is the same mechanism used when you give permissions while logging in with
-## JWT token with scopes
+## JWT token with scopes { #jwt-token-with-scopes }
Now, modify the token *path operation* to return the scopes requested.
@@ -226,73 +98,9 @@ But in your application, for security, you should make sure you only add the sco
///
-//// tab | Python 3.10+
+{* ../../docs_src/security/tutorial005_an_py310.py hl[157] *}
-```Python hl_lines="156"
-{!> ../../../docs_src/security/tutorial005_an_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="156"
-{!> ../../../docs_src/security/tutorial005_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="157"
-{!> ../../../docs_src/security/tutorial005_an.py!}
-```
-
-////
-
-//// tab | Python 3.10+ non-Annotated
-
-/// tip
-
-Prefer to use the `Annotated` version if possible.
-
-///
-
-```Python hl_lines="155"
-{!> ../../../docs_src/security/tutorial005_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+ non-Annotated
-
-/// tip
-
-Prefer to use the `Annotated` version if possible.
-
-///
-
-```Python hl_lines="156"
-{!> ../../../docs_src/security/tutorial005_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+ non-Annotated
-
-/// tip
-
-Prefer to use the `Annotated` version if possible.
-
-///
-
-```Python hl_lines="156"
-{!> ../../../docs_src/security/tutorial005.py!}
-```
-
-////
-
-## Declare scopes in *path operations* and dependencies
+## Declare scopes in *path operations* and dependencies { #declare-scopes-in-path-operations-and-dependencies }
Now we declare that the *path operation* for `/users/me/items/` requires the scope `items`.
@@ -316,73 +124,9 @@ We are doing it here to demonstrate how **FastAPI** handles scopes declared at d
///
-//// tab | Python 3.10+
+{* ../../docs_src/security/tutorial005_an_py310.py hl[5,141,172] *}
-```Python hl_lines="5 140 171"
-{!> ../../../docs_src/security/tutorial005_an_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="5 140 171"
-{!> ../../../docs_src/security/tutorial005_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="5 141 172"
-{!> ../../../docs_src/security/tutorial005_an.py!}
-```
-
-////
-
-//// tab | Python 3.10+ non-Annotated
-
-/// tip
-
-Prefer to use the `Annotated` version if possible.
-
-///
-
-```Python hl_lines="4 139 168"
-{!> ../../../docs_src/security/tutorial005_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+ non-Annotated
-
-/// tip
-
-Prefer to use the `Annotated` version if possible.
-
-///
-
-```Python hl_lines="5 140 169"
-{!> ../../../docs_src/security/tutorial005_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+ non-Annotated
-
-/// tip
-
-Prefer to use the `Annotated` version if possible.
-
-///
-
-```Python hl_lines="5 140 169"
-{!> ../../../docs_src/security/tutorial005.py!}
-```
-
-////
-
-/// info | "Technical Details"
+/// info | Technical Details
`Security` is actually a subclass of `Depends`, and it has just one extra parameter that we'll see later.
@@ -392,7 +136,7 @@ But when you import `Query`, `Path`, `Depends`, `Security` and others from `fast
///
-## Use `SecurityScopes`
+## Use `SecurityScopes` { #use-securityscopes }
Now update the dependency `get_current_user`.
@@ -406,73 +150,9 @@ 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).
-//// tab | Python 3.10+
+{* ../../docs_src/security/tutorial005_an_py310.py hl[9,106] *}
-```Python hl_lines="9 106"
-{!> ../../../docs_src/security/tutorial005_an_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="9 106"
-{!> ../../../docs_src/security/tutorial005_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="9 107"
-{!> ../../../docs_src/security/tutorial005_an.py!}
-```
-
-////
-
-//// tab | Python 3.10+ non-Annotated
-
-/// tip
-
-Prefer to use the `Annotated` version if possible.
-
-///
-
-```Python hl_lines="8 105"
-{!> ../../../docs_src/security/tutorial005_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+ non-Annotated
-
-/// tip
-
-Prefer to use the `Annotated` version if possible.
-
-///
-
-```Python hl_lines="9 106"
-{!> ../../../docs_src/security/tutorial005_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+ non-Annotated
-
-/// tip
-
-Prefer to use the `Annotated` version if possible.
-
-///
-
-```Python hl_lines="9 106"
-{!> ../../../docs_src/security/tutorial005.py!}
-```
-
-////
-
-## Use the `scopes`
+## Use the `scopes` { #use-the-scopes }
The parameter `security_scopes` will be of type `SecurityScopes`.
@@ -484,73 +164,9 @@ We create an `HTTPException` that we can reuse (`raise`) later at several points
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).
-//// tab | Python 3.10+
+{* ../../docs_src/security/tutorial005_an_py310.py hl[106,108:116] *}
-```Python hl_lines="106 108-116"
-{!> ../../../docs_src/security/tutorial005_an_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="106 108-116"
-{!> ../../../docs_src/security/tutorial005_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="107 109-117"
-{!> ../../../docs_src/security/tutorial005_an.py!}
-```
-
-////
-
-//// tab | Python 3.10+ non-Annotated
-
-/// tip
-
-Prefer to use the `Annotated` version if possible.
-
-///
-
-```Python hl_lines="105 107-115"
-{!> ../../../docs_src/security/tutorial005_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+ non-Annotated
-
-/// tip
-
-Prefer to use the `Annotated` version if possible.
-
-///
-
-```Python hl_lines="106 108-116"
-{!> ../../../docs_src/security/tutorial005_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+ non-Annotated
-
-/// tip
-
-Prefer to use the `Annotated` version if possible.
-
-///
-
-```Python hl_lines="106 108-116"
-{!> ../../../docs_src/security/tutorial005.py!}
-```
-
-////
-
-## Verify the `username` and data shape
+## Verify the `username` and data shape { #verify-the-username-and-data-shape }
We verify that we get a `username`, and extract the scopes.
@@ -564,145 +180,17 @@ 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.
-//// tab | Python 3.10+
+{* ../../docs_src/security/tutorial005_an_py310.py hl[47,117:129] *}
-```Python hl_lines="47 117-128"
-{!> ../../../docs_src/security/tutorial005_an_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="47 117-128"
-{!> ../../../docs_src/security/tutorial005_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="48 118-129"
-{!> ../../../docs_src/security/tutorial005_an.py!}
-```
-
-////
-
-//// tab | Python 3.10+ non-Annotated
-
-/// tip
-
-Prefer to use the `Annotated` version if possible.
-
-///
-
-```Python hl_lines="46 116-127"
-{!> ../../../docs_src/security/tutorial005_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+ non-Annotated
-
-/// tip
-
-Prefer to use the `Annotated` version if possible.
-
-///
-
-```Python hl_lines="47 117-128"
-{!> ../../../docs_src/security/tutorial005_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+ non-Annotated
-
-/// tip
-
-Prefer to use the `Annotated` version if possible.
-
-///
-
-```Python hl_lines="47 117-128"
-{!> ../../../docs_src/security/tutorial005.py!}
-```
-
-////
-
-## Verify the `scopes`
+## Verify the `scopes` { #verify-the-scopes }
We now verify that all the scopes required, by this dependency and all the dependants (including *path operations*), are included in the scopes provided in the token received, otherwise raise an `HTTPException`.
For this, we use `security_scopes.scopes`, that contains a `list` with all these scopes as `str`.
-//// tab | Python 3.10+
+{* ../../docs_src/security/tutorial005_an_py310.py hl[130:136] *}
-```Python hl_lines="129-135"
-{!> ../../../docs_src/security/tutorial005_an_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="129-135"
-{!> ../../../docs_src/security/tutorial005_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="130-136"
-{!> ../../../docs_src/security/tutorial005_an.py!}
-```
-
-////
-
-//// tab | Python 3.10+ non-Annotated
-
-/// tip
-
-Prefer to use the `Annotated` version if possible.
-
-///
-
-```Python hl_lines="128-134"
-{!> ../../../docs_src/security/tutorial005_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+ non-Annotated
-
-/// tip
-
-Prefer to use the `Annotated` version if possible.
-
-///
-
-```Python hl_lines="129-135"
-{!> ../../../docs_src/security/tutorial005_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+ non-Annotated
-
-/// tip
-
-Prefer to use the `Annotated` version if possible.
-
-///
-
-```Python hl_lines="129-135"
-{!> ../../../docs_src/security/tutorial005.py!}
-```
-
-////
-
-## Dependency tree and scopes
+## Dependency tree and scopes { #dependency-tree-and-scopes }
Let's review again this dependency tree and the scopes.
@@ -735,7 +223,7 @@ All depending on the `scopes` declared in each *path operation* and each depende
///
-## More details about `SecurityScopes`
+## More details about `SecurityScopes` { #more-details-about-securityscopes }
You can use `SecurityScopes` at any point, and in multiple places, it doesn't have to be at the "root" dependency.
@@ -745,7 +233,7 @@ Because the `SecurityScopes` will have all the scopes declared by dependants, yo
They will be checked independently for each *path operation*.
-## Check it
+## Check it { #check-it }
If you open the API docs, you can authenticate and specify which scopes you want to authorize.
@@ -757,7 +245,7 @@ And if you select the scope `me` but not the scope `items`, you will be able to
That's what would happen to a third party application that tried to access one of these *path operations* with a token provided by a user, depending on how many permissions the user gave the application.
-## About third party integrations
+## About third party integrations { #about-third-party-integrations }
In this example we are using the OAuth2 "password" flow.
@@ -769,7 +257,7 @@ But if you are building an OAuth2 application that others would connect to (i.e.
The most common is the implicit flow.
-The most secure is the code flow, but is more complex to implement as it requires more steps. As it is more complex, many providers end up suggesting the implicit flow.
+The most secure is the code flow, but it's more complex to implement as it requires more steps. As it is more complex, many providers end up suggesting the implicit flow.
/// note
@@ -781,6 +269,6 @@ But in the end, they are implementing the same OAuth2 standard.
**FastAPI** includes utilities for all these OAuth2 authentication flows in `fastapi.security.oauth2`.
-## `Security` in decorator `dependencies`
+## `Security` in decorator `dependencies` { #security-in-decorator-dependencies }
The same way you can define a `list` of `Depends` in the decorator's `dependencies` parameter (as explained in [Dependencies in path operation decorators](../../tutorial/dependencies/dependencies-in-path-operation-decorators.md){.internal-link target=_blank}), you could also use `Security` with `scopes` there.
diff --git a/docs/en/docs/advanced/settings.md b/docs/en/docs/advanced/settings.md
index 22bf7de200..a218c3d016 100644
--- a/docs/en/docs/advanced/settings.md
+++ b/docs/en/docs/advanced/settings.md
@@ -1,4 +1,4 @@
-# Settings and Environment Variables
+# Settings and Environment Variables { #settings-and-environment-variables }
In many cases your application could need some external settings or configurations, for example secret keys, database credentials, credentials for email services, etc.
@@ -12,17 +12,17 @@ To understand environment variables you can read [Environment Variables](../envi
///
-## Types and validation
+## Types and validation { #types-and-validation }
These environment variables can only handle text strings, as they are external to Python and have to be compatible with other programs and the rest of the system (and even with different operating systems, as Linux, Windows, macOS).
That means that any value read in Python from an environment variable will be a `str`, and any conversion to a different type or any validation has to be done in code.
-## Pydantic `Settings`
+## Pydantic `Settings` { #pydantic-settings }
Fortunately, Pydantic provides a great utility to handle these settings coming from environment variables with Pydantic: Settings management.
-### Install `pydantic-settings`
+### Install `pydantic-settings` { #install-pydantic-settings }
First, make sure you create your [virtual environment](../virtual-environments.md){.internal-link target=_blank}, activate it, and then install the `pydantic-settings` package:
@@ -52,7 +52,7 @@ In Pydantic v1 it came included with the main package. Now it is distributed as
///
-### Create the `Settings` object
+### Create the `Settings` object { #create-the-settings-object }
Import `BaseSettings` from Pydantic and create a sub-class, very much like with a Pydantic model.
@@ -62,9 +62,7 @@ You can use all the same validation features and tools you use for Pydantic mode
//// tab | Pydantic v2
-```Python hl_lines="2 5-8 11"
-{!> ../../../docs_src/settings/tutorial001.py!}
-```
+{* ../../docs_src/settings/tutorial001.py hl[2,5:8,11] *}
////
@@ -76,9 +74,7 @@ In Pydantic v1 you would import `BaseSettings` directly from `pydantic` instead
///
-```Python hl_lines="2 5-8 11"
-{!> ../../../docs_src/settings/tutorial001_pv1.py!}
-```
+{* ../../docs_src/settings/tutorial001_pv1.py hl[2,5:8,11] *}
////
@@ -92,15 +88,13 @@ Then, when you create an instance of that `Settings` class (in this case, in the
Next it will convert and validate the data. So, when you use that `settings` object, you will have data of the types you declared (e.g. `items_per_user` will be an `int`).
-### Use the `settings`
+### Use the `settings` { #use-the-settings }
Then you can use the new `settings` object in your application:
-```Python hl_lines="18-20"
-{!../../../docs_src/settings/tutorial001.py!}
-```
+{* ../../docs_src/settings/tutorial001.py hl[18:20] *}
-### Run the server
+### Run the server { #run-the-server }
Next, you would run the server passing the configurations as environment variables, for example you could set an `ADMIN_EMAIL` and `APP_NAME` with:
@@ -126,21 +120,17 @@ The `app_name` would be `"ChimichangApp"`.
And the `items_per_user` would keep its default value of `50`.
-## Settings in another module
+## Settings in another module { #settings-in-another-module }
You could put those settings in another module file as you saw in [Bigger Applications - Multiple Files](../tutorial/bigger-applications.md){.internal-link target=_blank}.
For example, you could have a file `config.py` with:
-```Python
-{!../../../docs_src/settings/app01/config.py!}
-```
+{* ../../docs_src/settings/app01/config.py *}
And then use it in a file `main.py`:
-```Python hl_lines="3 11-13"
-{!../../../docs_src/settings/app01/main.py!}
-```
+{* ../../docs_src/settings/app01/main.py hl[3,11:13] *}
/// tip
@@ -148,55 +138,25 @@ You would also need a file `__init__.py` as you saw in [Bigger Applications - Mu
///
-## Settings in a dependency
+## Settings in a dependency { #settings-in-a-dependency }
In some occasions it might be useful to provide the settings from a dependency, instead of having a global object with `settings` that is used everywhere.
This could be especially useful during testing, as it's very easy to override a dependency with your own custom settings.
-### The config file
+### The config file { #the-config-file }
Coming from the previous example, your `config.py` file could look like:
-```Python hl_lines="10"
-{!../../../docs_src/settings/app02/config.py!}
-```
+{* ../../docs_src/settings/app02/config.py hl[10] *}
Notice that now we don't create a default instance `settings = Settings()`.
-### The main app file
+### The main app file { #the-main-app-file }
Now we create a dependency that returns a new `config.Settings()`.
-//// tab | Python 3.9+
-
-```Python hl_lines="6 12-13"
-{!> ../../../docs_src/settings/app02_an_py39/main.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="6 12-13"
-{!> ../../../docs_src/settings/app02_an/main.py!}
-```
-
-////
-
-//// tab | Python 3.8+ non-Annotated
-
-/// tip
-
-Prefer to use the `Annotated` version if possible.
-
-///
-
-```Python hl_lines="5 11-12"
-{!> ../../../docs_src/settings/app02/main.py!}
-```
-
-////
+{* ../../docs_src/settings/app02_an_py39/main.py hl[6,12:13] *}
/// tip
@@ -208,49 +168,19 @@ For now you can assume `get_settings()` is a normal function.
And then we can require it from the *path operation function* as a dependency and use it anywhere we need it.
-//// tab | Python 3.9+
+{* ../../docs_src/settings/app02_an_py39/main.py hl[17,19:21] *}
-```Python hl_lines="17 19-21"
-{!> ../../../docs_src/settings/app02_an_py39/main.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="17 19-21"
-{!> ../../../docs_src/settings/app02_an/main.py!}
-```
-
-////
-
-//// tab | Python 3.8+ 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
+### Settings and testing { #settings-and-testing }
Then it would be very easy to provide a different settings object during testing by creating a dependency override for `get_settings`:
-```Python hl_lines="9-10 13 21"
-{!../../../docs_src/settings/app02/test_main.py!}
-```
+{* ../../docs_src/settings/app02/test_main.py hl[9:10,13,21] *}
In the dependency override we set a new value for the `admin_email` when creating the new `Settings` object, and then we return that new object.
Then we can test that it is used.
-## Reading a `.env` file
+## Reading a `.env` file { #reading-a-env-file }
If you have many settings that possibly change a lot, maybe in different environments, it might be useful to put them on a file and then read them from it as if they were environment variables.
@@ -272,7 +202,7 @@ For this to work, you need to `pip install python-dotenv`.
///
-### The `.env` file
+### The `.env` file { #the-env-file }
You could have a `.env` file with:
@@ -281,15 +211,13 @@ ADMIN_EMAIL="deadpool@example.com"
APP_NAME="ChimichangApp"
```
-### Read settings from `.env`
+### Read settings from `.env` { #read-settings-from-env }
And then update your `config.py` with:
//// tab | Pydantic v2
-```Python hl_lines="9"
-{!> ../../../docs_src/settings/app03_an/config.py!}
-```
+{* ../../docs_src/settings/app03_an/config.py hl[9] *}
/// tip
@@ -301,9 +229,7 @@ The `model_config` attribute is used just for Pydantic configuration. You can re
//// tab | Pydantic v1
-```Python hl_lines="9-10"
-{!> ../../../docs_src/settings/app03_an/config_pv1.py!}
-```
+{* ../../docs_src/settings/app03_an/config_pv1.py hl[9:10] *}
/// tip
@@ -321,7 +247,7 @@ In Pydantic version 1 the configuration was done in an internal class `Config`,
Here we define the config `env_file` inside of your Pydantic `Settings` class, and set the value to the filename with the dotenv file we want to use.
-### Creating the `Settings` only once with `lru_cache`
+### Creating the `Settings` only once with `lru_cache` { #creating-the-settings-only-once-with-lru-cache }
Reading a file from disk is normally a costly (slow) operation, so you probably want to do it only once and then reuse the same settings object, instead of reading it for each request.
@@ -344,39 +270,11 @@ 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. ✔️
-//// tab | Python 3.9+
+{* ../../docs_src/settings/app03_an_py39/main.py hl[1,11] *}
-```Python hl_lines="1 11"
-{!> ../../../docs_src/settings/app03_an_py39/main.py!}
-```
+Then for any subsequent call 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.
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="1 11"
-{!> ../../../docs_src/settings/app03_an/main.py!}
-```
-
-////
-
-//// tab | Python 3.8+ 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.
-
-#### `lru_cache` Technical Details
+#### `lru_cache` Technical Details { #lru-cache-technical-details }
`@lru_cache` modifies the function it decorates to return the same value that was returned the first time, instead of computing it again, executing the code of the function every time.
@@ -439,7 +337,7 @@ That way, it behaves almost as if it was just a global variable. But as it uses
`@lru_cache` is part of `functools` which is part of Python's standard library, you can read more about it in the Python docs for `@lru_cache`.
-## Recap
+## Recap { #recap }
You can use Pydantic Settings to handle the settings or configurations for your application, with all the power of Pydantic models.
diff --git a/docs/en/docs/advanced/sub-applications.md b/docs/en/docs/advanced/sub-applications.md
index 8c52e091f0..fbd0e1af39 100644
--- a/docs/en/docs/advanced/sub-applications.md
+++ b/docs/en/docs/advanced/sub-applications.md
@@ -1,47 +1,41 @@
-# Sub Applications - Mounts
+# Sub Applications - Mounts { #sub-applications-mounts }
If you need to have two independent FastAPI applications, with their own independent OpenAPI and their own docs UIs, you can have a main app and "mount" one (or more) sub-application(s).
-## Mounting a **FastAPI** application
+## Mounting a **FastAPI** application { #mounting-a-fastapi-application }
"Mounting" means adding a completely "independent" application in a specific path, that then takes care of handling everything under that path, with the _path operations_ declared in that sub-application.
-### Top-level application
+### Top-level application { #top-level-application }
First, create the main, top-level, **FastAPI** application, and its *path operations*:
-```Python hl_lines="3 6-8"
-{!../../../docs_src/sub_applications/tutorial001.py!}
-```
+{* ../../docs_src/sub_applications/tutorial001.py hl[3, 6:8] *}
-### Sub-application
+### Sub-application { #sub-application }
Then, create your sub-application, and its *path operations*.
This sub-application is just another standard FastAPI application, but this is the one that will be "mounted":
-```Python hl_lines="11 14-16"
-{!../../../docs_src/sub_applications/tutorial001.py!}
-```
+{* ../../docs_src/sub_applications/tutorial001.py hl[11, 14:16] *}
-### Mount the sub-application
+### Mount the sub-application { #mount-the-sub-application }
In your top-level application, `app`, mount the sub-application, `subapi`.
In this case, it will be mounted at the path `/subapi`:
-```Python hl_lines="11 19"
-{!../../../docs_src/sub_applications/tutorial001.py!}
-```
+{* ../../docs_src/sub_applications/tutorial001.py hl[11, 19] *}
-### Check the automatic API docs
+### Check the automatic API docs { #check-the-automatic-api-docs }
-Now, run `uvicorn` with the main app, if your file is `main.py`, it would be:
+Now, run the `fastapi` command with your file:
-
-## More info
-
-You can read more about `encode/databases` at its GitHub page.
diff --git a/docs/en/docs/how-to/conditional-openapi.md b/docs/en/docs/how-to/conditional-openapi.md
index add16fbec5..e5893e584b 100644
--- a/docs/en/docs/how-to/conditional-openapi.md
+++ b/docs/en/docs/how-to/conditional-openapi.md
@@ -1,8 +1,8 @@
-# Conditional OpenAPI
+# Conditional OpenAPI { #conditional-openapi }
If you needed to, you could use settings and environment variables to configure OpenAPI conditionally depending on the environment, and even disable it entirely.
-## About security, APIs, and docs
+## About security, APIs, and docs { #about-security-apis-and-docs }
Hiding your documentation user interfaces in production *shouldn't* be the way to protect your API.
@@ -17,21 +17,19 @@ If you want to secure your API, there are several better things you can do, for
* Make sure you have well defined Pydantic models for your request bodies and responses.
* Configure any required permissions and roles using dependencies.
* Never store plaintext passwords, only password hashes.
-* Implement and use well-known cryptographic tools, like Passlib and JWT tokens, etc.
+* Implement and use well-known cryptographic tools, like pwdlib and JWT tokens, etc.
* Add more granular permission controls with OAuth2 scopes where needed.
* ...etc.
Nevertheless, you might have a very specific use case where you really need to disable the API docs for some environment (e.g. for production) or depending on configurations from environment variables.
-## Conditional OpenAPI from settings and env vars
+## Conditional OpenAPI from settings and env vars { #conditional-openapi-from-settings-and-env-vars }
You can easily use the same Pydantic settings to configure your generated OpenAPI and the docs UIs.
For example:
-```Python hl_lines="6 11"
-{!../../../docs_src/conditional_openapi/tutorial001.py!}
-```
+{* ../../docs_src/conditional_openapi/tutorial001.py hl[6,11] *}
Here we declare the setting `openapi_url` with the same default of `"/openapi.json"`.
diff --git a/docs/en/docs/how-to/configure-swagger-ui.md b/docs/en/docs/how-to/configure-swagger-ui.md
index 108afb929b..2d7b99f8fa 100644
--- a/docs/en/docs/how-to/configure-swagger-ui.md
+++ b/docs/en/docs/how-to/configure-swagger-ui.md
@@ -1,6 +1,6 @@
-# Configure Swagger UI
+# Configure Swagger UI { #configure-swagger-ui }
-You can configure some extra Swagger UI parameters.
+You can configure some extra Swagger UI parameters.
To configure them, pass the `swagger_ui_parameters` argument when creating the `FastAPI()` app object or to the `get_swagger_ui_html()` function.
@@ -8,7 +8,7 @@ To configure them, pass the `swagger_ui_parameters` argument when creating the `
FastAPI converts the configurations to **JSON** to make them compatible with JavaScript, as that's what Swagger UI needs.
-## Disable Syntax Highlighting
+## Disable Syntax Highlighting { #disable-syntax-highlighting }
For example, you could disable syntax highlighting in Swagger UI.
@@ -18,49 +18,41 @@ Without changing the settings, syntax highlighting is enabled by default:
But you can disable it by setting `syntaxHighlight` to `False`:
-```Python hl_lines="3"
-{!../../../docs_src/configure_swagger_ui/tutorial001.py!}
-```
+{* ../../docs_src/configure_swagger_ui/tutorial001.py hl[3] *}
...and then Swagger UI won't show the syntax highlighting anymore:
-## Change the Theme
+## Change the Theme { #change-the-theme }
The same way you could set the syntax highlighting theme with the key `"syntaxHighlight.theme"` (notice that it has a dot in the middle):
-```Python hl_lines="3"
-{!../../../docs_src/configure_swagger_ui/tutorial002.py!}
-```
+{* ../../docs_src/configure_swagger_ui/tutorial002.py hl[3] *}
That configuration would change the syntax highlighting color theme:
-## Change Default Swagger UI Parameters
+## Change Default Swagger UI Parameters { #change-default-swagger-ui-parameters }
FastAPI includes some default configuration parameters appropriate for most of the use cases.
It includes these default configurations:
-```Python
-{!../../../fastapi/openapi/docs.py[ln:7-23]!}
-```
+{* ../../fastapi/openapi/docs.py ln[8:23] hl[17:23] *}
You can override any of them by setting a different value in the argument `swagger_ui_parameters`.
For example, to disable `deepLinking` you could pass these settings to `swagger_ui_parameters`:
-```Python hl_lines="3"
-{!../../../docs_src/configure_swagger_ui/tutorial003.py!}
-```
+{* ../../docs_src/configure_swagger_ui/tutorial003.py hl[3] *}
-## Other Swagger UI Parameters
+## Other Swagger UI Parameters { #other-swagger-ui-parameters }
-To see all the other possible configurations you can use, read the official docs for Swagger UI parameters.
+To see all the other possible configurations you can use, read the official docs for Swagger UI parameters.
-## JavaScript-only settings
+## JavaScript-only settings { #javascript-only-settings }
Swagger UI also allows other configurations to be **JavaScript-only** objects (for example, JavaScript functions).
diff --git a/docs/en/docs/how-to/custom-docs-ui-assets.md b/docs/en/docs/how-to/custom-docs-ui-assets.md
index 0c766d3e43..91228c8c93 100644
--- a/docs/en/docs/how-to/custom-docs-ui-assets.md
+++ b/docs/en/docs/how-to/custom-docs-ui-assets.md
@@ -1,4 +1,4 @@
-# Custom Docs UI Static Assets (Self-Hosting)
+# Custom Docs UI Static Assets (Self-Hosting) { #custom-docs-ui-static-assets-self-hosting }
The API docs use **Swagger UI** and **ReDoc**, and each of those need some JavaScript and CSS files.
@@ -6,23 +6,21 @@ By default, those files are served from a CDN, for example you want to use `https://unpkg.com/`.
This could be useful if for example you live in a country that restricts some URLs.
-### Disable the automatic docs
+### Disable the automatic docs { #disable-the-automatic-docs }
The first step is to disable the automatic docs, as by default, those use the default CDN.
To disable them, set their URLs to `None` when creating your `FastAPI` app:
-```Python hl_lines="8"
-{!../../../docs_src/custom_docs_ui/tutorial001.py!}
-```
+{* ../../docs_src/custom_docs_ui/tutorial001.py hl[8] *}
-### Include the custom docs
+### Include the custom docs { #include-the-custom-docs }
Now you can create the *path operations* for the custom docs.
@@ -36,9 +34,7 @@ You can reuse FastAPI's internal functions to create the HTML pages for the docs
And similarly for ReDoc...
-```Python hl_lines="2-6 11-19 22-24 27-33"
-{!../../../docs_src/custom_docs_ui/tutorial001.py!}
-```
+{* ../../docs_src/custom_docs_ui/tutorial001.py hl[2:6,11:19,22:24,27:33] *}
/// tip
@@ -50,25 +46,23 @@ Swagger UI will handle it behind the scenes for you, but it needs this "redirect
///
-### Create a *path operation* to test it
+### Create a *path operation* to test it { #create-a-path-operation-to-test-it }
Now, to be able to test that everything works, create a *path operation*:
-```Python hl_lines="36-38"
-{!../../../docs_src/custom_docs_ui/tutorial001.py!}
-```
+{* ../../docs_src/custom_docs_ui/tutorial001.py hl[36:38] *}
-### Test it
+### Test it { #test-it }
Now, you should be able to go to your docs at http://127.0.0.1:8000/docs, and reload the page, it will load those assets from the new CDN.
-## Self-hosting JavaScript and CSS for docs
+## Self-hosting JavaScript and CSS for docs { #self-hosting-javascript-and-css-for-docs }
Self-hosting the JavaScript and CSS could be useful if, for example, you need your app to keep working even while offline, without open Internet access, or in a local network.
Here you'll see how to serve those files yourself, in the same FastAPI app, and configure the docs to use them.
-### Project file structure
+### Project file structure { #project-file-structure }
Let's say your project file structure looks like this:
@@ -91,11 +85,11 @@ Your new file structure could look like this:
└── static/
```
-### Download the files
+### Download the files { #download-the-files }
Download the static files needed for the docs and put them on that `static/` directory.
-You can probably right-click each link and select an option similar to `Save link as...`.
+You can probably right-click each link and select an option similar to "Save link as...".
**Swagger UI** uses the files:
@@ -104,7 +98,7 @@ You can probably right-click each link and select an option similar to `Save lin
And **ReDoc** uses the file:
-* `redoc.standalone.js`
+* `redoc.standalone.js`
After that, your file structure could look like:
@@ -119,16 +113,14 @@ After that, your file structure could look like:
└── swagger-ui.css
```
-### Serve the static files
+### Serve the static files { #serve-the-static-files }
* Import `StaticFiles`.
* "Mount" a `StaticFiles()` instance in a specific path.
-```Python hl_lines="7 11"
-{!../../../docs_src/custom_docs_ui/tutorial002.py!}
-```
+{* ../../docs_src/custom_docs_ui/tutorial002.py hl[7,11] *}
-### Test the static files
+### Test the static files { #test-the-static-files }
Start your application and go to http://127.0.0.1:8000/static/redoc.standalone.js.
@@ -137,14 +129,8 @@ You should see a very long JavaScript file for **ReDoc**.
It could start with something like:
```JavaScript
-/*!
- * ReDoc - OpenAPI/Swagger-generated API Reference Documentation
- * -------------------------------------------------------------
- * Version: "2.0.0-rc.18"
- * Repo: https://github.com/Redocly/redoc
- */
-!function(e,t){"object"==typeof exports&&"object"==typeof m
-
+/*! For license information please see redoc.standalone.js.LICENSE.txt */
+!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("null")):
...
```
@@ -152,17 +138,15 @@ That confirms that you are being able to serve static files from your app, and t
Now we can configure the app to use those static files for the docs.
-### Disable the automatic docs for static files
+### Disable the automatic docs for static files { #disable-the-automatic-docs-for-static-files }
The same as when using a custom CDN, the first step is to disable the automatic docs, as those use the CDN by default.
To disable them, set their URLs to `None` when creating your `FastAPI` app:
-```Python hl_lines="9"
-{!../../../docs_src/custom_docs_ui/tutorial002.py!}
-```
+{* ../../docs_src/custom_docs_ui/tutorial002.py hl[9] *}
-### Include the custom docs for static files
+### Include the custom docs for static files { #include-the-custom-docs-for-static-files }
And the same way as with a custom CDN, now you can create the *path operations* for the custom docs.
@@ -176,9 +160,7 @@ Again, you can reuse FastAPI's internal functions to create the HTML pages for t
And similarly for ReDoc...
-```Python hl_lines="2-6 14-22 25-27 30-36"
-{!../../../docs_src/custom_docs_ui/tutorial002.py!}
-```
+{* ../../docs_src/custom_docs_ui/tutorial002.py hl[2:6,14:22,25:27,30:36] *}
/// tip
@@ -190,15 +172,13 @@ Swagger UI will handle it behind the scenes for you, but it needs this "redirect
///
-### Create a *path operation* to test static files
+### Create a *path operation* to test static files { #create-a-path-operation-to-test-static-files }
Now, to be able to test that everything works, create a *path operation*:
-```Python hl_lines="39-41"
-{!../../../docs_src/custom_docs_ui/tutorial002.py!}
-```
+{* ../../docs_src/custom_docs_ui/tutorial002.py hl[39:41] *}
-### Test Static Files UI
+### Test Static Files UI { #test-static-files-ui }
Now, you should be able to disconnect your WiFi, go to your docs at http://127.0.0.1:8000/docs, and reload the page.
diff --git a/docs/en/docs/how-to/custom-request-and-route.md b/docs/en/docs/how-to/custom-request-and-route.md
index 20e1904f1e..884c8ed04f 100644
--- a/docs/en/docs/how-to/custom-request-and-route.md
+++ b/docs/en/docs/how-to/custom-request-and-route.md
@@ -1,4 +1,4 @@
-# Custom Request and APIRoute class
+# Custom Request and APIRoute class { #custom-request-and-apiroute-class }
In some cases, you may want to override the logic used by the `Request` and `APIRoute` classes.
@@ -14,7 +14,7 @@ If you are just starting with **FastAPI** you might want to skip this section.
///
-## Use cases
+## Use cases { #use-cases }
Some use cases include:
@@ -22,13 +22,13 @@ Some use cases include:
* Decompressing gzip-compressed request bodies.
* Automatically logging all request bodies.
-## Handling custom request body encodings
+## Handling custom request body encodings { #handling-custom-request-body-encodings }
Let's see how to make use of a custom `Request` subclass to decompress gzip requests.
And an `APIRoute` subclass to use that custom request class.
-### Create a custom `GzipRequest` class
+### Create a custom `GzipRequest` class { #create-a-custom-gziprequest-class }
/// tip
@@ -42,11 +42,9 @@ If there's no `gzip` in the header, it will not try to decompress the body.
That way, the same route class can handle gzip compressed or uncompressed requests.
-```Python hl_lines="8-15"
-{!../../../docs_src/custom_request_and_route/tutorial001.py!}
-```
+{* ../../docs_src/custom_request_and_route/tutorial001.py hl[8:15] *}
-### Create a custom `GzipRoute` class
+### Create a custom `GzipRoute` class { #create-a-custom-gziproute-class }
Next, we create a custom subclass of `fastapi.routing.APIRoute` that will make use of the `GzipRequest`.
@@ -56,11 +54,9 @@ This method returns a function. And that function is what will receive a request
Here we use it to create a `GzipRequest` from the original request.
-```Python hl_lines="18-26"
-{!../../../docs_src/custom_request_and_route/tutorial001.py!}
-```
+{* ../../docs_src/custom_request_and_route/tutorial001.py hl[18:26] *}
-/// note | "Technical Details"
+/// note | Technical Details
A `Request` has a `request.scope` attribute, that's just a Python `dict` containing the metadata related to the request.
@@ -70,7 +66,7 @@ The `scope` `dict` and `receive` function are both part of the ASGI specificatio
And those two things, `scope` and `receive`, are what is needed to create a new `Request` instance.
-To learn more about the `Request` check Starlette's docs about Requests.
+To learn more about the `Request` check Starlette's docs about Requests.
///
@@ -82,7 +78,7 @@ After that, all of the processing logic is the same.
But because of our changes in `GzipRequest.body`, the request body will be automatically decompressed when it is loaded by **FastAPI** when needed.
-## Accessing the request body in an exception handler
+## Accessing the request body in an exception handler { #accessing-the-request-body-in-an-exception-handler }
/// tip
@@ -96,26 +92,18 @@ We can also use this same approach to access the request body in an exception ha
All we need to do is handle the request inside a `try`/`except` block:
-```Python hl_lines="13 15"
-{!../../../docs_src/custom_request_and_route/tutorial002.py!}
-```
+{* ../../docs_src/custom_request_and_route/tutorial002.py hl[13,15] *}
If an exception occurs, the`Request` instance will still be in scope, so we can read and make use of the request body when handling the error:
-```Python hl_lines="16-18"
-{!../../../docs_src/custom_request_and_route/tutorial002.py!}
-```
+{* ../../docs_src/custom_request_and_route/tutorial002.py hl[16:18] *}
-## Custom `APIRoute` class in a router
+## Custom `APIRoute` class in a router { #custom-apiroute-class-in-a-router }
You can also set the `route_class` parameter of an `APIRouter`:
-```Python hl_lines="26"
-{!../../../docs_src/custom_request_and_route/tutorial003.py!}
-```
+{* ../../docs_src/custom_request_and_route/tutorial003.py hl[26] *}
In this example, the *path operations* under the `router` will use the custom `TimedRoute` class, and will have an extra `X-Response-Time` header in the response with the time it took to generate the response:
-```Python hl_lines="13-20"
-{!../../../docs_src/custom_request_and_route/tutorial003.py!}
-```
+{* ../../docs_src/custom_request_and_route/tutorial003.py hl[13:20] *}
diff --git a/docs/en/docs/how-to/extending-openapi.md b/docs/en/docs/how-to/extending-openapi.md
index 9909f778c3..5e672665ef 100644
--- a/docs/en/docs/how-to/extending-openapi.md
+++ b/docs/en/docs/how-to/extending-openapi.md
@@ -1,10 +1,10 @@
-# Extending OpenAPI
+# Extending OpenAPI { #extending-openapi }
There are some cases where you might need to modify the generated OpenAPI schema.
In this section you will see how.
-## The normal process
+## The normal process { #the-normal-process }
The normal (default) process, is as follows.
@@ -33,37 +33,31 @@ The parameter `summary` is available in OpenAPI 3.1.0 and above, supported by Fa
///
-## Overriding the defaults
+## Overriding the defaults { #overriding-the-defaults }
Using the information above, you can use the same utility function to generate the OpenAPI schema and override each part that you need.
For example, let's add ReDoc's OpenAPI extension to include a custom logo.
-### Normal **FastAPI**
+### Normal **FastAPI** { #normal-fastapi }
First, write all your **FastAPI** application as normally:
-```Python hl_lines="1 4 7-9"
-{!../../../docs_src/extending_openapi/tutorial001.py!}
-```
+{* ../../docs_src/extending_openapi/tutorial001.py hl[1,4,7:9] *}
-### Generate the OpenAPI schema
+### Generate the OpenAPI schema { #generate-the-openapi-schema }
Then, use the same utility function to generate the OpenAPI schema, inside a `custom_openapi()` function:
-```Python hl_lines="2 15-21"
-{!../../../docs_src/extending_openapi/tutorial001.py!}
-```
+{* ../../docs_src/extending_openapi/tutorial001.py hl[2,15:21] *}
-### Modify the OpenAPI schema
+### Modify the OpenAPI schema { #modify-the-openapi-schema }
Now you can add the ReDoc extension, adding a custom `x-logo` to the `info` "object" in the OpenAPI schema:
-```Python hl_lines="22-24"
-{!../../../docs_src/extending_openapi/tutorial001.py!}
-```
+{* ../../docs_src/extending_openapi/tutorial001.py hl[22:24] *}
-### Cache the OpenAPI schema
+### Cache the OpenAPI schema { #cache-the-openapi-schema }
You can use the property `.openapi_schema` as a "cache", to store your generated schema.
@@ -71,19 +65,15 @@ That way, your application won't have to generate the schema every time a user o
It will be generated only once, and then the same cached schema will be used for the next requests.
-```Python hl_lines="13-14 25-26"
-{!../../../docs_src/extending_openapi/tutorial001.py!}
-```
+{* ../../docs_src/extending_openapi/tutorial001.py hl[13:14,25:26] *}
-### Override the method
+### Override the method { #override-the-method }
Now you can replace the `.openapi()` method with your new function.
-```Python hl_lines="29"
-{!../../../docs_src/extending_openapi/tutorial001.py!}
-```
+{* ../../docs_src/extending_openapi/tutorial001.py hl[29] *}
-### Check it
+### Check it { #check-it }
Once you go to http://127.0.0.1:8000/redoc you will see that you are using your custom logo (in this example, **FastAPI**'s logo):
diff --git a/docs/en/docs/how-to/general.md b/docs/en/docs/how-to/general.md
index 04367c6b76..9347192607 100644
--- a/docs/en/docs/how-to/general.md
+++ b/docs/en/docs/how-to/general.md
@@ -1,39 +1,39 @@
-# General - How To - Recipes
+# General - How To - Recipes { #general-how-to-recipes }
Here are several pointers to other places in the docs, for general or frequent questions.
-## Filter Data - Security
+## Filter Data - Security { #filter-data-security }
To ensure that you don't return more data than you should, read the docs for [Tutorial - Response Model - Return Type](../tutorial/response-model.md){.internal-link target=_blank}.
-## Documentation Tags - OpenAPI
+## Documentation Tags - OpenAPI { #documentation-tags-openapi }
To add tags to your *path operations*, and group them in the docs UI, read the docs for [Tutorial - Path Operation Configurations - Tags](../tutorial/path-operation-configuration.md#tags){.internal-link target=_blank}.
-## Documentation Summary and Description - OpenAPI
+## Documentation Summary and Description - OpenAPI { #documentation-summary-and-description-openapi }
To add a summary and description to your *path operations*, and show them in the docs UI, read the docs for [Tutorial - Path Operation Configurations - Summary and Description](../tutorial/path-operation-configuration.md#summary-and-description){.internal-link target=_blank}.
-## Documentation Response description - OpenAPI
+## Documentation Response description - OpenAPI { #documentation-response-description-openapi }
To define the description of the response, shown in the docs UI, read the docs for [Tutorial - Path Operation Configurations - Response description](../tutorial/path-operation-configuration.md#response-description){.internal-link target=_blank}.
-## Documentation Deprecate a *Path Operation* - OpenAPI
+## Documentation Deprecate a *Path Operation* - OpenAPI { #documentation-deprecate-a-path-operation-openapi }
To deprecate a *path operation*, and show it in the docs UI, read the docs for [Tutorial - Path Operation Configurations - Deprecation](../tutorial/path-operation-configuration.md#deprecate-a-path-operation){.internal-link target=_blank}.
-## Convert any Data to JSON-compatible
+## Convert any Data to JSON-compatible { #convert-any-data-to-json-compatible }
To convert any data to JSON-compatible, read the docs for [Tutorial - JSON Compatible Encoder](../tutorial/encoder.md){.internal-link target=_blank}.
-## OpenAPI Metadata - Docs
+## OpenAPI Metadata - Docs { #openapi-metadata-docs }
To add metadata to your OpenAPI schema, including a license, version, contact, etc, read the docs for [Tutorial - Metadata and Docs URLs](../tutorial/metadata.md){.internal-link target=_blank}.
-## OpenAPI Custom URL
+## OpenAPI Custom URL { #openapi-custom-url }
To customize the OpenAPI URL (or remove it), read the docs for [Tutorial - Metadata and Docs URLs](../tutorial/metadata.md#openapi-url){.internal-link target=_blank}.
-## OpenAPI Docs URLs
+## OpenAPI Docs URLs { #openapi-docs-urls }
To update the URLs used for the automatically generated docs user interfaces, read the docs for [Tutorial - Metadata and Docs URLs](../tutorial/metadata.md#docs-urls){.internal-link target=_blank}.
diff --git a/docs/en/docs/how-to/graphql.md b/docs/en/docs/how-to/graphql.md
index d4b7cfdaa5..99b024d39b 100644
--- a/docs/en/docs/how-to/graphql.md
+++ b/docs/en/docs/how-to/graphql.md
@@ -1,4 +1,4 @@
-# GraphQL
+# GraphQL { #graphql }
As **FastAPI** is based on the **ASGI** standard, it's very easy to integrate any **GraphQL** library also compatible with ASGI.
@@ -14,7 +14,7 @@ Make sure you evaluate if the **benefits** for your use case compensate the **dr
///
-## GraphQL Libraries
+## GraphQL Libraries { #graphql-libraries }
Here are some of the **GraphQL** libraries that have **ASGI** support. You could use them with **FastAPI**:
@@ -27,7 +27,7 @@ Here are some of the **GraphQL** libraries that have **ASGI** support. You could
* Graphene
* With starlette-graphene3
-## GraphQL with Strawberry
+## GraphQL with Strawberry { #graphql-with-strawberry }
If you need or want to work with **GraphQL**, **Strawberry** is the **recommended** library as it has the design closest to **FastAPI's** design, it's all based on **type annotations**.
@@ -35,15 +35,13 @@ Depending on your use case, you might prefer to use a different library, but if
Here's a small preview of how you could integrate Strawberry with FastAPI:
-```Python hl_lines="3 22 25-26"
-{!../../../docs_src/graphql/tutorial001.py!}
-```
+{* ../../docs_src/graphql/tutorial001.py hl[3,22,25] *}
You can learn more about Strawberry in the Strawberry documentation.
And also the docs about Strawberry with FastAPI.
-## Older `GraphQLApp` from Starlette
+## Older `GraphQLApp` from Starlette { #older-graphqlapp-from-starlette }
Previous versions of Starlette included a `GraphQLApp` class to integrate with Graphene.
@@ -55,7 +53,7 @@ If you need GraphQL, I still would recommend you check out official GraphQL documentation.
diff --git a/docs/en/docs/how-to/index.md b/docs/en/docs/how-to/index.md
index 730dce5d55..5a8ce08de7 100644
--- a/docs/en/docs/how-to/index.md
+++ b/docs/en/docs/how-to/index.md
@@ -1,4 +1,4 @@
-# How To - Recipes
+# How To - Recipes { #how-to-recipes }
Here you will see different recipes or "how to" guides for **several topics**.
diff --git a/docs/en/docs/how-to/migrate-from-pydantic-v1-to-pydantic-v2.md b/docs/en/docs/how-to/migrate-from-pydantic-v1-to-pydantic-v2.md
new file mode 100644
index 0000000000..e85d122be8
--- /dev/null
+++ b/docs/en/docs/how-to/migrate-from-pydantic-v1-to-pydantic-v2.md
@@ -0,0 +1,133 @@
+# Migrate from Pydantic v1 to Pydantic v2 { #migrate-from-pydantic-v1-to-pydantic-v2 }
+
+If you have an old FastAPI app, you might be using Pydantic version 1.
+
+FastAPI has had support for either Pydantic v1 or v2 since version 0.100.0.
+
+If you had installed Pydantic v2, it would use it. If instead you had Pydantic v1, it would use that.
+
+Pydantic v1 is now deprecated and support for it will be removed in the next versions of FastAPI, you should **migrate to Pydantic v2**. This way you will get the latest features, improvements, and fixes.
+
+/// warning
+
+Also, the Pydantic team stopped support for Pydantic v1 for the latest versions of Python, starting with **Python 3.14**.
+
+If you want to use the latest features of Python, you will need to make sure you use Pydantic v2.
+
+///
+
+If you have an old FastAPI app with Pydantic v1, here I'll show you how to migrate it to Pydantic v2, and the **new features in FastAPI 0.119.0** to help you with a gradual migration.
+
+## Official Guide { #official-guide }
+
+Pydantic has an official Migration Guide from v1 to v2.
+
+It also includes what has changed, how validations are now more correct and strict, possible caveats, etc.
+
+You can read it to understand better what has changed.
+
+## Tests { #tests }
+
+Make sure you have [tests](../tutorial/testing.md){.internal-link target=_blank} for your app and you run them on continuous integration (CI).
+
+This way, you can do the upgrade and make sure everything is still working as expected.
+
+## `bump-pydantic` { #bump-pydantic }
+
+In many cases, when you use regular Pydantic models without customizations, you will be able to automate most of the process of migrating from Pydantic v1 to Pydantic v2.
+
+You can use `bump-pydantic` from the same Pydantic team.
+
+This tool will help you to automatically change most of the code that needs to be changed.
+
+After this, you can run the tests and check if everything works. If it does, you are done. 😎
+
+## Pydantic v1 in v2 { #pydantic-v1-in-v2 }
+
+Pydantic v2 includes everything from Pydantic v1 as a submodule `pydantic.v1`.
+
+This means that you can install the latest version of Pydantic v2 and import and use the old Pydantic v1 components from this submodule, as if you had the old Pydantic v1 installed.
+
+{* ../../docs_src/pydantic_v1_in_v2/tutorial001_an_py310.py hl[1,4] *}
+
+### FastAPI support for Pydantic v1 in v2 { #fastapi-support-for-pydantic-v1-in-v2 }
+
+Since FastAPI 0.119.0, there's also partial support for Pydantic v1 from inside of Pydantic v2, to facilitate the migration to v2.
+
+So, you could upgrade Pydantic to the latest version 2, and change the imports to use the `pydantic.v1` submodule, and in many cases it would just work.
+
+{* ../../docs_src/pydantic_v1_in_v2/tutorial002_an_py310.py hl[2,5,15] *}
+
+/// warning
+
+Have in mind that as the Pydantic team no longer supports Pydantic v1 in recent versions of Python, starting from Python 3.14, using `pydantic.v1` is also not supported in Python 3.14 and above.
+
+///
+
+### Pydantic v1 and v2 on the same app { #pydantic-v1-and-v2-on-the-same-app }
+
+It's **not supported** by Pydantic to have a model of Pydantic v2 with its own fields defined as Pydantic v1 models or vice versa.
+
+```mermaid
+graph TB
+ subgraph "❌ Not Supported"
+ direction TB
+ subgraph V2["Pydantic v2 Model"]
+ V1Field["Pydantic v1 Model"]
+ end
+ subgraph V1["Pydantic v1 Model"]
+ V2Field["Pydantic v2 Model"]
+ end
+ end
+
+ style V2 fill:#f9fff3
+ style V1 fill:#fff6f0
+ style V1Field fill:#fff6f0
+ style V2Field fill:#f9fff3
+```
+
+...but, you can have separated models using Pydantic v1 and v2 in the same app.
+
+```mermaid
+graph TB
+ subgraph "✅ Supported"
+ direction TB
+ subgraph V2["Pydantic v2 Model"]
+ V2Field["Pydantic v2 Model"]
+ end
+ subgraph V1["Pydantic v1 Model"]
+ V1Field["Pydantic v1 Model"]
+ end
+ end
+
+ style V2 fill:#f9fff3
+ style V1 fill:#fff6f0
+ style V1Field fill:#fff6f0
+ style V2Field fill:#f9fff3
+```
+
+In some cases, it's even possible to have both Pydantic v1 and v2 models in the same **path operation** in your FastAPI app:
+
+{* ../../docs_src/pydantic_v1_in_v2/tutorial003_an_py310.py hl[2:3,6,12,21:22] *}
+
+In this example above, the input model is a Pydantic v1 model, and the output model (defined in `response_model=ItemV2`) is a Pydantic v2 model.
+
+### Pydantic v1 parameters { #pydantic-v1-parameters }
+
+If you need to use some of the FastAPI-specific tools for parameters like `Body`, `Query`, `Form`, etc. with Pydantic v1 models, you can import them from `fastapi.temp_pydantic_v1_params` while you finish the migration to Pydantic v2:
+
+{* ../../docs_src/pydantic_v1_in_v2/tutorial004_an_py310.py hl[4,18] *}
+
+### Migrate in steps { #migrate-in-steps }
+
+/// tip
+
+First try with `bump-pydantic`, if your tests pass and that works, then you're done in one command. ✨
+
+///
+
+If `bump-pydantic` doesn't work for your use case, you can use the support for both Pydantic v1 and v2 models in the same app to do the migration to Pydantic v2 gradually.
+
+You could fist upgrade Pydantic to use the latest version 2, and change the imports to use `pydantic.v1` for all your models.
+
+Then, you can start migrating your models from Pydantic v1 to v2 in groups, in gradual steps. 🚶
diff --git a/docs/en/docs/how-to/nosql-databases-couchbase.md b/docs/en/docs/how-to/nosql-databases-couchbase.md
deleted file mode 100644
index a0abfe21d2..0000000000
--- a/docs/en/docs/how-to/nosql-databases-couchbase.md
+++ /dev/null
@@ -1,178 +0,0 @@
-# ~~NoSQL (Distributed / Big Data) Databases with Couchbase~~ (deprecated)
-
-/// info
-
-These docs are about to be updated. 🎉
-
-The current version assumes Pydantic v1.
-
-The new docs will hopefully use Pydantic v2 and will use ODMantic with MongoDB.
-
-///
-
-/// warning | "Deprecated"
-
-This tutorial is deprecated and will be removed in a future version.
-
-///
-
-**FastAPI** can also be integrated with any NoSQL.
-
-Here we'll see an example using **Couchbase**, a document based NoSQL database.
-
-You can adapt it to any other NoSQL database like:
-
-* **MongoDB**
-* **Cassandra**
-* **CouchDB**
-* **ArangoDB**
-* **ElasticSearch**, etc.
-
-/// tip
-
-There is an official project generator with **FastAPI** and **Couchbase**, all based on **Docker**, including a frontend and more tools: https://github.com/tiangolo/full-stack-fastapi-couchbase
-
-///
-
-## Import Couchbase components
-
-For now, don't pay attention to the rest, only the imports:
-
-```Python hl_lines="3-5"
-{!../../../docs_src/nosql_databases/tutorial001.py!}
-```
-
-## Define a constant to use as a "document type"
-
-We will use it later as a fixed field `type` in our documents.
-
-This is not required by Couchbase, but is a good practice that will help you afterwards.
-
-```Python hl_lines="9"
-{!../../../docs_src/nosql_databases/tutorial001.py!}
-```
-
-## Add a function to get a `Bucket`
-
-In **Couchbase**, a bucket is a set of documents, that can be of different types.
-
-They are generally all related to the same application.
-
-The analogy in the relational database world would be a "database" (a specific database, not the database server).
-
-The analogy in **MongoDB** would be a "collection".
-
-In the code, a `Bucket` represents the main entrypoint of communication with the database.
-
-This utility function will:
-
-* Connect to a **Couchbase** cluster (that might be a single machine).
- * Set defaults for timeouts.
-* Authenticate in the cluster.
-* Get a `Bucket` instance.
- * Set defaults for timeouts.
-* Return it.
-
-```Python hl_lines="12-21"
-{!../../../docs_src/nosql_databases/tutorial001.py!}
-```
-
-## Create Pydantic models
-
-As **Couchbase** "documents" are actually just "JSON objects", we can model them with Pydantic.
-
-### `User` model
-
-First, let's create a `User` model:
-
-```Python hl_lines="24-28"
-{!../../../docs_src/nosql_databases/tutorial001.py!}
-```
-
-We will use this model in our *path operation function*, so, we don't include in it the `hashed_password`.
-
-### `UserInDB` model
-
-Now, let's create a `UserInDB` model.
-
-This will have the data that is actually stored in the database.
-
-We don't create it as a subclass of Pydantic's `BaseModel` but as a subclass of our own `User`, because it will have all the attributes in `User` plus a couple more:
-
-```Python hl_lines="31-33"
-{!../../../docs_src/nosql_databases/tutorial001.py!}
-```
-
-/// note
-
-Notice that we have a `hashed_password` and a `type` field that will be stored in the database.
-
-But it is not part of the general `User` model (the one we will return in the *path operation*).
-
-///
-
-## Get the user
-
-Now create a function that will:
-
-* Take a username.
-* Generate a document ID from it.
-* Get the document with that ID.
-* Put the contents of the document in a `UserInDB` model.
-
-By creating a function that is only dedicated to getting your user from a `username` (or any other parameter) independent of your *path operation function*, you can more easily reuse it in multiple parts and also add unit tests for it:
-
-```Python hl_lines="36-42"
-{!../../../docs_src/nosql_databases/tutorial001.py!}
-```
-
-### f-strings
-
-If you are not familiar with the `f"userprofile::{username}"`, it is a Python "f-string".
-
-Any variable that is put inside of `{}` in an f-string will be expanded / injected in the string.
-
-### `dict` unpacking
-
-If you are not familiar with the `UserInDB(**result.value)`, it is using `dict` "unpacking".
-
-It will take the `dict` at `result.value`, and take each of its keys and values and pass them as key-values to `UserInDB` as keyword arguments.
-
-So, if the `dict` contains:
-
-```Python
-{
- "username": "johndoe",
- "hashed_password": "some_hash",
-}
-```
-
-It will be passed to `UserInDB` as:
-
-```Python
-UserInDB(username="johndoe", hashed_password="some_hash")
-```
-
-## Create your **FastAPI** code
-
-### Create the `FastAPI` app
-
-```Python hl_lines="46"
-{!../../../docs_src/nosql_databases/tutorial001.py!}
-```
-
-### Create the *path operation function*
-
-As our code is calling Couchbase and we are not using the experimental Python await support, we should declare our function with normal `def` instead of `async def`.
-
-Also, Couchbase recommends not using a single `Bucket` object in multiple "threads", so, we can just get the bucket directly and pass it to our utility functions:
-
-```Python hl_lines="49-53"
-{!../../../docs_src/nosql_databases/tutorial001.py!}
-```
-
-## Recap
-
-You can integrate any third party NoSQL database, just using their standard packages.
-
-The same applies to any other external tool, system or API.
diff --git a/docs/en/docs/how-to/separate-openapi-schemas.md b/docs/en/docs/how-to/separate-openapi-schemas.md
index 0ab5b13371..3c78a56d36 100644
--- a/docs/en/docs/how-to/separate-openapi-schemas.md
+++ b/docs/en/docs/how-to/separate-openapi-schemas.md
@@ -1,4 +1,4 @@
-# Separate OpenAPI Schemas for Input and Output or Not
+# Separate OpenAPI Schemas for Input and Output or Not { #separate-openapi-schemas-for-input-and-output-or-not }
When using **Pydantic v2**, the generated OpenAPI is a bit more exact and **correct** than before. 😎
@@ -6,131 +6,21 @@ In fact, in some cases, it will even have **two JSON Schemas** in OpenAPI for th
Let's see how that works and how to change it if you need to do that.
-## Pydantic Models for Input and Output
+## Pydantic Models for Input and Output { #pydantic-models-for-input-and-output }
Let's say you have a Pydantic model with default values, like this one:
-//// tab | Python 3.10+
+{* ../../docs_src/separate_openapi_schemas/tutorial001_py310.py ln[1:7] hl[7] *}
-```Python hl_lines="7"
-{!> ../../../docs_src/separate_openapi_schemas/tutorial001_py310.py[ln:1-7]!}
-
-# Code below omitted 👇
-```
-
-
- תשתית FastAPI, ביצועים גבוהים, קלה ללמידה, מהירה לתכנות, מוכנה לסביבת ייצור -
- - ---- - -**תיעוד**: https://fastapi.tiangolo.com - -**קוד**: https://github.com/fastapi/fastapi - ---- - -FastAPI היא תשתית רשת מודרנית ומהירה (ביצועים גבוהים) לבניית ממשקי תכנות יישומים (API) עם פייתון 3.6+ בהתבסס על רמזי טיפוסים סטנדרטיים. - -תכונות המפתח הן: - -- **מהירה**: ביצועים גבוהים מאוד, בקנה אחד עם NodeJS ו - Go (תודות ל - Starlette ו - Pydantic). [אחת מתשתיות הפייתון המהירות ביותר](#_14). - -- **מהירה לתכנות**: הגבירו את מהירות פיתוח התכונות החדשות בכ - %200 עד %300. \* -- **פחות שגיאות**: מנעו כ - %40 משגיאות אנוש (מפתחים). \* -- **אינטואיטיבית**: תמיכת עורך מעולה. השלמה בכל מקום. פחות זמן ניפוי שגיאות. -- **קלה**: מתוכננת להיות קלה לשימוש וללמידה. פחות זמן קריאת תיעוד. -- **קצרה**: מזערו שכפול קוד. מספר תכונות מכל הכרזת פרמטר. פחות שגיאות. -- **חסונה**: קבלו קוד מוכן לסביבת ייצור. עם תיעוד אינטרקטיבי אוטומטי. -- **מבוססת סטנדרטים**: מבוססת על (ותואמת לחלוטין ל -) הסטדנרטים הפתוחים לממשקי תכנות יישומים: OpenAPI (ידועים לשעבר כ - Swagger) ו - JSON Schema. - -\* הערכה מבוססת על בדיקות של צוות פיתוח פנימי שבונה אפליקציות בסביבת ייצור. - -## נותני חסות - - - -{% if sponsors %} -{% for sponsor in sponsors.gold -%} -async def...uvicorn main:app --reload...app = FastAPI().
-- --reload: גרמו לשרת להתאתחל לאחר שינויים בקוד. עשו זאת רק בסביבת פיתוח.
-
-/items/{item_id}.
-- שני ה _נתיבים_ מקבלים _בקשות_ `GET` (ידועות גם כ*מתודות* HTTP).
-- ה _נתיב_ /items/{item_id} כולל \*פרמטר נתיב\_ `item_id` שאמור להיות `int`.
-- ה _נתיב_ /items/{item_id} \*פרמטר שאילתא\_ אופציונלי `q`.
-
-### תיעוד API אינטרקטיבי
-
-כעת פנו לכתובת http://127.0.0.1:8000/docs.
-
-אתם תראו את התיעוד האוטומטי (מסופק על ידי Swagger UI):
-
-
-
-### תיעוד אלטרנטיבי
-
-כעת פנו לכתובת http://127.0.0.1:8000/redoc.
-
-אתם תראו תיעוד אלטרנטיבי (מסופק על ידי ReDoc):
-
-
-
-## שדרוג לדוגמא
-
-כעת ערכו את הקובץ `main.py` כך שיוכל לקבל גוף מבקשת `PUT`.
-
-הגדירו את הגוף בעזרת רמזי טיפוסים סטנדרטיים, הודות ל - `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}
-```
-
-השרת אמול להתאתחל אוטומטית (מאחר והוספתם --reload לפקודת `uvicorn` שלמעלה).
-
-### שדרוג התיעוד האינטרקטיבי
-
-כעת פנו לכתובת http://127.0.0.1:8000/docs.
-
-- התיעוד האוטומטי יתעדכן, כולל הגוף החדש:
-
-
-
-- לחצו על הכפתור "Try it out", הוא יאפשר לכם למלא את הפרמטרים ולעבוד ישירות מול ה - API.
-
-
-
-- אחר כך לחצו על הכפתור "Execute", האתר יתקשר עם ה - API שלכם, ישלח את הפרמטרים, ישיג את התוצאות ואז יראה אותן על המסך:
-
-
-
-### שדרוג התיעוד האלטרנטיבי
-
-כעת פנו לכתובת http://127.0.0.1:8000/redoc.
-
-- התיעוד האלטרנטיבי גם יראה את פרמטר השאילתא והגוף החדשים.
-
-
-
-### סיכום
-
-לסיכום, אתם מכריזים ** פעם אחת** על טיפוסי הפרמטרים, גוף וכו' כפרמטרים לפונקציה.
-
-אתם עושים את זה עם טיפוסי פייתון מודרניים.
-
-אתם לא צריכים ללמוד תחביר חדש, מתודות או מחלקות של ספרייה ספיציפית, וכו'
-
-רק **פייתון 3.6+** סטנדרטי.
-
-לדוגמא, ל - `int`:
-
-```Python
-item_id: int
-```
-
-או למודל `Item` מורכב יותר:
-
-```Python
-item: Item
-```
-
-...ועם הכרזת הטיפוס האחת הזו אתם מקבלים:
-
-- תמיכת עורך, כולל:
- - השלמות.
- - בדיקת טיפוסים.
-- אימות מידע:
- - שגיאות ברורות ואטומטיות כאשר מוכנס מידע לא חוקי .
- - אימות אפילו לאובייקטי JSON מקוננים.
-- המרה של מידע קלט: המרה של מידע שמגיע מהרשת למידע וטיפוסים של פייתון. קורא מ:
- - JSON.
- - פרמטרי נתיב.
- - פרמטרי שאילתא.
- - עוגיות.
- - כותרות.
- - טפסים.
- - קבצים.
-- המרה של מידע פלט: המרה של מידע וטיפוסים מפייתון למידע רשת (כ - JSON):
- - המירו טיפוסי פייתון (`str`, `int`, `float`, `bool`, `list`, etc).
- - עצמי `datetime`.
- - עצמי `UUID`.
- - מודלי בסיסי נתונים.
- - ...ורבים אחרים.
-- תיעוד API אוטומטי ואינטרקטיבית כולל שתי אלטרנטיבות לממשק המשתמש:
- - Swagger UI.
- - ReDoc.
-
----
-
-בחזרה לדוגמאת הקוד הקודמת, **FastAPI** ידאג:
-
-- לאמת שיש `item_id` בנתיב בבקשות `GET` ו - `PUT`.
-- לאמת שה - `item_id` הוא מטיפוס `int` בבקשות `GET` ו - `PUT`.
- - אם הוא לא, הלקוח יראה שגיאה ברורה ושימושית.
-- לבדוק האם קיים פרמטר שאילתא בשם `q` (קרי `http://127.0.0.1:8000/items/foo?q=somequery`) לבקשות `GET`.
- - מאחר והפרמטר `q` מוגדר עם = None, הוא אופציונלי.
- - לולא ה - `None` הוא היה חובה (כמו הגוף במקרה של `PUT`).
-- לבקשות `PUT` לנתיב /items/{item_id}, לקרוא את גוף הבקשה כ - JSON:
- - לאמת שהוא כולל את מאפיין החובה `name` שאמור להיות מטיפוס `str`.
- - לאמת שהוא כולל את מאפיין החובה `price` שחייב להיות מטיפוס `float`.
- - לבדוק האם הוא כולל את מאפיין הרשות `is_offer` שאמור להיות מטיפוס `bool`, אם הוא נמצא.
- - כל זה יעבוד גם לאובייקט JSON מקונן.
-- להמיר מ - JSON ול- JSON אוטומטית.
-- לתעד הכל באמצעות OpenAPI, תיעוד שבו יוכלו להשתמש:
- - מערכות תיעוד אינטרקטיביות.
- - מערכות ייצור קוד אוטומטיות, להרבה שפות.
-- לספק ישירות שתי מערכות תיעוד רשתיות.
-
----
-
-רק גרדנו את קצה הקרחון, אבל כבר יש לכם רעיון של איך הכל עובד.
-
-נסו לשנות את השורה:
-
-```Python
- return {"item_name": item.name, "item_id": item_id}
-```
-
-...מ:
-
-```Python
- ... "item_name": item.name ...
-```
-
-...ל:
-
-```Python
- ... "item_price": item.price ...
-```
-
-...וראו איך העורך שלכם משלים את המאפיינים ויודע את הטיפוסים שלהם:
-
-
-
-לדוגמא יותר שלמה שכוללת עוד תכונות, ראו את המדריך - למשתמש.
-
-**התראת ספוילרים**: המדריך - למשתמש כולל:
-
-- הכרזה על **פרמטרים** ממקורות אחרים ושונים כגון: **כותרות**, **עוגיות**, **טפסים** ו - **קבצים**.
-- איך לקבוע **מגבלות אימות** בעזרת `maximum_length` או `regex`.
-- דרך חזקה וקלה להשתמש ב**הזרקת תלויות**.
-- אבטחה והתאמתות, כולל תמיכה ב - **OAuth2** עם **JWT** והתאמתות **HTTP Basic**.
-- טכניקות מתקדמות (אבל קלות באותה מידה) להכרזת אובייקטי JSON מקוננים (תודות ל - Pydantic).
-- אינטרקציה עם **GraphQL** דרך Strawberry וספריות אחרות.
-- תכונות נוספות רבות (תודות ל - Starlette) כגון:
- - **WebSockets**
- - בדיקות קלות במיוחד מבוססות על `requests` ו - `pytest`
- - **CORS**
- - **Cookie Sessions**
- - ...ועוד.
-
-## ביצועים
-
-בדיקות עצמאיות של TechEmpower הראו שאפליקציות **FastAPI** שרצות תחת Uvicorn הן מתשתיות הפייתון המהירות ביותר, רק מתחת ל - Starlette ו - Uvicorn עצמן (ש - FastAPI מבוססת עליהן). (\*)
-
-כדי להבין עוד על הנושא, ראו את הפרק Benchmarks.
-
-## תלויות אופציונליות
-
-בשימוש Pydantic:
-
-- email-validator - לאימות כתובות אימייל.
-
-בשימוש Starlette:
-
-- httpx - דרוש אם ברצונכם להשתמש ב - `TestClient`.
-- jinja2 - דרוש אם ברצונכם להשתמש בברירת המחדל של תצורת הטמפלייטים.
-- python-multipart - דרוש אם ברצונכם לתמוך ב "פרסור" טפסים, באצמעות request.form().
-- itsdangerous - דרוש אם ברצונכם להשתמש ב - `SessionMiddleware`.
-- pyyaml - דרוש אם ברצונכם להשתמש ב - `SchemaGenerator` של Starlette (כנראה שאתם לא צריכים את זה עם FastAPI).
-
-בשימוש FastAPI / Starlette:
-
-- uvicorn - לשרת שטוען ומגיש את האפליקציה שלכם.
-- orjson - דרוש אם ברצונכם להשתמש ב - `ORJSONResponse`.
-- ujson - דרוש אם ברצונכם להשתמש ב - `UJSONResponse`.
-
-תוכלו להתקין את כל אלו באמצעות pip install "fastapi[all]".
-
-## רשיון
-
-הפרויקט הזה הוא תחת התנאים של רשיון MIT.
diff --git a/docs/he/mkdocs.yml b/docs/he/mkdocs.yml
deleted file mode 100644
index de18856f44..0000000000
--- a/docs/he/mkdocs.yml
+++ /dev/null
@@ -1 +0,0 @@
-INHERIT: ../en/mkdocs.yml
diff --git a/docs/hu/docs/index.md b/docs/hu/docs/index.md
deleted file mode 100644
index 8e326a78b3..0000000000
--- a/docs/hu/docs/index.md
+++ /dev/null
@@ -1,467 +0,0 @@
-
-- FastAPI keretrendszer, nagy teljesítmény, könnyen tanulható, gyorsan kódolható, productionre kész -
- - ---- - -**Dokumentáció**: https://fastapi.tiangolo.com - -**Forrás kód**: https://github.com/fastapi/fastapi - ---- -A FastAPI egy modern, gyors (nagy teljesítményű), webes keretrendszer API-ok építéséhez Python -al, a Python szabványos típusjelöléseire építve. - - -Kulcs funkciók: - -* **Gyors**: Nagyon nagy teljesítmény, a **NodeJS**-el és a **Go**-val egyenrangú (a Starlettenek és a Pydantic-nek köszönhetően). [Az egyik leggyorsabb Python keretrendszer](#performance). -* **Gyorsan kódolható**: A funkciók fejlesztési sebességét 200-300 százalékkal megnöveli. * -* **Kevesebb hiba**: Körülbelül 40%-al csökkenti az emberi (fejlesztői) hibák számát. * -* **Intuitív**: Kiváló szerkesztő támogatás. Kiegészítés mindenhol. Kevesebb hibakereséssel töltött idő. -* **Egyszerű**: Egyszerű tanulásra és használatra tervezve. Kevesebb dokumentáció olvasással töltött idő. -* **Rövid**: Kód duplikáció minimalizálása. Több funkció minden paraméter deklarálásával. Kevesebb hiba. -* **Robosztus**: Production ready kód. Automatikus interaktív dokumentáció val. -* **Szabvány alapú**: Az API-ok nyílt szabványaira alapuló (és azokkal teljesen kompatibilis): OpenAPI (korábban Swagger néven ismert) és a JSON Schema. - -* Egy production alkalmazásokat építő belső fejlesztői csapat tesztjein alapuló becslés. - -## Szponzorok - - - -{% if sponsors %} -{% for sponsor in sponsors.gold -%} -async def-et...uvicorn main:app --reload...email-validator - e-mail validációkra.
-* pydantic-settings - Beállítások követésére.
-* pydantic-extra-types - Extra típusok Pydantic-hoz.
-
-Starlette által használt:
-
-* httpx - Követelmény ha a `TestClient`-et akarod használni.
-* jinja2 - Követelmény ha az alap template konfigurációt akarod használni.
-* python-multipart - Követelmény ha "parsing"-ot akarsz támogatni, `request.form()`-al.
-* itsdangerous - Követelmény `SessionMiddleware` támogatáshoz.
-* pyyaml - Követelmény a Starlette `SchemaGenerator`-ának támogatásához (valószínűleg erre nincs szükség FastAPI használása esetén).
-
-FastAPI / Starlette által használt
-
-* uvicorn - Szerverekhez amíg betöltik és szolgáltatják az applikációdat.
-* orjson - Követelmény ha `ORJSONResponse`-t akarsz használni.
-* ujson - Követelmény ha `UJSONResponse`-t akarsz használni.
-
-Ezeket mind telepítheted a `pip install "fastapi[all]"` paranccsal.
-
-## Licensz
-Ez a projekt az MIT license, licensz alatt fut
diff --git a/docs/hu/mkdocs.yml b/docs/hu/mkdocs.yml
deleted file mode 100644
index de18856f44..0000000000
--- a/docs/hu/mkdocs.yml
+++ /dev/null
@@ -1 +0,0 @@
-INHERIT: ../en/mkdocs.yml
diff --git a/docs/id/docs/tutorial/index.md b/docs/id/docs/tutorial/index.md
deleted file mode 100644
index f0dee3d730..0000000000
--- a/docs/id/docs/tutorial/index.md
+++ /dev/null
@@ -1,83 +0,0 @@
-# Tutorial - Pedoman Pengguna - Pengenalan
-
-Tutorial ini menunjukan cara menggunakan ***FastAPI*** dengan semua fitur-fiturnya, tahap demi tahap.
-
-Setiap bagian dibangun secara bertahap dari bagian sebelumnya, tetapi terstruktur untuk memisahkan banyak topik, sehingga kamu bisa secara langsung menuju ke topik spesifik untuk menyelesaikan kebutuhan API tertentu.
-
-Ini juga dibangun untuk digunakan sebagai referensi yang akan datang.
-
-Sehingga kamu dapat kembali lagi dan mencari apa yang kamu butuhkan dengan tepat.
-
-## Jalankan kode
-
-Semua blok-blok kode dapat disalin dan digunakan langsung (Mereka semua sebenarnya adalah file python yang sudah teruji).
-
-Untuk menjalankan setiap contoh, salin kode ke file `main.py`, dan jalankan `uvicorn` dengan:
-
-- FastAPI framework, alte prestazioni, facile da imparare, rapido da implementare, pronto per il rilascio in produzione -
- - ---- - -**Documentazione**: https://fastapi.tiangolo.com - -**Codice Sorgente**: https://github.com/fastapi/fastapi - ---- - -FastAPI è un web framework moderno e veloce (a prestazioni elevate) che serve a creare API con Python 3.6+ basato sulle annotazioni di tipo di Python. - -Le sue caratteristiche principali sono: - -* **Velocità**: Prestazioni molto elevate, alla pari di **NodeJS** e **Go** (grazie a Starlette e Pydantic). [Uno dei framework Python più veloci in circolazione](#performance). -* **Veloce da programmare**: Velocizza il lavoro consentendo il rilascio di nuove funzionalità tra il 200% e il 300% più rapidamente. * -* **Meno bug**: Riduce di circa il 40% gli errori che commettono gli sviluppatori durante la scrittura del codice. * -* **Intuitivo**: Grande supporto per gli editor di testo con autocompletamento in ogni dove. In questo modo si può dedicare meno tempo al debugging. -* **Facile**: Progettato per essere facile da usare e imparare. Si riduce il tempo da dedicare alla lettura della documentazione. -* **Sintentico**: Minimizza la duplicazione di codice. Molteplici funzionalità, ognuna con la propria dichiarazione dei parametri. Meno errori. -* **Robusto**: Crea codice pronto per la produzione con documentazione automatica interattiva. -* **Basato sugli standard**: Basato su (e completamente compatibile con) gli open standard per le API: OpenAPI (precedentemente Swagger) e JSON Schema. - -* Stima basata sull'esito di test eseguiti su codice sorgente di applicazioni rilasciate in produzione da un team interno di sviluppatori. - -## Sponsor - - - -{% if sponsors %} -{% for sponsor in sponsors.gold -%} -async def...uvicorn main:app --reload...email-validator - per la validazione di email.
-
-Usate da Starlette:
-
-* requests - Richiesto se vuoi usare il `TestClient`.
-* aiofiles - Richiesto se vuoi usare `FileResponse` o `StaticFiles`.
-* jinja2 - Richiesto se vuoi usare la configurazione template di default.
-* python-multipart - Richiesto se vuoi supportare il "parsing" con `request.form()`.
-* itsdangerous - Richiesto per usare `SessionMiddleware`.
-* pyyaml - Richiesto per il supporto dello `SchemaGenerator` di Starlette (probabilmente non ti serve con FastAPI).
-* graphene - Richiesto per il supporto di `GraphQLApp`.
-
-Usate da FastAPI / Starlette:
-
-* uvicorn - per il server che carica e serve la tua applicazione.
-* orjson - ichiesto se vuoi usare `ORJSONResponse`.
-* ujson - Richiesto se vuoi usare `UJSONResponse`.
-
-Puoi installarle tutte con `pip install fastapi[all]`.
-
-## Licenza
-
-Questo progetto è concesso in licenza in base ai termini della licenza MIT.
diff --git a/docs/it/mkdocs.yml b/docs/it/mkdocs.yml
deleted file mode 100644
index de18856f44..0000000000
--- a/docs/it/mkdocs.yml
+++ /dev/null
@@ -1 +0,0 @@
-INHERIT: ../en/mkdocs.yml
diff --git a/docs/ja/docs/advanced/additional-status-codes.md b/docs/ja/docs/advanced/additional-status-codes.md
index 622affa6eb..33457f591c 100644
--- a/docs/ja/docs/advanced/additional-status-codes.md
+++ b/docs/ja/docs/advanced/additional-status-codes.md
@@ -14,11 +14,9 @@
これを達成するには、 `JSONResponse` をインポートし、 `status_code` を設定して直接内容を返します。
-```Python hl_lines="4 25"
-{!../../../docs_src/additional_status_codes/tutorial001.py!}
-```
+{* ../../docs_src/additional_status_codes/tutorial001.py hl[4,25] *}
-/// warning | "注意"
+/// warning | 注意
上記の例のように `Response` を明示的に返す場合、それは直接返されます。
@@ -28,7 +26,7 @@
///
-/// note | "技術詳細"
+/// note | 技術詳細
`from starlette.responses import JSONResponse` を利用することもできます。
diff --git a/docs/ja/docs/advanced/custom-response.md b/docs/ja/docs/advanced/custom-response.md
index a7ce6b54d9..1b2cd914d4 100644
--- a/docs/ja/docs/advanced/custom-response.md
+++ b/docs/ja/docs/advanced/custom-response.md
@@ -12,7 +12,7 @@
そしてもし、`Response` が、`JSONResponse` や `UJSONResponse` の場合のようにJSONメディアタイプ (`application/json`) ならば、データは *path operationデコレータ* に宣言したPydantic `response_model` により自動的に変換 (もしくはフィルタ) されます。
-/// note | "備考"
+/// note | 備考
メディアタイプを指定せずにレスポンスクラスを利用すると、FastAPIは何もコンテンツがないことを期待します。そのため、生成されるOpenAPIドキュメントにレスポンスフォーマットが記載されません。
@@ -24,11 +24,9 @@
使いたい `Response` クラス (サブクラス) をインポートし、 *path operationデコレータ* に宣言します。
-```Python hl_lines="2 7"
-{!../../../docs_src/custom_response/tutorial001b.py!}
-```
+{* ../../docs_src/custom_response/tutorial001b.py hl[2,7] *}
-/// info | "情報"
+/// info | 情報
パラメータ `response_class` は、レスポンスの「メディアタイプ」を定義するために利用することもできます。
@@ -38,7 +36,7 @@
///
-/// tip | "豆知識"
+/// tip | 豆知識
`ORJSONResponse` は、現在はFastAPIのみで利用可能で、Starletteでは利用できません。
@@ -51,11 +49,9 @@
* `HTMLResponse` をインポートする。
* *path operation* のパラメータ `content_type` に `HTMLResponse` を渡す。
-```Python hl_lines="2 7"
-{!../../../docs_src/custom_response/tutorial002.py!}
-```
+{* ../../docs_src/custom_response/tutorial002.py hl[2,7] *}
-/// info | "情報"
+/// info | 情報
パラメータ `response_class` は、レスポンスの「メディアタイプ」を定義するために利用されます。
@@ -71,17 +67,15 @@
上記と同じ例において、 `HTMLResponse` を返すと、このようになります:
-```Python hl_lines="2 7 19"
-{!../../../docs_src/custom_response/tutorial003.py!}
-```
+{* ../../docs_src/custom_response/tutorial003.py hl[2,7,19] *}
-/// warning | "注意"
+/// warning | 注意
*path operation関数* から直接返される `Response` は、OpenAPIにドキュメントされず (例えば、 `Content-Type` がドキュメントされない) 、自動的な対話的ドキュメントからも閲覧できません。
///
-/// info | "情報"
+/// info | 情報
もちろん、実際の `Content-Type` ヘッダーやステータスコードなどは、返された `Response` オブジェクトに由来しています。
@@ -97,9 +91,7 @@
例えば、このようになります:
-```Python hl_lines="7 21 23"
-{!../../../docs_src/custom_response/tutorial004.py!}
-```
+{* ../../docs_src/custom_response/tutorial004.py hl[7,21,23] *}
この例では、関数 `generate_html_response()` は、`str` のHTMLを返すのではなく `Response` を生成して返しています。
@@ -115,7 +107,7 @@
`Response` を使って他の何かを返せますし、カスタムのサブクラスも作れることを覚えておいてください。
-/// note | "技術詳細"
+/// note | 技術詳細
`from starlette.responses import HTMLResponse` も利用できます。
@@ -138,9 +130,7 @@
FastAPI (実際にはStarlette) は自動的にContent-Lengthヘッダーを含みます。また、media_typeに基づいたContent-Typeヘッダーを含み、テキストタイプのためにcharsetを追加します。
-```Python hl_lines="1 18"
-{!../../../docs_src/response_directly/tutorial002.py!}
-```
+{* ../../docs_src/response_directly/tutorial002.py hl[1,18] *}
### `HTMLResponse`
@@ -150,9 +140,7 @@ FastAPI (実際にはStarlette) は自動的にContent-Lengthヘッダーを含
テキストやバイトを受け取り、プレーンテキストのレスポンスを返します。
-```Python hl_lines="2 7 9"
-{!../../../docs_src/custom_response/tutorial005.py!}
-```
+{* ../../docs_src/custom_response/tutorial005.py hl[2,7,9] *}
### `JSONResponse`
@@ -168,17 +156,15 @@ FastAPI (実際にはStarlette) は自動的にContent-Lengthヘッダーを含
`ujson`を使った、代替のJSONレスポンスです。
-/// warning | "注意"
+/// warning | 注意
`ujson` は、いくつかのエッジケースの取り扱いについて、Pythonにビルトインされた実装よりも作りこまれていません。
///
-```Python hl_lines="2 7"
-{!../../../docs_src/custom_response/tutorial001.py!}
-```
+{* ../../docs_src/custom_response/tutorial001.py hl[2,7] *}
-/// tip | "豆知識"
+/// tip | 豆知識
`ORJSONResponse` のほうが高速な代替かもしれません。
@@ -188,17 +174,13 @@ FastAPI (実際にはStarlette) は自動的にContent-Lengthヘッダーを含
HTTPリダイレクトを返します。デフォルトでは307ステータスコード (Temporary Redirect) となります。
-```Python hl_lines="2 9"
-{!../../../docs_src/custom_response/tutorial006.py!}
-```
+{* ../../docs_src/custom_response/tutorial006.py hl[2,9] *}
### `StreamingResponse`
非同期なジェネレータか通常のジェネレータ・イテレータを受け取り、レスポンスボディをストリームします。
-```Python hl_lines="2 14"
-{!../../../docs_src/custom_response/tutorial007.py!}
-```
+{* ../../docs_src/custom_response/tutorial007.py hl[2,14] *}
#### `StreamingResponse` をファイルライクなオブジェクトとともに使う
@@ -206,11 +188,9 @@ HTTPリダイレクトを返します。デフォルトでは307ステータス
これにはクラウドストレージとの連携や映像処理など、多くのライブラリが含まれています。
-```Python hl_lines="2 10-12 14"
-{!../../../docs_src/custom_response/tutorial008.py!}
-```
+{* ../../docs_src/custom_response/tutorial008.py hl[2,10:12,14] *}
-/// tip | "豆知識"
+/// tip | 豆知識
ここでは `async` や `await` をサポートしていない標準の `open()` を使っているので、通常の `def` でpath operationを宣言していることに注意してください。
@@ -229,9 +209,7 @@ HTTPリダイレクトを返します。デフォルトでは307ステータス
ファイルレスポンスには、適切な `Content-Length` 、 `Last-Modified` 、 `ETag` ヘッダーが含まれます。
-```Python hl_lines="2 10"
-{!../../../docs_src/custom_response/tutorial009.py!}
-```
+{* ../../docs_src/custom_response/tutorial009.py hl[2,10] *}
## デフォルトレスポンスクラス
@@ -241,11 +219,9 @@ HTTPリダイレクトを返します。デフォルトでは307ステータス
以下の例では、 **FastAPI** は、全ての *path operation* で `JSONResponse` の代わりに `ORJSONResponse` をデフォルトとして利用します。
-```Python hl_lines="2 4"
-{!../../../docs_src/custom_response/tutorial010.py!}
-```
+{* ../../docs_src/custom_response/tutorial010.py hl[2,4] *}
-/// tip | "豆知識"
+/// tip | 豆知識
前に見たように、 *path operation* の中で `response_class` をオーバーライドできます。
diff --git a/docs/ja/docs/advanced/index.md b/docs/ja/docs/advanced/index.md
index da3c2a2bf6..22eaf6eb80 100644
--- a/docs/ja/docs/advanced/index.md
+++ b/docs/ja/docs/advanced/index.md
@@ -6,7 +6,7 @@
以降のセクションでは、チュートリアルでは説明しきれなかったオプションや設定、および機能について説明します。
-/// tip | "豆知識"
+/// tip | 豆知識
以降のセクションは、 **必ずしも"応用編"ではありません**。
diff --git a/docs/ja/docs/advanced/path-operation-advanced-configuration.md b/docs/ja/docs/advanced/path-operation-advanced-configuration.md
index ae60126cbc..05188d5b25 100644
--- a/docs/ja/docs/advanced/path-operation-advanced-configuration.md
+++ b/docs/ja/docs/advanced/path-operation-advanced-configuration.md
@@ -2,7 +2,7 @@
## OpenAPI operationId
-/// warning | "注意"
+/// warning | 注意
あなたがOpenAPIの「エキスパート」でなければ、これは必要ないかもしれません。
@@ -12,9 +12,7 @@
`operation_id` は各オペレーションで一意にする必要があります。
-```Python hl_lines="6"
-{!../../../docs_src/path_operation_advanced_configuration/tutorial001.py!}
-```
+{* ../../docs_src/path_operation_advanced_configuration/tutorial001.py hl[6] *}
### *path operation関数* の名前をoperationIdとして使用する
@@ -22,17 +20,15 @@ APIの関数名を `operationId` として利用したい場合、すべてのAP
そうする場合は、すべての *path operation* を追加した後に行う必要があります。
-```Python hl_lines="2 12-21 24"
-{!../../../docs_src/path_operation_advanced_configuration/tutorial002.py!}
-```
+{* ../../docs_src/path_operation_advanced_configuration/tutorial002.py hl[2,12:21,24] *}
-/// tip | "豆知識"
+/// tip | 豆知識
`app.openapi()` を手動でコールする場合、その前に`operationId`を更新する必要があります。
///
-/// warning | "注意"
+/// warning | 注意
この方法をとる場合、各 *path operation関数* が一意な名前である必要があります。
@@ -44,9 +40,7 @@ APIの関数名を `operationId` として利用したい場合、すべてのAP
生成されるOpenAPIスキーマ (つまり、自動ドキュメント生成の仕組み) から *path operation* を除外するには、 `include_in_schema` パラメータを `False` にします。
-```Python hl_lines="6"
-{!../../../docs_src/path_operation_advanced_configuration/tutorial003.py!}
-```
+{* ../../docs_src/path_operation_advanced_configuration/tutorial003.py hl[6] *}
## docstringによる説明の高度な設定
@@ -56,6 +50,4 @@ APIの関数名を `operationId` として利用したい場合、すべてのAP
ドキュメントには表示されませんが、他のツール (例えばSphinx) では残りの部分を利用できるでしょう。
-```Python hl_lines="19-29"
-{!../../../docs_src/path_operation_advanced_configuration/tutorial004.py!}
-```
+{* ../../docs_src/path_operation_advanced_configuration/tutorial004.py hl[19:29] *}
diff --git a/docs/ja/docs/advanced/response-directly.md b/docs/ja/docs/advanced/response-directly.md
index 5c25471b1b..42412d5070 100644
--- a/docs/ja/docs/advanced/response-directly.md
+++ b/docs/ja/docs/advanced/response-directly.md
@@ -14,7 +14,7 @@
実際は、`Response` やそのサブクラスを返すことができます。
-/// tip | "豆知識"
+/// tip | 豆知識
`JSONResponse` それ自体は、 `Response` のサブクラスです。
@@ -34,11 +34,9 @@
このようなケースでは、レスポンスにデータを含める前に `jsonable_encoder` を使ってデータを変換できます。
-```Python hl_lines="6-7 21-22"
-{!../../../docs_src/response_directly/tutorial001.py!}
-```
+{* ../../docs_src/response_directly/tutorial001.py hl[6:7,21:22] *}
-/// note | "技術詳細"
+/// note | 技術詳細
また、`from starlette.responses import JSONResponse` も利用できます。
@@ -56,9 +54,7 @@
XMLを文字列にし、`Response` に含め、それを返します。
-```Python hl_lines="1 18"
-{!../../../docs_src/response_directly/tutorial002.py!}
-```
+{* ../../docs_src/response_directly/tutorial002.py hl[1,18] *}
## 備考
diff --git a/docs/ja/docs/advanced/websockets.md b/docs/ja/docs/advanced/websockets.md
index 615f9d17c8..2517530abe 100644
--- a/docs/ja/docs/advanced/websockets.md
+++ b/docs/ja/docs/advanced/websockets.md
@@ -38,19 +38,15 @@ $ pip install websockets
しかし、これはWebSocketのサーバーサイドに焦点を当て、実用的な例を示す最も簡単な方法です。
-```Python hl_lines="2 6-38 41-43"
-{!../../../docs_src/websockets/tutorial001.py!}
-```
+{* ../../docs_src/websockets/tutorial001.py hl[2,6:38,41:43] *}
## `websocket` を作成する
**FastAPI** アプリケーションで、`websocket` を作成します。
-```Python hl_lines="1 46-47"
-{!../../../docs_src/websockets/tutorial001.py!}
-```
+{* ../../docs_src/websockets/tutorial001.py hl[1,46:47] *}
-/// note | "技術詳細"
+/// note | 技術詳細
`from starlette.websockets import WebSocket` を使用しても構いません.
@@ -62,9 +58,7 @@ $ pip install websockets
WebSocketルートでは、 `await` を使ってメッセージの送受信ができます。
-```Python hl_lines="48-52"
-{!../../../docs_src/websockets/tutorial001.py!}
-```
+{* ../../docs_src/websockets/tutorial001.py hl[48:52] *}
バイナリやテキストデータ、JSONデータを送受信できます。
@@ -115,11 +109,9 @@ WebSocketエンドポイントでは、`fastapi` から以下をインポート
これらは、他のFastAPI エンドポイント/*path operation* の場合と同じように機能します。
-```Python hl_lines="58-65 68-83"
-{!../../../docs_src/websockets/tutorial002.py!}
-```
+{* ../../docs_src/websockets/tutorial002.py hl[58:65,68:83] *}
-/// info | "情報"
+/// info | 情報
WebSocket で `HTTPException` を発生させることはあまり意味がありません。したがって、WebSocketの接続を直接閉じる方がよいでしょう。
@@ -150,7 +142,7 @@ $ uvicorn main:app --reload
* パスで使用される「Item ID」
* クエリパラメータとして使用される「Token」
-/// tip | "豆知識"
+/// tip | 豆知識
クエリ `token` は依存パッケージによって処理されることに注意してください。
@@ -164,9 +156,7 @@ $ uvicorn main:app --reload
WebSocket接続が閉じられると、 `await websocket.receive_text()` は例外 `WebSocketDisconnect` を発生させ、この例のようにキャッチして処理することができます。
-```Python hl_lines="81-83"
-{!../../../docs_src/websockets/tutorial003.py!}
-```
+{* ../../docs_src/websockets/tutorial003.py hl[81:83] *}
試してみるには、
@@ -180,7 +170,7 @@ WebSocket接続が閉じられると、 `await websocket.receive_text()` は例
Client #1596980209979 left the chat
```
-/// tip | "豆知識"
+/// tip | 豆知識
上記のアプリは、複数の WebSocket 接続に対してメッセージを処理し、ブロードキャストする方法を示すための最小限のシンプルな例です。
@@ -194,5 +184,5 @@ Client #1596980209979 left the chat
オプションの詳細については、Starletteのドキュメントを確認してください。
-* `WebSocket` クラス
-* クラスベースのWebSocket処理
+* `WebSocket` クラス
+* クラスベースのWebSocket処理
diff --git a/docs/ja/docs/alternatives.md b/docs/ja/docs/alternatives.md
index 343ae4ed87..9f5152c08a 100644
--- a/docs/ja/docs/alternatives.md
+++ b/docs/ja/docs/alternatives.md
@@ -30,13 +30,13 @@ Mozilla、Red Hat、Eventbrite など多くの企業で利用されています
これは**自動的なAPIドキュメント生成**の最初の例であり、これは**FastAPI**に向けた「調査」を触発した最初のアイデアの一つでした。
-/// note | "備考"
+/// note | 備考
Django REST Framework は Tom Christie によって作成されました。StarletteとUvicornの生みの親であり、**FastAPI**のベースとなっています。
///
-/// check | "**FastAPI**へ与えたインスピレーション"
+/// check | **FastAPI**へ与えたインスピレーション
自動でAPIドキュメントを生成するWebユーザーインターフェースを持っている点。
@@ -56,7 +56,7 @@ Flask は「マイクロフレームワーク」であり、データベース
Flaskのシンプルさを考えると、APIを構築するのに適しているように思えました。次に見つけるべきは、Flask 用の「Django REST Framework」でした。
-/// check | "**FastAPI**へ与えたインスピレーション"
+/// check | **FastAPI**へ与えたインスピレーション
マイクロフレームワークであること。ツールやパーツを目的に合うように簡単に組み合わせられる点。
@@ -98,7 +98,7 @@ def read_url():
`requests.get(...)` と`@app.get(...)` には類似点が見受けられます。
-/// check | "**FastAPI**へ与えたインスピレーション"
+/// check | **FastAPI**へ与えたインスピレーション
* シンプルで直感的なAPIを持っている点。
* HTTPメソッド名を直接利用し、単純で直感的である。
@@ -118,7 +118,7 @@ def read_url():
そのため、バージョン2.0では「Swagger」、バージョン3以上では「OpenAPI」と表記するのが一般的です。
-/// check | "**FastAPI**へ与えたインスピレーション"
+/// check | **FastAPI**へ与えたインスピレーション
独自のスキーマの代わりに、API仕様のオープンな標準を採用しました。
@@ -147,7 +147,7 @@ APIが必要とするもう一つの大きな機能はデータのバリデー
しかし、それはPythonの型ヒントが存在する前に作られたものです。そのため、すべてのスキーマを定義するためには、Marshmallowが提供する特定のユーティリティやクラスを使用する必要があります。
-/// check | "**FastAPI**へ与えたインスピレーション"
+/// check | **FastAPI**へ与えたインスピレーション
コードで「スキーマ」を定義し、データの型やバリデーションを自動で提供する点。
@@ -163,13 +163,13 @@ WebargsはFlaskをはじめとするいくつかのフレームワークの上
素晴らしいツールで、私も**FastAPI**を持つ前はよく使っていました。
-/// info | "情報"
+/// info | 情報
Webargsは、Marshmallowと同じ開発者により作られました。
///
-/// check | "**FastAPI**へ与えたインスピレーション"
+/// check | **FastAPI**へ与えたインスピレーション
受信したデータに対する自動的なバリデーションを持っている点。
@@ -193,13 +193,13 @@ Flask, Starlette, Responderなどにおいてはそのように動作します
エディタでは、この問題を解決することはできません。また、パラメータやMarshmallowスキーマを変更したときに、YAMLのdocstringを変更するのを忘れてしまうと、生成されたスキーマが古くなってしまいます。
-/// info | "情報"
+/// info | 情報
APISpecは、Marshmallowと同じ開発者により作成されました。
///
-/// check | "**FastAPI**へ与えたインスピレーション"
+/// check | **FastAPI**へ与えたインスピレーション
OpenAPIという、APIについてのオープンな標準をサポートしている点。
@@ -225,13 +225,13 @@ Flask、Flask-apispec、Marshmallow、Webargsの組み合わせは、**FastAPI**
そして、これらのフルスタックジェネレーターは、[**FastAPI** Project Generators](project-generation.md){.internal-link target=_blank}の元となっていました。
-/// info | "情報"
+/// info | 情報
Flask-apispecはMarshmallowと同じ開発者により作成されました。
///
-/// check | "**FastAPI**へ与えたインスピレーション"
+/// check | **FastAPI**へ与えたインスピレーション
シリアライゼーションとバリデーションを定義したコードから、OpenAPIスキーマを自動的に生成する点。
@@ -251,7 +251,7 @@ Angular 2にインスピレーションを受けた、統合された依存性
入れ子になったモデルをうまく扱えません。そのため、リクエストのJSONボディが内部フィールドを持つJSONオブジェクトで、それが順番にネストされたJSONオブジェクトになっている場合、適切にドキュメント化やバリデーションをすることができません。
-/// check | "**FastAPI**へ与えたインスピレーション"
+/// check | **FastAPI**へ与えたインスピレーション
素晴らしいエディターの補助を得るために、Pythonの型ヒントを利用している点。
@@ -263,7 +263,7 @@ Angular 2にインスピレーションを受けた、統合された依存性
`asyncio`に基づいた、Pythonのフレームワークの中でも非常に高速なものの一つです。Flaskと非常に似た作りになっています。
-/// note | "技術詳細"
+/// note | 技術詳細
Pythonの`asyncio`ループの代わりに、`uvloop`が利用されています。それにより、非常に高速です。
@@ -271,7 +271,7 @@ Pythonの`asyncio`ループの代わりに、`uvloop`が利用されています
///
-/// check | "**FastAPI**へ与えたインスピレーション"
+/// check | **FastAPI**へ与えたインスピレーション
物凄い性能を出す方法を見つけた点。
@@ -289,7 +289,7 @@ Pythonのウェブフレームワーク標準規格 (WSGI) を使用していま
そのため、データのバリデーション、シリアライゼーション、ドキュメント化は、自動的にできずコードの中で行わなければなりません。あるいは、HugのようにFalconの上にフレームワークとして実装されなければなりません。このような分断は、パラメータとして1つのリクエストオブジェクトと1つのレスポンスオブジェクトを持つというFalconのデザインにインスピレーションを受けた他のフレームワークでも起こります。
-/// check | "**FastAPI**へ与えたインスピレーション"
+/// check | **FastAPI**へ与えたインスピレーション
素晴らしい性能を得るための方法を見つけた点。
@@ -315,7 +315,7 @@ Pydanticのようなデータのバリデーション、シリアライゼーシ
ルーティングは一つの場所で宣言され、他の場所で宣言された関数を使用します (エンドポイントを扱う関数のすぐ上に配置できるデコレータを使用するのではなく) 。これはFlask (やStarlette) よりも、Djangoに近いです。これは、比較的緊密に結合されているものをコードの中で分離しています。
-/// check | "**FastAPI**へ与えたインスピレーション"
+/// check | **FastAPI**へ与えたインスピレーション
モデルの属性の「デフォルト」値を使用したデータ型の追加バリデーションを定義します。これはエディタの補助を改善するもので、以前はPydanticでは利用できませんでした。
@@ -337,13 +337,13 @@ OpenAPIやJSON Schemaのような標準に基づいたものではありませ
以前のPythonの同期型Webフレームワーク標準 (WSGI) をベースにしているため、Websocketなどは扱えませんが、それでも高性能です。
-/// info | "情報"
+/// info | 情報
HugはTimothy Crosleyにより作成されました。彼は`isort`など、Pythonのファイル内のインポートの並び替えを自動的におこうなう素晴らしいツールの開発者です。
///
-/// check | "**FastAPI**へ与えたインスピレーション"
+/// check | **FastAPI**へ与えたインスピレーション
HugはAPIStarに部分的なインスピレーションを与えており、私が発見した中ではAPIStarと同様に最も期待の持てるツールの一つでした。
@@ -377,7 +377,7 @@ Hugは、**FastAPI**がヘッダーやクッキーを設定するために関数
今ではAPIStarはOpenAPI仕様を検証するためのツールセットであり、ウェブフレームワークではありません。
-/// info | "情報"
+/// info | 情報
APIStarはTom Christieにより開発されました。以下の開発者でもあります:
@@ -387,7 +387,7 @@ APIStarはTom Christieにより開発されました。以下の開発者でも
///
-/// check | "**FastAPI**へ与えたインスピレーション"
+/// check | **FastAPI**へ与えたインスピレーション
存在そのもの。
@@ -411,7 +411,7 @@ Pydanticは、Pythonの型ヒントを元にデータのバリデーション、
Marshmallowに匹敵しますが、ベンチマークではMarshmallowよりも高速です。また、Pythonの型ヒントを元にしているので、エディタの補助が素晴らしいです。
-/// check | "**FastAPI**での使用用途"
+/// check | **FastAPI**での使用用途
データのバリデーション、データのシリアライゼーション、自動的なモデルの (JSON Schemaに基づいた) ドキュメント化の全てを扱えます。
@@ -419,7 +419,7 @@ Marshmallowに匹敵しますが、ベンチマークではMarshmallowよりも
///
-### Starlette
+### Starlette
Starletteは、軽量なASGIフレームワーク/ツールキットで、高性能な非同期サービスの構築に最適です。
@@ -447,7 +447,7 @@ Starletteは基本的なWebマイクロフレームワークの機能をすべ
これは **FastAPI** が追加する主な機能の一つで、すべての機能は Pythonの型ヒントに基づいています (Pydanticを使用しています) 。これに加えて、依存性注入の仕組み、セキュリティユーティリティ、OpenAPIスキーマ生成などがあります。
-/// note | "技術詳細"
+/// note | 技術詳細
ASGIはDjangoのコアチームメンバーにより開発された新しい「標準」です。まだ「Pythonの標準 (PEP) 」ではありませんが、現在そうなるように進めています。
@@ -455,7 +455,7 @@ ASGIはDjangoのコアチームメンバーにより開発された新しい「
///
-/// check | "**FastAPI**での使用用途"
+/// check | **FastAPI**での使用用途
webに関するコアな部分を全て扱います。その上に機能を追加します。
@@ -465,7 +465,7 @@ webに関するコアな部分を全て扱います。その上に機能を追
///
-### Uvicorn
+### Uvicorn
Uvicornは非常に高速なASGIサーバーで、uvloopとhttptoolsにより構成されています。
@@ -473,7 +473,7 @@ Uvicornは非常に高速なASGIサーバーで、uvloopとhttptoolsにより構
Starletteや**FastAPI**のサーバーとして推奨されています。
-/// check | "**FastAPI**が推奨する理由"
+/// check | **FastAPI**が推奨する理由
**FastAPI**アプリケーションを実行するメインのウェブサーバーである点。
diff --git a/docs/ja/docs/async.md b/docs/ja/docs/async.md
index ce9dac56fe..90a2e2ee55 100644
--- a/docs/ja/docs/async.md
+++ b/docs/ja/docs/async.md
@@ -21,7 +21,7 @@ async def read_results():
return results
```
-/// note | "備考"
+/// note | 備考
`async def` を使用して作成された関数の内部でしか `await` は使用できません。
@@ -338,7 +338,7 @@ async def read_burgers():
以前のバージョンのPythonでは、スレッドやGeventが利用できました。しかし、コードは理解、デバック、そして、考察がはるかに複雑です。
-以前のバージョンのNodeJS / ブラウザJavaScriptでは、「コールバック」を使用していました。これは、コールバック地獄につながります。
+以前のバージョンのNodeJS / ブラウザJavaScriptでは、「コールバック」を使用していました。これは、「コールバック地獄」につながります。
## コルーチン
@@ -358,7 +358,7 @@ async def read_burgers():
## 非常に発展的な技術的詳細
-/// warning | "注意"
+/// warning | 注意
恐らくスキップしても良いでしょう。
diff --git a/docs/ja/docs/contributing.md b/docs/ja/docs/contributing.md
deleted file mode 100644
index 86926b2132..0000000000
--- a/docs/ja/docs/contributing.md
+++ /dev/null
@@ -1,498 +0,0 @@
-# 開発 - 貢献
-
-まず、[FastAPIを応援 - ヘルプの入手](help-fastapi.md){.internal-link target=_blank}の基本的な方法を見て、ヘルプを得た方がいいかもしれません。
-
-## 開発
-
-すでにリポジトリをクローンし、コードを詳しく調べる必要があるとわかっている場合に、環境構築のためのガイドラインをいくつか紹介します。
-
-### `venv`を使用した仮想環境
-
-Pythonの`venv`モジュールを使用して、ディレクトリに仮想環境を作成します:
-
-uvicorn - アプリケーションをロードしてサーブするサーバーのため。
+- uvicorn - アプリケーションをロードしてサーブするサーバーのため。
- orjson - `ORJSONResponse`を使用したい場合は必要です。
- ujson - `UJSONResponse`を使用する場合は必須です。
diff --git a/docs/ja/docs/python-types.md b/docs/ja/docs/python-types.md
index 730a209ab1..a847ce5d54 100644
--- a/docs/ja/docs/python-types.md
+++ b/docs/ja/docs/python-types.md
@@ -12,7 +12,7 @@
しかしたとえまったく **FastAPI** を使用しない場合でも、それらについて少し学ぶことで利点を得ることができるでしょう。
-/// note | "備考"
+/// note | 備考
もしあなたがPythonの専門家で、すでに型ヒントについてすべて知っているのであれば、次の章まで読み飛ばしてください。
@@ -22,9 +22,8 @@
簡単な例から始めてみましょう:
-```Python
-{!../../../docs_src/python_types/tutorial001.py!}
-```
+{* ../../docs_src/python_types/tutorial001.py *}
+
このプログラムを実行すると以下が出力されます:
@@ -38,9 +37,8 @@ John Doe
* `title()`を用いて、それぞれの最初の文字を大文字に変換します。
* 真ん中にスペースを入れて連結します。
-```Python hl_lines="2"
-{!../../../docs_src/python_types/tutorial001.py!}
-```
+{* ../../docs_src/python_types/tutorial001.py hl[2] *}
+
### 編集
@@ -82,9 +80,8 @@ John Doe
それが「型ヒント」です:
-```Python hl_lines="1"
-{!../../../docs_src/python_types/tutorial002.py!}
-```
+{* ../../docs_src/python_types/tutorial002.py hl[1] *}
+
これは、以下のようにデフォルト値を宣言するのと同じではありません:
@@ -112,9 +109,8 @@ John Doe
この関数を見てください。すでに型ヒントを持っています:
-```Python hl_lines="1"
-{!../../../docs_src/python_types/tutorial003.py!}
-```
+{* ../../docs_src/python_types/tutorial003.py hl[1] *}
+
エディタは変数の型を知っているので、補完だけでなく、エラーチェックをすることもできます。
@@ -122,9 +118,8 @@ John Doe
これで`age`を`str(age)`で文字列に変換して修正する必要があることがわかります:
-```Python hl_lines="2"
-{!../../../docs_src/python_types/tutorial004.py!}
-```
+{* ../../docs_src/python_types/tutorial004.py hl[2] *}
+
## 型の宣言
@@ -143,9 +138,8 @@ John Doe
* `bool`
* `bytes`
-```Python hl_lines="1"
-{!../../../docs_src/python_types/tutorial005.py!}
-```
+{* ../../docs_src/python_types/tutorial005.py hl[1] *}
+
### 型パラメータを持つジェネリック型
@@ -161,9 +155,8 @@ John Doe
`typing`から`List`をインポートします(大文字の`L`を含む):
-```Python hl_lines="1"
-{!../../../docs_src/python_types/tutorial006.py!}
-```
+{* ../../docs_src/python_types/tutorial006.py hl[1] *}
+
同じようにコロン(`:`)の構文で変数を宣言します。
@@ -171,11 +164,10 @@ John Doe
リストはいくつかの内部の型を含む型なので、それらを角括弧で囲んでいます。
-```Python hl_lines="4"
-{!../../../docs_src/python_types/tutorial006.py!}
-```
+{* ../../docs_src/python_types/tutorial006.py hl[4] *}
-/// tip | "豆知識"
+
+/// tip | 豆知識
角括弧内の内部の型は「型パラメータ」と呼ばれています。
@@ -199,9 +191,8 @@ John Doe
`tuple`と`set`の宣言も同様です:
-```Python hl_lines="1 4"
-{!../../../docs_src/python_types/tutorial007.py!}
-```
+{* ../../docs_src/python_types/tutorial007.py hl[1,4] *}
+
つまり:
@@ -217,9 +208,8 @@ John Doe
2番目の型パラメータは`dict`の値です。
-```Python hl_lines="1 4"
-{!../../../docs_src/python_types/tutorial008.py!}
-```
+{* ../../docs_src/python_types/tutorial008.py hl[1,4] *}
+
つまり:
@@ -232,7 +222,7 @@ John Doe
また、`Optional`を使用して、変数が`str`のような型を持つことを宣言することもできますが、それは「オプション」であり、`None`にすることもできます。
```Python hl_lines="1 4"
-{!../../../docs_src/python_types/tutorial009.py!}
+{!../../docs_src/python_types/tutorial009.py!}
```
ただの`str`の代わりに`Optional[str]`を使用することで、エディタは値が常に`str`であると仮定している場合に実際には`None`である可能性があるエラーを検出するのに役立ちます。
@@ -256,15 +246,13 @@ John Doe
例えば、`Person`クラスという名前のクラスがあるとしましょう:
-```Python hl_lines="1 2 3"
-{!../../../docs_src/python_types/tutorial010.py!}
-```
+{* ../../docs_src/python_types/tutorial010.py hl[1,2,3] *}
+
変数の型を`Person`として宣言することができます:
-```Python hl_lines="6"
-{!../../../docs_src/python_types/tutorial010.py!}
-```
+{* ../../docs_src/python_types/tutorial010.py hl[6] *}
+
そして、再び、すべてのエディタのサポートを得ることができます:
@@ -284,11 +272,10 @@ John Doe
Pydanticの公式ドキュメントから引用:
-```Python
-{!../../../docs_src/python_types/tutorial011.py!}
-```
+{* ../../docs_src/python_types/tutorial011.py *}
-/// info | "情報"
+
+/// info | 情報
Pydanticについてより学びたい方はドキュメントを参照してください.
@@ -320,7 +307,7 @@ Pydanticについてより学びたい方は`mypy`のチートシートを参照してください
diff --git a/docs/ja/docs/tutorial/background-tasks.md b/docs/ja/docs/tutorial/background-tasks.md
index 6094c370fd..b289faf12d 100644
--- a/docs/ja/docs/tutorial/background-tasks.md
+++ b/docs/ja/docs/tutorial/background-tasks.md
@@ -15,9 +15,7 @@
まず初めに、`BackgroundTasks` をインポートし、` BackgroundTasks` の型宣言と共に、*path operation 関数* のパラメーターを定義します:
-```Python hl_lines="1 13"
-{!../../../docs_src/background_tasks/tutorial001.py!}
-```
+{* ../../docs_src/background_tasks/tutorial001.py hl[1,13] *}
**FastAPI** は、`BackgroundTasks` 型のオブジェクトを作成し、そのパラメーターに渡します。
@@ -33,17 +31,13 @@
また、書き込み操作では `async` と `await` を使用しないため、通常の `def` で関数を定義します。
-```Python hl_lines="6-9"
-{!../../../docs_src/background_tasks/tutorial001.py!}
-```
+{* ../../docs_src/background_tasks/tutorial001.py hl[6:9] *}
## バックグラウンドタスクの追加
*path operations 関数* 内で、`.add_task()` メソッドを使用してタスク関数を *background tasks* オブジェクトに渡します。
-```Python hl_lines="14"
-{!../../../docs_src/background_tasks/tutorial001.py!}
-```
+{* ../../docs_src/background_tasks/tutorial001.py hl[14] *}
`.add_task()` は以下の引数を受け取ります:
@@ -57,9 +51,7 @@
**FastAPI** は、それぞれの場合の処理方法と同じオブジェクトの再利用方法を知っているため、すべてのバックグラウンドタスクがマージされ、バックグラウンドで後で実行されます。
-```Python hl_lines="13 15 22 25"
-{!../../../docs_src/background_tasks/tutorial002.py!}
-```
+{* ../../docs_src/background_tasks/tutorial002.py hl[13,15,22,25] *}
この例では、レスポンスが送信された *後* にメッセージが `log.txt` ファイルに書き込まれます。
@@ -69,7 +61,7 @@
## 技術的な詳細
-`BackgroundTasks` クラスは、`starlette.background`から直接取得されます。
+`BackgroundTasks` クラスは、`starlette.background`から直接取得されます。
これは、FastAPI に直接インポート/インクルードされるため、`fastapi` からインポートできる上に、`starlette.background`から別の `BackgroundTask` (末尾に `s` がない) を誤ってインポートすることを回避できます。
@@ -77,7 +69,7 @@
それでも、FastAPI で `BackgroundTask` を単独で使用することは可能ですが、コード内でオブジェクトを作成し、それを含むStarlette `Response` を返す必要があります。
-詳細については、バックグラウンドタスクに関する Starlette の公式ドキュメントを参照して下さい。
+詳細については、バックグラウンドタスクに関する Starlette の公式ドキュメントを参照して下さい。
## 警告
@@ -85,8 +77,6 @@
これらは、より複雑な構成、RabbitMQ や Redis などのメッセージ/ジョブキューマネージャーを必要とする傾向がありますが、複数のプロセス、特に複数のサーバーでバックグラウンドタスクを実行できます。
-例を確認するには、[Project Generators](../project-generation.md){.internal-link target=_blank} を参照してください。これらにはすべて、Celery が構築済みです。
-
ただし、同じ **FastAPI** アプリから変数とオブジェクトにアクセスする必要がある場合、または小さなバックグラウンドタスク (電子メール通知の送信など) を実行する必要がある場合は、単に `BackgroundTasks` を使用できます。
## まとめ
diff --git a/docs/ja/docs/tutorial/body-fields.md b/docs/ja/docs/tutorial/body-fields.md
index b9f6d694b6..ce5630351e 100644
--- a/docs/ja/docs/tutorial/body-fields.md
+++ b/docs/ja/docs/tutorial/body-fields.md
@@ -6,11 +6,9 @@
まず、以下のようにインポートします:
-```Python hl_lines="4"
-{!../../../docs_src/body_fields/tutorial001.py!}
-```
+{* ../../docs_src/body_fields/tutorial001.py hl[4] *}
-/// warning | "注意"
+/// warning | 注意
`Field`は他の全てのもの(`Query`、`Path`、`Body`など)とは違い、`fastapi`からではなく、`pydantic`から直接インポートされていることに注意してください。
@@ -20,13 +18,11 @@
以下のように`Field`をモデルの属性として使用することができます:
-```Python hl_lines="11 12 13 14"
-{!../../../docs_src/body_fields/tutorial001.py!}
-```
+{* ../../docs_src/body_fields/tutorial001.py hl[11,12,13,14] *}
`Field`は`Query`や`Path`、`Body`と同じように動作し、全く同様のパラメータなどを持ちます。
-/// note | "技術詳細"
+/// note | 技術詳細
実際には次に見る`Query`や`Path`などは、共通の`Param`クラスのサブクラスのオブジェクトを作成しますが、それ自体はPydanticの`FieldInfo`クラスのサブクラスです。
@@ -38,7 +34,7 @@
///
-/// tip | "豆知識"
+/// tip | 豆知識
型、デフォルト値、`Field`を持つ各モデルの属性が、`Path`や`Query`、`Body`の代わりに`Field`を持つ、*path operation 関数の*パラメータと同じ構造になっていることに注目してください。
@@ -48,7 +44,7 @@
追加情報は`Field`や`Query`、`Body`などで宣言することができます。そしてそれは生成されたJSONスキーマに含まれます。
-後に例を用いて宣言を学ぶ際に、追加情報を句悪方法を学べます。
+後に例を用いて宣言を学ぶ際に、追加情報を追加する方法を学べます。
## まとめ
diff --git a/docs/ja/docs/tutorial/body-multiple-params.md b/docs/ja/docs/tutorial/body-multiple-params.md
index c051fde248..cbfdda4b21 100644
--- a/docs/ja/docs/tutorial/body-multiple-params.md
+++ b/docs/ja/docs/tutorial/body-multiple-params.md
@@ -8,11 +8,9 @@
また、デフォルトの`None`を設定することで、ボディパラメータをオプションとして宣言することもできます:
-```Python hl_lines="19 20 21"
-{!../../../docs_src/body_multiple_params/tutorial001.py!}
-```
+{* ../../docs_src/body_multiple_params/tutorial001.py hl[19,20,21] *}
-/// note | "備考"
+/// note | 備考
この場合、ボディから取得する`item`はオプションであることに注意してください。デフォルト値は`None`です。
@@ -33,9 +31,7 @@
しかし、`item`と`user`のように複数のボディパラメータを宣言することもできます:
-```Python hl_lines="22"
-{!../../../docs_src/body_multiple_params/tutorial002.py!}
-```
+{* ../../docs_src/body_multiple_params/tutorial002.py hl[22] *}
この場合、**FastAPI**は関数内に複数のボディパラメータ(Pydanticモデルである2つのパラメータ)があることに気付きます。
@@ -56,7 +52,7 @@
}
```
-/// note | "備考"
+/// note | 備考
以前と同じように`item`が宣言されていたにもかかわらず、`item`はキー`item`を持つボディの内部にあることが期待されていることに注意してください。
@@ -77,9 +73,7 @@
しかし、`Body`を使用して、**FastAPI** に別のボディキーとして扱うように指示することができます:
-```Python hl_lines="23"
-{!../../../docs_src/body_multiple_params/tutorial003.py!}
-```
+{* ../../docs_src/body_multiple_params/tutorial003.py hl[23] *}
この場合、**FastAPI** は以下のようなボディを期待します:
@@ -114,11 +108,9 @@ q: str = None
以下において:
-```Python hl_lines="27"
-{!../../../docs_src/body_multiple_params/tutorial004.py!}
-```
+{* ../../docs_src/body_multiple_params/tutorial004.py hl[27] *}
-/// info | "情報"
+/// info | 情報
`Body`もまた、後述する `Query` や `Path` などと同様に、すべての検証パラメータとメタデータパラメータを持っています。
@@ -138,9 +130,7 @@ item: Item = Body(..., embed=True)
以下において:
-```Python hl_lines="17"
-{!../../../docs_src/body_multiple_params/tutorial005.py!}
-```
+{* ../../docs_src/body_multiple_params/tutorial005.py hl[17] *}
この場合、**FastAPI** は以下のようなボディを期待します:
diff --git a/docs/ja/docs/tutorial/body-nested-models.md b/docs/ja/docs/tutorial/body-nested-models.md
index 59ee672954..a1680d10f2 100644
--- a/docs/ja/docs/tutorial/body-nested-models.md
+++ b/docs/ja/docs/tutorial/body-nested-models.md
@@ -6,9 +6,7 @@
属性をサブタイプとして定義することができます。例えば、Pythonの`list`は以下のように定義できます:
-```Python hl_lines="12"
-{!../../../docs_src/body_nested_models/tutorial001.py!}
-```
+{* ../../docs_src/body_nested_models/tutorial001.py hl[12] *}
これにより、各項目の型は宣言されていませんが、`tags`はある項目のリストになります。
@@ -20,9 +18,7 @@
まず、Pythonの標準の`typing`モジュールから`List`をインポートします:
-```Python hl_lines="1"
-{!../../../docs_src/body_nested_models/tutorial002.py!}
-```
+{* ../../docs_src/body_nested_models/tutorial002.py hl[1] *}
### タイプパラメータを持つ`List`の宣言
@@ -43,9 +39,7 @@ my_list: List[str]
そのため、以下の例では`tags`を具体的な「文字列のリスト」にすることができます:
-```Python hl_lines="14"
-{!../../../docs_src/body_nested_models/tutorial002.py!}
-```
+{* ../../docs_src/body_nested_models/tutorial002.py hl[14] *}
## セット型
@@ -55,9 +49,7 @@ my_list: List[str]
そのため、以下のように、`Set`をインポートして`str`の`set`として`tags`を宣言することができます:
-```Python hl_lines="1 14"
-{!../../../docs_src/body_nested_models/tutorial003.py!}
-```
+{* ../../docs_src/body_nested_models/tutorial003.py hl[1,14] *}
これを使えば、データが重複しているリクエストを受けた場合でも、ユニークな項目のセットに変換されます。
@@ -79,17 +71,13 @@ Pydanticモデルの各属性には型があります。
例えば、`Image`モデルを定義することができます:
-```Python hl_lines="9 10 11"
-{!../../../docs_src/body_nested_models/tutorial004.py!}
-```
+{* ../../docs_src/body_nested_models/tutorial004.py hl[9,10,11] *}
### サブモデルを型として使用
そして、それを属性の型として使用することができます:
-```Python hl_lines="20"
-{!../../../docs_src/body_nested_models/tutorial004.py!}
-```
+{* ../../docs_src/body_nested_models/tutorial004.py hl[20] *}
これは **FastAPI** が以下のようなボディを期待することを意味します:
@@ -122,9 +110,7 @@ Pydanticモデルの各属性には型があります。
例えば、`Image`モデルのように`url`フィールドがある場合、`str`の代わりにPydanticの`HttpUrl`を指定することができます:
-```Python hl_lines="4 10"
-{!../../../docs_src/body_nested_models/tutorial005.py!}
-```
+{* ../../docs_src/body_nested_models/tutorial005.py hl[4,10] *}
文字列は有効なURLであることが確認され、そのようにJSONスキーマ・OpenAPIで文書化されます。
@@ -132,9 +118,7 @@ Pydanticモデルの各属性には型があります。
Pydanticモデルを`list`や`set`などのサブタイプとして使用することもできます:
-```Python hl_lines="20"
-{!../../../docs_src/body_nested_models/tutorial006.py!}
-```
+{* ../../docs_src/body_nested_models/tutorial006.py hl[20] *}
これは、次のようなJSONボディを期待します(変換、検証、ドキュメントなど):
@@ -162,7 +146,7 @@ Pydanticモデルを`list`や`set`などのサブタイプとして使用する
}
```
-/// info | "情報"
+/// info | 情報
`images`キーが画像オブジェクトのリストを持つようになったことに注目してください。
@@ -172,11 +156,9 @@ Pydanticモデルを`list`や`set`などのサブタイプとして使用する
深くネストされた任意のモデルを定義することができます:
-```Python hl_lines="9 14 20 23 27"
-{!../../../docs_src/body_nested_models/tutorial007.py!}
-```
+{* ../../docs_src/body_nested_models/tutorial007.py hl[9,14,20,23,27] *}
-/// info | "情報"
+/// info | 情報
`Offer`は`Item`のリストであり、オプションの`Image`のリストを持っていることに注目してください。
@@ -192,9 +174,7 @@ images: List[Image]
以下のように:
-```Python hl_lines="15"
-{!../../../docs_src/body_nested_models/tutorial008.py!}
-```
+{* ../../docs_src/body_nested_models/tutorial008.py hl[15] *}
## あらゆる場所でのエディタサポート
@@ -224,11 +204,9 @@ Pydanticモデルではなく、`dict`を直接使用している場合はこの
この場合、`int`のキーと`float`の値を持つものであれば、どんな`dict`でも受け入れることができます:
-```Python hl_lines="15"
-{!../../../docs_src/body_nested_models/tutorial009.py!}
-```
+{* ../../docs_src/body_nested_models/tutorial009.py hl[15] *}
-/// tip | "豆知識"
+/// tip | 豆知識
JSONはキーとして`str`しかサポートしていないことに注意してください。
diff --git a/docs/ja/docs/tutorial/body-updates.md b/docs/ja/docs/tutorial/body-updates.md
index 672a03a642..ffbe52e1db 100644
--- a/docs/ja/docs/tutorial/body-updates.md
+++ b/docs/ja/docs/tutorial/body-updates.md
@@ -6,9 +6,7 @@
`jsonable_encoder`を用いて、入力データをJSON形式で保存できるデータに変換することができます(例:NoSQLデータベース)。例えば、`datetime`を`str`に変換します。
-```Python hl_lines="30 31 32 33 34 35"
-{!../../../docs_src/body_updates/tutorial001.py!}
-```
+{* ../../docs_src/body_updates/tutorial001.py hl[30,31,32,33,34,35] *}
既存のデータを置き換えるべきデータを受け取るために`PUT`は使用されます。
@@ -34,7 +32,7 @@
つまり、更新したいデータだけを送信して、残りはそのままにしておくことができます。
-/// note | "備考"
+/// note | 備考
`PATCH`は`PUT`よりもあまり使われておらず、知られていません。
@@ -56,9 +54,7 @@
これを使うことで、デフォルト値を省略して、設定された(リクエストで送られた)データのみを含む`dict`を生成することができます:
-```Python hl_lines="34"
-{!../../../docs_src/body_updates/tutorial002.py!}
-```
+{* ../../docs_src/body_updates/tutorial002.py hl[34] *}
### Pydanticの`update`パラメータ
@@ -66,9 +62,7 @@
`stored_item_model.copy(update=update_data)`のように:
-```Python hl_lines="35"
-{!../../../docs_src/body_updates/tutorial002.py!}
-```
+{* ../../docs_src/body_updates/tutorial002.py hl[35] *}
### 部分的更新のまとめ
@@ -85,11 +79,9 @@
* データをDBに保存します。
* 更新されたモデルを返します。
-```Python hl_lines="30 31 32 33 34 35 36 37"
-{!../../../docs_src/body_updates/tutorial002.py!}
-```
+{* ../../docs_src/body_updates/tutorial002.py hl[30,31,32,33,34,35,36,37] *}
-/// tip | "豆知識"
+/// tip | 豆知識
実際には、HTTPの`PUT`操作でも同じテクニックを使用することができます。
@@ -97,7 +89,7 @@
///
-/// note | "備考"
+/// note | 備考
入力モデルがまだ検証されていることに注目してください。
diff --git a/docs/ja/docs/tutorial/body.md b/docs/ja/docs/tutorial/body.md
index 017ff89863..1298eec7eb 100644
--- a/docs/ja/docs/tutorial/body.md
+++ b/docs/ja/docs/tutorial/body.md
@@ -8,7 +8,7 @@ APIはほとんどの場合 **レスポンス** ボディを送らなければ
**リクエスト** ボディを宣言するために Pydantic モデルを使用します。そして、その全てのパワーとメリットを利用します。
-/// info | "情報"
+/// info | 情報
データを送るには、`POST` (もっともよく使われる)、`PUT`、`DELETE` または `PATCH` を使うべきです。
@@ -22,9 +22,7 @@ GET リクエストでボディを送信することは、仕様では未定義
ます初めに、 `pydantic` から `BaseModel` をインポートする必要があります:
-```Python hl_lines="2"
-{!../../../docs_src/body/tutorial001.py!}
-```
+{* ../../docs_src/body/tutorial001.py hl[4] *}
## データモデルの作成
@@ -32,9 +30,7 @@ GET リクエストでボディを送信することは、仕様では未定義
すべての属性にpython標準の型を使用します:
-```Python hl_lines="5-9"
-{!../../../docs_src/body/tutorial001.py!}
-```
+{* ../../docs_src/body/tutorial001.py hl[7:11] *}
クエリパラメータの宣言と同様に、モデル属性がデフォルト値をもつとき、必須な属性ではなくなります。それ以外は必須になります。オプショナルな属性にしたい場合は `None` を使用してください。
@@ -62,9 +58,7 @@ GET リクエストでボディを送信することは、仕様では未定義
*パスオペレーション* に加えるために、パスパラメータやクエリパラメータと同じ様に宣言します:
-```Python hl_lines="16"
-{!../../../docs_src/body/tutorial001.py!}
-```
+{* ../../docs_src/body/tutorial001.py hl[18] *}
...そして、作成したモデル `Item` で型を宣言します。
@@ -113,7 +107,7 @@ GET リクエストでボディを送信することは、仕様では未定義
-/// tip | "豆知識"
+/// tip | 豆知識
PyCharmエディタを使用している場合は、Pydantic PyCharm Pluginが使用可能です。
@@ -131,9 +125,7 @@ GET リクエストでボディを送信することは、仕様では未定義
関数内部で、モデルの全ての属性に直接アクセスできます:
-```Python hl_lines="19"
-{!../../../docs_src/body/tutorial002.py!}
-```
+{* ../../docs_src/body/tutorial002.py hl[21] *}
## リクエストボディ + パスパラメータ
@@ -141,9 +133,7 @@ GET リクエストでボディを送信することは、仕様では未定義
**FastAPI** はパスパラメータである関数パラメータは**パスから受け取り**、Pydanticモデルによって宣言された関数パラメータは**リクエストボディから受け取る**ということを認識します。
-```Python hl_lines="15-16"
-{!../../../docs_src/body/tutorial003.py!}
-```
+{* ../../docs_src/body/tutorial003.py hl[17:18] *}
## リクエストボディ + パスパラメータ + クエリパラメータ
@@ -151,9 +141,7 @@ GET リクエストでボディを送信することは、仕様では未定義
**FastAPI** はそれぞれを認識し、適切な場所からデータを取得します。
-```Python hl_lines="16"
-{!../../../docs_src/body/tutorial004.py!}
-```
+{* ../../docs_src/body/tutorial004.py hl[18] *}
関数パラメータは以下の様に認識されます:
@@ -161,7 +149,7 @@ GET リクエストでボディを送信することは、仕様では未定義
* パラメータが**単数型** (`int`、`float`、`str`、`bool` など)の場合は**クエリ**パラメータとして解釈されます。
* パラメータが **Pydantic モデル**型で宣言された場合、リクエスト**ボディ**として解釈されます。
-/// note | "備考"
+/// note | 備考
FastAPIは、`= None`があるおかげで、`q`がオプショナルだとわかります。
diff --git a/docs/ja/docs/tutorial/cookie-param-models.md b/docs/ja/docs/tutorial/cookie-param-models.md
new file mode 100644
index 0000000000..8285f44efd
--- /dev/null
+++ b/docs/ja/docs/tutorial/cookie-param-models.md
@@ -0,0 +1,77 @@
+# クッキーパラメータモデル
+
+もし関連する**複数のクッキー**から成るグループがあるなら、それらを宣言するために、**Pydanticモデル**を作成できます。🍪
+
+こうすることで、**複数の場所**で**そのPydanticモデルを再利用**でき、バリデーションやメタデータを、すべてのクッキーパラメータに対して一度に宣言できます。😎
+
+/// note | 備考
+
+この機能は、FastAPIのバージョン `0.115.0` からサポートされています。🤓
+
+///
+
+/// tip | 豆知識
+
+これと同じテクニックは `Query` 、 `Cookie` 、 `Header` にも適用できます。 😎
+
+///
+
+## クッキーにPydanticモデルを使用する
+
+必要な複数の**クッキー**パラメータを**Pydanticモデル**で宣言し、さらに、それを `Cookie` として宣言しましょう:
+
+{* ../../docs_src/cookie_param_models/tutorial001_an_py310.py hl[9:12,16] *}
+
+**FastAPI**は、リクエストの**クッキー**から**それぞれのフィールド**のデータを**抽出**し、定義された**Pydanticモデル**を提供します。
+
+## ドキュメントの確認
+
+対話的APIドキュメントUI `/docs` で、定義されているクッキーを確認できます:
+
+
+kwargsとしても知られています。たとえデフォルト値がなくても。
-```Python hl_lines="8"
-{!../../../docs_src/path_params_numeric_validations/tutorial003.py!}
-```
+{* ../../docs_src/path_params_numeric_validations/tutorial003.py hl[8] *}
## 数値の検証: 以上
`Query`と`Path`(、そして後述する他のもの)を用いて、文字列の制約を宣言することができますが、数値の制約も同様に宣言できます。
-ここで、`ge=1`の場合、`item_id`は`1`「より大きい`g`か、同じ`e`」整数でなれけばなりません。
+ここで、`ge=1`の場合、`item_id`は`1`「より大きい`g`か、同じ`e`」整数でなれけばなりません。
-```Python hl_lines="8"
-{!../../../docs_src/path_params_numeric_validations/tutorial004.py!}
-```
+{* ../../docs_src/path_params_numeric_validations/tutorial004.py hl[8] *}
## 数値の検証: より大きいと小なりイコール
@@ -79,9 +69,7 @@ Pythonはその`*`で何かをすることはありませんが、それ以降
* `gt`: より大きい(`g`reater `t`han)
* `le`: 小なりイコール(`l`ess than or `e`qual)
-```Python hl_lines="9"
-{!../../../docs_src/path_params_numeric_validations/tutorial005.py!}
-```
+{* ../../docs_src/path_params_numeric_validations/tutorial005.py hl[9] *}
## 数値の検証: 浮動小数点、 大なり小なり
@@ -93,9 +81,7 @@ Pythonはその`*`で何かをすることはありませんが、それ以降
これはltも同じです。
-```Python hl_lines="11"
-{!../../../docs_src/path_params_numeric_validations/tutorial006.py!}
-```
+{* ../../docs_src/path_params_numeric_validations/tutorial006.py hl[11] *}
## まとめ
@@ -108,7 +94,7 @@ Pythonはその`*`で何かをすることはありませんが、それ以降
* `lt`: より小さい(`l`ess `t`han)
* `le`: 以下(`l`ess than or `e`qual)
-/// info | "情報"
+/// info | 情報
`Query`、`Path`などは後に共通の`Param`クラスのサブクラスを見ることになります。(使う必要はありません)
@@ -116,9 +102,9 @@ Pythonはその`*`で何かをすることはありませんが、それ以降
///
-/// note | "技術詳細"
+/// note | 技術詳細
-`fastapi`から`Query`、`Path`などをインポートすると、これらは実際には関数です。
+`fastapi`から`Query`、`Path`などをインポートすると、これらは実際には関数です。
呼び出されると、同じ名前のクラスのインスタンスを返します。
diff --git a/docs/ja/docs/tutorial/path-params.md b/docs/ja/docs/tutorial/path-params.md
index 0a79160127..1893ec12f4 100644
--- a/docs/ja/docs/tutorial/path-params.md
+++ b/docs/ja/docs/tutorial/path-params.md
@@ -2,9 +2,7 @@
Pythonのformat文字列と同様のシンタックスで「パスパラメータ」や「パス変数」を宣言できます:
-```Python hl_lines="6 7"
-{!../../../docs_src/path_params/tutorial001.py!}
-```
+{* ../../docs_src/path_params/tutorial001.py hl[6,7] *}
パスパラメータ `item_id` の値は、引数 `item_id` として関数に渡されます。
@@ -18,13 +16,11 @@ Pythonのformat文字列と同様のシンタックスで「パスパラメー
標準のPythonの型アノテーションを使用して、関数内のパスパラメータの型を宣言できます:
-```Python hl_lines="7"
-{!../../../docs_src/path_params/tutorial002.py!}
-```
+{* ../../docs_src/path_params/tutorial002.py hl[7] *}
ここでは、 `item_id` は `int` として宣言されています。
-/// check | "確認"
+/// check | 確認
これにより、関数内でのエディターサポート (エラーチェックや補完など) が提供されます。
@@ -38,7 +34,7 @@ Pythonのformat文字列と同様のシンタックスで「パスパラメー
{"item_id":3}
```
-/// check | "確認"
+/// check | 確認
関数が受け取った(および返した)値は、文字列の `"3"` ではなく、Pythonの `int` としての `3` であることに注意してください。
@@ -69,7 +65,7 @@ Pythonのformat文字列と同様のシンタックスで「パスパラメー
http://127.0.0.1:8000/items/4.2 で見られるように、intのかわりに `float` が与えられた場合にも同様なエラーが表示されます。
-/// check | "確認"
+/// check | 確認
したがって、Pythonの型宣言を使用することで、**FastAPI**はデータのバリデーションを行います。
@@ -85,7 +81,7 @@ Pythonのformat文字列と同様のシンタックスで「パスパラメー
-/// check | "確認"
+/// check | 確認
繰り返しになりますが、Python型宣言を使用するだけで、**FastAPI**は対話的なAPIドキュメントを自動的に生成します(Swagger UIを統合)。
@@ -121,9 +117,7 @@ Pythonのformat文字列と同様のシンタックスで「パスパラメー
*path operations* は順に評価されるので、 `/users/me` が `/users/{user_id}` よりも先に宣言されているか確認する必要があります:
-```Python hl_lines="6 11"
-{!../../../docs_src/path_params/tutorial003.py!}
-```
+{* ../../docs_src/path_params/tutorial003.py hl[6,11] *}
それ以外の場合、 `/users/{users_id}` は `/users/me` としてもマッチします。値が「"me"」であるパラメータ `user_id` を受け取ると「考え」ます。
@@ -139,17 +133,15 @@ Pythonのformat文字列と同様のシンタックスで「パスパラメー
そして、固定値のクラス属性を作ります。すると、その値が使用可能な値となります:
-```Python hl_lines="1 6 7 8 9"
-{!../../../docs_src/path_params/tutorial005.py!}
-```
+{* ../../docs_src/path_params/tutorial005.py hl[1,6,7,8,9] *}
-/// info | "情報"
+/// info | 情報
Enumerations (もしくは、enums)はPython 3.4以降で利用できます。
///
-/// tip | "豆知識"
+/// tip | 豆知識
"AlexNet"、"ResNet"そして"LeNet"は機械学習モデルの名前です。
@@ -159,9 +151,7 @@ Pythonのformat文字列と同様のシンタックスで「パスパラメー
次に、作成したenumクラスである`ModelName`を使用した型アノテーションをもつ*パスパラメータ*を作成します:
-```Python hl_lines="16"
-{!../../../docs_src/path_params/tutorial005.py!}
-```
+{* ../../docs_src/path_params/tutorial005.py hl[16] *}
### ドキュメントの確認
@@ -177,19 +167,15 @@ Pythonのformat文字列と同様のシンタックスで「パスパラメー
これは、作成した列挙型 `ModelName` の*列挙型メンバ*と比較できます:
-```Python hl_lines="17"
-{!../../../docs_src/path_params/tutorial005.py!}
-```
+{* ../../docs_src/path_params/tutorial005.py hl[17] *}
#### *列挙値*の取得
`model_name.value` 、もしくは一般に、 `your_enum_member.value` を使用して実際の値 (この場合は `str`) を取得できます。
-```Python hl_lines="20"
-{!../../../docs_src/path_params/tutorial005.py!}
-```
+{* ../../docs_src/path_params/tutorial005.py hl[20] *}
-/// tip | "豆知識"
+/// tip | 豆知識
`ModelName.lenet.value` でも `"lenet"` 値にアクセスできます。
@@ -201,9 +187,7 @@ Pythonのformat文字列と同様のシンタックスで「パスパラメー
それらはクライアントに返される前に適切な値 (この場合は文字列) に変換されます。
-```Python hl_lines="18 21 23"
-{!../../../docs_src/path_params/tutorial005.py!}
-```
+{* ../../docs_src/path_params/tutorial005.py hl[18,21,23] *}
クライアントは以下の様なJSONレスポンスを得ます:
@@ -242,11 +226,9 @@ Starletteのオプションを直接使用することで、以下のURLの様
したがって、以下の様に使用できます:
-```Python hl_lines="6"
-{!../../../docs_src/path_params/tutorial004.py!}
-```
+{* ../../docs_src/path_params/tutorial004.py hl[6] *}
-/// tip | "豆知識"
+/// tip | 豆知識
最初のスラッシュ (`/`)が付いている `/home/johndoe/myfile.txt` をパラメータが含んでいる必要があります。
diff --git a/docs/ja/docs/tutorial/query-param-models.md b/docs/ja/docs/tutorial/query-param-models.md
new file mode 100644
index 0000000000..053d0740bc
--- /dev/null
+++ b/docs/ja/docs/tutorial/query-param-models.md
@@ -0,0 +1,68 @@
+# クエリパラメータモデル
+
+もし関連する**複数のクエリパラメータ**から成るグループがあるなら、それらを宣言するために、**Pydanticモデル**を作成できます。
+
+こうすることで、**複数の場所**で**そのPydanticモデルを再利用**でき、バリデーションやメタデータを、すべてのクエリパラメータに対して一度に宣言できます。😎
+
+/// note | 備考
+
+この機能は、FastAPIのバージョン `0.115.0` からサポートされています。🤓
+
+///
+
+## クエリパラメータにPydanticモデルを使用する
+
+必要な**複数のクエリパラメータ**を**Pydanticモデル**で宣言し、さらに、それを `Query` として宣言しましょう:
+
+{* ../../docs_src/query_param_models/tutorial001_an_py310.py hl[9:13,17] *}
+
+**FastAPI**は、リクエストの**クエリパラメータ**からそれぞれの**フィールド**のデータを**抽出**し、定義された**Pydanticモデル**を提供します。
+
+## ドキュメントの確認
+
+対話的APIドキュメント `/docs` でクエリパラメータを確認できます:
+
+
+
-/// note | "備考"
+/// note | 備考
いくつかのレスポンスコード(次のセクションを参照)は、レスポンスにボディがないことを示しています。
@@ -43,7 +41,7 @@ FastAPIはこれを知っていて、レスポンスボディがないというO
## HTTPステータスコードについて
-/// note | "備考"
+/// note | 備考
すでにHTTPステータスコードが何であるかを知っている場合は、次のセクションにスキップしてください。
@@ -66,7 +64,7 @@ HTTPでは、レスポンスの一部として3桁の数字のステータス
* クライアントからの一般的なエラーについては、`400`を使用することができます。
* `500`以上はサーバーエラーのためのものです。これらを直接使うことはほとんどありません。アプリケーションコードやサーバーのどこかで何か問題が発生した場合、これらのステータスコードのいずれかが自動的に返されます。
-/// tip | "豆知識"
+/// tip | 豆知識
それぞれのステータスコードとどのコードが何のためのコードなのかについて詳細はMDN HTTP レスポンスステータスコードについてのドキュメントを参照してください。
@@ -76,9 +74,7 @@ HTTPでは、レスポンスの一部として3桁の数字のステータス
先ほどの例をもう一度見てみましょう:
-```Python hl_lines="6"
-{!../../../docs_src/response_status_code/tutorial001.py!}
-```
+{* ../../docs_src/response_status_code/tutorial001.py hl[6] *}
`201`は「作成完了」のためのステータスコードです。
@@ -86,15 +82,13 @@ HTTPでは、レスポンスの一部として3桁の数字のステータス
`fastapi.status`の便利な変数を利用することができます。
-```Python hl_lines="1 6"
-{!../../../docs_src/response_status_code/tutorial002.py!}
-```
+{* ../../docs_src/response_status_code/tutorial002.py hl[1,6] *}
それらは便利です。それらは同じ番号を保持しており、その方法ではエディタの自動補完を使用してそれらを見つけることができます。
-/// note | "技術詳細"
+/// note | 技術詳細
また、`from starlette import status`を使うこともできます。
diff --git a/docs/ja/docs/tutorial/schema-extra-example.md b/docs/ja/docs/tutorial/schema-extra-example.md
index a3cd5eb549..1834e67b27 100644
--- a/docs/ja/docs/tutorial/schema-extra-example.md
+++ b/docs/ja/docs/tutorial/schema-extra-example.md
@@ -10,9 +10,7 @@ JSON Schemaの追加情報を宣言する方法はいくつかあります。
Pydanticのドキュメント: スキーマのカスタマイズで説明されているように、`Config`と`schema_extra`を使ってPydanticモデルの例を宣言することができます:
-```Python hl_lines="15 16 17 18 19 20 21 22 23"
-{!../../../docs_src/schema_extra_example/tutorial001.py!}
-```
+{* ../../docs_src/schema_extra_example/tutorial001.py hl[15,16,17,18,19,20,21,22,23] *}
その追加情報はそのまま出力され、JSON Schemaに追加されます。
@@ -20,11 +18,9 @@ JSON Schemaの追加情報を宣言する方法はいくつかあります。
後述する`Field`、`Path`、`Query`、`Body`などでは、任意の引数を関数に渡すことでJSON Schemaの追加情報を宣言することもできます:
-```Python hl_lines="4 10 11 12 13"
-{!../../../docs_src/schema_extra_example/tutorial002.py!}
-```
+{* ../../docs_src/schema_extra_example/tutorial002.py hl[4,10,11,12,13] *}
-/// warning | "注意"
+/// warning | 注意
これらの追加引数が渡されても、文書化のためのバリデーションは追加されず、注釈だけが追加されることを覚えておいてください。
@@ -36,9 +32,7 @@ JSON Schemaの追加情報を宣言する方法はいくつかあります。
例えば、`Body`にボディリクエストの`example`を渡すことができます:
-```Python hl_lines="21 22 23 24 25 26"
-{!../../../docs_src/schema_extra_example/tutorial003.py!}
-```
+{* ../../docs_src/schema_extra_example/tutorial003.py hl[21,22,23,24,25,26] *}
## ドキュメントのUIの例
diff --git a/docs/ja/docs/tutorial/security/first-steps.md b/docs/ja/docs/tutorial/security/first-steps.md
index c78a3755e9..0ce0f929be 100644
--- a/docs/ja/docs/tutorial/security/first-steps.md
+++ b/docs/ja/docs/tutorial/security/first-steps.md
@@ -20,13 +20,11 @@
`main.py`に、下記の例をコピーします:
-```Python
-{!../../../docs_src/security/tutorial001.py!}
-```
+{* ../../docs_src/security/tutorial001.py *}
## 実行
-/// info | "情報"
+/// info | 情報
まず`python-multipart`をインストールします。
@@ -56,7 +54,7 @@ $ uvicorn main:app --reload
-/// check | "Authorizeボタン!"
+/// check | Authorizeボタン!
すでにピカピカの新しい「Authorize」ボタンがあります。
@@ -68,7 +66,7 @@ $ uvicorn main:app --reload
-/// note | "備考"
+/// note | 備考
フォームに何を入力しても、まだうまくいきません。ですが、これから動くようになります。
@@ -114,7 +112,7 @@ OAuth2は、バックエンドやAPIがユーザーを認証するサーバー
この例では、**Bearer**トークンを使用して**OAuth2**を**パスワード**フローで使用します。これには`OAuth2PasswordBearer`クラスを使用します。
-/// info | "情報"
+/// info | 情報
「bearer」トークンが、唯一の選択肢ではありません。
@@ -128,11 +126,9 @@ OAuth2は、バックエンドやAPIがユーザーを認証するサーバー
`OAuth2PasswordBearer` クラスのインスタンスを作成する時に、パラメーター`tokenUrl`を渡します。このパラメーターには、クライアント (ユーザーのブラウザで動作するフロントエンド) がトークンを取得するために`ユーザー名`と`パスワード`を送信するURLを指定します。
-```Python hl_lines="6"
-{!../../../docs_src/security/tutorial001.py!}
-```
+{* ../../docs_src/security/tutorial001.py hl[6] *}
-/// tip | "豆知識"
+/// tip | 豆知識
ここで、`tokenUrl="token"`は、まだ作成していない相対URL`token`を指します。相対URLなので、`./token`と同じです。
@@ -146,7 +142,7 @@ OAuth2は、バックエンドやAPIがユーザーを認証するサーバー
実際のpath operationもすぐに作ります。
-/// info | "情報"
+/// info | 情報
非常に厳格な「Pythonista」であれば、パラメーター名のスタイルが`token_url`ではなく`tokenUrl`であることを気に入らないかもしれません。
@@ -168,15 +164,13 @@ oauth2_scheme(some, parameters)
これで`oauth2_scheme`を`Depends`で依存関係に渡すことができます。
-```Python hl_lines="10"
-{!../../../docs_src/security/tutorial001.py!}
-```
+{* ../../docs_src/security/tutorial001.py hl[10] *}
この依存関係は、*path operation function*のパラメーター`token`に代入される`str`を提供します。
**FastAPI**は、この依存関係を使用してOpenAPIスキーマ (および自動APIドキュメント) で「セキュリティスキーム」を定義できることを知っています。
-/// info | "技術詳細"
+/// info | 技術詳細
**FastAPI**は、`OAuth2PasswordBearer` クラス (依存関係で宣言されている) を使用してOpenAPIのセキュリティスキームを定義できることを知っています。これは`fastapi.security.oauth2.OAuth2`、`fastapi.security.base.SecurityBase`を継承しているからです。
diff --git a/docs/ja/docs/tutorial/security/get-current-user.md b/docs/ja/docs/tutorial/security/get-current-user.md
index 250f66b818..9fc46c07c5 100644
--- a/docs/ja/docs/tutorial/security/get-current-user.md
+++ b/docs/ja/docs/tutorial/security/get-current-user.md
@@ -2,9 +2,7 @@
一つ前の章では、(依存性注入システムに基づいた)セキュリティシステムは、 *path operation関数* に `str` として `token` を与えていました:
-```Python hl_lines="10"
-{!../../../docs_src/security/tutorial001.py!}
-```
+{* ../../docs_src/security/tutorial001.py hl[10] *}
しかし、それはまだそんなに有用ではありません。
@@ -16,9 +14,7 @@
ボディを宣言するのにPydanticを使用するのと同じやり方で、Pydanticを別のどんなところでも使うことができます:
-```Python hl_lines="5 12-16"
-{!../../../docs_src/security/tutorial002.py!}
-```
+{* ../../docs_src/security/tutorial002.py hl[5,12:16] *}
## 依存関係 `get_current_user` を作成
@@ -30,31 +26,25 @@
以前直接 *path operation* の中でしていたのと同じように、新しい依存関係である `get_current_user` は `str` として `token` を受け取るようになります:
-```Python hl_lines="25"
-{!../../../docs_src/security/tutorial002.py!}
-```
+{* ../../docs_src/security/tutorial002.py hl[25] *}
## ユーザーの取得
`get_current_user` は作成した(偽物の)ユーティリティ関数を使って、 `str` としてトークンを受け取り、先ほどのPydanticの `User` モデルを返却します:
-```Python hl_lines="19-22 26-27"
-{!../../../docs_src/security/tutorial002.py!}
-```
+{* ../../docs_src/security/tutorial002.py hl[19:22,26:27] *}
## 現在のユーザーの注入
ですので、 `get_current_user` に対して同様に *path operation* の中で `Depends` を利用できます。
-```Python hl_lines="31"
-{!../../../docs_src/security/tutorial002.py!}
-```
+{* ../../docs_src/security/tutorial002.py hl[31] *}
Pydanticモデルの `User` として、 `current_user` の型を宣言することに注意してください。
その関数の中ですべての入力補完や型チェックを行う際に役に立ちます。
-/// tip | "豆知識"
+/// tip | 豆知識
リクエストボディはPydanticモデルでも宣言できることを覚えているかもしれません。
@@ -62,7 +52,7 @@ Pydanticモデルの `User` として、 `current_user` の型を宣言するこ
///
-/// check | "確認"
+/// check | 確認
依存関係システムがこのように設計されているおかげで、 `User` モデルを返却する別の依存関係(別の"dependables")を持つことができます。
@@ -103,9 +93,7 @@ Pydanticモデルの `User` として、 `current_user` の型を宣言するこ
さらに、こうした何千もの *path operations* は、たった3行で表現できるのです:
-```Python hl_lines="30-32"
-{!../../../docs_src/security/tutorial002.py!}
-```
+{* ../../docs_src/security/tutorial002.py hl[30:32] *}
## まとめ
diff --git a/docs/ja/docs/tutorial/security/index.md b/docs/ja/docs/tutorial/security/index.md
index c68e7e7f2f..14f2c8f448 100644
--- a/docs/ja/docs/tutorial/security/index.md
+++ b/docs/ja/docs/tutorial/security/index.md
@@ -22,7 +22,7 @@ OAuth2は、認証と認可を処理するためのいくつかの方法を定
これには「サードパーティ」を使用して認証する方法が含まれています。
-これが、「Facebook、Google、Twitter、GitHubを使ってログイン」を使用したすべてのシステムの背後で使われている仕組みです。
+これが、「Facebook、Google、X (Twitter)、GitHubを使ってログイン」を使用したすべてのシステムの背後で使われている仕組みです。
### OAuth 1
@@ -32,7 +32,7 @@ OAuth 1というものもありましたが、これはOAuth2とは全く異な
OAuth2は、通信を暗号化する方法を指定せず、アプリケーションがHTTPSで提供されることを想定しています。
-/// tip | "豆知識"
+/// tip | 豆知識
**デプロイ**のセクションでは、TraefikとLet's Encryptを使用して、無料でHTTPSを設定する方法が紹介されています。
@@ -79,7 +79,7 @@ OpenAPIでは、以下のセキュリティスキームを定義しています:
* HTTP Basic認証
* HTTP ダイジェスト認証など
* `oauth2`: OAuth2のセキュリティ処理方法(「フロー」と呼ばれます)のすべて。
- * これらのフローのいくつかは、OAuth 2.0認証プロバイダ(Google、Facebook、Twitter、GitHubなど)を構築するのに適しています。
+ * これらのフローのいくつかは、OAuth 2.0認証プロバイダ(Google、Facebook、X (Twitter)、GitHubなど)を構築するのに適しています。
* `implicit`
* `clientCredentials`
* `authorizationCode`
@@ -89,9 +89,9 @@ OpenAPIでは、以下のセキュリティスキームを定義しています:
* この自動検出メカニズムは、OpenID Connectの仕様で定義されているものです。
-/// tip | "豆知識"
+/// tip | 豆知識
-Google、Facebook、Twitter、GitHubなど、他の認証/認可プロバイダを統合することも可能で、比較的簡単です。
+Google、Facebook、X (Twitter)、GitHubなど、他の認証/認可プロバイダを統合することも可能で、比較的簡単です。
最も複雑な問題は、それらのような認証/認可プロバイダを構築することですが、**FastAPI**は、あなたのために重い仕事をこなしながら、それを簡単に行うためのツールを提供します。
diff --git a/docs/ja/docs/tutorial/security/oauth2-jwt.md b/docs/ja/docs/tutorial/security/oauth2-jwt.md
index 4f6aebd4c3..599fc7b069 100644
--- a/docs/ja/docs/tutorial/security/oauth2-jwt.md
+++ b/docs/ja/docs/tutorial/security/oauth2-jwt.md
@@ -44,7 +44,7 @@ $ pip install python-jose[cryptography]
ここでは、推奨されているものを使用します:pyca/cryptography。
-/// tip | "豆知識"
+/// tip | 豆知識
このチュートリアルでは以前、PyJWTを使用していました。
@@ -86,7 +86,7 @@ $ pip install passlib[bcrypt]
-/// note | "備考"
+/// note | 備考
ヘッダーの`Authorization`には、`Bearer`で始まる値があります。
@@ -280,4 +272,4 @@ OAuth2には、「スコープ」という概念があります。
また、OAuth2のような安全で標準的なプロトコルを比較的簡単な方法で使用できるだけではなく、実装することもできます。
-OAuth2の「スコープ」を使って、同じ基準でより細かい権限システムを実現する方法については、**高度なユーザーガイド**で詳しく説明しています。スコープ付きのOAuth2は、Facebook、Google、GitHub、Microsoft、Twitterなど、多くの大手認証プロバイダが、サードパーティのアプリケーションと自社のAPIとのやり取りをユーザーに代わって認可するために使用している仕組みです。
+OAuth2の「スコープ」を使って、同じ基準でより細かい権限システムを実現する方法については、**高度なユーザーガイド**で詳しく説明しています。スコープ付きのOAuth2は、Facebook、Google、GitHub、Microsoft、X (Twitter)など、多くの大手認証プロバイダが、サードパーティのアプリケーションと自社のAPIとのやり取りをユーザーに代わって認可するために使用している仕組みです。
diff --git a/docs/ja/docs/tutorial/static-files.md b/docs/ja/docs/tutorial/static-files.md
index c9d95bc347..f910d7e36d 100644
--- a/docs/ja/docs/tutorial/static-files.md
+++ b/docs/ja/docs/tutorial/static-files.md
@@ -7,11 +7,9 @@
* `StaticFiles` をインポート。
* `StaticFiles()` インスタンスを生成し、特定のパスに「マウント」。
-```Python hl_lines="2 6"
-{!../../../docs_src/static_files/tutorial001.py!}
-```
+{* ../../docs_src/static_files/tutorial001.py hl[2,6] *}
-/// note | "技術詳細"
+/// note | 技術詳細
`from starlette.staticfiles import StaticFiles` も使用できます。
@@ -39,4 +37,4 @@
## より詳しい情報
-詳細とオプションについては、Starletteの静的ファイルに関するドキュメントを確認してください。
+詳細とオプションについては、Starletteの静的ファイルに関するドキュメントを確認してください。
diff --git a/docs/ja/docs/tutorial/testing.md b/docs/ja/docs/tutorial/testing.md
index 3ed03ebeaa..4e8ad4f7cc 100644
--- a/docs/ja/docs/tutorial/testing.md
+++ b/docs/ja/docs/tutorial/testing.md
@@ -1,6 +1,6 @@
# テスト
-Starlette のおかげで、**FastAPI** アプリケーションのテストは簡単で楽しいものになっています。
+Starlette のおかげで、**FastAPI** アプリケーションのテストは簡単で楽しいものになっています。
HTTPX がベースなので、非常に使いやすく直感的です。
@@ -18,11 +18,9 @@
チェックしたい Python の標準的な式と共に、シンプルに `assert` 文を記述します。
-```Python hl_lines="2 12 15-18"
-{!../../../docs_src/app_testing/tutorial001.py!}
-```
+{* ../../docs_src/app_testing/tutorial001.py hl[2,12,15:18] *}
-/// tip | "豆知識"
+/// tip | 豆知識
テスト関数は `async def` ではなく、通常の `def` であることに注意してください。
@@ -32,7 +30,7 @@
///
-/// note | "技術詳細"
+/// note | 技術詳細
`from starlette.testclient import TestClient` も使用できます。
@@ -40,7 +38,7 @@
///
-/// tip | "豆知識"
+/// tip | 豆知識
FastAPIアプリケーションへのリクエストの送信とは別に、テストで `async` 関数 (非同期データベース関数など) を呼び出したい場合は、高度なチュートリアルの[Async Tests](../advanced/async-tests.md){.internal-link target=_blank} を参照してください。
@@ -56,17 +54,13 @@ FastAPIアプリケーションへのリクエストの送信とは別に、テ
**FastAPI** アプリに `main.py` ファイルがあるとします:
-```Python
-{!../../../docs_src/app_testing/main.py!}
-```
+{* ../../docs_src/app_testing/main.py *}
### テストファイル
次に、テストを含む `test_main.py` ファイルを作成し、`main` モジュール (`main.py`) から `app` をインポートします:
-```Python
-{!../../../docs_src/app_testing/test_main.py!}
-```
+{* ../../docs_src/app_testing/test_main.py *}
## テスト: 例の拡張
@@ -83,29 +77,13 @@ FastAPIアプリケーションへのリクエストの送信とは別に、テ
これらの *path operation* には `X-Token` ヘッダーが必要です。
-//// tab | Python 3.10+
-
-```Python
-{!> ../../../docs_src/app_testing/app_b_py310/main.py!}
-```
-
-////
-
-//// tab | Python 3.6+
-
-```Python
-{!> ../../../docs_src/app_testing/app_b/main.py!}
-```
-
-////
+{* ../../docs_src/app_testing/app_b_py310/main.py *}
### 拡張版テストファイル
次に、先程のものに拡張版のテストを加えた、`test_main_b.py` を作成します。
-```Python
-{!> ../../../docs_src/app_testing/app_b/test_main.py!}
-```
+{* ../../docs_src/app_testing/app_b/test_main.py *}
リクエストに情報を渡せるクライアントが必要で、その方法がわからない場合はいつでも、`httpx` での実現方法を検索 (Google) できます。
@@ -121,7 +99,7 @@ FastAPIアプリケーションへのリクエストの送信とは別に、テ
(`httpx` または `TestClient` を使用して) バックエンドにデータを渡す方法の詳細は、HTTPXのドキュメントを確認してください。
-/// info | "情報"
+/// info | 情報
`TestClient` は、Pydanticモデルではなく、JSONに変換できるデータを受け取ることに注意してください。
diff --git a/docs/ja/docs/virtual-environments.md b/docs/ja/docs/virtual-environments.md
new file mode 100644
index 0000000000..791cf64a84
--- /dev/null
+++ b/docs/ja/docs/virtual-environments.md
@@ -0,0 +1,831 @@
+# 仮想環境
+
+Pythonプロジェクトの作業では、**仮想環境**(または類似の仕組み)を使用し、プロジェクトごとにインストールするパッケージを分離するべきでしょう。
+
+/// info | 情報
+
+もし、仮想環境の概要や作成方法、使用方法について既にご存知なら、このセクションをスキップすることができます。🤓
+
+///
+
+/// tip | 豆知識
+
+**仮想環境**は、**環境変数**とは異なります。
+
+**環境変数**は、プログラムが使用できるシステム内の変数です。
+
+**仮想環境**は、ファイルをまとめたディレクトリのことです。
+
+///
+
+/// info | 情報
+このページでは、**仮想環境**の使用方法と、そのはたらきについて説明します。
+
+もし**すべてを管理するツール**(Pythonのインストールも含む)を導入する準備ができているなら、uv をお試しください。
+
+///
+
+## プロジェクトの作成
+
+まず、プロジェクト用のディレクトリを作成します。
+
+私は通常 home/user ディレクトリの中に `code` というディレクトリを用意していて、プロジェクトごとに1つのディレクトリをその中に作成しています。
+
+
+
+## 사용 가능한 응답들
+
+다음은 사용할 수 있는 몇가지 응답들 입니다.
+
+`Response`를 사용하여 다른 어떤 것도 반환 할수 있으며, 직접 하위 클래스를 만들 수도 있습니다.
+
+/// note | 기술 세부사항
+
+`from starlette.responses import HTMLResponse`를 사용할 수도 있습니다.
+
+**FastAPI**는 개발자인 여러분의 편의를 위해 `starlette.responses`를 `fastapi.responses`로 제공 하지만, 대부분의 사용 가능한 응답은 Starlette에서 직접 가져옵니다.
+
+///
+
+### `Response`
+
+기본 `Response` 클래스는 다른 모든 응답 클래스의 부모 클래스 입니다.
+
+이 클래스를 직접 반환할 수 있습니다.
+
+다음 매개변수를 받을 수 있습니다:
+
+* `content` - `str` 또는 `bytes`.
+* `status_code` - HTTP 상태코드를 나타내는 `int`.
+* `headers` - 문자열로 이루어진 `dict`.
+* `media_type` - 미디어 타입을 나타내는 `str` 예: `"text/html"`.
+
+FastAPI (실제로는 Starlette)가 자동으로 `Content-Length` 헤더를 포함시킵니다. 또한 `media_type`에 기반하여 `Content-Type` 헤더를 포함하며, 텍스트 타입의 경우 문자 집합을 추가 합니다.
+
+{* ../../docs_src/response_directly/tutorial002.py hl[1,18] *}
+
+### `HTMLResponse`
+
+텍스트 또는 바이트를 받아 HTML 응답을 반환합니다. 위에서 설명한 내용과 같습니다.
+
+### `PlainTextResponse`
+
+텍스트 또는 바이트를 받아 일반 텍스트 응답을 반환합니다.
+
+{* ../../docs_src/custom_response/tutorial005.py hl[2,7,9] *}
+
+### `JSONResponse`
+
+데이터를 받아 `application/json`으로 인코딩된 응답을 반환합니다.
+
+이는 위에서 설명했듯이 **FastAPI**에서 기본적으로 사용되는 응답 형식입니다.
+
+### `ORJSONResponse`
+
+ `orjson`을 사용하여 빠른 JSON 응답을 제공하는 대안입니다. 위에서 설명한 내용과 같습니다.
+
+/// info | 정보
+
+이를 사용하려면 `orjson`을 설치해야합니다. 예: `pip install orjson`.
+
+///
+
+### `UJSONResponse`
+
+`ujson`을 사용한 또 다른 JSON 응답 형식입니다.
+
+/// info | 정보
+
+이 응답을 사용하려면 `ujson`을 설치해야합니다. 예: 'pip install ujson`.
+
+///
+
+/// warning | 경고
+
+`ujson` 은 일부 예외 경우를 처리하는 데 있어 Python 내장 구현보다 덜 엄격합니다.
+
+///
+
+{* ../../docs_src/custom_response/tutorial001.py hl[2,7] *}
+
+/// tip | 팁
+
+`ORJSONResponse`가 더 빠른 대안일 가능성이 있습니다.
+
+///
+
+### `RedirectResponse`
+
+HTTP 리디렉션 응답을 반환합니다. 기본적으로 상태 코드는 307(임시 리디렉션)으로 설정됩니다.
+
+`RedirectResponse`를 직접 반환할 수 있습니다.
+
+{* ../../docs_src/custom_response/tutorial006.py hl[2,9] *}
+
+---
+
+또는 `response_class` 매개변수에서 사용할 수도 있습니다:
+
+
+{* ../../docs_src/custom_response/tutorial006b.py hl[2,7,9] *}
+
+이 경우, *경로 작업* 함수에서 URL을 직접 반환할 수 있습니다.
+
+이 경우, 사용되는 `status_code`는 `RedirectResponse`의 기본값인 `307` 입니다.
+
+---
+
+`status_code` 매개변수를 `response_class` 매개변수와 함께 사용할 수도 있습니다:
+
+{* ../../docs_src/custom_response/tutorial006c.py hl[2,7,9] *}
+
+### `StreamingResponse`
+
+비동기 제너레이터 또는 일반 제너레이터/이터레이터를 받아 응답 본문을 스트리밍 합니다.
+
+{* ../../docs_src/custom_response/tutorial007.py hl[2,14] *}
+
+#### 파일과 같은 객체를 사용한 `StreamingResponse`
+
+파일과 같은 객체(예: `open()`으로 반환된 객체)가 있는 경우, 해당 파일과 같은 객체를 반복(iterate)하는 제너레이터 함수를 만들 수 있습니다.
+
+이 방식으로, 파일 전체를 메모리에 먼저 읽어들일 필요 없이, 제너레이터 함수를 `StreamingResponse`에 전달하여 반환할 수 있습니다.
+
+이 방식은 클라우드 스토리지, 비디오 처리 등의 다양한 라이브러리와 함께 사용할 수 있습니다.
+
+{* ../../docs_src/custom_response/tutorial008.py hl[2,10:12,14] *}
+
+1. 이것이 제너레이터 함수입니다. `yield` 문을 포함하고 있으므로 "제너레이터 함수"입니다.
+2. `with` 블록을 사용함으로써, 제너레이터 함수가 완료된 후 파일과 같은 객체가 닫히도록 합니다. 즉, 응답 전송이 끝난 후 닫힙니다.
+3. 이 `yield from`은 함수가 `file_like`라는 객체를 반복(iterate)하도록 합니다. 반복된 각 부분은 이 제너레이터 함수(`iterfile`)에서 생성된 것처럼 `yield` 됩니다.
+
+ 이렇게 하면 "생성(generating)" 작업을 내부적으로 다른 무언가에 위임하는 제너레이터 함수가 됩니다.
+
+ 이 방식을 사용하면 `with` 블록 안에서 파일을 열 수 있어, 작업이 완료된 후 파일과 같은 객체가 닫히는 것을 보장할 수 있습니다.
+
+/// tip | 팁
+
+여기서 표준 `open()`을 사용하고 있기 때문에 `async`와 `await`를 지원하지 않습니다. 따라서 경로 작업은 일반 `def`로 선언합니다.
+
+///
+
+### `FileResponse`
+
+파일을 비동기로 스트리밍하여 응답합니다.
+
+다른 응답 유형과는 다른 인수를 사용하여 객체를 생성합니다:
+
+* `path` - 스트리밍할 파일의 경로.
+* `headers` - 딕셔너리 형식의 사용자 정의 헤더.
+* `media_type` - 미디어 타입을 나타내는 문자열. 설정되지 않은 경우 파일 이름이나 경로를 사용하여 추론합니다.
+* `filename` - 설정된 경우 응답의 `Content-Disposition`에 포함됩니다.
+
+파일 응답에는 적절한 `Content-Length`, `Last-Modified`, 및 `ETag` 헤더가 포함됩니다.
+
+{* ../../docs_src/custom_response/tutorial009.py hl[2,10] *}
+
+또한 `response_class` 매개변수를 사용할 수도 있습니다:
+
+{* ../../docs_src/custom_response/tutorial009b.py hl[2,8,10] *}
+
+이 경우, 경로 작업 함수에서 파일 경로를 직접 반환할 수 있습니다.
+
+## 사용자 정의 응답 클래스
+
+`Response`를 상속받아 사용자 정의 응답 클래스를 생성하고 사용할 수 있습니다.
+
+예를 들어, 포함된 `ORJSONResponse` 클래스에서 사용되지 않는 설정으로 orjson을 사용하고 싶다고 가정해봅시다.
+
+만약 들여쓰기 및 포맷된 JSON을 반환하고 싶다면, `orjson.OPT_INDENT_2` 옵션을 사용할 수 있습니다.
+
+`CustomORJSONResponse`를 생성할 수 있습니다. 여기서 핵심은 `Response.render(content)` 메서드를 생성하여 내용을 `bytes`로 반환하는 것입니다:
+
+{* ../../docs_src/custom_response/tutorial009c.py hl[9:14,17] *}
+
+이제 다음 대신:
+
+```json
+{"message": "Hello World"}
+```
+
+이 응답은 이렇게 반환됩니다:
+
+```json
+{
+ "message": "Hello World"
+}
+```
+
+물론 JSON 포맷팅보다 더 유용하게 활용할 방법을 찾을 수 있을 것입니다. 😉
+
+## 기본 응답 클래스
+
+**FastAPI** 클래스 객체 또는 `APIRouter`를 생성할 때 기본적으로 사용할 응답 클래스를 지정할 수 있습니다.
+
+이를 정의하는 매개변수는 `default_response_class`입니다.
+
+아래 예제에서 **FastAPI**는 모든 경로 작업에서 기본적으로 `JSONResponse` 대신 `ORJSONResponse`를 사용합니다.
+
+{* ../../docs_src/custom_response/tutorial010.py hl[2,4] *}
+
+/// tip | 팁
+
+여전히 이전처럼 *경로 작업*에서 `response_class`를 재정의할 수 있습니다.
+
+///
+
+## 추가 문서화
+
+OpenAPI에서 `responses`를 사용하여 미디어 타입 및 기타 세부 정보를 선언할 수도 있습니다: [OpenAPI에서 추가 응답](additional-responses.md){.internal-link target=_blank}.
diff --git a/docs/ko/docs/advanced/events.md b/docs/ko/docs/advanced/events.md
index e155f41f10..4318ada54e 100644
--- a/docs/ko/docs/advanced/events.md
+++ b/docs/ko/docs/advanced/events.md
@@ -1,57 +1,165 @@
-# 이벤트: startup과 shutdown
+# Lifespan 이벤트
-필요에 따라 응용 프로그램이 시작되기 전이나 종료될 때 실행되는 이벤트 핸들러(함수)를 정의할 수 있습니다.
+애플리케이션 **시작 전**에 실행되어야 하는 로직(코드)을 정의할 수 있습니다. 이는 이 코드가 **한 번**만 실행되며, **애플리케이션이 요청을 받기 시작하기 전**에 실행된다는 의미입니다.
-이 함수들은 `async def` 또는 평범하게 `def`으로 선언할 수 있습니다.
+마찬가지로, 애플리케이션이 **종료될 때** 실행되어야 하는 로직(코드)을 정의할 수 있습니다. 이 경우, 이 코드는 **한 번**만 실행되며, **여러 요청을 처리한 후**에 실행됩니다.
-/// warning | "경고"
+이 코드가 애플리케이션이 **요청을 받기 시작하기 전에** 실행되고, 요청 처리가 끝난 후 **종료 직전에** 실행되기 때문에 전체 애플리케이션의 **수명(Lifespan)**을 다룹니다. (잠시 후 "수명"이라는 단어가 중요해집니다 😉)
-이벤트 핸들러는 주 응용 프로그램에서만 작동합니다. [하위 응용 프로그램 - 마운트](./sub-applications.md){.internal-link target=_blank}에서는 작동하지 않습니다.
+이 방법은 전체 애플리케이션에서 사용해야 하는 **자원**을 설정하거나 요청 간에 **공유되는** 자원을 설정하고, 또는 그 후에 **정리**하는 데 매우 유용할 수 있습니다. 예를 들어, 데이터베이스 연결 풀 또는 공유되는 머신러닝 모델을 로드하는 경우입니다.
+
+
+## 사용 사례
+
+먼저 **사용 사례**를 예로 들어보고, 이를 어떻게 해결할 수 있는지 살펴보겠습니다.
+
+우리가 요청을 처리하기 위해 사용하고 싶은 **머신러닝 모델**이 있다고 상상해 봅시다. 🤖
+
+이 모델들은 요청 간에 공유되므로, 요청마다 모델이 하나씩 있는 것이 아니라, 여러 요청에서 동일한 모델을 사용합니다.
+
+모델을 로드하는 데 **상당한 시간이 걸린다고 상상해 봅시다**, 왜냐하면 모델이 **디스크에서 많은 데이터를 읽어야** 하기 때문입니다. 따라서 모든 요청에 대해 모델을 매번 로드하고 싶지 않습니다.
+
+모듈/파일의 최상위에서 모델을 로드할 수도 있지만, 그러면 **모델을 로드하는데** 시간이 걸리기 때문에, 단순한 자동화된 테스트를 실행할 때도 모델이 로드될 때까지 기다려야 해서 **테스트 속도가 느려집니다**.
+
+이 문제를 해결하려고 하는 것입니다. 요청을 처리하기 전에 모델을 로드하되, 애플리케이션이 요청을 받기 시작하기 직전에만 로드하고, 코드가 로드되는 동안은 로드하지 않도록 하겠습니다.
+
+## Lifespan
+
+`FastAPI` 애플리케이션의 `lifespan` 매개변수와 "컨텍스트 매니저"를 사용하여 *시작*과 *종료* 로직을 정의할 수 있습니다. (컨텍스트 매니저가 무엇인지 잠시 후에 설명드리겠습니다.)
+
+예제를 통해 시작하고, 그 후에 자세히 살펴보겠습니다.
+
+우리는 `yield`를 사용하여 비동기 함수 `lifespan()`을 다음과 같이 생성합니다:
+
+{* ../../docs_src/events/tutorial003.py hl[16,19] *}
+
+여기서 우리는 모델을 로드하는 비싼 *시작* 작업을 시뮬레이션하고 있습니다. `yield` 앞에서 (가짜) 모델 함수를 머신러닝 모델이 담긴 딕셔너리에 넣습니다. 이 코드는 **애플리케이션이 요청을 받기 시작하기 전**, *시작* 동안에 실행됩니다.
+
+그리고 `yield` 직후에는 모델을 언로드합니다. 이 코드는 **애플리케이션이 요청 처리 완료 후**, *종료* 직전에 실행됩니다. 예를 들어, 메모리나 GPU와 같은 자원을 해제하는 작업을 할 수 있습니다.
+
+/// tip | 팁
+
+`shutdown`은 애플리케이션을 **종료**할 때 발생합니다.
+
+새로운 버전을 시작해야 하거나, 그냥 실행을 멈추고 싶을 수도 있습니다. 🤷
///
-## `startup` 이벤트
+### Lifespan 함수
-응용 프로그램을 시작하기 전에 실행하려는 함수를 "startup" 이벤트로 선언합니다:
+먼저 주목할 점은, `yield`를 사용하여 비동기 함수(async function)를 정의하고 있다는 것입니다. 이는 `yield`를 사용한 의존성과 매우 유사합니다.
-```Python hl_lines="8"
-{!../../../docs_src/events/tutorial001.py!}
+{* ../../docs_src/events/tutorial003.py hl[14:19] *}
+
+함수의 첫 번째 부분, 즉 `yield` 이전의 코드는 애플리케이션이 시작되기 **전에** 실행됩니다.
+
+그리고 `yield` 이후의 부분은 애플리케이션이 완료된 후 **나중에** 실행됩니다.
+
+### 비동기 컨텍스트 매니저
+
+함수를 확인해보면, `@asynccontextmanager`로 장식되어 있습니다.
+
+이것은 함수를 "**비동기 컨텍스트 매니저**"라고 불리는 것으로 변환시킵니다.
+
+{* ../../docs_src/events/tutorial003.py hl[1,13] *}
+
+파이썬에서 **컨텍스트 매니저**는 `with` 문에서 사용할 수 있는 것입니다. 예를 들어, `open()`은 컨텍스트 매니저로 사용할 수 있습니다:
+
+```Python
+with open("file.txt") as file:
+ file.read()
+```
+최근 버전의 파이썬에서는 **비동기 컨텍스트 매니저**도 있습니다. 이를 `async with`와 함께 사용합니다:
+
+```Python
+async with lifespan(app):
+ await do_stuff()
```
-이 경우 `startup` 이벤트 핸들러 함수는 단순히 몇 가지 값으로 구성된 `dict` 형식의 "데이터베이스"를 초기화합니다.
+컨텍스트 매니저나 위와 같은 비동기 컨텍스트 매니저를 만들면, `with` 블록에 들어가기 전에 `yield` 이전의 코드가 실행되고, `with` 블록을 벗어난 후에는 `yield` 이후의 코드가 실행됩니다.
-하나 이상의 이벤트 핸들러 함수를 추가할 수도 있습니다.
+위의 코드 예제에서는 직접 사용하지 않고, FastAPI에 전달하여 사용하도록 합니다.
-그리고 응용 프로그램은 모든 `startup` 이벤트 핸들러가 완료될 때까지 요청을 받지 않습니다.
+`FastAPI` 애플리케이션의 `lifespan` 매개변수는 **비동기 컨텍스트 매니저**를 받기 때문에, 새로운 `lifespan` 비동기 컨텍스트 매니저를 FastAPI에 전달할 수 있습니다.
-## `shutdown` 이벤트
+{* ../../docs_src/events/tutorial003.py hl[22] *}
-응용 프로그램이 종료될 때 실행하려는 함수를 추가하려면 `"shutdown"` 이벤트로 선언합니다:
+## 대체 이벤트 (사용 중단)
-```Python hl_lines="6"
-{!../../../docs_src/events/tutorial002.py!}
-```
+/// warning | 경고
-이 예제에서 `shutdown` 이벤트 핸들러 함수는 `"Application shutdown"`이라는 텍스트가 적힌 `log.txt` 파일을 추가할 것입니다.
+*시작*과 *종료*를 처리하는 권장 방법은 위에서 설명한 대로 `FastAPI` 애플리케이션의 `lifespan` 매개변수를 사용하는 것입니다. `lifespan` 매개변수를 제공하면 `startup`과 `shutdown` 이벤트 핸들러는 더 이상 호출되지 않습니다. `lifespan`을 사용할지, 모든 이벤트를 사용할지 선택해야 하며 둘 다 사용할 수는 없습니다.
-/// info | "정보"
-
-`open()` 함수에서 `mode="a"`는 "추가"를 의미합니다. 따라서 이미 존재하는 파일의 내용을 덮어쓰지 않고 새로운 줄을 추가합니다.
+이 부분은 건너뛰셔도 좋습니다.
///
-/// tip | "팁"
+*시작*과 *종료* 동안 실행될 이 로직을 정의하는 대체 방법이 있습니다.
-이 예제에서는 파일과 상호작용 하기 위해 파이썬 표준 함수인 `open()`을 사용하고 있습니다.
+애플리케이션이 시작되기 전에 또는 종료될 때 실행해야 하는 이벤트 핸들러(함수)를 정의할 수 있습니다.
-따라서 디스크에 데이터를 쓰기 위해 "대기"가 필요한 I/O (입력/출력) 작업을 수행합니다.
+이 함수들은 `async def` 또는 일반 `def`로 선언할 수 있습니다.
-그러나 `open()`은 `async`와 `await`을 사용하지 않기 때문에 이벤트 핸들러 함수는 `async def`가 아닌 표준 `def`로 선언하고 있습니다.
+### `startup` 이벤트
+
+애플리케이션이 시작되기 전에 실행되어야 하는 함수를 추가하려면, `"startup"` 이벤트로 선언합니다:
+
+{* ../../docs_src/events/tutorial001.py hl[8] *}
+
+이 경우, `startup` 이벤트 핸들러 함수는 "database"라는 항목(단지 `dict`)을 일부 값으로 초기화합니다.
+
+여러 개의 이벤트 핸들러 함수를 추가할 수 있습니다.
+
+애플리케이션은 모든 `startup` 이벤트 핸들러가 완료될 때까지 요청을 받기 시작하지 않습니다.
+
+### `shutdown` 이벤트
+
+애플리케이션이 종료될 때 실행되어야 하는 함수를 추가하려면, `"shutdown"` 이벤트로 선언합니다:
+
+{* ../../docs_src/events/tutorial002.py hl[6] *}
+
+여기서, `shutdown` 이벤트 핸들러 함수는 `"Application shutdown"`이라는 텍스트를 `log.txt` 파일에 기록합니다.
+
+/// info | 정보
+
+`open()` 함수에서 `mode="a"`는 "추가"를 의미하므로, 파일에 있는 기존 내용은 덮어쓰지 않고 새로운 줄이 추가됩니다.
///
-/// info | "정보"
+/// tip | 팁
-이벤트 핸들러에 관한 내용은 Starlette 이벤트 문서에서 추가로 확인할 수 있습니다.
+이 경우, 우리는 표준 파이썬 `open()` 함수를 사용하여 파일과 상호작용하고 있습니다.
+
+따라서 I/O(입출력) 작업이 포함되어 있어 디스크에 기록되는 것을 "기다리는" 과정이 필요합니다.
+
+하지만 `open()`은 `async`와 `await`를 사용하지 않습니다.
+
+그래서 우리는 이벤트 핸들러 함수를 `async def` 대신 일반 `def`로 선언합니다.
///
+
+### `startup`과 `shutdown`을 함께 사용
+
+*시작*과 *종료* 로직이 연결될 가능성이 높습니다. 예를 들어, 무언가를 시작한 후 끝내거나, 자원을 획득한 후 해제하는 등의 작업을 할 수 있습니다.
+
+이러한 작업을 별도의 함수로 처리하면 서로 로직이나 변수를 공유하지 않기 때문에 더 어려워집니다. 값들을 전역 변수에 저장하거나 비슷한 트릭을 사용해야 할 수 있습니다.
+
+그렇기 때문에 위에서 설명한 대로 `lifespan`을 사용하는 것이 권장됩니다.
+
+## 기술적 세부사항
+
+호기심 많은 분들을 위한 기술적인 세부사항입니다. 🤓
+
+ASGI 기술 사양에 따르면, 이는 Lifespan Protocol의 일부이며, `startup`과 `shutdown`이라는 이벤트를 정의합니다.
+
+/// info | 정보
+
+Starlette의 `lifespan` 핸들러에 대해 더 읽고 싶다면 Starlette의 Lifespan 문서에서 확인할 수 있습니다.
+
+이 문서에는 코드의 다른 영역에서 사용할 수 있는 lifespan 상태를 처리하는 방법도 포함되어 있습니다.
+
+///
+
+## 서브 애플리케이션
+
+🚨 이 lifespan 이벤트(`startup`과 `shutdown`)는 메인 애플리케이션에 대해서만 실행되며, [서브 애플리케이션 - Mounts](sub-applications.md){.internal-link target=_blank}에는 실행되지 않음을 유의하세요.
diff --git a/docs/ko/docs/advanced/index.md b/docs/ko/docs/advanced/index.md
index cb628fa10b..31704727ca 100644
--- a/docs/ko/docs/advanced/index.md
+++ b/docs/ko/docs/advanced/index.md
@@ -6,7 +6,7 @@
이어지는 장에서는 여러분이 다른 옵션, 구성 및 추가 기능을 보실 수 있습니다.
-/// tip | "팁"
+/// tip | 팁
다음 장들이 **반드시 "심화"**인 것은 아닙니다.
diff --git a/docs/ko/docs/advanced/middlewares.md b/docs/ko/docs/advanced/middlewares.md
new file mode 100644
index 0000000000..5778528a8e
--- /dev/null
+++ b/docs/ko/docs/advanced/middlewares.md
@@ -0,0 +1,96 @@
+# 고급 미들웨어
+
+메인 튜토리얼에서 [Custom Middleware](../tutorial/middleware.md){.internal-link target=_blank}를 응용프로그램에 추가하는 방법을 읽으셨습니다.
+
+그리고 [CORS with the `CORSMiddleware`](){.internal-link target=_blank}하는 방법도 보셨습니다.
+
+이 섹션에서는 다른 미들웨어들을 사용하는 방법을 알아보겠습니다.
+
+## ASGI 미들웨어 추가하기
+
+**FastAPI**는 Starlette을 기반으로 하고 있으며, ASGI 사양을 구현하므로 ASGI 미들웨어를 사용할 수 있습니다.
+
+미들웨어가 FastAPI나 Starlette용으로 만들어지지 않아도 ASGI 사양을 준수하는 한 동작할 수 있습니다.
+
+일반적으로 ASGI 미들웨어는 첫 번째 인수로 ASGI 앱을 받는 클래스들입니다.
+
+따라서 타사 ASGI 미들웨어 문서에서 일반적으로 다음과 같이 사용하도록 안내할 것입니다.
+
+```Python
+from unicorn import UnicornMiddleware
+
+app = SomeASGIApp()
+
+new_app = UnicornMiddleware(app, some_config="rainbow")
+```
+
+하지만 내부 미들웨어가 서버 오류를 처리하고 사용자 정의 예외 처리기가 제대로 작동하도록 하는 더 간단한 방법을 제공하는 FastAPI(실제로는 Starlette)가 있습니다.
+
+이를 위해 `app.add_middleware()`를 사용합니다(CORS의 예에서와 같이).
+
+```Python
+from fastapi import FastAPI
+from unicorn import UnicornMiddleware
+
+app = FastAPI()
+
+app.add_middleware(UnicornMiddleware, some_config="rainbow")
+```
+
+`app.add_middleware()`는 첫 번째 인수로 미들웨어 클래스와 미들웨어에 전달할 추가 인수를 받습니다.
+
+## 통합 미들웨어
+
+**FastAPI**에는 일반적인 사용 사례를 위한 여러 미들웨어가 포함되어 있으며, 사용 방법은 다음에서 살펴보겠습니다.
+
+/// note | 기술 세부 사항
+
+다음 예제에서는 `from starlette.middleware.something import SomethingMiddleware`를 사용할 수도 있습니다.
+
+**FastAPI**는 개발자의 편의를 위해 `fastapi.middleware`에 여러 미들웨어를 제공합니다. 그러나 사용 가능한 대부분의 미들웨어는 Starlette에서 직접 제공합니다.
+
+///
+
+## `HTTPSRedirectMiddleware`
+
+들어오는 모든 요청이 `https` 또는 `wss`여야 합니다.
+
+`http` 또는 `ws`로 들어오는 모든 요청은 대신 보안 체계로 리디렉션됩니다.
+
+{* ../../docs_src/advanced_middleware/tutorial001.py hl[2,6] *}
+
+## `TrustedHostMiddleware`
+
+HTTP 호스트 헤더 공격을 방지하기 위해 모든 수신 요청에 올바르게 설정된 `Host` 헤더를 갖도록 강제합니다.
+
+{* ../../docs_src/advanced_middleware/tutorial002.py hl[2,6:8] *}
+
+다음 인수가 지원됩니다:
+
+* `allowed_hosts` - 호스트 이름으로 허용해야 하는 도메인 이름 목록입니다. 일치하는 하위 도메인에 대해 `*.example.com`과 같은 와일드카드 도메인이 지원됩니다. 모든 호스트 이름을 허용하려면 `allowed_hosts=[“*”]`를 사용하거나 미들웨어를 생략하세요.
+
+수신 요청의 유효성이 올바르게 확인되지 않으면 `400`이라는 응답이 전송됩니다.
+
+## `GZipMiddleware`
+
+`Accept-Encoding` 헤더에 `“gzip”`이 포함된 모든 요청에 대해 GZip 응답을 처리합니다.
+
+미들웨어는 표준 응답과 스트리밍 응답을 모두 처리합니다.
+
+{* ../../docs_src/advanced_middleware/tutorial003.py hl[2,6] *}
+
+지원되는 인수는 다음과 같습니다:
+
+* `minimum_size` - 이 최소 크기(바이트)보다 작은 응답은 GZip하지 않습니다. 기본값은 `500`입니다.
+* `compresslevel` - GZip 압축 중에 사용됩니다. 1에서 9 사이의 정수입니다. 기본값은 `9`입니다. 값이 낮을수록 압축 속도는 빨라지지만 파일 크기는 커지고, 값이 높을수록 압축 속도는 느려지지만 파일 크기는 작아집니다.
+
+## 기타 미들웨어
+
+다른 많은 ASGI 미들웨어가 있습니다.
+
+예를 들어:
+
+유비콘의 `ProxyHeadersMiddleware`>
+MessagePack
+
+사용 가능한 다른 미들웨어를 확인하려면 스타렛의 미들웨어 문서 및 ASGI Awesome List를 참조하세요.
diff --git a/docs/ko/docs/advanced/response-change-status-code.md b/docs/ko/docs/advanced/response-change-status-code.md
new file mode 100644
index 0000000000..1ba9aa3ccb
--- /dev/null
+++ b/docs/ko/docs/advanced/response-change-status-code.md
@@ -0,0 +1,31 @@
+# 응답 - 상태 코드 변경
+
+기본 [응답 상태 코드 설정](../tutorial/response-status-code.md){.internal-link target=_blank}이 가능하다는 걸 이미 알고 계실 겁니다.
+
+하지만 경우에 따라 기본 설정과 다른 상태 코드를 반환해야 할 때가 있습니다.
+
+## 사용 예
+
+예를 들어 기본적으로 HTTP 상태 코드 "OK" `200`을 반환하고 싶다고 가정해 봅시다.
+
+하지만 데이터가 존재하지 않으면 이를 새로 생성하고, HTTP 상태 코드 "CREATED" `201`을 반환하고자 할 때가 있을 수 있습니다.
+
+이때도 여전히 `response_model`을 사용하여 반환하는 데이터를 필터링하고 변환하고 싶을 수 있습니다.
+
+이런 경우에는 `Response` 파라미터를 사용할 수 있습니다.
+
+## `Response` 파라미터 사용하기
+
+*경로 작동 함수*에 `Response` 타입의 파라미터를 선언할 수 있습니다. (쿠키와 헤더에 대해 선언하는 것과 유사하게)
+
+그리고 이 *임시* 응답 객체에서 `status_code`를 설정할 수 있습니다.
+
+{* ../../docs_src/response_change_status_code/tutorial001.py hl[1,9,12] *}
+
+그리고 평소처럼 원하는 객체(`dict`, 데이터베이스 모델 등)를 반환할 수 있습니다.
+
+`response_model`을 선언했다면 반환된 객체는 여전히 필터링되고 변환됩니다.
+
+**FastAPI**는 이 *임시* 응답 객체에서 상태 코드(쿠키와 헤더 포함)를 추출하여, `response_model`로 필터링된 반환 값을 최종 응답에 넣습니다.
+
+또한, 의존성에서도 `Response` 파라미터를 선언하고 그 안에서 상태 코드를 설정할 수 있습니다. 단, 마지막으로 설정된 상태 코드가 우선 적용된다는 점을 유의하세요.
diff --git a/docs/ko/docs/advanced/response-cookies.md b/docs/ko/docs/advanced/response-cookies.md
new file mode 100644
index 0000000000..50da713fe6
--- /dev/null
+++ b/docs/ko/docs/advanced/response-cookies.md
@@ -0,0 +1,49 @@
+# 응답 쿠키
+
+## `Response` 매개변수 사용하기
+
+*경로 작동 함수*에서 `Response` 타입의 매개변수를 선언할 수 있습니다.
+
+그런 다음 해당 *임시* 응답 객체에서 쿠키를 설정할 수 있습니다.
+
+{* ../../docs_src/response_cookies/tutorial002.py hl[1,8:9] *}
+
+그런 다음 필요한 객체(`dict`, 데이터베이스 모델 등)를 반환할 수 있습니다.
+
+그리고 `response_model`을 선언했다면 반환한 객체를 거르고 변환하는 데 여전히 사용됩니다.
+
+**FastAPI**는 그 *임시* 응답에서 쿠키(또한 헤더 및 상태 코드)를 추출하고, 반환된 값이 포함된 최종 응답에 이를 넣습니다. 이 값은 `response_model`로 걸러지게 됩니다.
+
+또한 의존관계에서 `Response` 매개변수를 선언하고, 해당 의존성에서 쿠키(및 헤더)를 설정할 수도 있습니다.
+
+## `Response`를 직접 반환하기
+
+코드에서 `Response`를 직접 반환할 때도 쿠키를 생성할 수 있습니다.
+
+이를 위해 [Response를 직접 반환하기](response-directly.md){.internal-link target=_blank}에서 설명한 대로 응답을 생성할 수 있습니다.
+
+그런 다음 쿠키를 설정하고 반환하면 됩니다:
+{* ../../docs_src/response_directly/tutorial002.py hl[1,18] *}
+/// tip
+
+`Response` 매개변수를 사용하지 않고 응답을 직접 반환하는 경우, FastAPI는 이를 직접 반환한다는 점에 유의하세요.
+
+따라서 데이터가 올바른 유형인지 확인해야 합니다. 예: `JSONResponse`를 반환하는 경우, JSON과 호환되는지 확인하세요.
+
+또한 `response_model`로 걸러져야 할 데이터가 전달되지 않도록 확인하세요.
+
+///
+
+### 추가 정보
+
+/// note | 기술적 세부사항
+
+`from starlette.responses import Response` 또는 `from starlette.responses import JSONResponse`를 사용할 수도 있습니다.
+
+**FastAPI**는 개발자의 편의를 위해 `fastapi.responses`로 동일한 `starlette.responses`를 제공합니다. 그러나 대부분의 응답은 Starlette에서 직접 제공됩니다.
+
+또한 `Response`는 헤더와 쿠키를 설정하는 데 자주 사용되므로, **FastAPI**는 이를 `fastapi.Response`로도 제공합니다.
+
+///
+
+사용 가능한 모든 매개변수와 옵션은 Starlette 문서에서 확인할 수 있습니다.
diff --git a/docs/ko/docs/advanced/response-directly.md b/docs/ko/docs/advanced/response-directly.md
new file mode 100644
index 0000000000..08d63c43ce
--- /dev/null
+++ b/docs/ko/docs/advanced/response-directly.md
@@ -0,0 +1,63 @@
+# 응답을 직접 반환하기
+
+**FastAPI**에서 *경로 작업(path operation)*을 생성할 때, 일반적으로 `dict`, `list`, Pydantic 모델, 데이터베이스 모델 등의 데이터를 반환할 수 있습니다.
+
+기본적으로 **FastAPI**는 [JSON 호환 가능 인코더](../tutorial/encoder.md){.internal-link target=_blank}에 설명된 `jsonable_encoder`를 사용해 해당 반환 값을 자동으로 `JSON`으로 변환합니다.
+
+그런 다음, JSON 호환 데이터(예: `dict`)를 `JSONResponse`에 넣어 사용자의 응답을 전송하는 방식으로 처리됩니다.
+
+그러나 *경로 작업*에서 `JSONResponse`를 직접 반환할 수도 있습니다.
+
+예를 들어, 사용자 정의 헤더나 쿠키를 반환해야 하는 경우에 유용할 수 있습니다.
+
+## `Response` 반환하기
+
+사실, `Response` 또는 그 하위 클래스를 반환할 수 있습니다.
+
+/// tip
+
+`JSONResponse` 자체도 `Response`의 하위 클래스입니다.
+
+///
+
+그리고 `Response`를 반환하면 **FastAPI**가 이를 그대로 전달합니다.
+
+Pydantic 모델로 데이터 변환을 수행하지 않으며, 내용을 다른 형식으로 변환하지 않습니다.
+
+이로 인해 많은 유연성을 얻을 수 있습니다. 어떤 데이터 유형이든 반환할 수 있고, 데이터 선언이나 유효성 검사를 재정의할 수 있습니다.
+
+## `Response`에서 `jsonable_encoder` 사용하기
+
+**FastAPI**는 반환하는 `Response`에 아무런 변환을 하지 않으므로, 그 내용이 준비되어 있어야 합니다.
+
+예를 들어, Pydantic 모델을 `dict`로 변환해 `JSONResponse`에 넣지 않으면 JSON 호환 유형으로 변환된 데이터 유형(예: `datetime`, `UUID` 등)이 사용되지 않습니다.
+
+이러한 경우, 데이터를 응답에 전달하기 전에 `jsonable_encoder`를 사용하여 변환할 수 있습니다:
+
+{* ../../docs_src/response_directly/tutorial001.py hl[6:7,21:22] *}
+
+/// note | 기술적 세부 사항
+
+`from starlette.responses import JSONResponse`를 사용할 수도 있습니다.
+
+**FastAPI**는 개발자의 편의를 위해 `starlette.responses`를 `fastapi.responses`로 제공합니다. 그러나 대부분의 가능한 응답은 Starlette에서 직접 제공합니다.
+
+///
+
+## 사용자 정의 `Response` 반환하기
+위 예제는 필요한 모든 부분을 보여주지만, 아직 유용하지는 않습니다. 사실 데이터를 직접 반환하면 **FastAPI**가 이를 `JSONResponse`에 넣고 `dict`로 변환하는 등 모든 작업을 자동으로 처리합니다.
+
+이제, 사용자 정의 응답을 반환하는 방법을 알아보겠습니다.
+
+예를 들어 XML 응답을 반환하고 싶다고 가정해보겠습니다.
+
+XML 내용을 문자열에 넣고, 이를 `Response`에 넣어 반환할 수 있습니다:
+
+{* ../../docs_src/response_directly/tutorial002.py hl[1,18] *}
+
+## 참고 사항
+`Response`를 직접 반환할 때, 그 데이터는 자동으로 유효성 검사되거나, 변환(직렬화)되거나, 문서화되지 않습니다.
+
+그러나 [OpenAPI에서 추가 응답](additional-responses.md){.internal-link target=_blank}에서 설명된 대로 문서화할 수 있습니다.
+
+이후 단락에서 자동 데이터 변환, 문서화 등을 사용하면서 사용자 정의 `Response`를 선언하는 방법을 확인할 수 있습니다.
diff --git a/docs/ko/docs/advanced/response-headers.md b/docs/ko/docs/advanced/response-headers.md
new file mode 100644
index 0000000000..e4e022c9b7
--- /dev/null
+++ b/docs/ko/docs/advanced/response-headers.md
@@ -0,0 +1,41 @@
+# 응답 헤더
+
+## `Response` 매개변수 사용하기
+
+여러분은 *경로 작동 함수*에서 `Response` 타입의 매개변수를 선언할 수 있습니다 (쿠키와 같이 사용할 수 있습니다).
+
+그런 다음, 여러분은 해당 *임시* 응답 객체에서 헤더를 설정할 수 있습니다.
+
+{* ../../docs_src/response_headers/tutorial002.py hl[1,7:8] *}
+
+그 후, 일반적으로 사용하듯이 필요한 객체(`dict`, 데이터베이스 모델 등)를 반환할 수 있습니다.
+
+`response_model`을 선언한 경우, 반환한 객체를 필터링하고 변환하는 데 여전히 사용됩니다.
+
+**FastAPI**는 해당 *임시* 응답에서 헤더(쿠키와 상태 코드도 포함)를 추출하여, 여러분이 반환한 값을 포함하는 최종 응답에 `response_model`로 필터링된 값을 넣습니다.
+
+또한, 종속성에서 `Response` 매개변수를 선언하고 그 안에서 헤더(및 쿠키)를 설정할 수 있습니다.
+
+## `Response` 직접 반환하기
+
+`Response`를 직접 반환할 때에도 헤더를 추가할 수 있습니다.
+
+[응답을 직접 반환하기](response-directly.md){.internal-link target=_blank}에서 설명한 대로 응답을 생성하고, 헤더를 추가 매개변수로 전달하세요.
+
+{* ../../docs_src/response_headers/tutorial001.py hl[10:12] *}
+
+/// note | 기술적 세부사항
+
+`from starlette.responses import Response`나 `from starlette.responses import JSONResponse`를 사용할 수도 있습니다.
+
+**FastAPI**는 `starlette.responses`를 `fastapi.responses`로 개발자의 편의를 위해 직접 제공하지만, 대부분의 응답은 Starlette에서 직접 제공됩니다.
+
+그리고 `Response`는 헤더와 쿠키를 설정하는 데 자주 사용될 수 있으므로, **FastAPI**는 `fastapi.Response`로도 이를 제공합니다.
+
+///
+
+## 커스텀 헤더
+
+‘X-’ 접두어를 사용하여 커스텀 사설 헤더를 추가할 수 있습니다.
+
+하지만, 여러분이 브라우저에서 클라이언트가 볼 수 있기를 원하는 커스텀 헤더가 있는 경우, CORS 설정에 이를 추가해야 합니다([CORS (Cross-Origin Resource Sharing)](../tutorial/cors.md){.internal-link target=_blank}에서 자세히 알아보세요). `expose_headers` 매개변수를 사용하여 Starlette의 CORS 설명서에 문서화된 대로 설정할 수 있습니다.
diff --git a/docs/ko/docs/advanced/sub-applications.md b/docs/ko/docs/advanced/sub-applications.md
new file mode 100644
index 0000000000..c5835de15c
--- /dev/null
+++ b/docs/ko/docs/advanced/sub-applications.md
@@ -0,0 +1,67 @@
+# 하위 응용프로그램 - 마운트
+
+만약 각각의 독립적인 OpenAPI와 문서 UI를 갖는 두 개의 독립적인 FastAPI 응용프로그램이 필요하다면, 메인 어플리케이션에 하나 (또는 그 이상의) 하위-응용프로그램(들)을 “마운트"해서 사용할 수 있습니다.
+
+## **FastAPI** 응용프로그램 마운트
+
+“마운트"이란 완전히 “독립적인" 응용프로그램을 특정 경로에 추가하여 해당 하위 응용프로그램에서 선언된 *경로 동작*을 통해 해당 경로 아래에 있는 모든 작업들을 처리할 수 있도록 하는 것을 의미합니다.
+
+### 최상단 응용프로그램
+
+먼저, 메인, 최상단의 **FastAPI** 응용프로그램과 이것의 *경로 동작*을 생성합니다:
+
+{* ../../docs_src/sub_applications/tutorial001.py hl[3, 6:8] *}
+
+### 하위 응용프로그램
+
+다음으로, 하위 응용프로그램과 이것의 *경로 동작*을 생성합니다:
+
+이 하위 응용프로그램은 또 다른 표준 FastAPI 응용프로그램입니다. 다만 이것은 “마운트”될 것입니다:
+
+{* ../../docs_src/sub_applications/tutorial001.py hl[11, 14:16] *}
+
+### 하위 응용프로그램 마운트
+
+최상단 응용프로그램, `app`에 하위 응용프로그램, `subapi`를 마운트합니다.
+
+이 예시에서, 하위 응용프로그램션은 `/subapi` 경로에 마운트 될 것입니다:
+
+{* ../../docs_src/sub_applications/tutorial001.py hl[11, 19] *}
+
+### 자동으로 생성된 API 문서 확인
+
+이제, `uvicorn`으로 메인 응용프로그램을 실행하십시오. 당신의 파일이 `main.py`라면, 이렇게 실행합니다:
+
+
+
+다음으로, http://127.0.0.1:8000/subapi/docs에서 하위 응용프로그램의 문서를 여십시오.
+
+하위 경로 접두사 `/subapi` 아래에 선언된 *경로 동작* 을 포함하는, 하위 응용프로그램에 대한 자동 API 문서를 확인할 수 있습니다:
+
+
+
+두 사용자 인터페이스 중 어느 하나를 사용해야하는 경우, 브라우저는 특정 응용프로그램 또는 하위 응용프로그램과 각각 통신할 수 있기 때문에 올바르게 동작할 것입니다.
+
+### 기술적 세부사항: `root_path`
+
+위에 설명된 것과 같이 하위 응용프로그램을 마운트하는 경우, FastAPI는 `root_path`라고 하는 ASGI 명세의 매커니즘을 사용하여 하위 응용프로그램에 대한 마운트 경로 통신을 처리합니다.
+
+이를 통해, 하위 응용프로그램은 문서 UI를 위해 경로 접두사를 사용해야 한다는 사실을 인지합니다.
+
+하위 응용프로그램에도 역시 다른 하위 응용프로그램을 마운트하는 것이 가능하며 FastAPI가 모든 `root_path` 들을 자동적으로 처리하기 때문에 모든 것은 올바르게 동작할 것입니다.
+
+`root_path`와 이것을 사용하는 방법에 대해서는 [프록시의 뒷단](./behind-a-proxy.md){.internal-link target=_blank} 섹션에서 배울 수 있습니다.
diff --git a/docs/ko/docs/advanced/templates.md b/docs/ko/docs/advanced/templates.md
new file mode 100644
index 0000000000..6126357132
--- /dev/null
+++ b/docs/ko/docs/advanced/templates.md
@@ -0,0 +1,127 @@
+# 템플릿
+
+**FastAPI**와 함께 원하는 어떤 템플릿 엔진도 사용할 수 있습니다.
+
+일반적인 선택은 Jinja2로, Flask와 다른 도구에서도 사용됩니다.
+
+설정을 쉽게 할 수 있는 유틸리티가 있으며, 이를 **FastAPI** 애플리케이션에서 직접 사용할 수 있습니다(Starlette 제공).
+
+## 의존성 설치
+
+가상 환경을 생성하고(virtual environment{.internal-link target=_blank}), 활성화한 후 jinja2를 설치해야 합니다:
+
+
+
+
+입력창에 메시지를 입력하고 전송할 수 있습니다:
+
+
+
+**FastAPI** WebSocket 응용 프로그램이 응답을 돌려줄 것입니다:
+
+
+
+여러 메시지를 전송(그리고 수신)할 수 있습니다:
+
+
+
+모든 메시지는 동일한 WebSocket 연결을 사용합니다.
+
+## `Depends` 및 기타 사용하기
+
+WebSocket 엔드포인트에서 `fastapi`에서 다음을 가져와 사용할 수 있습니다:
+
+* `Depends`
+* `Security`
+* `Cookie`
+* `Header`
+* `Path`
+* `Query`
+
+이들은 다른 FastAPI 엔드포인트/*경로 작동*과 동일하게 동작합니다:
+
+{* ../../docs_src/websockets/tutorial002_an_py310.py hl[68:69,82] *}
+
+/// info | 정보
+
+WebSocket에서는 `HTTPException`을 발생시키는 것이 적합하지 않습니다. 대신 `WebSocketException`을 발생시킵니다.
+
+명세서에 정의된 유효한 코드를 사용하여 종료 코드를 설정할 수 있습니다.
+
+///
+
+### 종속성을 가진 WebSockets 테스트
+
+파일 이름이 `main.py`라고 가정하고 응용 프로그램을 실행합니다:
+
+
+
+## 연결 해제 및 다중 클라이언트 처리
+
+WebSocket 연결이 닫히면, `await websocket.receive_text()`가 `WebSocketDisconnect` 예외를 발생시킵니다. 이를 잡아 처리할 수 있습니다:
+
+{* ../../docs_src/websockets/tutorial003_py39.py hl[79:81] *}
+
+테스트해보기:
+
+* 여러 브라우저 탭에서 앱을 엽니다.
+* 각 탭에서 메시지를 작성합니다.
+* 한 탭을 닫아보세요.
+
+`WebSocketDisconnect` 예외가 발생하며, 다른 모든 클라이언트가 다음과 같은 메시지를 수신합니다:
+
+```
+Client #1596980209979 left the chat
+```
+
+/// tip | 팁
+
+위 응용 프로그램은 여러 WebSocket 연결에 메시지를 브로드캐스트하는 방법을 보여주는 간단한 예제입니다.
+
+그러나 모든 것을 메모리의 단일 리스트로 처리하므로, 프로세스가 실행 중인 동안만 동작하며 단일 프로세스에서만 작동합니다.
+
+FastAPI와 쉽게 통합할 수 있으면서 더 견고하고 Redis, PostgreSQL 등을 지원하는 도구를 찾고 있다면, encode/broadcaster를 확인하세요.
+
+///
+
+## 추가 정보
+
+다음 옵션에 대한 자세한 내용을 보려면 Starlette의 문서를 확인하세요:
+
+* `WebSocket` 클래스.
+* 클래스 기반 WebSocket 처리.
diff --git a/docs/ko/docs/advanced/wsgi.md b/docs/ko/docs/advanced/wsgi.md
new file mode 100644
index 0000000000..3e9de3e6ca
--- /dev/null
+++ b/docs/ko/docs/advanced/wsgi.md
@@ -0,0 +1,35 @@
+# WSGI 포함하기 - Flask, Django 그 외
+
+[서브 응용 프로그램 - 마운트](sub-applications.md){.internal-link target=_blank}, [프록시 뒤편에서](behind-a-proxy.md){.internal-link target=_blank}에서 보았듯이 WSGI 응용 프로그램들을 다음과 같이 마운트 할 수 있습니다.
+
+`WSGIMiddleware`를 사용하여 WSGI 응용 프로그램(예: Flask, Django 등)을 감쌀 수 있습니다.
+
+## `WSGIMiddleware` 사용하기
+
+`WSGIMiddleware`를 불러와야 합니다.
+
+그런 다음, WSGI(예: Flask) 응용 프로그램을 미들웨어로 포장합니다.
+
+그 후, 해당 경로에 마운트합니다.
+
+{* ../../docs_src/wsgi/tutorial001.py hl[2:3,23] *}
+
+## 확인하기
+
+이제 `/v1/` 경로에 있는 모든 요청은 Flask 응용 프로그램에서 처리됩니다.
+
+그리고 나머지는 **FastAPI**에 의해 처리됩니다.
+
+실행하면 http://localhost:8000/v1/으로 이동해서 Flask의 응답을 볼 수 있습니다:
+
+```txt
+Hello, World from Flask!
+```
+
+그리고 다음으로 이동하면 http://localhost:8000/v2 Flask의 응답을 볼 수 있습니다:
+
+```JSON
+{
+ "message": "Hello World"
+}
+```
diff --git a/docs/ko/docs/async.md b/docs/ko/docs/async.md
index dfc2caa789..ec503d5406 100644
--- a/docs/ko/docs/async.md
+++ b/docs/ko/docs/async.md
@@ -21,7 +21,7 @@ async def read_results():
return results
```
-/// note | "참고"
+/// note | 참고
`async def`로 생성된 함수 내부에서만 `await`를 사용할 수 있습니다.
@@ -349,7 +349,7 @@ FastAPI를 사용하지 않더라도, 높은 호환성 및 Gevent를 사용할 수 있을 것입니다. 하지만 코드를 이해하고, 디버깅하고, 이에 대해 생각하는게 훨씬 복잡합니다.
-예전 버전의 NodeJS / 브라우저 자바스크립트라면, "콜백 함수"를 사용했을 것입니다. 그리고 이로 인해 콜백 지옥에 빠지게 될 수 있습니다.
+예전 버전의 NodeJS / 브라우저 자바스크립트라면, "콜백 함수"를 사용했을 것입니다. 그리고 이로 인해 "콜백 지옥"에 빠지게 될 수 있습니다.
## 코루틴
@@ -369,7 +369,7 @@ FastAPI를 사용하지 않더라도, 높은 호환성 및 가장 빠른 Python 프레임워크 중 하나로 실행되며, Starlette와 Uvicorn 자체(내부적으로 FastAPI가 사용하는 도구)보다 조금 아래에 위치합니다.
+
+그러나 벤치마크와 비교를 확인할 때 다음 사항을 염두에 두어야 합니다.
+
+## 벤치마크와 속도
+
+벤치마크를 확인할 때, 일반적으로 여러 가지 유형의 도구가 동등한 것으로 비교되는 것을 볼 수 있습니다.
+
+특히, Uvicorn, Starlette, FastAPI가 함께 비교되는 경우가 많습니다(다른 여러 도구와 함께).
+
+도구가 해결하는 문제가 단순할수록 성능이 더 좋아집니다. 그리고 대부분의 벤치마크는 도구가 제공하는 추가 기능을 테스트하지 않습니다.
+
+계층 구조는 다음과 같습니다:
+
+* **Uvicorn**: ASGI 서버
+ * **Starlette**: (Uvicorn 사용) 웹 마이크로 프레임워크
+ * **FastAPI**: (Starlette 사용) API 구축을 위한 데이터 검증 등 여러 추가 기능이 포함된 API 마이크로 프레임워크
+
+* **Uvicorn**:
+ * 서버 자체 외에는 많은 추가 코드가 없기 때문에 최고의 성능을 발휘합니다.
+ * 직접 Uvicorn으로 응용 프로그램을 작성하지는 않을 것입니다. 즉, 사용자의 코드에는 적어도 Starlette(또는 **FastAPI**)에서 제공하는 모든 코드가 포함되어야 합니다. 그렇게 하면 최종 응용 프로그램은 프레임워크를 사용하고 앱 코드와 버그를 최소화하는 것과 동일한 오버헤드를 갖게 됩니다.
+ * Uvicorn을 비교할 때는 Daphne, Hypercorn, uWSGI 등의 응용 프로그램 서버와 비교하세요.
+* **Starlette**:
+ * Uvicorn 다음으로 좋은 성능을 발휘합니다. 사실 Starlette는 Uvicorn을 사용하여 실행됩니다. 따라서 더 많은 코드를 실행해야 하기 때문에 Uvicorn보다 "느려질" 수밖에 없습니다.
+ * 하지만 경로 기반 라우팅 등 간단한 웹 응용 프로그램을 구축할 수 있는 도구를 제공합니다.
+ * Starlette를 비교할 때는 Sanic, Flask, Django 등의 웹 프레임워크(또는 마이크로 프레임워크)와 비교하세요.
+* **FastAPI**:
+ * Starlette가 Uvicorn을 사용하므로 Uvicorn보다 빨라질 수 없는 것과 마찬가지로, **FastAPI**는 Starlette를 사용하므로 더 빠를 수 없습니다.
+ * FastAPI는 Starlette에 추가적으로 더 많은 기능을 제공합니다. API를 구축할 때 거의 항상 필요한 데이터 검증 및 직렬화와 같은 기능들이 포함되어 있습니다. 그리고 이를 사용하면 문서 자동화 기능도 제공됩니다(문서 자동화는 응용 프로그램 실행 시 오버헤드를 추가하지 않고 시작 시 생성됩니다).
+ * FastAPI를 사용하지 않고 직접 Starlette(또는 Sanic, Flask, Responder 등)를 사용했다면 데이터 검증 및 직렬화를 직접 구현해야 합니다. 따라서 최종 응용 프로그램은 FastAPI를 사용한 것과 동일한 오버헤드를 가지게 될 것입니다. 많은 경우 데이터 검증 및 직렬화가 응용 프로그램에서 작성된 코드 중 가장 많은 부분을 차지합니다.
+ * 따라서 FastAPI를 사용함으로써 개발 시간, 버그, 코드 라인을 줄일 수 있으며, FastAPI를 사용하지 않았을 때와 동일하거나 더 나은 성능을 얻을 수 있습니다(코드에서 모두 구현해야 하기 때문에).
+ * FastAPI를 비교할 때는 Flask-apispec, NestJS, Molten 등 데이터 검증, 직렬화 및 문서화가 통합된 자동 데이터 검증, 직렬화 및 문서화를 제공하는 웹 응용 프로그램 프레임워크(또는 도구 집합)와 비교하세요.
diff --git a/docs/ko/docs/deployment/cloud.md b/docs/ko/docs/deployment/cloud.md
index 2d6938e200..dbc814bbdb 100644
--- a/docs/ko/docs/deployment/cloud.md
+++ b/docs/ko/docs/deployment/cloud.md
@@ -10,7 +10,4 @@
이는 FastAPI와 **커뮤니티** (여러분)에 대한 진정한 헌신을 보여줍니다. 그들은 여러분에게 **좋은 서비스**를 제공할 뿐 만이 아니라 여러분이 **훌륭하고 건강한 프레임워크인** FastAPI 를 사용하길 원하기 때문입니다. 🙇
-아래와 같은 서비스를 사용해보고 각 서비스의 가이드를 따를 수도 있습니다:
-
-* Platform.sh
-* Porter
+아래와 같은 서비스를 사용해보고 각 서비스의 가이드를 따를 수도 있습니다.
diff --git a/docs/ko/docs/deployment/docker.md b/docs/ko/docs/deployment/docker.md
index 502a36fc05..e8b2746c5f 100644
--- a/docs/ko/docs/deployment/docker.md
+++ b/docs/ko/docs/deployment/docker.md
@@ -4,7 +4,7 @@ FastAPI 어플리케이션을 배포할 때 일반적인 접근 방법은 **리
리눅스 컨테이너를 사용하는 데에는 **보안**, **반복 가능성**, **단순함** 등의 장점이 있습니다.
-/// tip | "팁"
+/// tip | 팁
시간에 쫓기고 있고 이미 이런것들을 알고 있다면 [`Dockerfile`👇](#build-a-docker-image-for-fastapi)로 점프할 수 있습니다.
@@ -133,7 +133,7 @@ Successfully installed fastapi pydantic uvicorn
-/// info | "정보"
+/// info | 정보
패키지 종속성을 정의하고 설치하기 위한 방법과 도구는 다양합니다.
@@ -231,7 +231,7 @@ CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"]
프로그램이 `/code`에서 시작하고 그 속에 `./app` 디렉터리가 여러분의 코드와 함께 들어있기 때문에, **Uvicorn**은 이를 보고 `app`을 `app.main`으로부터 **불러 올** 것입니다.
-/// tip | "팁"
+/// tip | 팁
각 코드 라인을 코드의 숫자 버블을 클릭하여 리뷰할 수 있습니다. 👆
@@ -305,7 +305,7 @@ $ docker build -t myimage .
-/// tip | "팁"
+/// tip | 팁
맨 끝에 있는 `.` 에 주목합시다. 이는 `./`와 동등하며, 도커에게 컨테이너 이미지를 빌드하기 위한 디렉터리를 알려줍니다.
@@ -409,7 +409,7 @@ CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "80"]
**HTTPS**와 **인증서**의 **자동** 취득을 다루는 것은 다른 컨테이너가 될 수 있는데, 예를 들어 Traefik을 사용하는 것입니다.
-/// tip | "팁"
+/// tip | 팁
Traefik은 도커, 쿠버네티스, 그리고 다른 도구와 통합되어 있어 여러분의 컨테이너를 포함하는 HTTPS를 셋업하고 설정하는 것이 매우 쉽습니다.
@@ -441,7 +441,7 @@ Traefik은 도커, 쿠버네티스, 그리고 다른 도구와 통합되어 있
이 요소가 요청들의 **로드**를 읽어들이고 각 워커에게 (바라건대) **균형적으로** 분배한다면, 이 요소는 일반적으로 **로드 밸런서**라고 불립니다.
-/// tip | "팁"
+/// tip | 팁
HTTPS를 위해 사용된 **TLS 종료 프록시** 요소 또한 **로드 밸런서**가 될 수 있습니다.
@@ -524,7 +524,7 @@ HTTPS를 위해 사용된 **TLS 종료 프록시** 요소 또한 **로드 밸런
만약 여러분이 **여러개의 컨테이너**를 가지고 있다면, 아마도 각각의 컨테이너는 **하나의 프로세스**를 가지고 있을 것입니다(예를 들어, **쿠버네티스** 클러스터에서). 그러면 여러분은 복제된 워커 컨테이너를 실행하기 **이전에**, 하나의 컨테이너에 있는 **이전의 단계들을** 수행하는 단일 프로세스를 가지는 **별도의 컨테이너들**을 가지고 싶을 것입니다.
-/// info | "정보"
+/// info | 정보
만약 여러분이 쿠버네티스를 사용하고 있다면, 아마도 이는 Init Container일 것입니다.
@@ -544,7 +544,7 @@ HTTPS를 위해 사용된 **TLS 종료 프록시** 요소 또한 **로드 밸런
* tiangolo/uvicorn-gunicorn-fastapi.
-/// warning | "경고"
+/// warning | 경고
여러분이 이 베이스 이미지 또는 다른 유사한 이미지를 필요로 하지 **않을** 높은 가능성이 있으며, [위에서 설명된 것처럼: FastAPI를 위한 도커 이미지 빌드하기](#build-a-docker-image-for-fastapi) 처음부터 이미지를 빌드하는 것이 더 나을 수 있습니다.
@@ -556,7 +556,7 @@ HTTPS를 위해 사용된 **TLS 종료 프록시** 요소 또한 **로드 밸런
또한 스크립트를 통해 **시작하기 전 사전 단계**를 실행하는 것을 지원합니다.
-/// tip | "팁"
+/// tip | 팁
모든 설정과 옵션을 보려면, 도커 이미지 페이지로 이동합니다: tiangolo/uvicorn-gunicorn-fastapi.
@@ -687,7 +687,7 @@ CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"]
11. `uvicorn` 커맨드를 실행하여, `app.main`에서 불러온 `app` 객체를 사용하도록 합니다.
-/// tip | "팁"
+/// tip | 팁
버블 숫자를 클릭해 각 줄이 하는 일을 알아볼 수 있습니다.
diff --git a/docs/ko/docs/deployment/server-workers.md b/docs/ko/docs/deployment/server-workers.md
index 39976faf5b..b40b25cd8f 100644
--- a/docs/ko/docs/deployment/server-workers.md
+++ b/docs/ko/docs/deployment/server-workers.md
@@ -17,7 +17,7 @@
지금부터 **구니콘**을 **유비콘 워커 프로세스**와 함께 사용하는 방법을 알려드리겠습니다.
-/// info | "정보"
+/// info | 정보
만약 도커와 쿠버네티스 같은 컨테이너를 사용하고 있다면 다음 챕터 [FastAPI와 컨테이너 - 도커](docker.md){.internal-link target=_blank}에서 더 많은 정보를 얻을 수 있습니다.
diff --git a/docs/ko/docs/deployment/versions.md b/docs/ko/docs/deployment/versions.md
index f3b3c2d7b8..559a892ab9 100644
--- a/docs/ko/docs/deployment/versions.md
+++ b/docs/ko/docs/deployment/versions.md
@@ -43,7 +43,7 @@ fastapi>=0.45.0,<0.46.0
FastAPI는 오류를 수정하고, 일반적인 변경사항을 위해 "패치"버전의 관습을 따릅니다.
-/// tip | "팁"
+/// tip | 팁
여기서 말하는 "패치"란 버전의 마지막 숫자로, 예를 들어 `0.2.3` 버전에서 "패치"는 `3`을 의미합니다.
@@ -57,7 +57,7 @@ fastapi>=0.45.0,<0.46.0
수정된 사항과 새로운 요소들이 "마이너" 버전에 추가되었습니다.
-/// tip | "팁"
+/// tip | 팁
"마이너"란 버전 넘버의 가운데 숫자로, 예를 들어서 `0.2.3`의 "마이너" 버전은 `2`입니다.
diff --git a/docs/ko/docs/environment-variables.md b/docs/ko/docs/environment-variables.md
new file mode 100644
index 0000000000..1e6af3ceba
--- /dev/null
+++ b/docs/ko/docs/environment-variables.md
@@ -0,0 +1,298 @@
+# 환경 변수
+
+/// tip | 팁
+
+만약 "환경 변수"가 무엇이고, 어떻게 사용하는지 알고 계시다면, 이 챕터를 스킵하셔도 좋습니다.
+
+///
+
+환경 변수는 파이썬 코드의 **바깥**인, **운영 체제**에 존재하는 변수입니다. 파이썬 코드나 다른 프로그램에서 읽을 수 있습니다.
+
+환경 변수는 애플리케이션 **설정**을 처리하거나, 파이썬의 **설치** 과정의 일부로 유용합니다.
+
+## 환경 변수를 만들고 사용하기
+
+파이썬 없이도, **셸 (터미널)** 에서 환경 변수를 **생성** 하고 사용할 수 있습니다.
+
+//// tab | Linux, macOS, Windows Bash
+
+
+
+그러나 `syntaxHighlight`를 `False`로 설정하여 구문 강조 기능을 비활성화할 수 있습니다:
+
+{* ../../docs_src/configure_swagger_ui/tutorial001.py hl[3] *}
+
+...그럼 Swagger UI에서 더 이상 구문 강조 기능이 표시되지 않습니다:
+
+
+
+## 테마 변경
+
+동일한 방식으로 `"syntaxHighlight.theme"` 키를 사용하여 구문 강조 테마를 설정할 수 있습니다 (중간에 점이 포함된 것을 참고하십시오).
+
+{* ../../docs_src/configure_swagger_ui/tutorial002.py hl[3] *}
+
+이 설정은 구문 강조 색상 테마를 변경합니다:
+
+
+
+## 기본 Swagger UI 매개변수 변경
+
+FastAPI는 대부분의 사용 사례에 적합한 몇 가지 기본 구성 매개변수를 포함하고 있습니다.
+
+기본 구성에는 다음이 포함됩니다:
+
+{* ../../fastapi/openapi/docs.py ln[8:23] hl[17:23] *}
+
+`swagger_ui_parameters` 인수에 다른 값을 설정하여 이러한 기본값 중 일부를 재정의할 수 있습니다.
+
+예를 들어, `deepLinking`을 비활성화하려면 `swagger_ui_parameters`에 다음 설정을 전달할 수 있습니다:
+
+{* ../../docs_src/configure_swagger_ui/tutorial003.py hl[3] *}
+
+## 기타 Swagger UI 매개변수
+
+사용할 수 있는 다른 모든 구성 옵션을 확인하려면, Swagger UI 매개변수에 대한 공식 문서를 참조하십시오.
+
+## JavaScript 전용 설정
+
+Swagger UI는 **JavaScript 전용** 객체(예: JavaScript 함수)로 다른 구성을 허용하기도 합니다.
+
+FastAPI는 이러한 JavaScript 전용 `presets` 설정을 포함하고 있습니다:
+
+```JavaScript
+presets: [
+ SwaggerUIBundle.presets.apis,
+ SwaggerUIBundle.SwaggerUIStandalonePreset
+]
+```
+
+이들은 문자열이 아닌 **JavaScript** 객체이므로 Python 코드에서 직접 전달할 수 없습니다.
+
+이와 같은 JavaScript 전용 구성을 사용해야 하는 경우, 위의 방법 중 하나를 사용하여 모든 Swagger UI 경로 작업을 재정의하고 필요한 JavaScript를 수동으로 작성할 수 있습니다.
diff --git a/docs/ko/docs/index.md b/docs/ko/docs/index.md
index 8b00d90bc5..b6b4765dad 100644
--- a/docs/ko/docs/index.md
+++ b/docs/ko/docs/index.md
@@ -11,15 +11,18 @@
FastAPI 프레임워크, 고성능, 간편한 학습, 빠른 코드 작성, 준비된 프로덕션
---
@@ -85,7 +88,7 @@ FastAPI는 현대적이고, 빠르며(고성능), 파이썬 표준 타입 힌트
"_**FastAPI**가 너무 좋아서 구름 위를 걷는듯 합니다. 정말 즐겁습니다!_"
-uvicorn - 애플리케이션을 로드하고 제공하는 서버.
+* uvicorn - 애플리케이션을 로드하고 제공하는 서버.
* orjson - `ORJSONResponse`을 사용하려면 필요.
* ujson - `UJSONResponse`를 사용하려면 필요.
diff --git a/docs/ko/docs/openapi-webhooks.md b/docs/ko/docs/openapi-webhooks.md
new file mode 100644
index 0000000000..96339aa961
--- /dev/null
+++ b/docs/ko/docs/openapi-webhooks.md
@@ -0,0 +1,55 @@
+# OpenAPI 웹훅(Webhooks)
+
+API **사용자**에게 특정 **이벤트**가 발생할 때 *그들*의 앱(시스템)에 요청을 보내 **알림**을 전달할 수 있다는 것을 알리고 싶은 경우가 있습니다.
+
+즉, 일반적으로 사용자가 API에 요청을 보내는 것과는 반대로, **API**(또는 앱)가 **사용자의 시스템**(그들의 API나 앱)으로 **요청을 보내는** 상황을 의미합니다.
+
+이를 흔히 **웹훅(Webhook)**이라고 부릅니다.
+
+## 웹훅 스텝
+
+**코드에서** 웹훅으로 보낼 메시지, 즉 요청의 **바디(body)**를 정의하는 것이 일반적인 프로세스입니다.
+
+앱에서 해당 요청이나 이벤트를 전송할 **시점**을 정의합니다.
+
+**사용자**는 앱이 해당 요청을 보낼 **URL**을 정의합니다. (예: 웹 대시보드에서 설정)
+
+웹훅의 URL을 등록하는 방법과 이러한 요청을 실제로 전송하는 코드에 대한 모든 로직은 여러분에게 달려 있습니다. 원하는대로 **고유의 코드**를 작성하면 됩니다.
+
+## **FastAPI**와 OpenAPI로 웹훅 문서화하기
+
+**FastAPI**를 사용하여 OpenAPI와 함께 웹훅의 이름, 앱이 보낼 수 있는 HTTP 작업 유형(예: `POST`, `PUT` 등), 그리고 보낼 요청의 **바디**를 정의할 수 있습니다.
+
+이를 통해 사용자가 **웹훅** 요청을 수신할 **API 구현**을 훨씬 쉽게 할 수 있으며, 경우에 따라 사용자 API 코드의 일부를 자동 생성할 수도 있습니다.
+
+/// info
+
+웹훅은 OpenAPI 3.1.0 이상에서 지원되며, FastAPI `0.99.0` 이상 버전에서 사용할 수 있습니다.
+
+///
+
+## 웹훅이 포함된 앱 만들기
+
+**FastAPI** 애플리케이션을 만들 때, `webhooks` 속성을 사용하여 *웹훅*을 정의할 수 있습니다. 이는 `@app.webhooks.post()`와 같은 방식으로 *경로(path) 작업*을 정의하는 것과 비슷합니다.
+
+{* ../../docs_src/openapi_webhooks/tutorial001.py hl[9:13,36:53] *}
+
+이렇게 정의한 웹훅은 **OpenAPI** 스키마와 자동 **문서화 UI**에 표시됩니다.
+
+/// info
+
+`app.webhooks` 객체는 사실 `APIRouter`일 뿐이며, 여러 파일로 앱을 구성할 때 사용하는 것과 동일한 타입입니다.
+
+///
+
+웹훅에서는 실제 **경로(path)** (예: `/items/`)를 선언하지 않는 점에 유의해야 합니다. 여기서 전달하는 텍스트는 **식별자**로, 웹훅의 이름(이벤트 이름)입니다. 예를 들어, `@app.webhooks.post("new-subscription")`에서 웹훅 이름은 `new-subscription`입니다.
+
+이는 실제 **URL 경로**는 **사용자**가 다른 방법(예: 웹 대시보드)을 통해 지정하도록 기대되기 때문입니다.
+
+### 문서 확인하기
+
+이제 앱을 시작하고 http://127.0.0.1:8000/docs로 이동해 봅시다.
+
+문서에서 기존 *경로 작업*뿐만 아니라 **웹훅**도 표시된 것을 확인할 수 있습니다:
+
+
diff --git a/docs/ko/docs/project-generation.md b/docs/ko/docs/project-generation.md
new file mode 100644
index 0000000000..dd11fca703
--- /dev/null
+++ b/docs/ko/docs/project-generation.md
@@ -0,0 +1,28 @@
+# Full Stack FastAPI 템플릿
+
+템플릿은 일반적으로 특정 설정과 함께 제공되지만, 유연하고 커스터마이징이 가능하게 디자인 되었습니다. 이 특성들은 여러분이 프로젝트의 요구사항에 맞춰 수정, 적용을 할 수 있게 해주고, 템플릿이 완벽한 시작점이 되게 해줍니다. 🏁
+
+많은 초기 설정, 보안, 데이터베이스 및 일부 API 엔드포인트가 이미 준비되어 있으므로, 여러분은 이 템플릿을 (프로젝트를) 시작하는 데 사용할 수 있습니다.
+
+GitHub 저장소: Full Stack FastAPI 템플릿
+
+## Full Stack FastAPI 템플릿 - 기술 스택과 기능들
+
+- ⚡ [**FastAPI**](https://fastapi.tiangolo.com): Python 백엔드 API.
+ - 🧰 [SQLModel](https://sqlmodel.tiangolo.com): Python SQL 데이터 상호작용을 위한 (ORM).
+ - 🔍 [Pydantic](https://docs.pydantic.dev): FastAPI에 의해 사용되는, 데이터 검증과 설정관리.
+ - 💾 [PostgreSQL](https://www.postgresql.org): SQL 데이터베이스.
+- 🚀 [React](https://react.dev): 프론트엔드.
+ - 💃 TypeScript, hooks, [Vite](https://vitejs.dev) 및 기타 현대적인 프론트엔드 스택을 사용.
+ - 🎨 [Chakra UI](https://chakra-ui.com): 프론트엔드 컴포넌트.
+ - 🤖 자동으로 생성된 프론트엔드 클라이언트.
+ - 🧪 E2E 테스트를 위한 [Playwright](https://playwright.dev).
+ - 🦇 다크 모드 지원.
+- 🐋 [Docker Compose](https://www.docker.com): 개발 환경과 프로덕션(운영).
+- 🔒 기본으로 지원되는 안전한 비밀번호 해싱.
+- 🔑 JWT 토큰 인증.
+- 📫 이메일 기반 비밀번호 복구.
+- ✅ [Pytest]를 이용한 테스트(https://pytest.org).
+- 📞 [Traefik](https://traefik.io): 리버스 프록시 / 로드 밸런서.
+- 🚢 Docker Compose를 이용한 배포 지침: 자동 HTTPS 인증서를 처리하기 위한 프론트엔드 Traefik 프록시 설정 방법을 포함.
+- 🏭 GitHub Actions를 기반으로 CI (지속적인 통합) 및 CD (지속적인 배포).
diff --git a/docs/ko/docs/python-types.md b/docs/ko/docs/python-types.md
index 5c458e48de..18d4b341e4 100644
--- a/docs/ko/docs/python-types.md
+++ b/docs/ko/docs/python-types.md
@@ -12,7 +12,7 @@
비록 **FastAPI**를 쓰지 않는다고 하더라도, 조금이라도 알아두면 도움이 될 것입니다.
-/// note | "참고"
+/// note | 참고
파이썬에 능숙하셔서 타입 힌트에 대해 모두 아신다면, 다음 챕터로 건너뛰세요.
@@ -22,9 +22,8 @@
간단한 예제부터 시작해봅시다:
-```Python
-{!../../../docs_src/python_types/tutorial001.py!}
-```
+{* ../../docs_src/python_types/tutorial001.py *}
+
이 프로그램을 실행한 결과값:
@@ -38,9 +37,8 @@ John Doe
* `title()`로 각 첫 문자를 대문자로 변환시킵니다.
* 두 단어를 중간에 공백을 두고 연결합니다.
-```Python hl_lines="2"
-{!../../../docs_src/python_types/tutorial001.py!}
-```
+{* ../../docs_src/python_types/tutorial001.py hl[2] *}
+
### 코드 수정
@@ -82,9 +80,8 @@ John Doe
이게 "타입 힌트"입니다:
-```Python hl_lines="1"
-{!../../../docs_src/python_types/tutorial002.py!}
-```
+{* ../../docs_src/python_types/tutorial002.py hl[1] *}
+
타입힌트는 다음과 같이 기본 값을 선언하는 것과는 다릅니다:
@@ -112,9 +109,8 @@ John Doe
아래 함수를 보면, 이미 타입 힌트가 적용되어 있는 걸 볼 수 있습니다:
-```Python hl_lines="1"
-{!../../../docs_src/python_types/tutorial003.py!}
-```
+{* ../../docs_src/python_types/tutorial003.py hl[1] *}
+
편집기가 변수의 타입을 알고 있기 때문에, 자동완성 뿐 아니라 에러도 확인할 수 있습니다:
@@ -122,9 +118,8 @@ John Doe
이제 고쳐야하는 걸 알기 때문에, `age`를 `str(age)`과 같이 문자열로 바꾸게 됩니다:
-```Python hl_lines="2"
-{!../../../docs_src/python_types/tutorial004.py!}
-```
+{* ../../docs_src/python_types/tutorial004.py hl[2] *}
+
## 타입 선언
@@ -143,9 +138,8 @@ John Doe
* `bool`
* `bytes`
-```Python hl_lines="1"
-{!../../../docs_src/python_types/tutorial005.py!}
-```
+{* ../../docs_src/python_types/tutorial005.py hl[1] *}
+
### 타입 매개변수를 활용한 Generic(제네릭) 타입
@@ -161,9 +155,8 @@ John Doe
`typing`에서 `List`(대문자 `L`)를 import 합니다.
-```Python hl_lines="1"
-{!../../../docs_src/python_types/tutorial006.py!}
-```
+{* ../../docs_src/python_types/tutorial006.py hl[1] *}
+
콜론(`:`) 문법을 이용하여 변수를 선언합니다.
@@ -171,11 +164,10 @@ John Doe
이때 배열은 내부 타입을 포함하는 타입이기 때문에 대괄호 안에 넣어줍니다.
-```Python hl_lines="4"
-{!../../../docs_src/python_types/tutorial006.py!}
-```
+{* ../../docs_src/python_types/tutorial006.py hl[4] *}
-/// tip | "팁"
+
+/// tip | 팁
대괄호 안의 내부 타입은 "타입 매개변수(type paramters)"라고 합니다.
@@ -199,9 +191,8 @@ John Doe
`tuple`과 `set`도 동일하게 선언할 수 있습니다.
-```Python hl_lines="1 4"
-{!../../../docs_src/python_types/tutorial007.py!}
-```
+{* ../../docs_src/python_types/tutorial007.py hl[1,4] *}
+
이 뜻은 아래와 같습니다:
@@ -216,9 +207,8 @@ John Doe
두 번째 매개변수는 `dict`의 값(value)입니다.
-```Python hl_lines="1 4"
-{!../../../docs_src/python_types/tutorial008.py!}
-```
+{* ../../docs_src/python_types/tutorial008.py hl[1,4] *}
+
이 뜻은 아래와 같습니다:
@@ -231,7 +221,7 @@ John Doe
`str`과 같이 타입을 선언할 때 `Optional`을 쓸 수도 있는데, "선택적(Optional)"이기때문에 `None`도 될 수 있습니다:
```Python hl_lines="1 4"
-{!../../../docs_src/python_types/tutorial009.py!}
+{!../../docs_src/python_types/tutorial009.py!}
```
`Optional[str]`을 `str` 대신 쓰게 되면, 특정 값이 실제로는 `None`이 될 수도 있는데 항상 `str`이라고 가정하는 상황에서 에디터가 에러를 찾게 도와줄 수 있습니다.
@@ -255,15 +245,13 @@ John Doe
이름(name)을 가진 `Person` 클래스가 있다고 해봅시다.
-```Python hl_lines="1-3"
-{!../../../docs_src/python_types/tutorial010.py!}
-```
+{* ../../docs_src/python_types/tutorial010.py hl[1:3] *}
+
그렇게 하면 변수를 `Person`이라고 선언할 수 있게 됩니다.
-```Python hl_lines="6"
-{!../../../docs_src/python_types/tutorial010.py!}
-```
+{* ../../docs_src/python_types/tutorial010.py hl[6] *}
+
그리고 역시나 모든 에디터 도움을 받게 되겠죠.
@@ -283,11 +271,10 @@ John Doe
Pydantic 공식 문서 예시:
-```Python
-{!../../../docs_src/python_types/tutorial011.py!}
-```
+{* ../../docs_src/python_types/tutorial011.py *}
-/// info | "정보"
+
+/// info | 정보
Pydantic<에 대해 더 배우고 싶다면 공식 문서를 참고하세요.
@@ -319,7 +306,7 @@ Pydantic<에 대해 더 배우고 싶다면 `mypy`에서 제공하는 "cheat sheet"이 좋은 자료가 될 겁니다.
diff --git a/docs/ko/docs/resources/index.md b/docs/ko/docs/resources/index.md
new file mode 100644
index 0000000000..e804dd4d56
--- /dev/null
+++ b/docs/ko/docs/resources/index.md
@@ -0,0 +1,3 @@
+# 리소스
+
+추가 리소스, 외부 링크, 기사 등. ✈️
diff --git a/docs/ko/docs/security/index.md b/docs/ko/docs/security/index.md
new file mode 100644
index 0000000000..5a6c733f05
--- /dev/null
+++ b/docs/ko/docs/security/index.md
@@ -0,0 +1,19 @@
+# 고급 보안
+
+## 추가 기능
+
+[자습서 - 사용자 가이드: 보안](../../tutorial/security/index.md){.internal-link target=_blank} 문서에서 다룬 내용 외에도 보안 처리를 위한 몇 가지 추가 기능이 있습니다.
+
+/// tip
+
+다음 섹션은 **반드시 "고급"** 기능은 아닙니다.
+
+그리고 여러분의 사용 사례에 따라, 적합한 해결책이 그 중 하나에 있을 가능성이 있습니다.
+
+///
+
+## 먼저 자습서 읽기
+
+다음 섹션은 이미 [자습서 - 사용자 가이드: 보안](../../tutorial/security/index.md){.internal-link target=_blank} 문서를 읽었다고 가정합니다.
+
+이 섹션들은 모두 동일한 개념을 바탕으로 하며, 추가 기능을 제공합니다.
diff --git a/docs/ko/docs/tutorial/background-tasks.md b/docs/ko/docs/tutorial/background-tasks.md
index 880a1c198b..9c4d57481f 100644
--- a/docs/ko/docs/tutorial/background-tasks.md
+++ b/docs/ko/docs/tutorial/background-tasks.md
@@ -15,9 +15,7 @@ FastAPI에서는 응답을 반환한 후에 실행할 백그라운드 작업을
먼저 아래와 같이 `BackgroundTasks`를 임포트하고, `BackgroundTasks`를 _경로 작동 함수_ 에서 매개변수로 가져오고 정의합니다.
-```Python hl_lines="1 13"
-{!../../../docs_src/background_tasks/tutorial001.py!}
-```
+{* ../../docs_src/background_tasks/tutorial001.py hl[1,13] *}
**FastAPI** 는 `BackgroundTasks` 개체를 생성하고, 매개 변수로 전달합니다.
@@ -33,17 +31,13 @@ FastAPI에서는 응답을 반환한 후에 실행할 백그라운드 작업을
그리고 이 작업은 `async`와 `await`를 사용하지 않으므로 일반 `def` 함수로 선언합니다.
-```Python hl_lines="6-9"
-{!../../../docs_src/background_tasks/tutorial001.py!}
-```
+{* ../../docs_src/background_tasks/tutorial001.py hl[6:9] *}
## 백그라운드 작업 추가
_경로 작동 함수_ 내에서 작업 함수를 `.add_task()` 함수 통해 _백그라운드 작업_ 개체에 전달합니다.
-```Python hl_lines="14"
-{!../../../docs_src/background_tasks/tutorial001.py!}
-```
+{* ../../docs_src/background_tasks/tutorial001.py hl[14] *}
`.add_task()` 함수는 다음과 같은 인자를 받습니다 :
@@ -57,21 +51,7 @@ _경로 작동 함수_ 내에서 작업 함수를 `.add_task()` 함수 통해 _
**FastAPI**는 각 경우에 수행할 작업과 동일한 개체를 내부적으로 재사용하기에, 모든 백그라운드 작업이 함께 병합되고 나중에 백그라운드에서 실행됩니다.
-//// tab | Python 3.6 and above
-
-```Python hl_lines="13 15 22 25"
-{!> ../../../docs_src/background_tasks/tutorial002.py!}
-```
-
-////
-
-//// tab | Python 3.10 and above
-
-```Python hl_lines="11 13 20 23"
-{!> ../../../docs_src/background_tasks/tutorial002_py310.py!}
-```
-
-////
+{* ../../docs_src/background_tasks/tutorial002.py hl[13,15,22,25] *}
이 예제에서는 응답이 반환된 후에 `log.txt` 파일에 메시지가 기록됩니다.
@@ -81,7 +61,7 @@ _경로 작동 함수_ 내에서 작업 함수를 `.add_task()` 함수 통해 _
## 기술적 세부사항
-`BackgroundTasks` 클래스는 `starlette.background`에서 직접 가져옵니다.
+`BackgroundTasks` 클래스는 `starlette.background`에서 직접 가져옵니다.
`BackgroundTasks` 클래스는 FastAPI에서 직접 임포트하거나 포함하기 때문에 실수로 `BackgroundTask` (끝에 `s`가 없음)을 임포트하더라도 starlette.background에서 `BackgroundTask`를 가져오는 것을 방지할 수 있습니다.
@@ -89,7 +69,7 @@ _경로 작동 함수_ 내에서 작업 함수를 `.add_task()` 함수 통해 _
FastAPI에서 `BackgroundTask`를 단독으로 사용하는 것은 여전히 가능합니다. 하지만 객체를 코드에서 생성하고, 이 객체를 포함하는 Starlette `Response`를 반환해야 합니다.
-`Starlette의 공식 문서`에서 백그라운드 작업에 대한 자세한 내용을 확인할 수 있습니다.
+`Starlette의 공식 문서`에서 백그라운드 작업에 대한 자세한 내용을 확인할 수 있습니다.
## 경고
@@ -97,8 +77,6 @@ FastAPI에서 `BackgroundTask`를 단독으로 사용하는 것은 여전히 가
RabbitMQ 또는 Redis와 같은 메시지/작업 큐 시스템 보다 복잡한 구성이 필요한 경향이 있지만, 여러 작업 프로세스를 특히 여러 서버의 백그라운드에서 실행할 수 있습니다.
-예제를 보시려면 [프로젝트 생성기](../project-generation.md){.internal-link target=\_blank} 를 참고하세요. 해당 예제에는 이미 구성된 `Celery`가 포함되어 있습니다.
-
그러나 동일한 FastAPI 앱에서 변수 및 개체에 접근해야햐는 작은 백그라운드 수행이 필요한 경우 (예 : 알림 이메일 보내기) 간단하게 `BackgroundTasks`를 사용해보세요.
## 요약
diff --git a/docs/ko/docs/tutorial/body-fields.md b/docs/ko/docs/tutorial/body-fields.md
index b74722e266..4708e7099f 100644
--- a/docs/ko/docs/tutorial/body-fields.md
+++ b/docs/ko/docs/tutorial/body-fields.md
@@ -6,59 +6,9 @@
먼저 이를 임포트해야 합니다:
-//// tab | Python 3.10+
+{* ../../docs_src/body_fields/tutorial001_an_py310.py hl[4] *}
-```Python hl_lines="4"
-{!> ../../../docs_src/body_fields/tutorial001_an_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="4"
-{!> ../../../docs_src/body_fields/tutorial001_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="4"
-{!> ../../../docs_src/body_fields/tutorial001_an.py!}
-```
-
-////
-
-//// tab | Python 3.10+ Annotated가 없는 경우
-
-/// tip | "팁"
-
-가능하다면 `Annotated`가 달린 버전을 권장합니다.
-
-///
-
-```Python hl_lines="2"
-{!> ../../../docs_src/body_fields/tutorial001_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+ Annotated가 없는 경우
-
-/// tip | "팁"
-
-가능하다면 `Annotated`가 달린 버전을 권장합니다.
-
-///
-
-```Python hl_lines="4"
-{!> ../../../docs_src/body_fields/tutorial001.py!}
-```
-
-////
-
-/// warning | "경고"
+/// warning | 경고
`Field`는 다른 것들처럼 (`Query`, `Path`, `Body` 등) `fastapi`에서가 아닌 `pydantic`에서 바로 임포트 되는 점에 주의하세요.
@@ -68,61 +18,11 @@
그 다음 모델 어트리뷰트와 함께 `Field`를 사용할 수 있습니다:
-//// tab | Python 3.10+
-
-```Python hl_lines="11-14"
-{!> ../../../docs_src/body_fields/tutorial001_an_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="11-14"
-{!> ../../../docs_src/body_fields/tutorial001_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="12-15"
-{!> ../../../docs_src/body_fields/tutorial001_an.py!}
-```
-
-////
-
-//// tab | Python 3.10+ Annotated가 없는 경우
-
-/// tip | "팁"
-
-가능하다면 `Annotated`가 달린 버전을 권장합니다.
-
-///
-
-```Python hl_lines="9-12"
-{!> ../../../docs_src/body_fields/tutorial001_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+ Annotated가 없는 경우
-
-/// tip | "팁"
-
-가능하다면 `Annotated`가 달린 버전을 권장합니다.
-
-///
-
-```Python hl_lines="11-14"
-{!> ../../../docs_src/body_fields/tutorial001.py!}
-```
-
-////
+{* ../../docs_src/body_fields/tutorial001_an_py310.py hl[11:14] *}
`Field`는 `Query`, `Path`와 `Body`와 같은 방식으로 동작하며, 모두 같은 매개변수들 등을 가집니다.
-/// note | "기술적 세부사항"
+/// note | 기술적 세부사항
실제로 `Query`, `Path`등, 여러분이 앞으로 볼 다른 것들은 공통 클래스인 `Param` 클래스의 서브클래스 객체를 만드는데, 그 자체로 Pydantic의 `FieldInfo` 클래스의 서브클래스입니다.
@@ -134,7 +34,7 @@
///
-/// tip | "팁"
+/// tip | 팁
주목할 점은 타입, 기본 값 및 `Field`로 이루어진 각 모델 어트리뷰트가 `Path`, `Query`와 `Body`대신 `Field`를 사용하는 *경로 작동 함수*의 매개변수와 같은 구조를 가진다는 점 입니다.
@@ -146,7 +46,7 @@
여러분이 예제를 선언할 때 나중에 이 공식 문서에서 별도 정보를 추가하는 방법을 배울 것입니다.
-/// warning | "경고"
+/// warning | 경고
별도 키가 전달된 `Field` 또한 여러분의 어플리케이션의 OpenAPI 스키마에 나타날 것입니다.
이런 키가 OpenAPI 명세서, [the OpenAPI validator](https://validator.swagger.io/)같은 몇몇 OpenAPI 도구들에 포함되지 못할 수 있으며, 여러분이 생성한 스키마와 호환되지 않을 수 있습니다.
diff --git a/docs/ko/docs/tutorial/body-multiple-params.md b/docs/ko/docs/tutorial/body-multiple-params.md
index 023575e1bb..edf892dfab 100644
--- a/docs/ko/docs/tutorial/body-multiple-params.md
+++ b/docs/ko/docs/tutorial/body-multiple-params.md
@@ -10,11 +10,9 @@
또한, 기본 값을 `None`으로 설정해 본문 매개변수를 선택사항으로 선언할 수 있습니다.
-```Python hl_lines="19-21"
-{!../../../docs_src/body_multiple_params/tutorial001.py!}
-```
+{* ../../docs_src/body_multiple_params/tutorial001.py hl[19:21] *}
-/// note | "참고"
+/// note | 참고
이 경우에는 본문으로 부터 가져온 ` item`은 기본값이 `None`이기 때문에, 선택사항이라는 점을 유의해야 합니다.
@@ -35,9 +33,7 @@
하지만, 다중 본문 매개변수 역시 선언할 수 있습니다. 예. `item`과 `user`:
-```Python hl_lines="22"
-{!../../../docs_src/body_multiple_params/tutorial002.py!}
-```
+{* ../../docs_src/body_multiple_params/tutorial002.py hl[22] *}
이 경우에, **FastAPI**는 이 함수 안에 한 개 이상의 본문 매개변수(Pydantic 모델인 두 매개변수)가 있다고 알 것입니다.
@@ -58,7 +54,7 @@
}
```
-/// note | "참고"
+/// note | 참고
이전과 같이 `item`이 선언 되었더라도, 본문 내의 `item` 키가 있을 것이라고 예측합니다.
@@ -79,9 +75,7 @@ FastAPI는 요청을 자동으로 변환해, 매개변수의 `item`과 `user`를
하지만, **FastAPI**의 `Body`를 사용해 다른 본문 키로 처리하도록 제어할 수 있습니다:
-```Python hl_lines="23"
-{!../../../docs_src/body_multiple_params/tutorial003.py!}
-```
+{* ../../docs_src/body_multiple_params/tutorial003.py hl[23] *}
이 경우에는 **FastAPI**는 본문을 이와 같이 예측할 것입니다:
@@ -110,9 +104,7 @@ FastAPI는 요청을 자동으로 변환해, 매개변수의 `item`과 `user`를
기본적으로 단일 값은 쿼리 매개변수로 해석되므로, 명시적으로 `Query`를 추가할 필요가 없고, 아래처럼 할 수 있습니다:
-```Python hl_lines="27"
-{!../../../docs_src/body_multiple_params/tutorial004.py!}
-```
+{* ../../docs_src/body_multiple_params/tutorial004.py hl[27] *}
이렇게:
@@ -120,7 +112,7 @@ FastAPI는 요청을 자동으로 변환해, 매개변수의 `item`과 `user`를
q: Optional[str] = None
```
-/// info | "정보"
+/// info | 정보
`Body` 또한 `Query`, `Path` 그리고 이후에 볼 다른 것들처럼 동일한 추가 검증과 메타데이터 매개변수를 갖고 있습니다.
@@ -134,9 +126,7 @@ Pydantic 모델 `Item`의 `item`을 본문 매개변수로 오직 한개만 갖
하지만, 만약 모델 내용에 `item `키를 가진 JSON으로 예측하길 원한다면, 추가적인 본문 매개변수를 선언한 것처럼 `Body`의 특별한 매개변수인 `embed`를 사용할 수 있습니다:
-```Python hl_lines="17"
-{!../../../docs_src/body_multiple_params/tutorial005.py!}
-```
+{* ../../docs_src/body_multiple_params/tutorial005.py hl[17] *}
아래 처럼:
diff --git a/docs/ko/docs/tutorial/body-nested-models.md b/docs/ko/docs/tutorial/body-nested-models.md
index 4d785f64b1..ebd7b3ba6a 100644
--- a/docs/ko/docs/tutorial/body-nested-models.md
+++ b/docs/ko/docs/tutorial/body-nested-models.md
@@ -5,9 +5,7 @@
어트리뷰트를 서브타입으로 정의할 수 있습니다. 예를 들어 파이썬 `list`는:
-```Python hl_lines="14"
-{!../../../docs_src/body_nested_models/tutorial001.py!}
-```
+{* ../../docs_src/body_nested_models/tutorial001.py hl[14] *}
이는 `tags`를 항목 리스트로 만듭니다. 각 항목의 타입을 선언하지 않더라도요.
@@ -19,9 +17,7 @@
먼저, 파이썬 표준 `typing` 모듈에서 `List`를 임포트합니다:
-```Python hl_lines="1"
-{!../../../docs_src/body_nested_models/tutorial002.py!}
-```
+{* ../../docs_src/body_nested_models/tutorial002.py hl[1] *}
### 타입 매개변수로 `List` 선언
@@ -42,9 +38,7 @@ my_list: List[str]
마찬가지로 예제에서 `tags`를 구체적으로 "문자열의 리스트"로 만들 수 있습니다:
-```Python hl_lines="14"
-{!../../../docs_src/body_nested_models/tutorial002.py!}
-```
+{* ../../docs_src/body_nested_models/tutorial002.py hl[14] *}
## 집합 타입
@@ -54,9 +48,7 @@ my_list: List[str]
그렇다면 `Set`을 임포트 하고 `tags`를 `str`의 `set`으로 선언할 수 있습니다:
-```Python hl_lines="1 14"
-{!../../../docs_src/body_nested_models/tutorial003.py!}
-```
+{* ../../docs_src/body_nested_models/tutorial003.py hl[1,14] *}
덕분에 중복 데이터가 있는 요청을 수신하더라도 고유한 항목들의 집합으로 변환됩니다.
@@ -78,17 +70,13 @@ Pydantic 모델의 각 어트리뷰트는 타입을 갖습니다.
예를 들어, `Image` 모델을 선언할 수 있습니다:
-```Python hl_lines="9-11"
-{!../../../docs_src/body_nested_models/tutorial004.py!}
-```
+{* ../../docs_src/body_nested_models/tutorial004.py hl[9:11] *}
### 서브모듈을 타입으로 사용
그리고 어트리뷰트의 타입으로 사용할 수 있습니다:
-```Python hl_lines="20"
-{!../../../docs_src/body_nested_models/tutorial004.py!}
-```
+{* ../../docs_src/body_nested_models/tutorial004.py hl[20] *}
이는 **FastAPI**가 다음과 유사한 본문을 기대한다는 것을 의미합니다:
@@ -121,9 +109,7 @@ Pydantic 모델의 각 어트리뷰트는 타입을 갖습니다.
예를 들어 `Image` 모델 안에 `url` 필드를 `str` 대신 Pydantic의 `HttpUrl`로 선언할 수 있습니다:
-```Python hl_lines="4 10"
-{!../../../docs_src/body_nested_models/tutorial005.py!}
-```
+{* ../../docs_src/body_nested_models/tutorial005.py hl[4,10] *}
이 문자열이 유효한 URL인지 검사하고 JSON 스키마/OpenAPI로 문서화 됩니다.
@@ -131,9 +117,7 @@ Pydantic 모델의 각 어트리뷰트는 타입을 갖습니다.
`list`, `set` 등의 서브타입으로 Pydantic 모델을 사용할 수도 있습니다:
-```Python hl_lines="20"
-{!../../../docs_src/body_nested_models/tutorial006.py!}
-```
+{* ../../docs_src/body_nested_models/tutorial006.py hl[20] *}
아래와 같은 JSON 본문으로 예상(변환, 검증, 문서화 등을)합니다:
@@ -161,7 +145,7 @@ Pydantic 모델의 각 어트리뷰트는 타입을 갖습니다.
}
```
-/// info | "정보"
+/// info | 정보
`images` 키가 어떻게 이미지 객체 리스트를 갖는지 주목하세요.
@@ -171,11 +155,9 @@ Pydantic 모델의 각 어트리뷰트는 타입을 갖습니다.
단독으로 깊게 중첩된 모델을 정의할 수 있습니다:
-```Python hl_lines="9 14 20 23 27"
-{!../../../docs_src/body_nested_models/tutorial007.py!}
-```
+{* ../../docs_src/body_nested_models/tutorial007.py hl[9,14,20,23,27] *}
-/// info | "정보"
+/// info | 정보
`Offer`가 선택사항 `Image` 리스트를 차례로 갖는 `Item` 리스트를 어떻게 가지고 있는지 주목하세요
@@ -191,9 +173,7 @@ images: List[Image]
이를 아래처럼:
-```Python hl_lines="15"
-{!../../../docs_src/body_nested_models/tutorial008.py!}
-```
+{* ../../docs_src/body_nested_models/tutorial008.py hl[15] *}
## 어디서나 편집기 지원
@@ -223,11 +203,9 @@ Pydantic 모델 대신에 `dict`를 직접 사용하여 작업할 경우, 이러
이 경우, `float` 값을 가진 `int` 키가 있는 모든 `dict`를 받아들입니다:
-```Python hl_lines="15"
-{!../../../docs_src/body_nested_models/tutorial009.py!}
-```
+{* ../../docs_src/body_nested_models/tutorial009.py hl[15] *}
-/// tip | "팁"
+/// tip | 팁
JSON은 오직 `str`형 키만 지원한다는 것을 염두에 두세요.
diff --git a/docs/ko/docs/tutorial/body.md b/docs/ko/docs/tutorial/body.md
index 337218eb46..b3914fa4b4 100644
--- a/docs/ko/docs/tutorial/body.md
+++ b/docs/ko/docs/tutorial/body.md
@@ -8,7 +8,7 @@
**요청** 본문을 선언하기 위해서 모든 강력함과 이점을 갖춘 Pydantic 모델을 사용합니다.
-/// info | "정보"
+/// info | 정보
데이터를 보내기 위해, (좀 더 보편적인) `POST`, `PUT`, `DELETE` 혹은 `PATCH` 중에 하나를 사용하는 것이 좋습니다.
@@ -22,21 +22,7 @@
먼저 `pydantic`에서 `BaseModel`를 임포트해야 합니다:
-//// tab | Python 3.10+
-
-```Python hl_lines="2"
-{!> ../../../docs_src/body/tutorial001_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="4"
-{!> ../../../docs_src/body/tutorial001.py!}
-```
-
-////
+{* ../../docs_src/body/tutorial001_py310.py hl[2] *}
## 여러분의 데이터 모델 만들기
@@ -44,21 +30,7 @@
모든 어트리뷰트에 대해 표준 파이썬 타입을 사용합니다:
-//// tab | Python 3.10+
-
-```Python hl_lines="5-9"
-{!> ../../../docs_src/body/tutorial001_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="7-11"
-{!> ../../../docs_src/body/tutorial001.py!}
-```
-
-////
+{* ../../docs_src/body/tutorial001_py310.py hl[5:9] *}
쿼리 매개변수를 선언할 때와 같이, 모델 어트리뷰트가 기본 값을 가지고 있어도 이는 필수가 아닙니다. 그외에는 필수입니다. 그저 `None`을 사용하여 선택적으로 만들 수 있습니다.
@@ -86,21 +58,7 @@
여러분의 *경로 작동*에 추가하기 위해, 경로 매개변수 그리고 쿼리 매개변수에서 선언했던 것과 같은 방식으로 선언하면 됩니다.
-//// tab | Python 3.10+
-
-```Python hl_lines="16"
-{!> ../../../docs_src/body/tutorial001_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="18"
-{!> ../../../docs_src/body/tutorial001.py!}
-```
-
-////
+{* ../../docs_src/body/tutorial001_py310.py hl[16] *}
...그리고 만들어낸 모델인 `Item`으로 타입을 선언합니다.
@@ -149,7 +107,7 @@
-/// tip | "팁"
+/// tip | 팁
만약 PyCharm를 편집기로 사용한다면, Pydantic PyCharm Plugin을 사용할 수 있습니다.
@@ -167,21 +125,7 @@
함수 안에서 모델 객체의 모든 어트리뷰트에 직접 접근 가능합니다:
-//// tab | Python 3.10+
-
-```Python hl_lines="19"
-{!> ../../../docs_src/body/tutorial002_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="21"
-{!> ../../../docs_src/body/tutorial002.py!}
-```
-
-////
+{* ../../docs_src/body/tutorial002_py310.py hl[19] *}
## 요청 본문 + 경로 매개변수
@@ -189,21 +133,7 @@
**FastAPI**는 경로 매개변수와 일치하는 함수 매개변수가 **경로에서 가져와야 한다**는 것을 인지하며, Pydantic 모델로 선언된 그 함수 매개변수는 **요청 본문에서 가져와야 한다**는 것을 인지할 것입니다.
-//// tab | Python 3.10+
-
-```Python hl_lines="15-16"
-{!> ../../../docs_src/body/tutorial003_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="17-18"
-{!> ../../../docs_src/body/tutorial003.py!}
-```
-
-////
+{* ../../docs_src/body/tutorial003_py310.py hl[15:16] *}
## 요청 본문 + 경로 + 쿼리 매개변수
@@ -211,21 +141,7 @@
**FastAPI**는 각각을 인지하고 데이터를 옳바른 위치에 가져올 것입니다.
-//// tab | Python 3.10+
-
-```Python hl_lines="16"
-{!> ../../../docs_src/body/tutorial004_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="18"
-{!> ../../../docs_src/body/tutorial004.py!}
-```
-
-////
+{* ../../docs_src/body/tutorial004_py310.py hl[16] *}
함수 매개변수는 다음을 따라서 인지하게 됩니다:
@@ -233,7 +149,7 @@
* 만약 매개변수가 (`int`, `float`, `str`, `bool` 등과 같은) **유일한 타입**으로 되어있으면, **쿼리** 매개변수로 해석될 것입니다.
* 만약 매개변수가 **Pydantic 모델** 타입으로 선언되어 있으면, 요청 **본문**으로 해석될 것입니다.
-/// note | "참고"
+/// note | 참고
FastAPI는 `q`의 값이 필요없음을 알게 될 것입니다. 기본 값이 `= None`이기 때문입니다.
diff --git a/docs/ko/docs/tutorial/cookie-param-models.md b/docs/ko/docs/tutorial/cookie-param-models.md
new file mode 100644
index 0000000000..e7eef0b1de
--- /dev/null
+++ b/docs/ko/docs/tutorial/cookie-param-models.md
@@ -0,0 +1,76 @@
+# 쿠키 매개변수 모델
+
+관련있는 **쿠키**들의 그룹이 있는 경우, **Pydantic 모델**을 생성하여 선언할 수 있습니다. 🍪
+
+이를 통해 **여러 위치**에서 **모델을 재사용** 할 수 있고 모든 매개변수에 대한 유효성 검사 및 메타데이터를 한 번에 선언할 수도 있습니다. 😍
+
+/// note | 참고
+
+이 기능은 FastAPI 버전 `0.115.0` 이후부터 지원됩니다. 🤓
+
+///
+
+/// tip | 팁
+
+동일한 기술이 `Query`, `Cookie`, 그리고 `Header`에 적용됩니다. 😎
+
+///
+
+## Pydantic 모델을 사용한 쿠키
+
+**Pydantic 모델**에 필요한 **쿠키** 매개변수를 선언한 다음, 해당 매개변수를 `Cookie`로 선언합니다:
+
+{* ../../docs_src/cookie_param_models/tutorial001_an_py310.py hl[9:12,16] *}
+
+**FastAPI**는 요청에서 받은 **쿠키**에서 **각 필드**에 대한 데이터를 **추출**하고 정의한 Pydantic 모델을 줍니다.
+
+## 문서 확인하기
+
+문서 UI `/docs`에서 정의한 쿠키를 볼 수 있습니다:
+
+
+get 작동 사용
-/// info | "`@decorator` 정보"
+/// info | `@decorator` 정보
이 `@something` 문법은 파이썬에서 "데코레이터"라 부릅니다.
@@ -286,7 +276,7 @@ API를 설계할 때 일반적으로 특정 행동을 수행하기 위해 특정
* `@app.patch()`
* `@app.trace()`
-/// tip | "팁"
+/// tip | 팁
각 작동(HTTP 메소드)을 원하는 대로 사용해도 됩니다.
@@ -306,9 +296,7 @@ API를 설계할 때 일반적으로 특정 행동을 수행하기 위해 특정
* **작동**: 은 `get`입니다.
* **함수**: 는 "데코레이터" 아래에 있는 함수입니다 (`@app.get("/")` 아래).
-```Python hl_lines="7"
-{!../../../docs_src/first_steps/tutorial001.py!}
-```
+{* ../../docs_src/first_steps/tutorial001.py hl[7] *}
이것은 파이썬 함수입니다.
@@ -320,11 +308,9 @@ URL "`/`"에 대한 `GET` 작동을 사용하는 요청을 받을 때마다 **Fa
`async def`을 이용하는 대신 일반 함수로 정의할 수 있습니다:
-```Python hl_lines="7"
-{!../../../docs_src/first_steps/tutorial003.py!}
-```
+{* ../../docs_src/first_steps/tutorial003.py hl[7] *}
-/// note | "참고"
+/// note | 참고
차이점을 모르겠다면 [Async: *"바쁘신 경우"*](../async.md#_1){.internal-link target=_blank}을 확인하세요.
@@ -332,9 +318,7 @@ URL "`/`"에 대한 `GET` 작동을 사용하는 요청을 받을 때마다 **Fa
### 5 단계: 콘텐츠 반환
-```Python hl_lines="8"
-{!../../../docs_src/first_steps/tutorial001.py!}
-```
+{* ../../docs_src/first_steps/tutorial001.py hl[8] *}
`dict`, `list`, 단일값을 가진 `str`, `int` 등을 반환할 수 있습니다.
diff --git a/docs/ko/docs/tutorial/header-param-models.md b/docs/ko/docs/tutorial/header-param-models.md
new file mode 100644
index 0000000000..bab7291e3c
--- /dev/null
+++ b/docs/ko/docs/tutorial/header-param-models.md
@@ -0,0 +1,56 @@
+# 헤더 매개변수 모델
+
+관련 있는 **헤더 매개변수** 그룹이 있는 경우, **Pydantic 모델**을 생성하여 선언할 수 있습니다.
+
+이를 통해 **여러 위치**에서 **모델을 재사용** 할 수 있고 모든 매개변수에 대한 유효성 검사 및 메타데이터를 한 번에 선언할 수도 있습니다. 😎
+
+/// note | 참고
+
+이 기능은 FastAPI 버전 `0.115.0` 이후부터 지원됩니다. 🤓
+
+///
+
+## Pydantic 모델을 사용한 헤더 매개변수
+
+**Pydantic 모델**에 필요한 **헤더 매개변수**를 선언한 다음, 해당 매개변수를 `Header`로 선언합니다:
+
+{* ../../docs_src/header_param_models/tutorial001_an_py310.py hl[9:14,18] *}
+
+**FastAPI**는 요청에서 받은 **헤더**에서 **각 필드**에 대한 데이터를 **추출**하고 정의한 Pydantic 모델을 줍니다.
+
+## 문서 확인하기
+
+문서 UI `/docs`에서 필요한 헤더를 볼 수 있습니다:
+
+
+contact 필드| 매개변수 | 타입 | 설명 |
|---|---|---|
name | str | 연락처 인물/조직의 식별명입니다. |
url | str | 연락처 정보가 담긴 URL입니다. URL 형식이어야 합니다. |
email | str | 연락처 인물/조직의 이메일 주소입니다. 이메일 주소 형식이어야 합니다. |
license_info 필드| 매개변수 | 타입 | 설명 |
|---|---|---|
name | str | 필수 (license_info가 설정된 경우). API에 사용된 라이선스 이름입니다. |
identifier | str | API에 대한 SPDX 라이선스 표현입니다. identifier 필드는 url 필드와 상호 배타적입니다. OpenAPI 3.1.0, FastAPI 0.99.0부터 사용 가능 |
url | str | API에 사용된 라이선스의 URL입니다. URL 형식이어야 합니다. |
+
+## 라이선스 식별자
+
+OpenAPI 3.1.0 및 FastAPI 0.99.0부터 `license_info`에 `identifier`를 URL 대신 설정할 수 있습니다.
+
+예:
+
+{* ../../docs_src/metadata/tutorial001_1.py hl[31] *}
+
+## 태그에 대한 메타데이터
+
+`openapi_tags` 매개변수를 사용하여 경로 작동을 그룹화하는 데 사용되는 태그에 추가 메타데이터를 추가할 수 있습니다.
+
+리스트는 각 태그에 대해 하나의 딕셔너리를 포함해야 합니다.
+
+각 딕셔너리에는 다음이 포함될 수 있습니다:
+
+* `name` (**필수**): `tags` 매개변수에서 *경로 작동*과 `APIRouter`에 사용된 태그 이름과 동일한 `str`입니다.
+* `description`: 태그에 대한 간단한 설명을 담은 `str`입니다. 마크다운을 사용할 수 있으며 문서 UI에 표시됩니다.
+* `externalDocs`: 외부 문서를 설명하는 `dict`이며:
+ * `description`: 외부 문서에 대한 간단한 설명을 담은 `str`입니다.
+ * `url` (**필수**): 외부 문서의 URL을 담은 `str`입니다.
+
+### 태그에 대한 메타데이터 생성
+
+`users` 및 `items`에 대한 태그 예시와 함께 메타데이터를 생성하고 이를 `openapi_tags` 매개변수로 전달해 보겠습니다:
+
+{* ../../docs_src/metadata/tutorial004.py hl[3:16,18] *}
+
+설명 안에 마크다운을 사용할 수 있습니다. 예를 들어 "login"은 굵게(**login**) 표시되고, "fancy"는 기울임꼴(_fancy_)로 표시됩니다.
+
+/// tip
+
+사용 중인 모든 태그에 메타데이터를 추가할 필요는 없습니다.
+
+///
+
+### 태그 사용
+
+`tags` 매개변수를 *경로 작동* 및 `APIRouter`와 함께 사용하여 태그에 할당할 수 있습니다:
+
+{* ../../docs_src/metadata/tutorial004.py hl[21,26] *}
+
+/// info
+
+태그에 대한 자세한 내용은 [경로 작동 구성](path-operation-configuration.md#tags){.internal-link target=_blank}에서 읽어보세요.
+
+///
+
+### 문서 확인
+
+이제 문서를 확인하면 모든 추가 메타데이터가 표시됩니다:
+
+
+
+### 태그 순서
+
+각 태그 메타데이터 딕셔너리의 순서는 문서 UI에 표시되는 순서를 정의합니다.
+
+예를 들어, 알파벳 순서상 `users`는 `items` 뒤에 오지만, 우리는 `users` 메타데이터를 리스트의 첫 번째 딕셔너리로 추가했기 때문에 먼저 표시됩니다.
+
+## OpenAPI URL
+
+OpenAPI 구조는 기본적으로 `/openapi.json`에서 제공됩니다.
+
+`openapi_url` 매개변수를 통해 이를 설정할 수 있습니다.
+
+예를 들어, 이를 `/api/v1/openapi.json`에 제공하도록 설정하려면:
+
+{* ../../docs_src/metadata/tutorial002.py hl[3] *}
+
+OpenAPI 구조를 완전히 비활성화하려면 `openapi_url=None`으로 설정할 수 있으며, 이를 사용하여 문서화 사용자 인터페이스도 비활성화됩니다.
+
+## 문서화 URL
+
+포함된 두 가지 문서화 사용자 인터페이스를 설정할 수 있습니다:
+
+* **Swagger UI**: `/docs`에서 제공됩니다.
+ * `docs_url` 매개변수로 URL을 설정할 수 있습니다.
+ * `docs_url=None`으로 설정하여 비활성화할 수 있습니다.
+* **ReDoc**: `/redoc`에서 제공됩니다.
+ * `redoc_url` 매개변수로 URL을 설정할 수 있습니다.
+ * `redoc_url=None`으로 설정하여 비활성화할 수 있습니다.
+
+예를 들어, Swagger UI를 `/documentation`에서 제공하고 ReDoc을 비활성화하려면:
+
+{* ../../docs_src/metadata/tutorial003.py hl[3] *}
diff --git a/docs/ko/docs/tutorial/middleware.md b/docs/ko/docs/tutorial/middleware.md
index 84f67bd26f..e0daa3c99b 100644
--- a/docs/ko/docs/tutorial/middleware.md
+++ b/docs/ko/docs/tutorial/middleware.md
@@ -11,7 +11,7 @@
* **응답** 또는 다른 필요한 코드를 실행시키는 동작을 할 수 있습니다.
* **응답**를 반환합니다.
-/// note | "기술 세부사항"
+/// note | 기술 세부사항
만약 `yield`를 사용한 의존성을 가지고 있다면, 미들웨어가 실행되고 난 후에 exit이 실행됩니다.
@@ -31,19 +31,17 @@
* 그런 다음, *경로 작업*에 의해 생성된 `response` 를 반환합니다.
* `response`를 반환하기 전에 추가로 `response`를 수정할 수 있습니다.
-```Python hl_lines="8-9 11 14"
-{!../../../docs_src/middleware/tutorial001.py!}
-```
+{* ../../docs_src/middleware/tutorial001.py hl[8:9,11,14] *}
-/// tip | "팁"
+/// tip | 팁
사용자 정의 헤더는 'X-' 접두사를 사용하여 추가할 수 있습니다.
-그러나 만약 클라이언트의 브라우저에서 볼 수 있는 사용자 정의 헤더를 가지고 있다면, 그것들을 CORS 설정([CORS (Cross-Origin Resource Sharing)](cors.md){.internal-link target=_blank})에 Starlette CORS 문서에 명시된 `expose_headers` 매개변수를 이용하여 헤더들을 추가하여야합니다.
+그러나 만약 클라이언트의 브라우저에서 볼 수 있는 사용자 정의 헤더를 가지고 있다면, 그것들을 CORS 설정([CORS (Cross-Origin Resource Sharing)](cors.md){.internal-link target=_blank})에 Starlette CORS 문서에 명시된 `expose_headers` 매개변수를 이용하여 헤더들을 추가하여야합니다.
///
-/// note | "기술적 세부사항"
+/// note | 기술적 세부사항
`from starlette.requests import request`를 사용할 수도 있습니다.
@@ -59,9 +57,7 @@
예를 들어, 요청을 수행하고 응답을 생성하는데 까지 걸린 시간 값을 가지고 있는 `X-Process-Time` 같은 사용자 정의 헤더를 추가할 수 있습니다.
-```Python hl_lines="10 12-13"
-{!../../../docs_src/middleware/tutorial001.py!}
-```
+{* ../../docs_src/middleware/tutorial001.py hl[10,12:13] *}
## 다른 미들웨어
diff --git a/docs/ko/docs/tutorial/path-operation-configuration.md b/docs/ko/docs/tutorial/path-operation-configuration.md
index b6608a14d6..81914182af 100644
--- a/docs/ko/docs/tutorial/path-operation-configuration.md
+++ b/docs/ko/docs/tutorial/path-operation-configuration.md
@@ -2,7 +2,7 @@
*경로 작동 데코레이터*를 설정하기 위해서 전달할수 있는 몇 가지 매개변수가 있습니다.
-/// warning | "경고"
+/// warning | 경고
아래 매개변수들은 *경로 작동 함수*가 아닌 *경로 작동 데코레이터*에 직접 전달된다는 사실을 기억하십시오.
@@ -16,13 +16,11 @@
하지만 각 코드의 의미를 모른다면, `status`에 있는 단축 상수들을 사용할수 있습니다:
-```Python hl_lines="3 17"
-{!../../../docs_src/path_operation_configuration/tutorial001.py!}
-```
+{* ../../docs_src/path_operation_configuration/tutorial001.py hl[3,17] *}
각 상태 코드들은 응답에 사용되며, OpenAPI 스키마에 추가됩니다.
-/// note | "기술적 세부사항"
+/// note | 기술적 세부사항
다음과 같이 임포트하셔도 좋습니다. `from starlette import status`.
@@ -34,9 +32,7 @@
(보통 단일 `str`인) `str`로 구성된 `list`와 함께 매개변수 `tags`를 전달하여, `경로 작동`에 태그를 추가할 수 있습니다:
-```Python hl_lines="17 22 27"
-{!../../../docs_src/path_operation_configuration/tutorial002.py!}
-```
+{* ../../docs_src/path_operation_configuration/tutorial002.py hl[17,22,27] *}
전달된 태그들은 OpenAPI의 스키마에 추가되며, 자동 문서 인터페이스에서 사용됩니다:
@@ -46,9 +42,7 @@
`summary`와 `description`을 추가할 수 있습니다:
-```Python hl_lines="20-21"
-{!../../../docs_src/path_operation_configuration/tutorial003.py!}
-```
+{* ../../docs_src/path_operation_configuration/tutorial003.py hl[20:21] *}
## 독스트링으로 만든 기술
@@ -56,9 +50,7 @@
마크다운 문법으로 독스트링을 작성할 수 있습니다, 작성된 마크다운 형식의 독스트링은 (마크다운의 들여쓰기를 고려하여) 올바르게 화면에 출력됩니다.
-```Python hl_lines="19-27"
-{!../../../docs_src/path_operation_configuration/tutorial004.py!}
-```
+{* ../../docs_src/path_operation_configuration/tutorial004.py hl[19:27] *}
이는 대화형 문서에서 사용됩니다:
@@ -68,17 +60,15 @@
`response_description` 매개변수로 응답에 관한 설명을 명시할 수 있습니다:
-```Python hl_lines="21"
-{!../../../docs_src/path_operation_configuration/tutorial005.py!}
-```
+{* ../../docs_src/path_operation_configuration/tutorial005.py hl[21] *}
-/// info | "정보"
+/// info | 정보
`response_description`은 구체적으로 응답을 지칭하며, `description`은 일반적인 *경로 작동*을 지칭합니다.
///
-/// check | "확인"
+/// check | 확인
OpenAPI는 각 *경로 작동*이 응답에 관한 설명을 요구할 것을 명시합니다.
@@ -92,9 +82,7 @@ OpenAPI는 각 *경로 작동*이 응답에 관한 설명을 요구할 것을
단일 *경로 작동*을 없애지 않고 지원중단을 해야한다면, `deprecated` 매개변수를 전달하면 됩니다.
-```Python hl_lines="16"
-{!../../../docs_src/path_operation_configuration/tutorial006.py!}
-```
+{* ../../docs_src/path_operation_configuration/tutorial006.py hl[16] *}
대화형 문서에 지원중단이라고 표시됩니다.
diff --git a/docs/ko/docs/tutorial/path-params-numeric-validations.md b/docs/ko/docs/tutorial/path-params-numeric-validations.md
index 6d3215c245..f21c9290ed 100644
--- a/docs/ko/docs/tutorial/path-params-numeric-validations.md
+++ b/docs/ko/docs/tutorial/path-params-numeric-validations.md
@@ -6,9 +6,7 @@
먼저 `fastapi`에서 `Path`를 임포트합니다:
-```Python hl_lines="3"
-{!../../../docs_src/path_params_numeric_validations/tutorial001.py!}
-```
+{* ../../docs_src/path_params_numeric_validations/tutorial001.py hl[3] *}
## 메타데이터 선언
@@ -16,11 +14,9 @@
예를 들어, `title` 메타데이터 값을 경로 매개변수 `item_id`에 선언하려면 다음과 같이 입력할 수 있습니다:
-```Python hl_lines="10"
-{!../../../docs_src/path_params_numeric_validations/tutorial001.py!}
-```
+{* ../../docs_src/path_params_numeric_validations/tutorial001.py hl[10] *}
-/// note | "참고"
+/// note | 참고
경로 매개변수는 경로의 일부여야 하므로 언제나 필수적입니다.
@@ -46,9 +42,7 @@
따라서 함수를 다음과 같이 선언 할 수 있습니다:
-```Python hl_lines="7"
-{!../../../docs_src/path_params_numeric_validations/tutorial002.py!}
-```
+{* ../../docs_src/path_params_numeric_validations/tutorial002.py hl[7] *}
## 필요한 경우 매개변수 정렬하기, 트릭
@@ -58,9 +52,7 @@
파이썬은 `*`으로 아무런 행동도 하지 않지만, 따르는 매개변수들은 kwargs로도 알려진 키워드 인자(키-값 쌍)여야 함을 인지합니다. 기본값을 가지고 있지 않더라도 그렇습니다.
-```Python hl_lines="7"
-{!../../../docs_src/path_params_numeric_validations/tutorial003.py!}
-```
+{* ../../docs_src/path_params_numeric_validations/tutorial003.py hl[7] *}
## 숫자 검증: 크거나 같음
@@ -68,9 +60,7 @@
여기서 `ge=1`인 경우, `item_id`는 `1`보다 "크거나(`g`reater) 같은(`e`qual)" 정수형 숫자여야 합니다.
-```Python hl_lines="8"
-{!../../../docs_src/path_params_numeric_validations/tutorial004.py!}
-```
+{* ../../docs_src/path_params_numeric_validations/tutorial004.py hl[8] *}
## 숫자 검증: 크거나 같음 및 작거나 같음
@@ -79,9 +69,7 @@
* `gt`: 크거나(`g`reater `t`han)
* `le`: 작거나 같은(`l`ess than or `e`qual)
-```Python hl_lines="9"
-{!../../../docs_src/path_params_numeric_validations/tutorial005.py!}
-```
+{* ../../docs_src/path_params_numeric_validations/tutorial005.py hl[9] *}
## 숫자 검증: 부동소수, 크거나 및 작거나
@@ -93,9 +81,7 @@
lt 역시 마찬가지입니다.
-```Python hl_lines="11"
-{!../../../docs_src/path_params_numeric_validations/tutorial006.py!}
-```
+{* ../../docs_src/path_params_numeric_validations/tutorial006.py hl[11] *}
## 요약
@@ -108,7 +94,7 @@
* `lt`: 작거나(`l`ess `t`han)
* `le`: 작거나 같은(`l`ess than or `e`qual)
-/// info | "정보"
+/// info | 정보
`Query`, `Path`, 그리고 나중에게 보게될 것들은 (여러분이 사용할 필요가 없는) 공통 `Param` 클래스의 서브 클래스입니다.
@@ -116,7 +102,7 @@
///
-/// note | "기술 세부사항"
+/// note | 기술 세부사항
`fastapi`에서 `Query`, `Path` 등을 임포트 할 때, 이것들은 실제로 함수입니다.
diff --git a/docs/ko/docs/tutorial/path-params.md b/docs/ko/docs/tutorial/path-params.md
index 67a2d899d4..b72787e0b2 100644
--- a/docs/ko/docs/tutorial/path-params.md
+++ b/docs/ko/docs/tutorial/path-params.md
@@ -2,9 +2,7 @@
파이썬의 포맷 문자열 리터럴에서 사용되는 문법을 이용하여 경로 "매개변수" 또는 "변수"를 선언할 수 있습니다:
-```Python hl_lines="6-7"
-{!../../../docs_src/path_params/tutorial001.py!}
-```
+{* ../../docs_src/path_params/tutorial001.py hl[6:7] *}
경로 매개변수 `item_id`의 값은 함수의 `item_id` 인자로 전달됩니다.
@@ -18,13 +16,11 @@
파이썬 표준 타입 어노테이션을 사용하여 함수에 있는 경로 매개변수의 타입을 선언할 수 있습니다:
-```Python hl_lines="7"
-{!../../../docs_src/path_params/tutorial002.py!}
-```
+{* ../../docs_src/path_params/tutorial002.py hl[7] *}
위의 예시에서, `item_id`는 `int`로 선언되었습니다.
-/// check | "확인"
+/// check | 확인
이 기능은 함수 내에서 오류 검사, 자동완성 등의 편집기 기능을 활용할 수 있게 해줍니다.
@@ -38,7 +34,7 @@
{"item_id":3}
```
-/// check | "확인"
+/// check | 확인
함수가 받은(반환도 하는) 값은 문자열 `"3"`이 아니라 파이썬 `int` 형인 `3`입니다.
@@ -69,7 +65,7 @@
`int`가 아닌 `float`을 전달하는 경우에도 동일한 오류가 나타납니다: http://127.0.0.1:8000/items/4.2
-/// check | "확인"
+/// check | 확인
즉, 파이썬 타입 선언을 하면 **FastAPI**는 데이터 검증을 합니다.
@@ -85,7 +81,7 @@
-/// check | "확인"
+/// check | 확인
그저 파이썬 타입 선언을 하기만 하면 **FastAPI**는 자동 대화형 API 문서(Swagger UI)를 제공합니다.
@@ -121,9 +117,7 @@
*경로 작동*은 순차적으로 실행되기 때문에 `/users/{user_id}` 이전에 `/users/me`를 먼저 선언해야 합니다:
-```Python hl_lines="6 11"
-{!../../../docs_src/path_params/tutorial003.py!}
-```
+{* ../../docs_src/path_params/tutorial003.py hl[6,11] *}
그렇지 않으면 `/users/{user_id}`는 `/users/me` 요청 또한 매개변수 `user_id`의 값이 `"me"`인 것으로 "생각하게" 됩니다.
@@ -139,17 +133,15 @@
가능한 값들에 해당하는 고정된 값의 클래스 어트리뷰트들을 만듭니다:
-```Python hl_lines="1 6-9"
-{!../../../docs_src/path_params/tutorial005.py!}
-```
+{* ../../docs_src/path_params/tutorial005.py hl[1,6:9] *}
-/// info | "정보"
+/// info | 정보
열거형(또는 enums)은 파이썬 버전 3.4 이후로 사용 가능합니다.
///
-/// tip | "팁"
+/// tip | 팁
혹시 궁금하다면, "AlexNet", "ResNet", 그리고 "LeNet"은 그저 기계 학습 모델들의 이름입니다.
@@ -159,9 +151,7 @@
생성한 열거형 클래스(`ModelName`)를 사용하는 타입 어노테이션으로 *경로 매개변수*를 만듭니다:
-```Python hl_lines="16"
-{!../../../docs_src/path_params/tutorial005.py!}
-```
+{* ../../docs_src/path_params/tutorial005.py hl[16] *}
### 문서 확인
@@ -177,19 +167,15 @@
열거형 `ModelName`의 *열거형 멤버*를 비교할 수 있습니다:
-```Python hl_lines="17"
-{!../../../docs_src/path_params/tutorial005.py!}
-```
+{* ../../docs_src/path_params/tutorial005.py hl[17] *}
#### *열거형 값* 가져오기
`model_name.value` 또는 일반적으로 `your_enum_member.value`를 이용하여 실제 값(위 예시의 경우 `str`)을 가져올 수 있습니다:
-```Python hl_lines="20"
-{!../../../docs_src/path_params/tutorial005.py!}
-```
+{* ../../docs_src/path_params/tutorial005.py hl[20] *}
-/// tip | "팁"
+/// tip | 팁
`ModelName.lenet.value`로도 값 `"lenet"`에 접근할 수 있습니다.
@@ -201,9 +187,7 @@
클라이언트에 반환하기 전에 해당 값(이 경우 문자열)으로 변환됩니다:
-```Python hl_lines="18 21 23"
-{!../../../docs_src/path_params/tutorial005.py!}
-```
+{* ../../docs_src/path_params/tutorial005.py hl[18,21,23] *}
클라이언트는 아래의 JSON 응답을 얻습니다:
@@ -242,11 +226,9 @@ Starlette의 옵션을 직접 이용하여 다음과 같은 URL을 사용함으
따라서 다음과 같이 사용할 수 있습니다:
-```Python hl_lines="6"
-{!../../../docs_src/path_params/tutorial004.py!}
-```
+{* ../../docs_src/path_params/tutorial004.py hl[6] *}
-/// tip | "팁"
+/// tip | 팁
매개변수가 가져야 하는 값이 `/home/johndoe/myfile.txt`와 같이 슬래시로 시작(`/`)해야 할 수 있습니다.
diff --git a/docs/ko/docs/tutorial/query-param-models.md b/docs/ko/docs/tutorial/query-param-models.md
new file mode 100644
index 0000000000..2ca65a3319
--- /dev/null
+++ b/docs/ko/docs/tutorial/query-param-models.md
@@ -0,0 +1,68 @@
+# 쿼리 매개변수 모델
+
+연관된 쿼리 **매개변수** 그룹이 있다면 **Pydantic 모델** 을 사용해 선언할 수 있습니다.
+
+이렇게 하면 **여러 곳**에서 **모델을 재사용**할 수 있을 뿐만 아니라, 매개변수에 대한 검증 및 메타데이터도 한 번에 선언할 수 있습니다. 😎
+
+/// note | 참고
+
+이 기능은 FastAPI 버전 `0.115.0`부터 제공됩니다. 🤓
+
+///
+
+## 쿼리 매개변수와 Pydantic 모델
+
+필요한 **쿼리 매개변수**를 **Pydantic 모델** 안에 선언한 다음, 모델을 `Query`로 선언합니다.
+
+{* ../../docs_src/query_param_models/tutorial001_an_py310.py hl[9:13,17] *}
+
+**FastAPI**는 요청의 **쿼리 매개변수**에서 **각 필드**의 데이터를 **추출**해 정의한 Pydantic 모델로 제공합니다.
+
+## 문서 확인하기
+
+`/docs` 경로의 API 문서에서 매개변수를 확인할 수 있습니다.
+
+
+
+POST에 대한 MDN 웹 문서를 참조하세요.
+
+///
+
+/// warning | 경고
+
+*경로 작업*에서 여러 `Form` 매개변수를 선언할 수 있지만, JSON으로 수신할 것으로 예상되는 `Body` 필드와 함께 선언할 수 없습니다. 요청 본문은 `application/json` 대신에 `application/x-www-form-urlencoded`를 사용하여 인코딩되기 때문입니다.
+
+이는 **FastAPI**의 제한 사항이 아니며 HTTP 프로토콜의 일부입니다.
+
+///
+
+## 요약
+
+폼 데이터 입력 매개변수를 선언하려면 `Form`을 사용하세요.
diff --git a/docs/ko/docs/tutorial/response-model.md b/docs/ko/docs/tutorial/response-model.md
index fc74a60b3b..a71d649f90 100644
--- a/docs/ko/docs/tutorial/response-model.md
+++ b/docs/ko/docs/tutorial/response-model.md
@@ -8,11 +8,9 @@
* `@app.delete()`
* 기타.
-```Python hl_lines="17"
-{!../../../docs_src/response_model/tutorial001.py!}
-```
+{* ../../docs_src/response_model/tutorial001.py hl[17] *}
-/// note | "참고"
+/// note | 참고
`response_model`은 "데코레이터" 메소드(`get`, `post`, 등)의 매개변수입니다. 모든 매개변수들과 본문(body)처럼 *경로 작동 함수*가 아닙니다.
@@ -31,7 +29,7 @@ FastAPI는 이 `response_model`를 사용하여:
* 해당 모델의 출력 데이터 제한. 이것이 얼마나 중요한지 아래에서 볼 것입니다.
-/// note | "기술 세부사항"
+/// note | 기술 세부사항
응답 모델은 함수의 타입 어노테이션 대신 이 매개변수로 선언하는데, 경로 함수가 실제 응답 모델을 반환하지 않고 `dict`, 데이터베이스 객체나 기타 다른 모델을 `response_model`을 사용하여 필드 제한과 직렬화를 수행하고 반환할 수 있기 때문입니다
@@ -41,15 +39,11 @@ FastAPI는 이 `response_model`를 사용하여:
여기서 우리는 평문 비밀번호를 포함하는 `UserIn` 모델을 선언합니다:
-```Python hl_lines="9 11"
-{!../../../docs_src/response_model/tutorial002.py!}
-```
+{* ../../docs_src/response_model/tutorial002.py hl[9,11] *}
그리고 이 모델을 사용하여 입력을 선언하고 같은 모델로 출력을 선언합니다:
-```Python hl_lines="17-18"
-{!../../../docs_src/response_model/tutorial002.py!}
-```
+{* ../../docs_src/response_model/tutorial002.py hl[17:18] *}
이제 브라우저가 비밀번호로 사용자를 만들 때마다 API는 응답으로 동일한 비밀번호를 반환합니다.
@@ -57,7 +51,7 @@ FastAPI는 이 `response_model`를 사용하여:
그러나 동일한 모델을 다른 *경로 작동*에서 사용할 경우, 모든 클라이언트에게 사용자의 비밀번호를 발신할 수 있습니다.
-/// danger | "위험"
+/// danger | 위험
절대로 사용자의 평문 비밀번호를 저장하거나 응답으로 발신하지 마십시오.
@@ -67,21 +61,15 @@ FastAPI는 이 `response_model`를 사용하여:
대신 평문 비밀번호로 입력 모델을 만들고 해당 비밀번호 없이 출력 모델을 만들 수 있습니다:
-```Python hl_lines="9 11 16"
-{!../../../docs_src/response_model/tutorial003.py!}
-```
+{* ../../docs_src/response_model/tutorial003.py hl[9,11,16] *}
여기서 *경로 작동 함수*가 비밀번호를 포함하는 동일한 입력 사용자를 반환할지라도:
-```Python hl_lines="24"
-{!../../../docs_src/response_model/tutorial003.py!}
-```
+{* ../../docs_src/response_model/tutorial003.py hl[24] *}
...`response_model`을 `UserOut` 모델로 선언했기 때문에 비밀번호를 포함하지 않습니다:
-```Python hl_lines="22"
-{!../../../docs_src/response_model/tutorial003.py!}
-```
+{* ../../docs_src/response_model/tutorial003.py hl[22] *}
따라서 **FastAPI**는 출력 모델에서 선언하지 않은 모든 데이터를 (Pydantic을 사용하여) 필터링합니다.
@@ -99,9 +87,7 @@ FastAPI는 이 `response_model`를 사용하여:
응답 모델은 아래와 같이 기본값을 가질 수 있습니다:
-```Python hl_lines="11 13-14"
-{!../../../docs_src/response_model/tutorial004.py!}
-```
+{* ../../docs_src/response_model/tutorial004.py hl[11,13:14] *}
* `description: Optional[str] = None`은 기본값으로 `None`을 갖습니다.
* `tax: float = 10.5`는 기본값으로 `10.5`를 갖습니다.
@@ -115,9 +101,7 @@ FastAPI는 이 `response_model`를 사용하여:
*경로 작동 데코레이터* 매개변수를 `response_model_exclude_unset=True`로 설정 할 수 있습니다:
-```Python hl_lines="24"
-{!../../../docs_src/response_model/tutorial004.py!}
-```
+{* ../../docs_src/response_model/tutorial004.py hl[24] *}
이러한 기본값은 응답에 포함되지 않고 실제로 설정된 값만 포함됩니다.
@@ -130,13 +114,13 @@ FastAPI는 이 `response_model`를 사용하여:
}
```
-/// info | "정보"
+/// info | 정보
FastAPI는 이를 위해 Pydantic 모델의 `.dict()`의 `exclude_unset` 매개변수를 사용합니다.
///
-/// info | "정보"
+/// info | 정보
아래 또한 사용할 수 있습니다:
@@ -181,7 +165,7 @@ ID가 `baz`인 항목(items)처럼 기본값과 동일한 값을 갖는다면:
따라서 JSON 스키마에 포함됩니다.
-/// tip | "팁"
+/// tip | 팁
`None` 뿐만 아니라 다른 어떤 것도 기본값이 될 수 있습니다.
@@ -197,7 +181,7 @@ ID가 `baz`인 항목(items)처럼 기본값과 동일한 값을 갖는다면:
Pydantic 모델이 하나만 있고 출력에서 일부 데이터를 제거하려는 경우 빠른 지름길로 사용할 수 있습니다.
-/// tip | "팁"
+/// tip | 팁
하지만 이러한 매개변수 대신 여러 클래스를 사용하여 위 아이디어를 사용하는 것을 추천합니다.
@@ -207,11 +191,9 @@ Pydantic 모델이 하나만 있고 출력에서 일부 데이터를 제
///
-```Python hl_lines="31 37"
-{!../../../docs_src/response_model/tutorial005.py!}
-```
+{* ../../docs_src/response_model/tutorial005.py hl[31,37] *}
-/// tip | "팁"
+/// tip | 팁
문법 `{"name", "description"}`은 두 값을 갖는 `set`을 만듭니다.
@@ -223,9 +205,7 @@ Pydantic 모델이 하나만 있고 출력에서 일부 데이터를 제
`list` 또는 `tuple` 대신 `set`을 사용하는 법을 잊었더라도, FastAPI는 `set`으로 변환하고 정상적으로 작동합니다:
-```Python hl_lines="31 37"
-{!../../../docs_src/response_model/tutorial006.py!}
-```
+{* ../../docs_src/response_model/tutorial006.py hl[31,37] *}
## 요약
diff --git a/docs/ko/docs/tutorial/response-status-code.md b/docs/ko/docs/tutorial/response-status-code.md
index 48cb49cbfe..bcaf7843b9 100644
--- a/docs/ko/docs/tutorial/response-status-code.md
+++ b/docs/ko/docs/tutorial/response-status-code.md
@@ -8,11 +8,9 @@
* `@app.delete()`
* 기타
-```Python hl_lines="6"
-{!../../../docs_src/response_status_code/tutorial001.py!}
-```
+{* ../../docs_src/response_status_code/tutorial001.py hl[6] *}
-/// note | "참고"
+/// note | 참고
`status_code` 는 "데코레이터" 메소드(`get`, `post` 등)의 매개변수입니다. 모든 매개변수들과 본문처럼 *경로 작동 함수*가 아닙니다.
@@ -20,7 +18,7 @@
`status_code` 매개변수는 HTTP 상태 코드를 숫자로 입력받습니다.
-/// info | "정보"
+/// info | 정보
`status_code` 는 파이썬의 `http.HTTPStatus` 와 같은 `IntEnum` 을 입력받을 수도 있습니다.
@@ -33,7 +31,7 @@
-/// note | "참고"
+/// note | 참고
어떤 응답 코드들은 해당 응답에 본문이 없다는 것을 의미하기도 합니다 (다음 항목 참고).
@@ -43,7 +41,7 @@
## HTTP 상태 코드에 대하여
-/// note | "참고"
+/// note | 참고
만약 HTTP 상태 코드에 대하여 이미 알고있다면, 다음 항목으로 넘어가십시오.
@@ -66,7 +64,7 @@ HTTP는 세자리의 숫자 상태 코드를 응답의 일부로 전송합니다
* 일반적인 클라이언트 오류의 경우 `400` 을 사용할 수 있습니다.
* `5xx` 상태 코드는 서버 오류에 사용됩니다. 이것들을 직접 사용할 일은 거의 없습니다. 응용 프로그램 코드나 서버의 일부에서 문제가 발생하면 자동으로 이들 상태 코드 중 하나를 반환합니다.
-/// tip | "팁"
+/// tip | 팁
각각의 상태 코드와 이들이 의미하는 내용에 대해 더 알고싶다면 MDN HTTP 상태 코드에 관한 문서 를 확인하십시오.
@@ -76,9 +74,7 @@ HTTP는 세자리의 숫자 상태 코드를 응답의 일부로 전송합니다
상기 예시 참고:
-```Python hl_lines="6"
-{!../../../docs_src/response_status_code/tutorial001.py!}
-```
+{* ../../docs_src/response_status_code/tutorial001.py hl[6] *}
`201` 은 "생성됨"를 의미하는 상태 코드입니다.
@@ -86,15 +82,13 @@ HTTP는 세자리의 숫자 상태 코드를 응답의 일부로 전송합니다
`fastapi.status` 의 편의 변수를 사용할 수 있습니다.
-```Python hl_lines="1 6"
-{!../../../docs_src/response_status_code/tutorial002.py!}
-```
+{* ../../docs_src/response_status_code/tutorial002.py hl[1,6] *}
이것은 단순히 작업을 편리하게 하기 위한 것으로, HTTP 상태 코드와 동일한 번호를 갖고있지만, 이를 사용하면 편집기의 자동완성 기능을 사용할 수 있습니다:
-/// note | "기술적 세부사항"
+/// note | 기술적 세부사항
`from starlette import status` 역시 사용할 수 있습니다.
diff --git a/docs/ko/docs/tutorial/schema-extra-example.md b/docs/ko/docs/tutorial/schema-extra-example.md
index 7b5ccdd324..77e94db720 100644
--- a/docs/ko/docs/tutorial/schema-extra-example.md
+++ b/docs/ko/docs/tutorial/schema-extra-example.md
@@ -8,35 +8,15 @@
생성된 JSON 스키마에 추가될 Pydantic 모델을 위한 `examples`을 선언할 수 있습니다.
-//// tab | Python 3.10+ Pydantic v2
+//// tab | Pydantic v2
-```Python hl_lines="13-24"
-{!> ../../../docs_src/schema_extra_example/tutorial001_py310.py!}
-```
+{* ../../docs_src/schema_extra_example/tutorial001_py310.py hl[13:24] *}
////
-//// tab | Python 3.10+ Pydantic v1
+//// tab | Pydantic v1
-```Python hl_lines="13-23"
-{!> ../../../docs_src/schema_extra_example/tutorial001_py310_pv1.py!}
-```
-
-////
-
-//// tab | Python 3.8+ Pydantic v2
-
-```Python hl_lines="15-26"
-{!> ../../../docs_src/schema_extra_example/tutorial001.py!}
-```
-
-////
-
-//// tab | Python 3.8+ Pydantic v1
-
-```Python hl_lines="15-25"
-{!> ../../../docs_src/schema_extra_example/tutorial001_pv1.py!}
-```
+{* ../../docs_src/schema_extra_example/tutorial001_pv1_py310.py hl[13:23] *}
////
@@ -58,7 +38,7 @@ Pydantic v1에서 ../../../docs_src/schema_extra_example/tutorial002_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="4 10-13"
-{!> ../../../docs_src/schema_extra_example/tutorial002.py!}
-```
-
-////
+{* ../../docs_src/schema_extra_example/tutorial002_py310.py hl[2,8:11] *}
## JSON Schema에서의 `examples` - OpenAPI
@@ -114,57 +80,7 @@ Pydantic 모델과 같이 `Field()`를 사용할 때 추가적인 `examples`를
여기, `Body()`에 예상되는 예제 데이터 하나를 포함한 `examples`를 넘겼습니다:
-//// tab | Python 3.10+
-
-```Python hl_lines="22-29"
-{!> ../../../docs_src/schema_extra_example/tutorial003_an_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="22-29"
-{!> ../../../docs_src/schema_extra_example/tutorial003_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="23-30"
-{!> ../../../docs_src/schema_extra_example/tutorial003_an.py!}
-```
-
-////
-
-//// tab | Python 3.10+ Annotated가 없는 경우
-
-/// tip | "팁"
-
-가능하다면 `Annotated`가 달린 버전을 권장합니다.
-
-///
-
-```Python hl_lines="18-25"
-{!> ../../../docs_src/schema_extra_example/tutorial003_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+ Annotated가 없는 경우
-
-/// tip | "팁"
-
-가능하다면 `Annotated`가 달린 버전을 권장합니다.
-
-///
-
-```Python hl_lines="20-27"
-{!> ../../../docs_src/schema_extra_example/tutorial003.py!}
-```
-
-////
+{* ../../docs_src/schema_extra_example/tutorial003_an_py310.py hl[22:29] *}
### 문서 UI 예시
@@ -176,57 +92,7 @@ Pydantic 모델과 같이 `Field()`를 사용할 때 추가적인 `examples`를
물론 여러 `examples`를 넘길 수 있습니다:
-//// tab | Python 3.10+
-
-```Python hl_lines="23-38"
-{!> ../../../docs_src/schema_extra_example/tutorial004_an_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="23-38"
-{!> ../../../docs_src/schema_extra_example/tutorial004_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="24-39"
-{!> ../../../docs_src/schema_extra_example/tutorial004_an.py!}
-```
-
-////
-
-//// tab | Python 3.10+ Annotated가 없는 경우
-
-/// tip | "팁"
-
-가능하다면 `Annotated`가 달린 버전을 권장합니다.
-
-///
-
-```Python hl_lines="19-34"
-{!> ../../../docs_src/schema_extra_example/tutorial004_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+ Annotated가 없는 경우
-
-/// tip | "팁"
-
-가능하다면 `Annotated`가 달린 버전을 권장합니다.
-
-///
-
-```Python hl_lines="21-36"
-{!> ../../../docs_src/schema_extra_example/tutorial004.py!}
-```
-
-////
+{* ../../docs_src/schema_extra_example/tutorial004_an_py310.py hl[23:38] *}
이와 같이 하면 이 예제는 그 본문 데이터를 위한 내부 **JSON 스키마**의 일부가 될 것입니다.
@@ -267,57 +133,7 @@ Pydantic 모델과 같이 `Field()`를 사용할 때 추가적인 `examples`를
이를 다음과 같이 사용할 수 있습니다:
-//// tab | Python 3.10+
-
-```Python hl_lines="23-49"
-{!> ../../../docs_src/schema_extra_example/tutorial005_an_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="23-49"
-{!> ../../../docs_src/schema_extra_example/tutorial005_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="24-50"
-{!> ../../../docs_src/schema_extra_example/tutorial005_an.py!}
-```
-
-////
-
-//// tab | Python 3.10+ Annotated가 없는 경우
-
-/// tip | "팁"
-
-가능하다면 `Annotated`가 달린 버전을 권장합니다.
-
-///
-
-```Python hl_lines="19-45"
-{!> ../../../docs_src/schema_extra_example/tutorial005_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+ Annotated가 없는 경우
-
-/// tip | "팁"
-
-가능하다면 `Annotated`가 달린 버전을 권장합니다.
-
-///
-
-```Python hl_lines="21-47"
-{!> ../../../docs_src/schema_extra_example/tutorial005.py!}
-```
-
-////
+{* ../../docs_src/schema_extra_example/tutorial005_an_py310.py hl[23:49] *}
### 문서 UI에서의 OpenAPI 예시
@@ -327,7 +143,7 @@ Pydantic 모델과 같이 `Field()`를 사용할 때 추가적인 `examples`를
## 기술적 세부 사항
-/// tip | "팁"
+/// tip | 팁
이미 **FastAPI**의 **0.99.0 혹은 그 이상** 버전을 사용하고 있다면, 이 세부 사항을 **스킵**해도 상관 없을 것입니다.
@@ -337,7 +153,7 @@ Pydantic 모델과 같이 `Field()`를 사용할 때 추가적인 `examples`를
///
-/// warning | "경고"
+/// warning | 경고
표준 **JSON 스키마**와 **OpenAPI**에 대한 아주 기술적인 세부사항입니다.
@@ -361,7 +177,7 @@ OpenAPI는 또한 `example`과 `examples` 필드를 명세서의 다른 부분
* `File()`
* `Form()`
-/// info | "정보"
+/// info | 정보
이 예전 OpenAPI-특화 `examples` 매개변수는 이제 FastAPI `0.103.0`부터 `openapi_examples`입니다.
@@ -377,7 +193,7 @@ OpenAPI는 또한 `example`과 `examples` 필드를 명세서의 다른 부분
JSON 스키마의 새로운 `examples` 필드는 예제 속 **단순한 `list`**이며, (위에서 상술한 것처럼) OpenAPI의 다른 곳에 존재하는 dict으로 된 추가적인 메타데이터가 아닙니다.
-/// info | "정보"
+/// info | 정보
더 쉽고 새로운 JSON 스키마와의 통합과 함께 OpenAPI 3.1.0가 배포되었지만, 잠시동안 자동 문서 생성을 제공하는 도구인 Swagger UI는 OpenAPI 3.1.0을 지원하지 않았습니다 (5.0.0 버전부터 지원합니다 🎉).
diff --git a/docs/ko/docs/tutorial/security/get-current-user.md b/docs/ko/docs/tutorial/security/get-current-user.md
index 9bf3d4ee1b..98ef3885e4 100644
--- a/docs/ko/docs/tutorial/security/get-current-user.md
+++ b/docs/ko/docs/tutorial/security/get-current-user.md
@@ -2,9 +2,7 @@
이전 장에서 (의존성 주입 시스템을 기반으로 한)보안 시스템은 *경로 작동 함수*에서 `str`로 `token`을 제공했습니다:
-```Python hl_lines="10"
-{!../../../docs_src/security/tutorial001.py!}
-```
+{* ../../docs_src/security/tutorial001.py hl[10] *}
그러나 아직도 유용하지 않습니다.
@@ -16,21 +14,7 @@
Pydantic을 사용하여 본문을 선언하는 것과 같은 방식으로 다른 곳에서 사용할 수 있습니다.
-//// tab | 파이썬 3.7 이상
-
-```Python hl_lines="5 12-16"
-{!> ../../../docs_src/security/tutorial002.py!}
-```
-
-////
-
-//// tab | 파이썬 3.10 이상
-
-```Python hl_lines="3 10-14"
-{!> ../../../docs_src/security/tutorial002_py310.py!}
-```
-
-////
+{* ../../docs_src/security/tutorial002.py hl[5,12:16] *}
## `get_current_user` 의존성 생성하기
@@ -42,67 +26,25 @@ Pydantic을 사용하여 본문을 선언하는 것과 같은 방식으로 다
이전에 *경로 작동*에서 직접 수행했던 것과 동일하게 새 종속성 `get_current_user`는 하위 종속성 `oauth2_scheme`에서 `str`로 `token`을 수신합니다.
-//// tab | 파이썬 3.7 이상
-
-```Python hl_lines="25"
-{!> ../../../docs_src/security/tutorial002.py!}
-```
-
-////
-
-//// tab | 파이썬 3.10 이상
-
-```Python hl_lines="23"
-{!> ../../../docs_src/security/tutorial002_py310.py!}
-```
-
-////
+{* ../../docs_src/security/tutorial002.py hl[25] *}
## 유저 가져오기
`get_current_user`는 토큰을 `str`로 취하고 Pydantic `User` 모델을 반환하는 우리가 만든 (가짜) 유틸리티 함수를 사용합니다.
-//// tab | 파이썬 3.7 이상
-
-```Python hl_lines="19-22 26-27"
-{!> ../../../docs_src/security/tutorial002.py!}
-```
-
-////
-
-//// tab | 파이썬 3.10 이상
-
-```Python hl_lines="17-20 24-25"
-{!> ../../../docs_src/security/tutorial002_py310.py!}
-```
-
-////
+{* ../../docs_src/security/tutorial002.py hl[19:22,26:27] *}
## 현재 유저 주입하기
이제 *경로 작동*에서 `get_current_user`와 동일한 `Depends`를 사용할 수 있습니다.
-//// tab | 파이썬 3.7 이상
-
-```Python hl_lines="31"
-{!> ../../../docs_src/security/tutorial002.py!}
-```
-
-////
-
-//// tab | 파이썬 3.10 이상
-
-```Python hl_lines="29"
-{!> ../../../docs_src/security/tutorial002_py310.py!}
-```
-
-////
+{* ../../docs_src/security/tutorial002.py hl[31] *}
Pydantic 모델인 `User`로 `current_user`의 타입을 선언하는 것을 알아야 합니다.
이것은 모든 완료 및 타입 검사를 통해 함수 내부에서 우리를 도울 것입니다.
-/// tip | "팁"
+/// tip | 팁
요청 본문도 Pydantic 모델로 선언된다는 것을 기억할 것입니다.
@@ -110,7 +52,7 @@ Pydantic 모델인 `User`로 `current_user`의 타입을 선언하는 것을 알
///
-/// check | "확인"
+/// check | 확인
이 의존성 시스템이 설계된 방식은 모두 `User` 모델을 반환하는 다양한 의존성(다른 "의존적인")을 가질 수 있도록 합니다.
@@ -150,21 +92,7 @@ Pydantic 모델인 `User`로 `current_user`의 타입을 선언하는 것을 알
그리고 이 수천 개의 *경로 작동*은 모두 3줄 정도로 줄일 수 있습니다.
-//// tab | 파이썬 3.7 이상
-
-```Python hl_lines="30-32"
-{!> ../../../docs_src/security/tutorial002.py!}
-```
-
-////
-
-//// tab | 파이썬 3.10 이상
-
-```Python hl_lines="28-30"
-{!> ../../../docs_src/security/tutorial002_py310.py!}
-```
-
-////
+{* ../../docs_src/security/tutorial002.py hl[30:32] *}
## 요약
diff --git a/docs/ko/docs/tutorial/security/oauth2-jwt.md b/docs/ko/docs/tutorial/security/oauth2-jwt.md
new file mode 100644
index 0000000000..8d27856e84
--- /dev/null
+++ b/docs/ko/docs/tutorial/security/oauth2-jwt.md
@@ -0,0 +1,273 @@
+# 패스워드 해싱을 이용한 OAuth2, JWT 토큰을 사용하는 Bearer 인증
+
+모든 보안 흐름을 구성했으므로, 이제 JWT 토큰과 패스워드 해싱을 사용해 애플리케이션을 안전하게 만들 것입니다.
+
+이 코드는 실제로 애플리케이션에서 패스워드를 해싱하여 DB에 저장하는 등의 작업에 활용할 수 있습니다.
+
+이전 장에 이어서 시작해 봅시다.
+
+## JWT
+
+JWT 는 "JSON Web Tokens" 을 의미합니다.
+
+JSON 객체를 공백이 없는 긴 문자열로 인코딩하는 표준이며, 다음과 같은 형태입니다:
+
+```
+eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
+```
+
+JWT는 암호화되지 않아 누구든지 토큰에서 정보를 복원할 수 있습니다.
+
+하지만 JWT는 서명되어 있습니다. 그래서 자신이 발급한 토큰을 받았을 때, 실제로 자신이 발급한게 맞는지 검증할 수 있습니다.
+
+만료 기간이 일주일인 토큰을 발행했다고 가정해 봅시다. 다음 날 사용자가 토큰을 가져왔을 때, 그 사용자가 시스템에 여전히 로그인되어 있다는 것을 알 수 있습니다.
+
+일주일 뒤에는 토큰이 만료될 것이고, 사용자는 인가되지 않아 새 토큰을 받기 위해 다시 로그인해야 할 것입니다. 만약 사용자(또는 제3자)가 토큰을 수정하거나 만료일을 변경하면, 서명이 일치하지 않기 때문에 알아챌 수 있을 것입니다.
+
+만약 JWT 토큰을 다뤄보고, 작동 방식도 알아보고 싶다면 https://jwt.io 을 확인하십시오.
+
+## `PyJWT` 설치
+
+파이썬으로 JWT 토큰을 생성하고 검증하려면 `PyJWT` 를 설치해야 합니다.
+
+[가상환경](../../virtual-environments.md){.internal-link target=_blank} 을 만들고 활성화한 다음 `pyjwt` 를 설치하십시오:
+
+
+
+이전과 같은 방법으로 애플리케이션에 인증하십시오.
+
+다음 인증 정보를 사용하십시오:
+
+Username: `johndoe`
+Password: `secret`
+
+/// check
+
+코드 어디에도 평문 패스워드 "`secret`" 이 없다는 점에 유의하십시오. 해시된 버전만 있습니다.
+
+///
+
+
+
+`/users/me/` 를 호출하면 다음과 같은 응답을 얻을 수 있습니다:
+
+```JSON
+{
+ "username": "johndoe",
+ "email": "johndoe@example.com",
+ "full_name": "John Doe",
+ "disabled": false
+}
+```
+
+
+
+개발자 도구를 열어보면 전송된 데이터에 토큰만 포함된 것을 확인할 수 있습니다. 패스워드는 사용자를 인증하고 액세스 토큰을 받기 위한 첫 번째 요청에만 전송되며, 이후에는 전송되지 않습니다:
+
+
+
+/// note
+
+`Bearer `로 시작하는 `Authorization` 헤더에 주목하십시오.
+
+///
+
+## `scopes` 의 고급 사용법
+
+OAuth2는 "스코프(scopes)" 라는 개념을 갖고 있습니다.
+
+이를 사용하여 JWT 토큰에 특정 권한 집합을 추가할 수 있습니다.
+
+그 후 이 토큰을 사용자에게 직접 제공하거나 제3자에게 제공하여, 특정 제한사항 하에있는 API와 통신하도록 할 수 있습니다.
+
+**FastAPI** 에서의 사용 방법과 통합 방식은 **심화 사용자 안내서** 에서 자세히 배울 수 있습니다.
+
+## 요약
+
+지금까지 살펴본 내용을 바탕으로, OAuth2와 JWT 같은 표준을 사용하여 안전한 **FastAPI** 애플리케이션을 만들 수 있습니다.
+
+거의 모든 프레임워크에서 보안 처리는 상당히 복잡한 주제입니다.
+
+이를 단순화하는 많은 패키지는 데이터 모델, 데이터베이스, 사용 가능한 기능들에 대해 여러 제약이 있습니다. 그리고 지나치게 단순화하는 일부 패키지들은 심각한 보안 결함을 가질 수도 있습니다.
+
+---
+
+**FastAPI** 는 어떤 데이터베이스, 데이터 모델, 도구도 강요하지 않습니다.
+
+프로젝트에 가장 적합한 것을 선택할 수 있는 유연성을 제공합니다.
+
+그리고 `passlib` 와 `PyJWT` 처럼 잘 관리되고 널리 사용되는 패키지들을 바로 사용할 수 있습니다. **FastAPI** 는 외부 패키지 통합을 위해 복잡한 메커니즘이 필요하지 않기 때문입니다.
+
+그러나 유연성, 견고성, 보안성을 해치지 않으면서 과정을 단순화할 수 있는 도구들을 제공합니다.
+
+그리고 OAuth2와 같은 표준 프로토콜을 비교적 간단한 방법으로 구현하고 사용할 수 있습니다.
+
+더 세분화된 권한 체계를 위해 OAuth2의 "스코프"를 사용하는 방법은 **심화 사용자 안내서**에서 더 자세히 배울 수 있습니다. OAuth2의 스코프는 제3자 애플리케이션이 사용자를 대신해 그들의 API와 상호작용하도록 권한을 부여하기 위해, Facebook, Google, GitHub, Microsoft, X (Twitter) 등의 많은 대형 인증 제공업체들이 사용하는 메커니즘입니다.
diff --git a/docs/ko/docs/tutorial/security/simple-oauth2.md b/docs/ko/docs/tutorial/security/simple-oauth2.md
index 9593f96f5e..f10c4f588d 100644
--- a/docs/ko/docs/tutorial/security/simple-oauth2.md
+++ b/docs/ko/docs/tutorial/security/simple-oauth2.md
@@ -32,7 +32,7 @@ OAuth2는 (우리가 사용하고 있는) "패스워드 플로우"을 사용할
* `instagram_basic`은 페이스북/인스타그램에서 사용합니다.
* `https://www.googleapis.com/auth/drive`는 Google에서 사용합니다.
-/// 정보
+/// info | 정보
OAuth2에서 "범위"는 필요한 특정 권한을 선언하는 문자열입니다.
@@ -52,21 +52,7 @@ OAuth2의 경우 문자열일 뿐입니다.
먼저 `OAuth2PasswordRequestForm`을 가져와 `/token`에 대한 *경로 작동*에서 `Depends`의 의존성으로 사용합니다.
-//// tab | 파이썬 3.7 이상
-
-```Python hl_lines="4 76"
-{!> ../../../docs_src/security/tutorial003.py!}
-```
-
-////
-
-//// tab | 파이썬 3.10 이상
-
-```Python hl_lines="2 74"
-{!> ../../../docs_src/security/tutorial003_py310.py!}
-```
-
-////
+{* ../../docs_src/security/tutorial003.py hl[4,76] *}
`OAuth2PasswordRequestForm`은 다음을 사용하여 폼 본문을 선언하는 클래스 의존성입니다:
@@ -75,7 +61,7 @@ OAuth2의 경우 문자열일 뿐입니다.
* `scope`는 선택적인 필드로 공백으로 구분된 문자열로 구성된 큰 문자열입니다.
* `grant_type`(선택적으로 사용).
-/// 팁
+/// tip | 팁
OAuth2 사양은 실제로 `password`라는 고정 값이 있는 `grant_type` 필드를 *요구*하지만 `OAuth2PasswordRequestForm`은 이를 강요하지 않습니다.
@@ -86,7 +72,7 @@ OAuth2 사양은 실제로 `password`라는 고정 값이 있는 `grant_type`
* `client_id`(선택적으로 사용) (예제에서는 필요하지 않습니다).
* `client_secret`(선택적으로 사용) (예제에서는 필요하지 않습니다).
-/// 정보
+/// info | 정보
`OAuth2PasswordRequestForm`은 `OAuth2PasswordBearer`와 같이 **FastAPI**에 대한 특수 클래스가 아닙니다.
@@ -100,7 +86,7 @@ OAuth2 사양은 실제로 `password`라는 고정 값이 있는 `grant_type`
### 폼 데이터 사용하기
-/// 팁
+/// tip | 팁
종속성 클래스 `OAuth2PasswordRequestForm`의 인스턴스에는 공백으로 구분된 긴 문자열이 있는 `scope` 속성이 없고 대신 전송된 각 범위에 대한 실제 문자열 목록이 있는 `scopes` 속성이 있습니다.
@@ -114,21 +100,7 @@ OAuth2 사양은 실제로 `password`라는 고정 값이 있는 `grant_type`
오류의 경우 `HTTPException` 예외를 사용합니다:
-//// tab | 파이썬 3.7 이상
-
-```Python hl_lines="3 77-79"
-{!> ../../../docs_src/security/tutorial003.py!}
-```
-
-////
-
-//// tab | 파이썬 3.10 이상
-
-```Python hl_lines="1 75-77"
-{!> ../../../docs_src/security/tutorial003_py310.py!}
-```
-
-////
+{* ../../docs_src/security/tutorial003.py hl[3,77:79] *}
### 패스워드 확인하기
@@ -154,21 +126,13 @@ OAuth2 사양은 실제로 `password`라는 고정 값이 있는 `grant_type`
따라서 해커는 다른 시스템에서 동일한 암호를 사용하려고 시도할 수 없습니다(많은 사용자가 모든 곳에서 동일한 암호를 사용하므로 이는 위험할 수 있습니다).
-//// tab | P파이썬 3.7 이상
+//// tab | 파이썬 3.7 이상
-```Python hl_lines="80-83"
-{!> ../../../docs_src/security/tutorial003.py!}
-```
+{* ../../docs_src/security/tutorial003.py hl[80:83] *}
////
-//// tab | 파이썬 3.10 이상
-
-```Python hl_lines="78-81"
-{!> ../../../docs_src/security/tutorial003_py310.py!}
-```
-
-////
+{* ../../docs_src/security/tutorial003_py310.py hl[78:81] *}
#### `**user_dict`에 대해
@@ -186,7 +150,7 @@ UserInDB(
)
```
-/// 정보
+/// info | 정보
`**user_dict`에 대한 자세한 설명은 [**추가 모델** 문서](../extra-models.md#about-user_indict){.internal-link target=_blank}를 다시 읽어봅시다.
@@ -202,7 +166,7 @@ UserInDB(
이 간단한 예제에서는 완전히 안전하지 않고, 동일한 `username`을 토큰으로 반환합니다.
-/// 팁
+/// tip | 팁
다음 장에서는 패스워드 해싱 및 JWT 토큰을 사용하여 실제 보안 구현을 볼 수 있습니다.
@@ -210,23 +174,9 @@ UserInDB(
///
-//// tab | 파이썬 3.7 이상
+{* ../../docs_src/security/tutorial003.py hl[85] *}
-```Python hl_lines="85"
-{!> ../../../docs_src/security/tutorial003.py!}
-```
-
-////
-
-//// tab | 파이썬 3.10 이상
-
-```Python hl_lines="83"
-{!> ../../../docs_src/security/tutorial003_py310.py!}
-```
-
-////
-
-/// 팁
+/// tip | 팁
사양에 따라 이 예제와 동일하게 `access_token` 및 `token_type`이 포함된 JSON을 반환해야 합니다.
@@ -250,23 +200,9 @@ UserInDB(
따라서 엔드포인트에서는 사용자가 존재하고 올바르게 인증되었으며 활성 상태인 경우에만 사용자를 얻습니다:
-//// tab | 파이썬 3.7 이상
+{* ../../docs_src/security/tutorial003.py hl[58:66,69:72,90] *}
-```Python hl_lines="58-66 69-72 90"
-{!> ../../../docs_src/security/tutorial003.py!}
-```
-
-////
-
-//// tab | 파이썬 3.10 이상
-
-```Python hl_lines="55-64 67-70 88"
-{!> ../../../docs_src/security/tutorial003_py310.py!}
-```
-
-////
-
-/// 정보
+/// info | 정보
여기서 반환하는 값이 `Bearer`인 추가 헤더 `WWW-Authenticate`도 사양의 일부입니다.
diff --git a/docs/ko/docs/tutorial/sql-databases.md b/docs/ko/docs/tutorial/sql-databases.md
new file mode 100644
index 0000000000..58c7017d6a
--- /dev/null
+++ b/docs/ko/docs/tutorial/sql-databases.md
@@ -0,0 +1,360 @@
+# SQL (관계형) 데이터베이스
+
+**FastAPI**에서 SQL(관계형) 데이터베이스 사용은 필수가 아닙니다. 여러분이 원하는 **어떤 데이터베이스든** 사용할 수 있습니다.
+
+여기서는 SQLModel을 사용하는 예제를 살펴보겠습니다.
+
+**SQLModel**은 SQLAlchemy와 Pydantic을 기반으로 구축되었습니다.SQLModel은 **SQL 데이터베이스**를 사용하는 FastAPI 애플리케이션에 완벽히 어울리도록 **FastAPI**의 제작자가 설계한 도구입니다.
+
+/// tip | 팁
+
+다른 SQL 또는 NoSQL 데이터베이스 라이브러리를 사용할 수도 있습니다 (일부는 "ORM"이라고도 불립니다), FastAPI는 특정 라이브러리의 사용을 강요하지 않습니다. 😎
+
+///
+
+SQLModel은 SQLAlchemy를 기반으로 하므로, SQLAlchemy에서 **지원하는 모든 데이터베이스**를 손쉽게 사용할 수 있습니다(SQLModel에서도 동일하게 지원됩니다). 예를 들면:
+
+* PostgreSQL
+* MySQL
+* SQLite
+* Oracle
+* Microsoft SQL Server 등.
+
+이 예제에서는 **SQLite**를 사용합니다. SQLite는 단일 파일을 사용하고 파이썬에서 기본적으로 지원하기 때문입니다. 따라서 이 예제를 그대로 복사하여 실행할 수 있습니다.
+
+나중에 실제 프로덕션 애플리케이션에서는 **PostgreSQL**과 같은 데이터베이스 서버를 사용하는 것이 좋습니다.
+
+/// tip | 팁
+
+**FastAPI**와 **PostgreSQL**를 포함하여 프론트엔드와 다양한 도구를 제공하는 공식 프로젝트 생성기가 있습니다: https://github.com/fastapi/full-stack-fastapi-template
+
+///
+
+이 튜토리얼은 매우 간단하고 짧습니다. 데이터베이스 기본 개념, SQL, 또는 더 복잡한 기능에 대해 배우고 싶다면, SQLModel 문서를 참고하세요.
+
+## `SQLModel` 설치하기
+
+먼저, [가상 환경](../virtual-environments.md){.internal-link target=_blank}을 생성하고 활성화한 다음, `sqlmodel`을 설치하세요:
+
+
+
+- FastAPI to szybki, prosty w nauce i gotowy do użycia w produkcji framework -
- - ---- - -**Dokumentacja**: https://fastapi.tiangolo.com - -**Kod żródłowy**: https://github.com/fastapi/fastapi - ---- - -FastAPI to nowoczesny, wydajny framework webowy do budowania API z użyciem Pythona bazujący na standardowym typowaniu Pythona. - -Kluczowe cechy: - -* **Wydajność**: FastAPI jest bardzo wydajny, na równi z **NodeJS** oraz **Go** (dzięki Starlette i Pydantic). [Jeden z najszybszych dostępnych frameworków Pythonowych](#wydajnosc). -* **Szybkość kodowania**: Przyśpiesza szybkość pisania nowych funkcjonalności o około 200% do 300%. * -* **Mniejsza ilość błędów**: Zmniejsza ilość ludzkich (dewelopera) błędy o około 40%. * -* **Intuicyjność**: Wspaniałe wsparcie dla edytorów kodu. Dostępne wszędzie automatyczne uzupełnianie kodu. Krótszy czas debugowania. -* **Łatwość**: Zaprojektowany by być prosty i łatwy do nauczenia. Mniej czasu spędzonego na czytanie dokumentacji. -* **Kompaktowość**: Minimalizacja powtarzającego się kodu. Wiele funkcjonalności dla każdej deklaracji parametru. Mniej błędów. -* **Solidność**: Kod gotowy dla środowiska produkcyjnego. Wraz z automatyczną interaktywną dokumentacją. -* **Bazujący na standardach**: Oparty na (i w pełni kompatybilny z) otwartych standardach API: OpenAPI (wcześniej znane jako Swagger) oraz JSON Schema. - -* oszacowania bazowane na testach wykonanych przez wewnętrzny zespół deweloperów, budujących aplikacie używane na środowisku produkcyjnym. - -## Sponsorzy - - - -{% if sponsors %} -{% for sponsor in sponsors.gold -%} -async def...uvicorn main:app --reload...email-validator - dla walidacji adresów email.
-
-Używane przez Starlette:
-
-* httpx - Wymagane jeżeli chcesz korzystać z `TestClient`.
-* aiofiles - Wymagane jeżeli chcesz korzystać z `FileResponse` albo `StaticFiles`.
-* jinja2 - Wymagane jeżeli chcesz używać domyślnej konfiguracji szablonów.
-* python-multipart - Wymagane jeżelich chcesz wsparcie "parsowania" formularzy, używając `request.form()`.
-* itsdangerous - Wymagany dla wsparcia `SessionMiddleware`.
-* pyyaml - Wymagane dla wsparcia `SchemaGenerator` z Starlette (z FastAPI prawdopodobnie tego nie potrzebujesz).
-* graphene - Wymagane dla wsparcia `GraphQLApp`.
-
-Używane przez FastAPI / Starlette:
-
-* uvicorn - jako serwer, który ładuje i obsługuje Twoją aplikację.
-* orjson - Wymagane jeżeli chcesz używać `ORJSONResponse`.
-* ujson - Wymagane jeżeli chcesz korzystać z `UJSONResponse`.
-
-Możesz zainstalować wszystkie te aplikacje przy pomocy `pip install fastapi[all]`.
-
-## Licencja
-
-Ten projekt jest na licencji MIT.
diff --git a/docs/pl/docs/tutorial/first-steps.md b/docs/pl/docs/tutorial/first-steps.md
deleted file mode 100644
index 8f1b9b922c..0000000000
--- a/docs/pl/docs/tutorial/first-steps.md
+++ /dev/null
@@ -1,351 +0,0 @@
-# Pierwsze kroki
-
-Najprostszy plik FastAPI może wyglądać tak:
-
-```Python
-{!../../../docs_src/first_steps/tutorial001.py!}
-```
-
-Skopiuj to do pliku `main.py`.
-
-Uruchom serwer:
-
-get
-
-/// info | "`@decorator` Info"
-
-Składnia `@something` jest w Pythonie nazywana "dekoratorem".
-
-Umieszczasz to na szczycie funkcji. Jak ładną ozdobną czapkę (chyba stąd wzięła się nazwa).
-
-"Dekorator" przyjmuje funkcję znajdującą się poniżej jego i coś z nią robi.
-
-W naszym przypadku dekorator mówi **FastAPI**, że poniższa funkcja odpowiada **ścieżce** `/` z **operacją** `get`.
-
-Jest to "**dekorator operacji na ścieżce**".
-
-///
-
-Możesz również użyć innej operacji:
-
-* `@app.post()`
-* `@app.put()`
-* `@app.delete()`
-
-Oraz tych bardziej egzotycznych:
-
-* `@app.options()`
-* `@app.head()`
-* `@app.patch()`
-* `@app.trace()`
-
-/// tip
-
-Możesz dowolnie używać każdej operacji (metody HTTP).
-
-**FastAPI** nie narzuca żadnego konkretnego znaczenia.
-
-Informacje tutaj są przedstawione jako wskazówka, a nie wymóg.
-
-Na przykład, używając GraphQL, normalnie wykonujesz wszystkie akcje używając tylko operacji `POST`.
-
-///
-
-### Krok 4: zdefiniuj **funkcję obsługującą ścieżkę**
-
-To jest nasza "**funkcja obsługująca ścieżkę**":
-
-* **ścieżka**: to `/`.
-* **operacja**: to `get`.
-* **funkcja**: to funkcja poniżej "dekoratora" (poniżej `@app.get("/")`).
-
-```Python hl_lines="7"
-{!../../../docs_src/first_steps/tutorial001.py!}
-```
-
-Jest to funkcja Python.
-
-Zostanie ona wywołana przez **FastAPI** za każdym razem, gdy otrzyma żądanie do adresu URL "`/`" przy użyciu operacji `GET`.
-
-W tym przypadku jest to funkcja "asynchroniczna".
-
----
-
-Możesz również zdefiniować to jako normalną funkcję zamiast `async def`:
-
-```Python hl_lines="7"
-{!../../../docs_src/first_steps/tutorial003.py!}
-```
-
-/// note
-
-Jeśli nie znasz różnicy, sprawdź [Async: *"In a hurry?"*](../async.md#in-a-hurry){.internal-link target=_blank}.
-
-///
-
-### Krok 5: zwróć zawartość
-
-```Python hl_lines="8"
-{!../../../docs_src/first_steps/tutorial001.py!}
-```
-
-Możesz zwrócić `dict`, `list`, pojedynczą wartość jako `str`, `int`, itp.
-
-Możesz również zwrócić modele Pydantic (więcej o tym później).
-
-Istnieje wiele innych obiektów i modeli, które zostaną automatycznie skonwertowane do formatu JSON (w tym ORM itp.). Spróbuj użyć swoich ulubionych, jest bardzo prawdopodobne, że są już obsługiwane.
-
-## Podsumowanie
-
-* Zaimportuj `FastAPI`.
-* Stwórz instancję `app`.
-* Dodaj **dekorator operacji na ścieżce** (taki jak `@app.get("/")`).
-* Napisz **funkcję obsługującą ścieżkę** (taką jak `def root(): ...` powyżej).
-* Uruchom serwer deweloperski (`uvicorn main:app --reload`).
diff --git a/docs/pl/docs/tutorial/index.md b/docs/pl/docs/tutorial/index.md
deleted file mode 100644
index 66f7c6d621..0000000000
--- a/docs/pl/docs/tutorial/index.md
+++ /dev/null
@@ -1,83 +0,0 @@
-# Samouczek
-
-Ten samouczek pokaże Ci, krok po kroku, jak używać większości funkcji **FastAPI**.
-
-Każda część korzysta z poprzednich, ale jest jednocześnie osobnym tematem. Możesz przejść bezpośrednio do każdego rozdziału, jeśli szukasz rozwiązania konkretnego problemu.
-
-Samouczek jest tak zbudowany, żeby służył jako punkt odniesienia w przyszłości.
-
-Możesz wracać i sprawdzać dokładnie to czego potrzebujesz.
-
-## Wykonywanie kodu
-
-Wszystkie fragmenty kodu mogą być skopiowane bezpośrednio i użyte (są poprawnymi i przetestowanymi plikami).
-
-Żeby wykonać każdy przykład skopiuj kod to pliku `main.py` i uruchom `uvicorn` za pomocą:
-
-
+
+## Respostas disponíveis
+
+Aqui estão algumas dos tipos de resposta disponíveis.
+
+Lembre-se que você pode utilizar `Response` para retornar qualquer outra coisa, ou até mesmo criar uma subclasse personalizada.
+
+/// note | Detalhes Técnicos
+
+Você também pode utilizar `from starlette.responses import HTMLResponse`.
+
+O **FastAPI** provê a mesma `starlette.responses` como `fastapi.responses` apenas como uma facilidade para você, desenvolvedor. Mas a maioria das respostas disponíveis vêm diretamente do Starlette.
+
+///
+
+### `Response`
+
+A classe principal de respostas, todas as outras respostas herdam dela.
+
+Você pode retorná-la diretamente.
+
+Ela aceita os seguintes parâmetros:
+
+* `content` - Uma sequência de caracteres (`str`) ou `bytes`.
+* `status_code` - Um código de status HTTP do tipo `int`.
+* `headers` - Um dicionário `dict` de strings.
+* `media_type` - Uma `str` informando o media type. E.g. `"text/html"`.
+
+O FastAPI (Starlette, na verdade) irá incluir o cabeçalho Content-Length automaticamente. Ele também irá incluir o cabeçalho Content-Type, baseado no `media_type` e acrescentando uma codificação para tipos textuais.
+
+{* ../../docs_src/response_directly/tutorial002.py hl[1,18] *}
+
+### `HTMLResponse`
+
+Usa algum texto ou sequência de bytes e retorna uma resposta HTML. Como você leu acima.
+
+### `PlainTextResponse`
+
+Usa algum texto ou sequência de bytes para retornar uma resposta de texto não formatado.
+
+{* ../../docs_src/custom_response/tutorial005.py hl[2,7,9] *}
+
+### `JSONResponse`
+
+Pega alguns dados e retorna uma resposta com codificação `application/json`.
+
+É a resposta padrão utilizada no **FastAPI**, como você leu acima.
+
+### `ORJSONResponse`
+
+Uma alternativa mais rápida de resposta JSON utilizando o `orjson`, como você leu acima.
+
+/// info | Informação
+
+Essa resposta requer a instalação do pacote `orjson`, com o comando `pip install orjson`, por exemplo.
+
+///
+
+### `UJSONResponse`
+
+Uma alternativa de resposta JSON utilizando a biblioteca `ujson`.
+
+/// info | Informação
+
+Essa resposta requer a instalação do pacote `ujson`, com o comando `pip install ujson`, por exemplo.
+
+///
+
+/// warning | Aviso
+
+`ujson` é menos cauteloso que a implementação nativa do Python na forma que os casos especiais são tratados
+
+///
+
+{* ../../docs_src/custom_response/tutorial001.py hl[2,7] *}
+
+/// tip | Dica
+
+É possível que `ORJSONResponse` seja uma alternativa mais rápida.
+
+///
+
+### `RedirectResponse`
+
+Retorna um redirecionamento HTTP. Utiliza o código de status 307 (Redirecionamento Temporário) por padrão.
+
+Você pode retornar uma `RedirectResponse` diretamente:
+
+{* ../../docs_src/custom_response/tutorial006.py hl[2,9] *}
+
+---
+
+Ou você pode utilizá-la no parâmetro `response_class`:
+
+{* ../../docs_src/custom_response/tutorial006b.py hl[2,7,9] *}
+
+Se você fizer isso, então você pode retornar a URL diretamente da sua *função de operação de rota*
+
+Neste caso, o `status_code` utilizada será o padrão de `RedirectResponse`, que é `307`.
+
+---
+
+Você também pode utilizar o parâmetro `status_code` combinado com o parâmetro `response_class`:
+
+{* ../../docs_src/custom_response/tutorial006c.py hl[2,7,9] *}
+
+### `StreamingResponse`
+
+Recebe uma gerador assíncrono ou um gerador/iterador comum e retorna o corpo da requisição continuamente (stream).
+
+{* ../../docs_src/custom_response/tutorial007.py hl[2,14] *}
+
+#### Utilizando `StreamingResponse` com objetos semelhantes a arquivos
+
+Se você tiver um objeto semelhante a um arquivo (e.g. o objeto retornado por `open()`), você pode criar uma função geradora para iterar sobre esse objeto.
+
+Dessa forma, você não precisa ler todo o arquivo na memória primeiro, e você pode passar essa função geradora para `StreamingResponse` e retorná-la.
+
+Isso inclui muitas bibliotecas que interagem com armazenamento em nuvem, processamento de vídeos, entre outras.
+
+```{ .python .annotate hl_lines="2 10-12 14" }
+{!../../docs_src/custom_response/tutorial008.py!}
+```
+
+1. Essa é a função geradora. É definida como "função geradora" porque contém declarações `yield` nela.
+2. Ao utilizar o bloco `with`, nós garantimos que o objeto semelhante a um arquivo é fechado após a função geradora ser finalizada. Isto é, após a resposta terminar de ser enivada.
+3. Essa declaração `yield from` informa a função para iterar sobre essa coisa nomeada de `file_like`. E então, para cada parte iterada, fornece essa parte como se viesse dessa função geradora (`iterfile`).
+
+ Então, é uma função geradora que transfere o trabalho de "geração" para alguma outra coisa interna.
+
+ Fazendo dessa forma, podemos colocá-la em um bloco `with`, e assim garantir que o objeto semelhante a um arquivo é fechado quando a função termina.
+
+/// tip | Dica
+
+Perceba que aqui estamos utilizando o `open()` da biblioteca padrão que não suporta `async` e `await`, e declaramos a operação de rota com o `def` básico.
+
+///
+
+### `FileResponse`
+
+Envia um arquivo de forma assíncrona e contínua (stream).
+*
+Recebe um conjunto de argumentos do construtor diferente dos outros tipos de resposta:
+
+* `path` - O caminho do arquivo que será transmitido
+* `headers` - quaisquer cabeçalhos que serão incluídos, como um dicionário.
+* `media_type` - Uma string com o media type. Se não for definida, o media type é inferido a partir do nome ou caminho do arquivo.
+* `filename` - Se for definido, é incluído no cabeçalho `Content-Disposition`.
+
+Respostas de Arquivos incluem o tamanho do arquivo, data da última modificação e ETags apropriados, nos cabeçalhos `Content-Length`, `Last-Modified` e `ETag`, respectivamente.
+
+{* ../../docs_src/custom_response/tutorial009.py hl[2,10] *}
+
+Você também pode usar o parâmetro `response_class`:
+
+{* ../../docs_src/custom_response/tutorial009b.py hl[2,8,10] *}
+
+Nesse caso, você pode retornar o caminho do arquivo diretamente da sua *função de operação de rota*.
+
+## Classe de resposta personalizada
+
+Você pode criar sua própria classe de resposta, herdando de `Response` e usando essa nova classe.
+
+Por exemplo, vamos supor que você queira utilizar o `orjson`, mas com algumas configurações personalizadas que não estão incluídas na classe `ORJSONResponse`.
+
+Vamos supor também que você queira retornar um JSON indentado e formatado, então você quer utilizar a opção `orjson.OPT_INDENT_2` do orjson.
+
+Você poderia criar uma classe `CustomORJSONResponse`. A principal coisa a ser feita é sobrecarregar o método render da classe Response, `Response.render(content)`, que retorna o conteúdo em bytes, para retornar o conteúdo que você deseja:
+
+{* ../../docs_src/custom_response/tutorial009c.py hl[9:14,17] *}
+
+Agora em vez de retornar:
+
+```json
+{"message": "Hello World"}
+```
+
+...essa resposta retornará:
+
+```json
+{
+ "message": "Hello World"
+}
+```
+
+Obviamente, você provavelmente vai encontrar maneiras muito melhores de se aproveitar disso do que a formatação de JSON. 😉
+
+## Classe de resposta padrão
+
+Quando você criar uma instância da classe **FastAPI** ou um `APIRouter` você pode especificar qual classe de resposta utilizar por padrão.
+
+O padrão que define isso é o `default_response_class`.
+
+No exemplo abaixo, o **FastAPI** irá utilizar `ORJSONResponse` por padrão, em todas as *operações de rota*, em vez de `JSONResponse`.
+
+{* ../../docs_src/custom_response/tutorial010.py hl[2,4] *}
+
+/// tip | Dica
+
+Você ainda pode substituir `response_class` em *operações de rota* como antes.
+
+///
+
+## Documentação adicional
+
+Você também pode declarar o media type e muitos outros detalhes no OpenAPI utilizando `responses`: [Retornos Adicionais no OpenAPI](additional-responses.md){.internal-link target=_blank}.
diff --git a/docs/pt/docs/advanced/dataclasses.md b/docs/pt/docs/advanced/dataclasses.md
new file mode 100644
index 0000000000..600c8c2682
--- /dev/null
+++ b/docs/pt/docs/advanced/dataclasses.md
@@ -0,0 +1,97 @@
+# Usando Dataclasses
+
+FastAPI é construído em cima do **Pydantic**, e eu tenho mostrado como usar modelos Pydantic para declarar requisições e respostas.
+
+Mas o FastAPI também suporta o uso de `dataclasses` da mesma forma:
+
+{* ../../docs_src/dataclasses/tutorial001.py hl[1,7:12,19:20] *}
+
+Isso ainda é suportado graças ao **Pydantic**, pois ele tem suporte interno para `dataclasses`.
+
+Então, mesmo com o código acima que não usa Pydantic explicitamente, o FastAPI está usando Pydantic para converter essas dataclasses padrão para a versão do Pydantic.
+
+E claro, ele suporta o mesmo:
+
+* validação de dados
+* serialização de dados
+* documentação de dados, etc.
+
+Isso funciona da mesma forma que com os modelos Pydantic. E na verdade é alcançado da mesma maneira por baixo dos panos, usando Pydantic.
+
+/// info | Informação
+
+Lembre-se de que dataclasses não podem fazer tudo o que os modelos Pydantic podem fazer.
+
+Então, você ainda pode precisar usar modelos Pydantic.
+
+Mas se você tem um monte de dataclasses por aí, este é um truque legal para usá-las para alimentar uma API web usando FastAPI. 🤓
+
+///
+
+## Dataclasses em `response_model`
+
+Você também pode usar `dataclasses` no parâmetro `response_model`:
+
+{* ../../docs_src/dataclasses/tutorial002.py hl[1,7:13,19] *}
+
+A dataclass será automaticamente convertida para uma dataclass Pydantic.
+
+Dessa forma, seu esquema aparecerá na interface de documentação da API:
+
+
+
+## Dataclasses em Estruturas de Dados Aninhadas
+
+Você também pode combinar `dataclasses` com outras anotações de tipo para criar estruturas de dados aninhadas.
+
+Em alguns casos, você ainda pode ter que usar a versão do Pydantic das `dataclasses`. Por exemplo, se você tiver erros com a documentação da API gerada automaticamente.
+
+Nesse caso, você pode simplesmente trocar as `dataclasses` padrão por `pydantic.dataclasses`, que é um substituto direto:
+
+```{ .python .annotate hl_lines="1 5 8-11 14-17 23-25 28" }
+{!../../docs_src/dataclasses/tutorial003.py!}
+```
+
+1. Ainda importamos `field` das `dataclasses` padrão.
+
+2. `pydantic.dataclasses` é um substituto direto para `dataclasses`.
+
+3. A dataclass `Author` inclui uma lista de dataclasses `Item`.
+
+4. A dataclass `Author` é usada como o parâmetro `response_model`.
+
+5. Você pode usar outras anotações de tipo padrão com dataclasses como o corpo da requisição.
+
+ Neste caso, é uma lista de dataclasses `Item`.
+
+6. Aqui estamos retornando um dicionário que contém `items`, que é uma lista de dataclasses.
+
+ O FastAPI ainda é capaz de serializar os dados para JSON.
+
+7. Aqui o `response_model` está usando uma anotação de tipo de uma lista de dataclasses `Author`.
+
+ Novamente, você pode combinar `dataclasses` com anotações de tipo padrão.
+
+8. Note que esta *função de operação de rota* usa `def` regular em vez de `async def`.
+
+ Como sempre, no FastAPI você pode combinar `def` e `async def` conforme necessário.
+
+ Se você precisar de uma atualização sobre quando usar qual, confira a seção _"Com pressa?"_ na documentação sobre [`async` e `await`](../async.md#in-a-hurry){.internal-link target=_blank}.
+
+9. Esta *função de operação de rota* não está retornando dataclasses (embora pudesse), mas uma lista de dicionários com dados internos.
+
+ O FastAPI usará o parâmetro `response_model` (que inclui dataclasses) para converter a resposta.
+
+Você pode combinar `dataclasses` com outras anotações de tipo em muitas combinações diferentes para formar estruturas de dados complexas.
+
+Confira as dicas de anotação no código acima para ver mais detalhes específicos.
+
+## Saiba Mais
+
+Você também pode combinar `dataclasses` com outros modelos Pydantic, herdar deles, incluí-los em seus próprios modelos, etc.
+
+Para saber mais, confira a documentação do Pydantic sobre dataclasses.
+
+## Versão
+
+Isso está disponível desde a versão `0.67.0` do FastAPI. 🔖
diff --git a/docs/pt/docs/advanced/events.md b/docs/pt/docs/advanced/events.md
index 5f722763ed..2d38e0899c 100644
--- a/docs/pt/docs/advanced/events.md
+++ b/docs/pt/docs/advanced/events.md
@@ -31,15 +31,13 @@ Vamos iniciar com um exemplo e ver isso detalhadamente.
Nós criamos uma função assíncrona chamada `lifespan()` com `yield` como este:
-```Python hl_lines="16 19"
-{!../../../docs_src/events/tutorial003.py!}
-```
+{* ../../docs_src/events/tutorial003.py hl[16,19] *}
Aqui nós estamos simulando a *inicialização* custosa do carregamento do modelo colocando a (falsa) função de modelo no dicionário com modelos de _machine learning_ antes do `yield`. Este código será executado **antes** da aplicação **começar a receber requisições**, durante a *inicialização*.
E então, logo após o `yield`, descarregaremos o modelo. Esse código será executado **após** a aplicação **terminar de lidar com as requisições**, pouco antes do *encerramento*. Isso poderia, por exemplo, liberar recursos como memória ou GPU.
-/// tip | "Dica"
+/// tip | Dica
O `shutdown` aconteceria quando você estivesse **encerrando** a aplicação.
@@ -51,9 +49,7 @@ Talvez você precise inicializar uma nova versão, ou apenas cansou de executá-
A primeira coisa a notar, é que estamos definindo uma função assíncrona com `yield`. Isso é muito semelhante à Dependências com `yield`.
-```Python hl_lines="14-19"
-{!../../../docs_src/events/tutorial003.py!}
-```
+{* ../../docs_src/events/tutorial003.py hl[14:19] *}
A primeira parte da função, antes do `yield`, será executada **antes** da aplicação inicializar.
@@ -65,9 +61,7 @@ Se você verificar, a função está decorada com um `@asynccontextmanager`.
Que converte a função em algo chamado de "**Gerenciador de Contexto Assíncrono**".
-```Python hl_lines="1 13"
-{!../../../docs_src/events/tutorial003.py!}
-```
+{* ../../docs_src/events/tutorial003.py hl[1,13] *}
Um **gerenciador de contexto** em Python é algo que você pode usar em uma declaração `with`, por exemplo, `open()` pode ser usado como um gerenciador de contexto:
@@ -89,13 +83,11 @@ No nosso exemplo de código acima, nós não usamos ele diretamente, mas nós pa
O parâmetro `lifespan` da aplicação `FastAPI` usa um **Gerenciador de Contexto Assíncrono**, então nós podemos passar nosso novo gerenciador de contexto assíncrono do `lifespan` para ele.
-```Python hl_lines="22"
-{!../../../docs_src/events/tutorial003.py!}
-```
+{* ../../docs_src/events/tutorial003.py hl[22] *}
## Eventos alternativos (deprecados)
-/// warning | "Aviso"
+/// warning | Aviso
A maneira recomendada para lidar com a *inicialização* e o *encerramento* é usando o parâmetro `lifespan` da aplicação `FastAPI` como descrito acima.
@@ -113,9 +105,7 @@ Essas funções podem ser declaradas com `async def` ou `def` normal.
Para adicionar uma função que deve rodar antes da aplicação iniciar, declare-a com o evento `"startup"`:
-```Python hl_lines="8"
-{!../../../docs_src/events/tutorial001.py!}
-```
+{* ../../docs_src/events/tutorial001.py hl[8] *}
Nesse caso, a função de manipulação de evento `startup` irá inicializar os itens do "banco de dados" (só um `dict`) com alguns valores.
@@ -127,19 +117,17 @@ E sua aplicação não irá começar a receber requisições até que todos os m
Para adicionar uma função que deve ser executada quando a aplicação estiver encerrando, declare ela com o evento `"shutdown"`:
-```Python hl_lines="6"
-{!../../../docs_src/events/tutorial002.py!}
-```
+{* ../../docs_src/events/tutorial002.py hl[6] *}
Aqui, a função de manipulação de evento `shutdown` irá escrever uma linha de texto `"Application shutdown"` no arquivo `log.txt`.
-/// info | "Informação"
+/// info | Informação
Na função `open()`, o `mode="a"` significa "acrescentar", então, a linha irá ser adicionada depois de qualquer coisa que esteja naquele arquivo, sem sobrescrever o conteúdo anterior.
///
-/// tip | "Dica"
+/// tip | Dica
Perceba que nesse caso nós estamos usando a função padrão do Python `open()` que interage com um arquivo.
@@ -165,9 +153,9 @@ Só um detalhe técnico para nerds curiosos. 🤓
Por baixo, na especificação técnica ASGI, essa é a parte do Protocolo Lifespan, e define eventos chamados `startup` e `shutdown`.
-/// info | "Informação"
+/// info | Informação
-Você pode ler mais sobre o manipulador `lifespan` do Starlette na Documentação do Lifespan Starlette.
+Você pode ler mais sobre o manipulador `lifespan` do Starlette na Documentação do Lifespan Starlette.
Incluindo como manipular estado do lifespan que pode ser usado em outras áreas do seu código.
diff --git a/docs/pt/docs/advanced/generate-clients.md b/docs/pt/docs/advanced/generate-clients.md
new file mode 100644
index 0000000000..dc6b29511a
--- /dev/null
+++ b/docs/pt/docs/advanced/generate-clients.md
@@ -0,0 +1,261 @@
+# Generate Clients
+
+Como o **FastAPI** é baseado na especificação **OpenAPI**, você obtém compatibilidade automática com muitas ferramentas, incluindo a documentação automática da API (fornecida pelo Swagger UI).
+
+Uma vantagem particular que nem sempre é óbvia é que você pode **gerar clientes** (às vezes chamados de **SDKs**) para a sua API, para muitas **linguagens de programação** diferentes.
+
+## Geradores de Clientes OpenAPI
+
+Existem muitas ferramentas para gerar clientes a partir do **OpenAPI**.
+
+Uma ferramenta comum é o OpenAPI Generator.
+
+Se voce está construindo um **frontend**, uma alternativa muito interessante é o openapi-ts.
+
+## Geradores de Clientes e SDKs - Patrocinadores
+
+Existem também alguns geradores de clientes e SDKs baseados na OpenAPI (FastAPI) **patrocinados por empresas**, em alguns casos eles podem oferecer **recursos adicionais** além de SDKs/clientes gerados de alta qualidade.
+
+Alguns deles também ✨ [**patrocinam o FastAPI**](../help-fastapi.md#sponsor-the-author){.internal-link target=_blank} ✨, isso garante o **desenvolvimento** contínuo e saudável do FastAPI e seu **ecossistema**.
+
+E isso mostra o verdadeiro compromisso deles com o FastAPI e sua **comunidade** (você), pois eles não apenas querem fornecer um **bom serviço**, mas também querem garantir que você tenha um **framework bom e saudável**, o FastAPI. 🙇
+
+Por exemplo, você pode querer experimentar:
+
+* Speakeasy
+* Stainless
+* liblab
+
+Existem também várias outras empresas que oferecem serviços semelhantes que você pode pesquisar e encontrar online. 🤓
+
+## Gerar um Cliente Frontend TypeScript
+
+Vamos começar com um aplicativo **FastAPI** simples:
+
+{* ../../docs_src/generate_clients/tutorial001_py39.py hl[7:9,12:13,16:17,21] *}
+
+Note que as *operações de rota* definem os modelos que usam para o corpo da requisição e o corpo da resposta, usando os modelos `Item` e `ResponseMessage`.
+
+### Documentação da API
+
+Se você acessar a documentação da API, verá que ela tem os **schemas** para os dados a serem enviados nas requisições e recebidos nas respostas:
+
+
+
+Você pode ver esses schemas porque eles foram declarados com os modelos no app.
+
+Essas informações estão disponíveis no **OpenAPI schema** do app e são mostradas na documentação da API (pelo Swagger UI).
+
+E essas mesmas informações dos modelos que estão incluídas no OpenAPI são o que pode ser usado para **gerar o código do cliente**.
+
+### Gerar um Cliente TypeScript
+
+Agora que temos o app com os modelos, podemos gerar o código do cliente para o frontend.
+
+#### Instalar o `openapi-ts`
+
+Você pode instalar o `openapi-ts` no seu código frontend com:
+
+
+
+Você também obterá preenchimento automático para o corpo a ser enviado:
+
+
+
+/// tip | Dica
+
+Observe o preenchimento automático para `name` e `price`, que foi definido no aplicativo FastAPI, no modelo `Item`.
+
+///
+
+Você terá erros em linha para os dados que você envia:
+
+
+
+O objeto de resposta também terá preenchimento automático:
+
+
+
+## App FastAPI com Tags
+
+Em muitos casos seu app FastAPI será maior, e você provavelmente usará tags para separar diferentes grupos de *operações de rota*.
+
+Por exemplo, você poderia ter uma seção para **items** e outra seção para **users**, e elas poderiam ser separadas por tags:
+
+{* ../../docs_src/generate_clients/tutorial002_py39.py hl[21,26,34] *}
+
+### Gerar um Cliente TypeScript com Tags
+
+Se você gerar um cliente para um app FastAPI usando tags, normalmente também separará o código do cliente com base nas tags.
+
+Dessa forma, você poderá ter as coisas ordenadas e agrupadas corretamente para o código do cliente:
+
+
+
+Nesse caso você tem:
+
+* `ItemsService`
+* `UsersService`
+
+### Nomes dos Métodos do Cliente
+
+Agora os nomes dos métodos gerados como `createItemItemsPost` não parecem muito "limpos":
+
+```TypeScript
+ItemsService.createItemItemsPost({name: "Plumbus", price: 5})
+```
+
+...isto ocorre porque o gerador de clientes usa o **operation ID** interno do OpenAPI para cada *operação de rota*.
+
+O OpenAPI exige que cada operation ID seja único em todas as *operações de rota*, então o FastAPI usa o **nome da função**, o **caminho** e o **método/operacao HTTP** para gerar esse operation ID, porque dessa forma ele pode garantir que os operation IDs sejam únicos.
+
+Mas eu vou te mostrar como melhorar isso a seguir. 🤓
+
+### IDs de Operação Personalizados e Melhores Nomes de Método
+
+Você pode **modificar** a maneira como esses IDs de operação são **gerados** para torná-los mais simples e ter **nomes de método mais simples** nos clientes.
+
+Neste caso, você terá que garantir que cada ID de operação seja **único** de alguma outra maneira.
+
+Por exemplo, você poderia garantir que cada *operação de rota* tenha uma tag, e então gerar o ID da operação com base na **tag** e no **nome** da *operação de rota* (o nome da função).
+
+### Função Personalizada para Gerar IDs de Operação Únicos
+
+O FastAPI usa um **ID único** para cada *operação de rota*, ele é usado para o **ID da operação** e também para os nomes de quaisquer modelos personalizados necessários, para requisições ou respostas.
+
+Você pode personalizar essa função. Ela recebe uma `APIRoute` e gera uma string.
+
+Por exemplo, aqui está usando a primeira tag (você provavelmente terá apenas uma tag) e o nome da *operação de rota* (o nome da função).
+
+Você pode então passar essa função personalizada para o **FastAPI** como o parâmetro `generate_unique_id_function`:
+
+{* ../../docs_src/generate_clients/tutorial003_py39.py hl[6:7,10] *}
+
+### Gerar um Cliente TypeScript com IDs de Operação Personalizados
+
+Agora, se você gerar o cliente novamente, verá que ele tem os nomes dos métodos melhorados:
+
+
+
+Como você pode ver, os nomes dos métodos agora têm a tag e, em seguida, o nome da função. Agora eles não incluem informações do caminho da URL e da operação HTTP.
+
+### Pré-processar a Especificação OpenAPI para o Gerador de Clientes
+
+O código gerado ainda tem algumas **informações duplicadas**.
+
+Nós já sabemos que esse método está relacionado aos **items** porque essa palavra está no `ItemsService` (retirada da tag), mas ainda temos o nome da tag prefixado no nome do método também. 😕
+
+Provavelmente ainda queremos mantê-lo para o OpenAPI em geral, pois isso garantirá que os IDs de operação sejam **únicos**.
+
+Mas para o cliente gerado, poderíamos **modificar** os IDs de operação do OpenAPI logo antes de gerar os clientes, apenas para tornar esses nomes de método mais **simples**.
+
+Poderíamos baixar o JSON do OpenAPI para um arquivo `openapi.json` e então poderíamos **remover essa tag prefixada** com um script como este:
+
+{* ../../docs_src/generate_clients/tutorial004.py *}
+
+//// tab | Node.js
+
+```Javascript
+{!> ../../docs_src/generate_clients/tutorial004.js!}
+```
+
+////
+
+Com isso, os IDs de operação seriam renomeados de coisas como `items-get_items` para apenas `get_items`, dessa forma o gerador de clientes pode gerar nomes de métodos mais simples.
+
+### Gerar um Cliente TypeScript com o OpenAPI Pré-processado
+
+Agora, como o resultado final está em um arquivo `openapi.json`, você modificaria o `package.json` para usar esse arquivo local, por exemplo:
+
+```JSON hl_lines="7"
+{
+ "name": "frontend-app",
+ "version": "1.0.0",
+ "description": "",
+ "main": "index.js",
+ "scripts": {
+ "generate-client": "openapi-ts --input ./openapi.json --output ./src/client --client axios"
+ },
+ "author": "",
+ "license": "",
+ "devDependencies": {
+ "@hey-api/openapi-ts": "^0.27.38",
+ "typescript": "^4.6.2"
+ }
+}
+```
+
+Depois de gerar o novo cliente, você teria agora **nomes de métodos "limpos"**, com todo o **preenchimento automático**, **erros em linha**, etc:
+
+
+
+## Benefícios
+
+Ao usar os clientes gerados automaticamente, você teria **preenchimento automático** para:
+
+* Métodos.
+* Corpo de requisições, parâmetros da query, etc.
+* Corpo de respostas.
+
+Você também teria **erros em linha** para tudo.
+
+E sempre que você atualizar o código do backend, e **regenerar** o frontend, ele teria quaisquer novas *operações de rota* disponíveis como métodos, as antigas removidas, e qualquer outra alteração seria refletida no código gerado. 🤓
+
+Isso também significa que se algo mudar, será **refletido** no código do cliente automaticamente. E se você **construir** o cliente, ele dará erro se houver alguma **incompatibilidade** nos dados usados.
+
+Então, você **detectaria vários erros** muito cedo no ciclo de desenvolvimento, em vez de ter que esperar que os erros apareçam para seus usuários finais em produção e então tentar depurar onde está o problema. ✨
diff --git a/docs/pt/docs/advanced/index.md b/docs/pt/docs/advanced/index.md
index 2569fc914b..22ba2bf4a4 100644
--- a/docs/pt/docs/advanced/index.md
+++ b/docs/pt/docs/advanced/index.md
@@ -6,7 +6,7 @@ O [Tutorial - Guia de Usuário](../tutorial/index.md){.internal-link target=_bla
Na próxima seção você verá outras opções, configurações, e recursos adicionais.
-/// tip | "Dica"
+/// tip | Dica
As próximas seções **não são necessáriamente "avançadas"**
diff --git a/docs/pt/docs/advanced/middleware.md b/docs/pt/docs/advanced/middleware.md
new file mode 100644
index 0000000000..7700939f06
--- /dev/null
+++ b/docs/pt/docs/advanced/middleware.md
@@ -0,0 +1,96 @@
+# Middleware Avançado
+
+No tutorial principal você leu como adicionar [Middleware Personalizado](../tutorial/middleware.md){.internal-link target=_blank} à sua aplicação.
+
+E então você também leu como lidar com [CORS com o `CORSMiddleware`](../tutorial/cors.md){.internal-link target=_blank}.
+
+Nesta seção, veremos como usar outros middlewares.
+
+## Adicionando middlewares ASGI
+
+Como o **FastAPI** é baseado no Starlette e implementa a especificação ASGI, você pode usar qualquer middleware ASGI.
+
+O middleware não precisa ser feito para o FastAPI ou Starlette para funcionar, desde que siga a especificação ASGI.
+
+No geral, os middlewares ASGI são classes que esperam receber um aplicativo ASGI como o primeiro argumento.
+
+Então, na documentação de middlewares ASGI de terceiros, eles provavelmente dirão para você fazer algo como:
+
+```Python
+from unicorn import UnicornMiddleware
+
+app = SomeASGIApp()
+
+new_app = UnicornMiddleware(app, some_config="rainbow")
+```
+
+Mas, o FastAPI (na verdade, o Starlette) fornece uma maneira mais simples de fazer isso que garante que os middlewares internos lidem com erros do servidor e que os manipuladores de exceções personalizados funcionem corretamente.
+
+Para isso, você usa `app.add_middleware()` (como no exemplo para CORS).
+
+```Python
+from fastapi import FastAPI
+from unicorn import UnicornMiddleware
+
+app = FastAPI()
+
+app.add_middleware(UnicornMiddleware, some_config="rainbow")
+```
+
+`app.add_middleware()` recebe uma classe de middleware como o primeiro argumento e quaisquer argumentos adicionais a serem passados para o middleware.
+
+## Middlewares Integrados
+
+**FastAPI** inclui vários middlewares para casos de uso comuns, veremos a seguir como usá-los.
+
+/// note | Detalhes Técnicos
+
+Para o próximo exemplo, você também poderia usar `from starlette.middleware.something import SomethingMiddleware`.
+
+**FastAPI** fornece vários middlewares em `fastapi.middleware` apenas como uma conveniência para você, o desenvolvedor. Mas a maioria dos middlewares disponíveis vem diretamente do Starlette.
+
+///
+
+## `HTTPSRedirectMiddleware`
+
+Garante que todas as requisições devem ser `https` ou `wss`.
+
+Qualquer requisição para `http` ou `ws` será redirecionada para o esquema seguro.
+
+{* ../../docs_src/advanced_middleware/tutorial001.py hl[2,6] *}
+
+## `TrustedHostMiddleware`
+
+Garante que todas as requisições recebidas tenham um cabeçalho `Host` corretamente configurado, a fim de proteger contra ataques de cabeçalho de host HTTP.
+
+{* ../../docs_src/advanced_middleware/tutorial002.py hl[2,6:8] *}
+
+Os seguintes argumentos são suportados:
+
+* `allowed_hosts` - Uma lista de nomes de domínio que são permitidos como nomes de host. Domínios com coringa, como `*.example.com`, são suportados para corresponder a subdomínios. Para permitir qualquer nome de host, use `allowed_hosts=["*"]` ou omita o middleware.
+
+Se uma requisição recebida não for validada corretamente, uma resposta `400` será enviada.
+
+## `GZipMiddleware`
+
+Gerencia respostas GZip para qualquer requisição que inclua `"gzip"` no cabeçalho `Accept-Encoding`.
+
+O middleware lidará com respostas padrão e de streaming.
+
+{* ../../docs_src/advanced_middleware/tutorial003.py hl[2,6] *}
+
+Os seguintes argumentos são suportados:
+
+* `minimum_size` - Não comprima respostas menores que este tamanho mínimo em bytes. O padrão é `500`.
+* `compresslevel` - Usado durante a compressão GZip. É um inteiro variando de 1 a 9. O padrão é `9`. Um valor menor resulta em uma compressão mais rápida, mas em arquivos maiores, enquanto um valor maior resulta em uma compressão mais lenta, mas em arquivos menores.
+
+## Outros middlewares
+
+Há muitos outros middlewares ASGI.
+
+Por exemplo:
+
+* Uvicorn's `ProxyHeadersMiddleware`
+* MessagePack
+
+Para checar outros middlewares disponíveis, confira Documentação de Middlewares do Starlette e a Lista Incrível do ASGI.
diff --git a/docs/pt/docs/advanced/openapi-callbacks.md b/docs/pt/docs/advanced/openapi-callbacks.md
new file mode 100644
index 0000000000..b0659d3d6a
--- /dev/null
+++ b/docs/pt/docs/advanced/openapi-callbacks.md
@@ -0,0 +1,186 @@
+# Callbacks na OpenAPI
+
+Você poderia criar uma API com uma *operação de rota* que poderia acionar uma solicitação a uma *API externa* criada por outra pessoa (provavelmente o mesmo desenvolvedor que estaria *usando* sua API).
+
+O processo que acontece quando seu aplicativo de API chama a *API externa* é chamado de "callback". Porque o software que o desenvolvedor externo escreveu envia uma solicitação para sua API e então sua API *chama de volta*, enviando uma solicitação para uma *API externa* (que provavelmente foi criada pelo mesmo desenvolvedor).
+
+Nesse caso, você poderia querer documentar como essa API externa *deveria* ser. Que *operação de rota* ela deveria ter, que corpo ela deveria esperar, que resposta ela deveria retornar, etc.
+
+## Um aplicativo com callbacks
+
+Vamos ver tudo isso com um exemplo.
+
+Imagine que você tem um aplicativo que permite criar faturas.
+
+Essas faturas terão um `id`, `title` (opcional), `customer` e `total`.
+
+O usuário da sua API (um desenvolvedor externo) criará uma fatura em sua API com uma solicitação POST.
+
+Então sua API irá (vamos imaginar):
+
+* Enviar uma solicitação de pagamento para o desenvolvedor externo.
+* Coletar o dinheiro.
+* Enviar a notificação de volta para o usuário da API (o desenvolvedor externo).
+* Isso será feito enviando uma solicitação POST (de *sua API*) para alguma *API externa* fornecida por esse desenvolvedor externo (este é o "callback").
+
+## O aplicativo **FastAPI** normal
+
+Vamos primeiro ver como o aplicativo da API normal se pareceria antes de adicionar o callback.
+
+Ele terá uma *operação de rota* que receberá um corpo `Invoice`, e um parâmetro de consulta `callback_url` que conterá a URL para o callback.
+
+Essa parte é bastante normal, a maior parte do código provavelmente já é familiar para você:
+
+{* ../../docs_src/openapi_callbacks/tutorial001.py hl[9:13,36:53] *}
+
+/// tip | Dica
+
+O parâmetro de consulta `callback_url` usa um tipo Pydantic Url.
+
+///
+
+A única coisa nova é o argumento `callbacks=invoices_callback_router.routes` no decorador da *operação de rota*. Veremos o que é isso a seguir.
+
+## Documentando o callback
+
+O código real do callback dependerá muito do seu próprio aplicativo de API.
+
+E provavelmente variará muito de um aplicativo para o outro.
+
+Poderia ser apenas uma ou duas linhas de código, como:
+
+```Python
+callback_url = "https://example.com/api/v1/invoices/events/"
+httpx.post(callback_url, json={"description": "Invoice paid", "paid": True})
+```
+
+Mas possivelmente a parte mais importante do callback é garantir que o usuário da sua API (o desenvolvedor externo) implemente a *API externa* corretamente, de acordo com os dados que *sua API* vai enviar no corpo da solicitação do callback, etc.
+
+Então, o que faremos a seguir é adicionar o código para documentar como essa *API externa* deve ser para receber o callback de *sua API*.
+
+A documentação aparecerá na interface do Swagger em `/docs` em sua API, e permitirá que os desenvolvedores externos saibam como construir a *API externa*.
+
+Esse exemplo não implementa o callback em si (que poderia ser apenas uma linha de código), apenas a parte da documentação.
+
+/// tip | Dica
+
+O callback real é apenas uma solicitação HTTP.
+
+Quando implementando o callback por você mesmo, você pode usar algo como HTTPX ou Requisições.
+
+///
+
+## Escrevendo o código de documentação do callback
+
+Esse código não será executado em seu aplicativo, nós só precisamos dele para *documentar* como essa *API externa* deveria ser.
+
+Mas, você já sabe como criar facilmente documentação automática para uma API com o **FastAPI**.
+
+Então vamos usar esse mesmo conhecimento para documentar como a *API externa* deveria ser... criando as *operações de rota* que a *API externa* deveria implementar (as que sua API irá chamar).
+
+/// tip | Dica
+
+Quando escrever o código para documentar um callback, pode ser útil imaginar que você é aquele *desenvolvedor externo*. E que você está atualmente implementando a *API externa*, não *sua API*.
+
+Adotar temporariamente esse ponto de vista (do *desenvolvedor externo*) pode ajudar a sentir que é mais óbvio onde colocar os parâmetros, o modelo Pydantic para o corpo, para a resposta, etc. para essa *API externa*.
+
+///
+
+### Criar um `APIRouter` para o callback
+
+Primeiramente crie um novo `APIRouter` que conterá um ou mais callbacks.
+
+{* ../../docs_src/openapi_callbacks/tutorial001.py hl[3,25] *}
+
+### Crie a *operação de rota* do callback
+
+Para criar a *operação de rota* do callback, use o mesmo `APIRouter` que você criou acima.
+
+Ele deve parecer exatamente como uma *operação de rota* normal do FastAPI:
+
+* Ele provavelmente deveria ter uma declaração do corpo que deveria receber, por exemplo. `body: InvoiceEvent`.
+* E também deveria ter uma declaração de um código de status de resposta, por exemplo. `response_model=InvoiceEventReceived`.
+
+{* ../../docs_src/openapi_callbacks/tutorial001.py hl[16:18,21:22,28:32] *}
+
+Há 2 diferenças principais de uma *operação de rota* normal:
+
+* Ela não necessita ter nenhum código real, porque seu aplicativo nunca chamará esse código. Ele é usado apenas para documentar a *API externa*. Então, a função poderia ter apenas `pass`.
+* A *rota* pode conter uma expressão OpenAPI 3 (veja mais abaixo) onde pode usar variáveis com parâmetros e partes da solicitação original enviada para *sua API*.
+
+### A expressão do caminho do callback
+
+A *rota* do callback pode ter uma expressão OpenAPI 3 que pode conter partes da solicitação original enviada para *sua API*.
+
+Nesse caso, é a `str`:
+
+```Python
+"{$callback_url}/invoices/{$request.body.id}"
+```
+
+Então, se o usuário da sua API (o desenvolvedor externo) enviar uma solicitação para *sua API* para:
+
+```
+https://yourapi.com/invoices/?callback_url=https://www.external.org/events
+```
+
+com um corpo JSON de:
+
+```JSON
+{
+ "id": "2expen51ve",
+ "customer": "Mr. Richie Rich",
+ "total": "9999"
+}
+```
+
+então *sua API* processará a fatura e, em algum momento posterior, enviará uma solicitação de callback para o `callback_url` (a *API externa*):
+
+```
+https://www.external.org/events/invoices/2expen51ve
+```
+
+com um corpo JSON contendo algo como:
+
+```JSON
+{
+ "description": "Payment celebration",
+ "paid": true
+}
+```
+
+e esperaria uma resposta daquela *API externa* com um corpo JSON como:
+
+```JSON
+{
+ "ok": true
+}
+```
+
+/// tip | Dica
+
+Perceba como a URL de callback usada contém a URL recebida como um parâmetro de consulta em `callback_url` (`https://www.external.org/events`) e também o `id` da fatura de dentro do corpo JSON (`2expen51ve`).
+
+///
+
+### Adicionar o roteador de callback
+
+Nesse ponto você tem a(s) *operação de rota de callback* necessária(s) (a(s) que o *desenvolvedor externo* deveria implementar na *API externa*) no roteador de callback que você criou acima.
+
+Agora use o parâmetro `callbacks` no decorador da *operação de rota de sua API* para passar o atributo `.routes` (que é na verdade apenas uma `list` de rotas/*operações de rota*) do roteador de callback que você criou acima:
+
+{* ../../docs_src/openapi_callbacks/tutorial001.py hl[35] *}
+
+/// tip | Dica
+
+Perceba que você não está passando o roteador em si (`invoices_callback_router`) para `callback=`, mas o atributo `.routes`, como em `invoices_callback_router.routes`.
+
+///
+
+### Verifique a documentação
+
+Agora você pode iniciar seu aplicativo e ir para http://127.0.0.1:8000/docs.
+
+Você verá sua documentação incluindo uma seção "Callbacks" para sua *operação de rota* que mostra como a *API externa* deveria ser:
+
+
diff --git a/docs/pt/docs/advanced/openapi-webhooks.md b/docs/pt/docs/advanced/openapi-webhooks.md
index 2ccd0e8196..f35922234f 100644
--- a/docs/pt/docs/advanced/openapi-webhooks.md
+++ b/docs/pt/docs/advanced/openapi-webhooks.md
@@ -22,7 +22,7 @@ Com o **FastAPI**, utilizando o OpenAPI, você pode definir os nomes destes webh
Isto pode facilitar bastante para os seus usuários **implementarem as APIs deles** para receber as requisições dos seus **webhooks**, eles podem inclusive ser capazes de gerar parte do código da API deles.
-/// info | "Informação"
+/// info | Informação
Webhooks estão disponíveis a partir do OpenAPI 3.1.0, e possui suporte do FastAPI a partir da versão `0.99.0`.
@@ -32,13 +32,11 @@ Webhooks estão disponíveis a partir do OpenAPI 3.1.0, e possui suporte do Fast
Quando você cria uma aplicação com o **FastAPI**, existe um atributo chamado `webhooks`, que você utilizar para defini-los da mesma maneira que você definiria as suas **operações de rotas**, utilizando por exemplo `@app.webhooks.post()`.
-```Python hl_lines="9-13 36-53"
-{!../../../docs_src/openapi_webhooks/tutorial001.py!}
-```
+{* ../../docs_src/openapi_webhooks/tutorial001.py hl[9:13,36:53] *}
Os webhooks que você define aparecerão no esquema do **OpenAPI** e na **página de documentação** gerada automaticamente.
-/// info | "Informação"
+/// info | Informação
O objeto `app.webhooks` é na verdade apenas um `APIRouter`, o mesmo tipo que você utilizaria ao estruturar a sua aplicação com diversos arquivos.
diff --git a/docs/pt/docs/advanced/path-operation-advanced-configuration.md b/docs/pt/docs/advanced/path-operation-advanced-configuration.md
new file mode 100644
index 0000000000..411d0f9a7a
--- /dev/null
+++ b/docs/pt/docs/advanced/path-operation-advanced-configuration.md
@@ -0,0 +1,204 @@
+# Configuração Avançada da Operação de Rota
+
+## operationId do OpenAPI
+
+/// warning | Aviso
+
+Se você não é um "especialista" no OpenAPI, você provavelmente não precisa disso.
+
+///
+
+Você pode definir o `operationId` do OpenAPI que será utilizado na sua *operação de rota* com o parâmetro `operation_id`.
+
+Você precisa ter certeza que ele é único para cada operação.
+
+{* ../../docs_src/path_operation_advanced_configuration/tutorial001.py hl[6] *}
+
+### Utilizando o nome da *função de operação de rota* como o operationId
+
+Se você quiser utilizar o nome das funções da sua API como `operationId`s, você pode iterar sobre todos esses nomes e sobrescrever o `operationId` em cada *operação de rota* utilizando o `APIRoute.name` dela.
+
+Você deve fazer isso depois de adicionar todas as suas *operações de rota*.
+
+{* ../../docs_src/path_operation_advanced_configuration/tutorial002.py hl[2,12:21,24] *}
+
+/// tip | Dica
+
+Se você chamar `app.openapi()` manualmente, os `operationId`s devem ser atualizados antes dessa chamada.
+
+///
+
+/// warning | Aviso
+
+Se você fizer isso, você tem que ter certeza de que cada uma das suas *funções de operação de rota* tem um nome único.
+
+Mesmo que elas estejam em módulos (arquivos Python) diferentes.
+
+///
+
+## Excluir do OpenAPI
+
+Para excluir uma *operação de rota* do esquema OpenAPI gerado (e por consequência, dos sistemas de documentação automáticos), utilize o parâmetro `include_in_schema` e defina ele como `False`:
+
+{* ../../docs_src/path_operation_advanced_configuration/tutorial003.py hl[6] *}
+
+## Descrição avançada a partir de docstring
+
+Você pode limitar as linhas utilizadas a partir de uma docstring de uma *função de operação de rota* para o OpenAPI.
+
+Adicionar um `\f` (um caractere de escape para alimentação de formulário) faz com que o **FastAPI** restrinja a saída utilizada pelo OpenAPI até esse ponto.
+
+Ele não será mostrado na documentação, mas outras ferramentas (como o Sphinx) serão capazes de utilizar o resto do texto.
+
+{* ../../docs_src/path_operation_advanced_configuration/tutorial004.py hl[19:29] *}
+
+## Respostas Adicionais
+
+Você provavelmente já viu como declarar o `response_model` e `status_code` para uma *operação de rota*.
+
+Isso define os metadados sobre a resposta principal da *operação de rota*.
+
+Você também pode declarar respostas adicionais, com seus modelos, códigos de status, etc.
+
+Existe um capítulo inteiro da nossa documentação sobre isso, você pode ler em [Retornos Adicionais no OpenAPI](additional-responses.md){.internal-link target=_blank}.
+
+## Extras do OpenAPI
+
+Quando você declara uma *operação de rota* na sua aplicação, o **FastAPI** irá gerar os metadados relevantes da *operação de rota* automaticamente para serem incluídos no esquema do OpenAPI.
+
+/// note | Nota
+
+Na especificação do OpenAPI, isso é chamado de um Objeto de Operação.
+
+///
+
+Ele possui toda a informação sobre a *operação de rota* e é usado para gerar a documentação automaticamente.
+
+Ele inclui os atributos `tags`, `parameters`, `requestBody`, `responses`, etc.
+
+Esse esquema específico para uma *operação de rota* normalmente é gerado automaticamente pelo **FastAPI**, mas você também pode estender ele.
+
+/// tip | Dica
+
+Esse é um ponto de extensão de baixo nível.
+
+Caso você só precise declarar respostas adicionais, uma forma conveniente de fazer isso é com [Retornos Adicionais no OpenAPI](additional-responses.md){.internal-link target=_blank}.
+
+///
+
+Você pode estender o esquema do OpenAPI para uma *operação de rota* utilizando o parâmetro `openapi_extra`.
+
+### Extensões do OpenAPI
+
+Esse parâmetro `openapi_extra` pode ser útil, por exemplo, para declarar [Extensões do OpenAPI](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#specificationExtensions):
+
+{* ../../docs_src/path_operation_advanced_configuration/tutorial005.py hl[6] *}
+
+Se você abrir os documentos criados automaticamente para a API, sua extensão aparecerá no final da *operação de rota* específica.
+
+
+
+E se você olhar o esquema OpenAPI resultante (na rota `/openapi.json` da sua API), você verá que a sua extensão também faz parte da *operação de rota* específica:
+
+```JSON hl_lines="22"
+{
+ "openapi": "3.1.0",
+ "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"
+ }
+ }
+ }
+}
+```
+
+### Esquema de *operação de rota* do OpenAPI personalizado
+
+O dicionário em `openapi_extra` vai ter todos os seus níveis mesclados dentro do esquema OpenAPI gerado automaticamente para a *operação de rota*.
+
+Então, você pode adicionar dados extras para o esquema gerado automaticamente.
+
+Por exemplo, você poderia optar por ler e validar a requisição com seu próprio código, sem utilizar funcionalidades automatizadas do FastAPI com o Pydantic, mas você ainda pode quere definir a requisição no esquema OpenAPI.
+
+Você pode fazer isso com `openapi_extra`:
+
+{* ../../docs_src/path_operation_advanced_configuration/tutorial006.py hl[19:36,39:40] *}
+
+Nesse exemplo, nós não declaramos nenhum modelo do Pydantic. Na verdade, o corpo da requisição não está nem mesmo analisado como JSON, ele é lido diretamente como `bytes` e a função `magic_data_reader()` seria a responsável por analisar ele de alguma forma.
+
+De toda forma, nós podemos declarar o esquema esperado para o corpo da requisição.
+
+### Tipo de conteúdo do OpenAPI personalizado
+
+Utilizando esse mesmo truque, você pode utilizar um modelo Pydantic para definir o esquema JSON que é então incluído na seção do esquema personalizado do OpenAPI na *operação de rota*.
+
+E você pode fazer isso até mesmo quando os dados da requisição não seguem o formato JSON.
+
+Por exemplo, nesta aplicação nós não usamos a funcionalidade integrada ao FastAPI de extrair o esquema JSON dos modelos Pydantic nem a validação automática do JSON. Na verdade, estamos declarando o tipo do conteúdo da requisição como YAML, em vez de JSON:
+
+//// tab | Pydantic v2
+
+{* ../../docs_src/path_operation_advanced_configuration/tutorial007.py hl[17:22,24] *}
+
+////
+
+//// tab | Pydantic v1
+
+{* ../../docs_src/path_operation_advanced_configuration/tutorial007_pv1.py hl[17:22,24] *}
+
+////
+
+/// info | Informação
+
+Na versão 1 do Pydantic, o método para obter o esquema JSON de um modelo é `Item.schema()`, na versão 2 do Pydantic, o método é `Item.model_json_schema()`
+
+///
+
+Entretanto, mesmo que não utilizemos a funcionalidade integrada por padrão, ainda estamos usando um modelo Pydantic para gerar um esquema JSON manualmente para os dados que queremos receber no formato YAML.
+
+Então utilizamos a requisição diretamente, e extraímos o corpo como `bytes`. Isso significa que o FastAPI não vai sequer tentar analisar o corpo da requisição como JSON.
+
+E então no nosso código, nós analisamos o conteúdo YAML diretamente, e estamos utilizando o mesmo modelo Pydantic para validar o conteúdo YAML:
+
+//// tab | Pydantic v2
+
+{* ../../docs_src/path_operation_advanced_configuration/tutorial007.py hl[26:33] *}
+
+////
+
+//// tab | Pydantic v1
+
+{* ../../docs_src/path_operation_advanced_configuration/tutorial007_pv1.py hl[26:33] *}
+
+////
+
+/// info | Informação
+
+Na versão 1 do Pydantic, o método para analisar e validar um objeto era `Item.parse_obj()`, na versão 2 do Pydantic, o método é chamado de `Item.model_validate()`.
+
+///
+
+///tip | Dica
+
+Aqui reutilizamos o mesmo modelo do Pydantic.
+
+Mas da mesma forma, nós poderíamos ter validado de alguma outra forma.
+
+///
diff --git a/docs/pt/docs/advanced/response-change-status-code.md b/docs/pt/docs/advanced/response-change-status-code.md
index 99695c8317..358c12d549 100644
--- a/docs/pt/docs/advanced/response-change-status-code.md
+++ b/docs/pt/docs/advanced/response-change-status-code.md
@@ -20,9 +20,7 @@ Você pode declarar um parâmetro do tipo `Response` em sua *função de operaç
E então você pode definir o `status_code` neste objeto de retorno temporal.
-```Python hl_lines="1 9 12"
-{!../../../docs_src/response_change_status_code/tutorial001.py!}
-```
+{* ../../docs_src/response_change_status_code/tutorial001.py hl[1,9,12] *}
E então você pode retornar qualquer objeto que você precise, como você faria normalmente (um `dict`, um modelo de banco de dados, etc.).
diff --git a/docs/pt/docs/advanced/response-cookies.md b/docs/pt/docs/advanced/response-cookies.md
new file mode 100644
index 0000000000..f005f0b9b9
--- /dev/null
+++ b/docs/pt/docs/advanced/response-cookies.md
@@ -0,0 +1,51 @@
+# Cookies de Resposta
+
+## Usando um parâmetro `Response`
+
+Você pode declarar um parâmetro do tipo `Response` na sua *função de operação de rota*.
+
+E então você pode definir cookies nesse objeto de resposta *temporário*.
+
+{* ../../docs_src/response_cookies/tutorial002.py hl[1,8:9] *}
+
+Em seguida, você pode retornar qualquer objeto que precise, como normalmente faria (um `dict`, um modelo de banco de dados, etc).
+
+E se você declarou um `response_model`, ele ainda será usado para filtrar e converter o objeto que você retornou.
+
+**FastAPI** usará essa resposta *temporária* para extrair os cookies (também os cabeçalhos e código de status) e os colocará na resposta final que contém o valor que você retornou, filtrado por qualquer `response_model`.
+
+Você também pode declarar o parâmetro `Response` em dependências e definir cookies (e cabeçalhos) nelas.
+
+## Retornando uma `Response` diretamente
+
+Você também pode criar cookies ao retornar uma `Response` diretamente no seu código.
+
+Para fazer isso, você pode criar uma resposta como descrito em [Retornando uma Resposta Diretamente](response-directly.md){.internal-link target=_blank}.
+
+Então, defina os cookies nela e a retorne:
+
+{* ../../docs_src/response_cookies/tutorial001.py hl[10:12] *}
+
+/// tip | Dica
+
+Lembre-se de que se você retornar uma resposta diretamente em vez de usar o parâmetro `Response`, FastAPI a retornará diretamente.
+
+Portanto, você terá que garantir que seus dados sejam do tipo correto. E.g. será compatível com JSON se você estiver retornando um `JSONResponse`.
+
+E também que você não esteja enviando nenhum dado que deveria ter sido filtrado por um `response_model`.
+
+///
+
+### Mais informações
+
+/// note | Detalhes Técnicos
+
+Você também poderia usar `from starlette.responses import Response` ou `from starlette.responses import JSONResponse`.
+
+**FastAPI** fornece as mesmas `starlette.responses` em `fastapi.responses` apenas como uma conveniência para você, o desenvolvedor. Mas a maioria das respostas disponíveis vem diretamente do Starlette.
+
+E como o `Response` pode ser usado frequentemente para definir cabeçalhos e cookies, o **FastAPI** também o fornece em `fastapi.Response`.
+
+///
+
+Para ver todos os parâmetros e opções disponíveis, verifique a documentação no Starlette.
diff --git a/docs/pt/docs/advanced/response-directly.md b/docs/pt/docs/advanced/response-directly.md
new file mode 100644
index 0000000000..ea717be008
--- /dev/null
+++ b/docs/pt/docs/advanced/response-directly.md
@@ -0,0 +1,66 @@
+# Retornando uma Resposta Diretamente
+
+Quando você cria uma *operação de rota* no **FastAPI** você pode retornar qualquer dado nela: um dicionário (`dict`), uma lista (`list`), um modelo do Pydantic ou do seu banco de dados, etc.
+
+Por padrão, o **FastAPI** irá converter automaticamente o valor do retorno para JSON utilizando o `jsonable_encoder` explicado em [JSON Compatible Encoder](../tutorial/encoder.md){.internal-link target=_blank}.
+
+Então, por baixo dos panos, ele incluiria esses dados compatíveis com JSON (e.g. um `dict`) dentro de uma `JSONResponse` que é utilizada para enviar uma resposta para o cliente.
+
+Mas você pode retornar a `JSONResponse` diretamente nas suas *operações de rota*.
+
+Pode ser útil para retornar cabeçalhos e cookies personalizados, por exemplo.
+
+## Retornando uma `Response`
+
+Na verdade, você pode retornar qualquer `Response` ou subclasse dela.
+
+/// tip | Dica
+
+A própria `JSONResponse` é uma subclasse de `Response`.
+
+///
+
+E quando você retorna uma `Response`, o **FastAPI** vai repassá-la diretamente.
+
+Ele não vai fazer conversões de dados com modelos do Pydantic, não irá converter a tipagem de nenhum conteúdo, etc.
+
+Isso te dá bastante flexibilidade. Você pode retornar qualquer tipo de dado, sobrescrever qualquer declaração e validação nos dados, etc.
+
+## Utilizando o `jsonable_encoder` em uma `Response`
+
+Como o **FastAPI** não realiza nenhuma mudança na `Response` que você retorna, você precisa garantir que o conteúdo dela está pronto para uso.
+
+Por exemplo, você não pode colocar um modelo do Pydantic em uma `JSONResponse` sem antes convertê-lo em um `dict` com todos os tipos de dados (como `datetime`, `UUID`, etc) convertidos para tipos compatíveis com JSON.
+
+Para esses casos, você pode usar o `jsonable_encoder` para converter seus dados antes de repassá-los para a resposta:
+
+{* ../../docs_src/response_directly/tutorial001.py hl[6:7,21:22] *}
+
+/// note | Detalhes Técnicos
+
+Você também pode utilizar `from starlette.responses import JSONResponse`.
+
+**FastAPI** utiliza a mesma `starlette.responses` como `fastapi.responses` apenas como uma conveniência para você, desenvolvedor. Mas maior parte das respostas disponíveis vem diretamente do Starlette.
+
+///
+
+## Retornando uma `Response`
+
+O exemplo acima mostra todas as partes que você precisa, mas ainda não é muito útil, já que você poderia ter retornado o `item` diretamente, e o **FastAPI** colocaria em uma `JSONResponse` para você, convertendo em um `dict`, etc. Tudo isso por padrão.
+
+Agora, vamos ver como você pode usar isso para retornar uma resposta personalizada.
+
+Vamos dizer quer retornar uma resposta XML.
+
+Você pode colocar o seu conteúdo XML em uma string, colocar em uma `Response`, e retorná-lo:
+
+{* ../../docs_src/response_directly/tutorial002.py hl[1,18] *}
+
+## Notas
+
+Quando você retorna uma `Response` diretamente os dados não são validados, convertidos (serializados) ou documentados automaticamente.
+
+Mas você ainda pode documentar como descrito em [Retornos Adicionais no OpenAPI
+](additional-responses.md){.internal-link target=_blank}.
+
+Você pode ver nas próximas seções como usar/declarar essas `Responses` customizadas enquanto mantém a conversão e documentação automática dos dados.
diff --git a/docs/pt/docs/advanced/response-headers.md b/docs/pt/docs/advanced/response-headers.md
new file mode 100644
index 0000000000..a1fc84cc0e
--- /dev/null
+++ b/docs/pt/docs/advanced/response-headers.md
@@ -0,0 +1,41 @@
+# Cabeçalhos de resposta
+
+## Usando um parâmetro `Response`
+
+Você pode declarar um parâmetro do tipo `Response` na sua *função de operação de rota* (assim como você pode fazer para cookies).
+
+Então você pode definir os cabeçalhos nesse objeto de resposta *temporário*.
+
+{* ../../docs_src/response_headers/tutorial002.py hl[1,7:8] *}
+
+Em seguida você pode retornar qualquer objeto que precisar, da maneira que faria normalmente (um `dict`, um modelo de banco de dados, etc.).
+
+Se você declarou um `response_model`, ele ainda será utilizado para filtrar e converter o objeto que você retornou.
+
+**FastAPI** usará essa resposta *temporária* para extrair os cabeçalhos (cookies e código de status também) e os colocará na resposta final que contém o valor que você retornou, filtrado por qualquer `response_model`.
+
+Você também pode declarar o parâmetro `Response` em dependências e definir cabeçalhos (e cookies) nelas.
+
+## Retornar uma `Response` diretamente
+
+Você também pode adicionar cabeçalhos quando retornar uma `Response` diretamente.
+
+Crie uma resposta conforme descrito em [Retornar uma resposta diretamente](response-directly.md){.internal-link target=_blank} e passe os cabeçalhos como um parâmetro adicional:
+
+{* ../../docs_src/response_headers/tutorial001.py hl[10:12] *}
+
+/// note | Detalhes técnicos
+
+Você também pode usar `from starlette.responses import Response` ou `from starlette.responses import JSONResponse`.
+
+**FastAPI** fornece as mesmas `starlette.responses` como `fastapi.responses` apenas como uma conveniência para você, desenvolvedor. Mas a maioria das respostas disponíveis vem diretamente do Starlette.
+
+E como a `Response` pode ser usada frequentemente para definir cabeçalhos e cookies, **FastAPI** também a fornece em `fastapi.Response`.
+
+///
+
+## Cabeçalhos personalizados
+
+Tenha em mente que cabeçalhos personalizados proprietários podem ser adicionados usando o prefixo 'X-'.
+
+Porém, se voce tiver cabeçalhos personalizados que deseja que um cliente no navegador possa ver, você precisa adicioná-los às suas configurações de CORS (saiba mais em [CORS (Cross-Origin Resource Sharing)](../tutorial/cors.md){.internal-link target=_blank}), usando o parâmetro `expose_headers` descrito na documentação de CORS do Starlette.
diff --git a/docs/pt/docs/advanced/security/http-basic-auth.md b/docs/pt/docs/advanced/security/http-basic-auth.md
new file mode 100644
index 0000000000..3315139273
--- /dev/null
+++ b/docs/pt/docs/advanced/security/http-basic-auth.md
@@ -0,0 +1,108 @@
+# HTTP Basic Auth
+
+Para os casos mais simples, você pode utilizar o HTTP Basic Auth.
+
+No HTTP Basic Auth, a aplicação espera um cabeçalho que contém um usuário e uma senha.
+
+Caso ela não receba, ela retorna um erro HTTP 401 "Unauthorized" (*Não Autorizado*).
+
+E retorna um cabeçalho `WWW-Authenticate` com o valor `Basic`, e um parâmetro opcional `realm`.
+
+Isso sinaliza ao navegador para mostrar o prompt integrado para um usuário e senha.
+
+Então, quando você digitar o usuário e senha, o navegador os envia automaticamente no cabeçalho.
+
+## HTTP Basic Auth Simples
+
+* Importe `HTTPBasic` e `HTTPBasicCredentials`.
+* Crie um "esquema `security`" utilizando `HTTPBasic`.
+* Utilize o `security` com uma dependência em sua *operação de rota*.
+* Isso retorna um objeto do tipo `HTTPBasicCredentials`:
+ * Isto contém o `username` e o `password` enviado.
+
+{* ../../docs_src/security/tutorial006_an_py39.py hl[4,8,12] *}
+
+Quando você tentar abrir a URL pela primeira vez (ou clicar no botão "Executar" nos documentos) o navegador vai pedir pelo seu usuário e senha:
+
+
+
+## Verifique o usuário
+
+Aqui está um exemplo mais completo.
+
+Utilize uma dependência para verificar se o usuário e a senha estão corretos.
+
+Para isso, utilize o módulo padrão do Python `secrets` para verificar o usuário e senha.
+
+O `secrets.compare_digest()` necessita receber `bytes` ou `str` que possuem apenas caracteres ASCII (os em inglês). Isso significa que não funcionaria com caracteres como o `á`, como em `Sebastián`.
+
+Para lidar com isso, primeiramente nós convertemos o `username` e o `password` para `bytes`, codificando-os com UTF-8.
+
+Então nós podemos utilizar o `secrets.compare_digest()` para garantir que o `credentials.username` é `"stanleyjobson"`, e que o `credentials.password` é `"swordfish"`.
+
+{* ../../docs_src/security/tutorial007_an_py39.py hl[1,12:24] *}
+
+Isso seria parecido com:
+
+```Python
+if not (credentials.username == "stanleyjobson") or not (credentials.password == "swordfish"):
+ # Return some error
+ ...
+```
+
+Porém, ao utilizar o `secrets.compare_digest()`, isso estará seguro contra um tipo de ataque chamado "timing attacks" (ataques de temporização).
+
+### Ataques de Temporização
+
+Mas o que é um "timing attack" (ataque de temporização)?
+
+Vamos imaginar que alguns invasores estão tentando adivinhar o usuário e a senha.
+
+E eles enviam uma requisição com um usuário `johndoe` e uma senha `love123`.
+
+Então o código Python em sua aplicação seria equivalente a algo como:
+
+```Python
+if "johndoe" == "stanleyjobson" and "love123" == "swordfish":
+ ...
+```
+
+Mas no exato momento que o Python compara o primeiro `j` em `johndoe` contra o primeiro `s` em `stanleyjobson`, ele retornará `False`, porque ele já sabe que aquelas duas strings não são a mesma, pensando que "não existe a necessidade de desperdiçar mais poder computacional comparando o resto das letras". E a sua aplicação dirá "Usuário ou senha incorretos".
+
+Então os invasores vão tentar com o usuário `stanleyjobsox` e a senha `love123`.
+
+E a sua aplicação faz algo como:
+
+```Python
+if "stanleyjobsox" == "stanleyjobson" and "love123" == "swordfish":
+ ...
+```
+
+O Python terá que comparar todo o `stanleyjobso` tanto em `stanleyjobsox` como em `stanleyjobson` antes de perceber que as strings não são a mesma. Então isso levará alguns microssegundos a mais para retornar "Usuário ou senha incorretos".
+
+#### O tempo para responder ajuda os invasores
+
+Neste ponto, ao perceber que o servidor demorou alguns microssegundos a mais para enviar o retorno "Usuário ou senha incorretos", os invasores irão saber que eles acertaram _alguma coisa_, algumas das letras iniciais estavam certas.
+
+E eles podem tentar de novo sabendo que provavelmente é algo mais parecido com `stanleyjobsox` do que com `johndoe`.
+
+#### Um ataque "profissional"
+
+Claro, os invasores não tentariam tudo isso de forma manual, eles escreveriam um programa para fazer isso, possivelmente com milhares ou milhões de testes por segundo. E obteriam apenas uma letra a mais por vez.
+
+Mas fazendo isso, em alguns minutos ou horas os invasores teriam adivinhado o usuário e senha corretos, com a "ajuda" da nossa aplicação, apenas usando o tempo levado para responder.
+
+#### Corrija com o `secrets.compare_digest()`
+
+Mas em nosso código já estamos utilizando o `secrets.compare_digest()`.
+
+Resumindo, levará o mesmo tempo para comparar `stanleyjobsox` com `stanleyjobson` do que comparar `johndoe` com `stanleyjobson`. E o mesmo para a senha.
+
+Deste modo, ao utilizar `secrets.compare_digest()` no código de sua aplicação, ela estará a salvo contra toda essa gama de ataques de segurança.
+
+
+### Retorne o erro
+
+Após detectar que as credenciais estão incorretas, retorne um `HTTPException` com o status 401 (o mesmo retornado quando nenhuma credencial foi informada) e adicione o cabeçalho `WWW-Authenticate` para fazer com que o navegador mostre o prompt de login novamente:
+
+{* ../../docs_src/security/tutorial007_an_py39.py hl[26:30] *}
diff --git a/docs/pt/docs/advanced/security/index.md b/docs/pt/docs/advanced/security/index.md
new file mode 100644
index 0000000000..6c7becb67a
--- /dev/null
+++ b/docs/pt/docs/advanced/security/index.md
@@ -0,0 +1,19 @@
+# Segurança Avançada
+
+## Funcionalidades Adicionais
+
+Existem algumas funcionalidades adicionais para lidar com segurança além das cobertas em [Tutorial - Guia de Usuário: Segurança](../../tutorial/security/index.md){.internal-link target=_blank}.
+
+/// tip | Dica
+
+As próximas seções **não são necessariamente "avançadas"**.
+
+E é possível que para o seu caso de uso, a solução está em uma delas.
+
+///
+
+## Leia o Tutorial primeiro
+
+As próximas seções pressupõem que você já leu o principal [Tutorial - Guia de Usuário: Segurança](../../tutorial/security/index.md){.internal-link target=_blank}.
+
+Todas elas são baseadas nos mesmos conceitos, mas permitem algumas funcionalidades extras.
diff --git a/docs/pt/docs/advanced/security/oauth2-scopes.md b/docs/pt/docs/advanced/security/oauth2-scopes.md
new file mode 100644
index 0000000000..07b3459452
--- /dev/null
+++ b/docs/pt/docs/advanced/security/oauth2-scopes.md
@@ -0,0 +1,274 @@
+# Escopos OAuth2
+
+Você pode utilizar escopos do OAuth2 diretamente com o **FastAPI**, eles são integrados para funcionar perfeitamente.
+
+Isso permitiria que você tivesse um sistema de permissionamento mais refinado, seguindo o padrão do OAuth2 integrado na sua aplicação OpenAPI (e as documentações da API).
+
+OAuth2 com escopos é o mecanismo utilizado por muitos provedores de autenticação, como o Facebook, Google, GitHub, Microsoft, X (Twitter), etc. Eles utilizam isso para prover permissões específicas para os usuários e aplicações.
+
+Toda vez que você "se autentica com" Facebook, Google, GitHub, Microsoft, X (Twitter), aquela aplicação está utilizando o OAuth2 com escopos.
+
+Nesta seção, você verá como gerenciar a autenticação e autorização com os mesmos escopos do OAuth2 em sua aplicação **FastAPI**.
+
+/// warning | Aviso
+
+Isso é uma seção mais ou menos avançada. Se você está apenas começando, você pode pular.
+
+Você não necessariamente precisa de escopos do OAuth2, e você pode lidar com autenticação e autorização da maneira que você achar melhor.
+
+Mas o OAuth2 com escopos pode ser integrado de maneira fácil em sua API (com OpenAPI) e a sua documentação de API.
+
+No entando, você ainda aplica estes escopos, ou qualquer outro requisito de segurança/autorização, conforme necessário, em seu código.
+
+Em muitos casos, OAuth2 com escopos pode ser um exagero.
+
+Mas se você sabe que precisa, ou está curioso, continue lendo.
+
+///
+
+## Escopos OAuth2 e OpenAPI
+
+A especificação OAuth2 define "escopos" como uma lista de strings separadas por espaços.
+
+O conteúdo de cada uma dessas strings pode ter qualquer formato, mas não devem possuir espaços.
+
+Estes escopos representam "permissões".
+
+No OpenAPI (e.g. os documentos da API), você pode definir "esquemas de segurança".
+
+Quando um desses esquemas de segurança utiliza OAuth2, você pode também declarar e utilizar escopos.
+
+Cada "escopo" é apenas uma string (sem espaços).
+
+Eles são normalmente utilizados para declarar permissões de segurança específicas, como por exemplo:
+
+* `users:read` or `users:write` são exemplos comuns.
+* `instagram_basic` é utilizado pelo Facebook / Instagram.
+* `https://www.googleapis.com/auth/drive` é utilizado pelo Google.
+
+/// info | Informação
+
+No OAuth2, um "escopo" é apenas uma string que declara uma permissão específica necessária.
+
+Não importa se ela contém outros caracteres como `:` ou se ela é uma URL.
+
+Estes detalhes são específicos da implementação.
+
+Para o OAuth2, eles são apenas strings.
+
+///
+
+## Visão global
+
+Primeiro, vamos olhar rapidamente as partes que mudam dos exemplos do **Tutorial - Guia de Usuário** para [OAuth2 com Senha (e hash), Bearer com tokens JWT](../../tutorial/security/oauth2-jwt.md){.internal-link target=_blank}. Agora utilizando escopos OAuth2:
+
+{* ../../docs_src/security/tutorial005_an_py310.py hl[5,9,13,47,65,106,108:116,122:125,129:135,140,156] *}
+
+Agora vamos revisar essas mudanças passo a passo.
+
+## Esquema de segurança OAuth2
+
+A primeira mudança é que agora nós estamos declarando o esquema de segurança OAuth2 com dois escopos disponíveis, `me` e `items`.
+
+O parâmetro `scopes` recebe um `dict` contendo cada escopo como chave e a descrição como valor:
+
+{* ../../docs_src/security/tutorial005_an_py310.py hl[63:66] *}
+
+Pelo motivo de estarmos declarando estes escopos, eles aparecerão nos documentos da API quando você se autenticar/autorizar.
+
+E você poderá selecionar quais escopos você deseja dar acesso: `me` e `items`.
+
+Este é o mesmo mecanismo utilizado quando você adiciona permissões enquanto se autentica com o Facebook, Google, GitHub, etc:
+
+
+
+## Token JWT com escopos
+
+Agora, modifique o *caminho de rota* para retornar os escopos solicitados.
+
+Nós ainda estamos utilizando o mesmo `OAuth2PasswordRequestForm`. Ele inclui a propriedade `scopes` com uma `list` de `str`, com cada escopo que ele recebeu na requisição.
+
+E nós retornamos os escopos como parte do token JWT.
+
+/// danger | Cuidado
+
+Para manter as coisas simples, aqui nós estamos apenas adicionando os escopos recebidos diretamente ao token.
+
+Porém em sua aplicação, por segurança, você deve garantir que você apenas adiciona os escopos que o usuário possui permissão de fato, ou aqueles que você predefiniu.
+
+///
+
+{* ../../docs_src/security/tutorial005_an_py310.py hl[156] *}
+
+## Declare escopos em *operações de rota* e dependências
+
+Agora nós declaramos que a *operação de rota* para `/users/me/items/` exige o escopo `items`.
+
+Para isso, nós importamos e utilizamos `Security` de `fastapi`.
+
+Você pode utilizar `Security` para declarar dependências (assim como `Depends`), porém o `Security` também recebe o parâmetros `scopes` com uma lista de escopos (strings).
+
+Neste caso, nós passamos a função `get_current_active_user` como dependência para `Security` (da mesma forma que nós faríamos com `Depends`).
+
+Mas nós também passamos uma `list` de escopos, neste caso com apenas um escopo: `items` (poderia ter mais).
+
+E a função de dependência `get_current_active_user` também pode declarar subdependências, não apenas com `Depends`, mas também com `Security`. Ao declarar sua própria função de subdependência (`get_current_user`), e mais requisitos de escopo.
+
+Neste caso, ele requer o escopo `me` (poderia requerer mais de um escopo).
+
+/// note | Nota
+
+Você não necessariamente precisa adicionar diferentes escopos em diferentes lugares.
+
+Nós estamos fazendo isso aqui para demonstrar como o **FastAPI** lida com escopos declarados em diferentes níveis.
+
+///
+
+{* ../../docs_src/security/tutorial005_an_py310.py hl[5,140,171] *}
+
+/// info | Informações Técnicas
+
+`Security` é na verdade uma subclasse de `Depends`, e ele possui apenas um parâmetro extra que veremos depois.
+
+Porém ao utilizar `Security` no lugar de `Depends`, o **FastAPI** saberá que ele pode declarar escopos de segurança, utilizá-los internamente, e documentar a API com o OpenAPI.
+
+Mas quando você importa `Query`, `Path`, `Depends`, `Security` entre outros de `fastapi`, eles são na verdade funções que retornam classes especiais.
+
+///
+
+## Utilize `SecurityScopes`
+
+Agora atualize a dependência `get_current_user`.
+
+Este é o usado pelas dependências acima.
+
+Aqui é onde estamos utilizando o mesmo esquema OAuth2 que nós declaramos antes, declarando-o como uma dependência: `oauth2_scheme`.
+
+Porque esta função de dependência não possui nenhum requerimento de escopo, nós podemos utilizar `Depends` com o `oauth2_scheme`. Nós não precisamos utilizar `Security` quando nós não precisamos especificar escopos de segurança.
+
+Nós também declaramos um parâmetro especial do tipo `SecurityScopes`, importado de `fastapi.security`.
+
+A classe `SecurityScopes` é semelhante à classe `Request` (`Request` foi utilizada para obter o objeto da requisição diretamente).
+
+{* ../../docs_src/security/tutorial005_an_py310.py hl[9,106] *}
+
+## Utilize os `scopes`
+
+O parâmetro `security_scopes` será do tipo `SecurityScopes`.
+
+Ele terá a propriedade `scopes` com uma lista contendo todos os escopos requeridos por ele e todas as dependências que utilizam ele como uma subdependência. Isso significa, todos os "dependentes"... pode soar meio confuso, e isso será explicado novamente mais adiante.
+
+O objeto `security_scopes` (da classe `SecurityScopes`) também oferece um atributo `scope_str` com uma única string, contendo os escopos separados por espaços (nós vamos utilizar isso).
+
+Nós criamos uma `HTTPException` que nós podemos reutilizar (`raise`) mais tarde em diversos lugares.
+
+Nesta exceção, nós incluímos os escopos necessários (se houver algum) como uma string separada por espaços (utilizando `scope_str`). Nós colocamos esta string contendo os escopos no cabeçalho `WWW-Authenticate` (isso é parte da especificação).
+
+{* ../../docs_src/security/tutorial005_an_py310.py hl[106,108:116] *}
+
+## Verifique o `username` e o formato dos dados
+
+Nós verificamos que nós obtemos um `username`, e extraímos os escopos.
+
+E depois nós validamos esse dado com o modelo Pydantic (capturando a exceção `ValidationError`), e se nós obtemos um erro ao ler o token JWT ou validando os dados com o Pydantic, nós levantamos a exceção `HTTPException` que criamos anteriormente.
+
+Para isso, nós atualizamos o modelo Pydantic `TokenData` com a nova propriedade `scopes`.
+
+Ao validar os dados com o Pydantic nós podemos garantir que temos, por exemplo, exatamente uma `list` de `str` com os escopos e uma `str` com o `username`.
+
+No lugar de, por exemplo, um `dict`, ou alguma outra coisa, que poderia quebrar a aplicação em algum lugar mais tarde, tornando isso um risco de segurança.
+
+Nós também verificamos que nós temos um usuário com o "*username*", e caso contrário, nós levantamos a mesma exceção que criamos anteriormente.
+
+{* ../../docs_src/security/tutorial005_an_py310.py hl[47,117:128] *}
+
+## Verifique os `scopes`
+
+Nós verificamos agora que todos os escopos necessários, por essa dependência e todos os dependentes (incluindo as *operações de rota*) estão incluídas nos escopos fornecidos pelo token recebido, caso contrário, levantamos uma `HTTPException`.
+
+Para isso, nós utilizamos `security_scopes.scopes`, que contém uma `list` com todos esses escopos como uma `str`.
+
+{* ../../docs_src/security/tutorial005_an_py310.py hl[129:135] *}
+
+## Árvore de dependência e escopos
+
+Vamos rever novamente essa árvore de dependência e os escopos.
+
+Como a dependência `get_current_active_user` possui uma subdependência em `get_current_user`, o escopo `"me"` declarado em `get_current_active_user` será incluído na lista de escopos necessários em `security_scopes.scopes` passado para `get_current_user`.
+
+A própria *operação de rota* também declara o escopo, `"items"`, então ele também estará na lista de `security_scopes.scopes` passado para o `get_current_user`.
+
+Aqui está como a hierarquia de dependências e escopos parecem:
+
+* A *operação de rota* `read_own_items` possui:
+ * Escopos necessários `["items"]` com a dependência:
+ * `get_current_active_user`:
+ * A função de dependência `get_current_active_user` possui:
+ * Escopos necessários `["me"]` com a dependência:
+ * `get_current_user`:
+ * A função de dependência `get_current_user` possui:
+ * Nenhum escopo necessário.
+ * Uma dependência utilizando `oauth2_scheme`.
+ * Um parâmetro `security_scopes` do tipo `SecurityScopes`:
+ * Este parâmetro `security_scopes` possui uma propriedade `scopes` com uma `list` contendo todos estes escopos declarados acima, então:
+ * `security_scopes.scopes` terá `["me", "items"]` para a *operação de rota* `read_own_items`.
+ * `security_scopes.scopes` terá `["me"]` para a *operação de rota* `read_users_me`, porque ela declarou na dependência `get_current_active_user`.
+ * `security_scopes.scopes` terá `[]` (nada) para a *operação de rota* `read_system_status`, porque ele não declarou nenhum `Security` com `scopes`, e sua dependência, `get_current_user`, não declara nenhum `scopes` também.
+
+/// tip | Dica
+
+A coisa importante e "mágica" aqui é que `get_current_user` terá diferentes listas de `scopes` para validar para cada *operação de rota*.
+
+Tudo depende dos `scopes` declarados em cada *operação de rota* e cada dependência da árvore de dependências para aquela *operação de rota* específica.
+
+///
+
+## Mais detalhes sobre `SecurityScopes`
+
+Você pode utilizar `SecurityScopes` em qualquer lugar, e em diversos lugares. Ele não precisa estar na dependência "raiz".
+
+Ele sempre terá os escopos de segurança declarados nas dependências atuais de `Security` e todos os dependentes para **aquela** *operação de rota* **específica** e **aquela** árvore de dependência **específica**.
+
+Porque o `SecurityScopes` terá todos os escopos declarados por dependentes, você pode utilizá-lo para verificar se o token possui os escopos necessários em uma função de dependência central, e depois declarar diferentes requisitos de escopo em diferentes *operações de rota*.
+
+Todos eles serão validados independentemente para cada *operação de rota*.
+
+## Verifique
+
+Se você abrir os documentos da API, você pode antenticar e especificar quais escopos você quer autorizar.
+
+
+
+Se você não selecionar nenhum escopo, você terá "autenticado", mas quando você tentar acessar `/users/me/` ou `/users/me/items/`, você vai obter um erro dizendo que você não possui as permissões necessárias. Você ainda poderá acessar `/status/`.
+
+E se você selecionar o escopo `me`, mas não o escopo `items`, você poderá acessar `/users/me/`, mas não `/users/me/items/`.
+
+Isso é o que aconteceria se uma aplicação terceira que tentou acessar uma dessas *operações de rota* com um token fornecido por um usuário, dependendo de quantas permissões o usuário forneceu para a aplicação.
+
+## Sobre integrações de terceiros
+
+Neste exemplos nós estamos utilizando o fluxo de senha do OAuth2.
+
+Isso é apropriado quando nós estamos autenticando em nossa própria aplicação, provavelmente com o nosso próprio "*frontend*".
+
+Porque nós podemos confiar nele para receber o `username` e o `password`, pois nós controlamos isso.
+
+Mas se nós estamos construindo uma aplicação OAuth2 que outros poderiam conectar (i.e., se você está construindo um provedor de autenticação equivalente ao Facebook, Google, GitHub, etc.) você deveria utilizar um dos outros fluxos.
+
+O mais comum é o fluxo implícito.
+
+O mais seguro é o fluxo de código, mas ele é mais complexo para implementar, pois ele necessita mais passos. Como ele é mais complexo, muitos provedores terminam sugerindo o fluxo implícito.
+
+/// note | Nota
+
+É comum que cada provedor de autenticação nomeie os seus fluxos de forma diferente, para torná-lo parte de sua marca.
+
+Mas no final, eles estão implementando o mesmo padrão OAuth2.
+
+///
+
+O **FastAPI** inclui utilitários para todos esses fluxos de autenticação OAuth2 em `fastapi.security.oauth2`.
+
+## `Security` em docoradores de `dependências`
+
+Da mesma forma que você pode definir uma `list` de `Depends` no parâmetro de `dependencias` do decorador (como explicado em [Dependências em decoradores de operações de rota](../../tutorial/dependencies/dependencies-in-path-operation-decorators.md){.internal-link target=_blank}), você também pode utilizar `Security` com escopos lá.
diff --git a/docs/pt/docs/advanced/settings.md b/docs/pt/docs/advanced/settings.md
index db2b4c9cc4..cdc6400adf 100644
--- a/docs/pt/docs/advanced/settings.md
+++ b/docs/pt/docs/advanced/settings.md
@@ -8,7 +8,7 @@ Por isso é comum prover essas configurações como variáveis de ambiente que s
## Variáveis de Ambiente
-/// dica
+/// tip | Dica
Se você já sabe o que são variáveis de ambiente e como utilizá-las, sinta-se livre para avançar para o próximo tópico.
@@ -67,7 +67,7 @@ name = os.getenv("MY_NAME", "World")
print(f"Hello {name} from Python")
```
-/// dica
+/// tip | Dica
O segundo parâmetro em `os.getenv()` é o valor padrão para o retorno.
@@ -124,7 +124,7 @@ Hello World from Python
-/// dica
+/// tip | Dica
Você pode ler mais sobre isso em: The Twelve-Factor App: Configurações.
@@ -180,9 +180,7 @@ Você pode utilizar todas as ferramentas e funcionalidades de validação que s
//// tab | Pydantic v2
-```Python hl_lines="2 5-8 11"
-{!> ../../../docs_src/settings/tutorial001.py!}
-```
+{* ../../docs_src/settings/tutorial001.py hl[2,5:8,11] *}
////
@@ -194,13 +192,11 @@ Na versão 1 do Pydantic você importaria `BaseSettings` diretamente do módulo
///
-```Python hl_lines="2 5-8 11"
-{!> ../../../docs_src/settings/tutorial001_pv1.py!}
-```
+{* ../../docs_src/settings/tutorial001_pv1.py hl[2,5:8,11] *}
////
-/// dica
+/// tip | Dica
Se você quiser algo pronto para copiar e colar na sua aplicação, não use esse exemplo, mas sim o exemplo abaixo.
@@ -214,9 +210,7 @@ Depois ele irá converter e validar os dados. Assim, quando você utilizar aquel
Depois, Você pode utilizar o novo objeto `settings` na sua aplicação:
-```Python hl_lines="18-20"
-{!../../../docs_src/settings/tutorial001.py!}
-```
+{* ../../docs_src/settings/tutorial001.py hl[18:20] *}
### Executando o servidor
@@ -232,7 +226,7 @@ $ ADMIN_EMAIL="deadpool@example.com" APP_NAME="ChimichangApp" fastapi run main.p
-/// dica
+/// tip | Dica
Para definir múltiplas variáveis de ambiente para um único comando basta separá-las utilizando espaços, e incluir todas elas antes do comando.
@@ -250,17 +244,13 @@ Você também pode incluir essas configurações em um arquivo de um módulo sep
Por exemplo, você pode adicionar um arquivo `config.py` com:
-```Python
-{!../../../docs_src/settings/app01/config.py!}
-```
+{* ../../docs_src/settings/app01/config.py *}
E utilizar essa configuração em `main.py`:
-```Python hl_lines="3 11-13"
-{!../../../docs_src/settings/app01/main.py!}
-```
+{* ../../docs_src/settings/app01/main.py hl[3,11:13] *}
-/// dica
+/// tip | Dica
Você também precisa incluir um arquivo `__init__.py` como visto em [Bigger Applications - Multiple Files](../tutorial/bigger-applications.md){.internal-link target=\_blank}.
@@ -276,9 +266,7 @@ Isso é especialmente útil durante os testes, já que é bastante simples sobre
Baseando-se no exemplo anterior, seu arquivo `config.py` seria parecido com isso:
-```Python hl_lines="10"
-{!../../../docs_src/settings/app02/config.py!}
-```
+{* ../../docs_src/settings/app02/config.py hl[10] *}
Perceba que dessa vez não criamos uma instância padrão `settings = Settings()`.
@@ -286,37 +274,9 @@ Perceba que dessa vez não criamos uma instância padrão `settings = Settings()
Agora criamos a dependência que retorna um novo objeto `config.Settings()`.
-//// tab | Python 3.9+
+{* ../../docs_src/settings/app02_an_py39/main.py hl[6,12:13] *}
-```Python hl_lines="6 12-13"
-{!> ../../../docs_src/settings/app02_an_py39/main.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="6 12-13"
-{!> ../../../docs_src/settings/app02_an/main.py!}
-```
-
-////
-
-//// tab | Python 3.8+ non-Annotated
-
-/// dica
-
-Utilize a versão com `Annotated` se possível.
-
-///
-
-```Python hl_lines="5 11-12"
-{!> ../../../docs_src/settings/app02/main.py!}
-```
-
-////
-
-/// dica
+/// tip | Dica
Vamos discutir sobre `@lru_cache` logo mais.
@@ -326,43 +286,13 @@ Por enquanto, você pode considerar `get_settings()` como uma função normal.
E então podemos declarar essas configurações como uma dependência na função de operação da rota e utilizar onde for necessário.
-//// tab | Python 3.9+
-
-```Python hl_lines="17 19-21"
-{!> ../../../docs_src/settings/app02_an_py39/main.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="17 19-21"
-{!> ../../../docs_src/settings/app02_an/main.py!}
-```
-
-////
-
-//// tab | Python 3.8+ non-Annotated
-
-/// dica
-
-Utilize a versão com `Annotated` se possível.
-
-///
-
-```Python hl_lines="16 18-20"
-{!> ../../../docs_src/settings/app02/main.py!}
-```
-
-////
+{* ../../docs_src/settings/app02_an_py39/main.py hl[17,19:21] *}
### Configurações e testes
Então seria muito fácil fornecer uma configuração diferente durante a execução dos testes sobrescrevendo a dependência de `get_settings`:
-```Python hl_lines="9-10 13 21"
-{!../../../docs_src/settings/app02/test_main.py!}
-```
+{* ../../docs_src/settings/app02/test_main.py hl[9:10,13,21] *}
Na sobrescrita da dependência, definimos um novo valor para `admin_email` quando instanciamos um novo objeto `Settings`, e então retornamos esse novo objeto.
@@ -374,7 +304,7 @@ Se você tiver muitas configurações que variem bastante, talvez em ambientes d
Essa prática é tão comum que possui um nome, essas variáveis de ambiente normalmente são colocadas em um arquivo `.env`, e esse arquivo é chamado de "dotenv".
-/// dica
+/// tip | Dica
Um arquivo iniciando com um ponto final (`.`) é um arquivo oculto em sistemas baseados em Unix, como Linux e MacOS.
@@ -384,7 +314,7 @@ Mas um arquivo dotenv não precisa ter esse nome exato.
Pydantic suporta a leitura desses tipos de arquivos utilizando uma biblioteca externa. Você pode ler mais em Pydantic Settings: Dotenv (.env) support.
-/// dica
+/// tip | Dica
Para que isso funcione você precisa executar `pip install python-dotenv`.
@@ -405,11 +335,9 @@ E então adicionar o seguinte código em `config.py`:
//// tab | Pydantic v2
-```Python hl_lines="9"
-{!> ../../../docs_src/settings/app03_an/config.py!}
-```
+{* ../../docs_src/settings/app03_an/config.py hl[9] *}
-/// dica
+/// tip | Dica
O atributo `model_config` é usado apenas para configuração do Pydantic. Você pode ler mais em Pydantic Model Config.
@@ -419,11 +347,9 @@ O atributo `model_config` é usado apenas para configuração do Pydantic. Você
//// tab | Pydantic v1
-```Python hl_lines="9-10"
-{!> ../../../docs_src/settings/app03_an/config_pv1.py!}
-```
+{* ../../docs_src/settings/app03_an/config_pv1.py hl[9:10] *}
-/// dica
+/// tip | Dica
A classe `Config` é usada apenas para configuração do Pydantic. Você pode ler mais em Pydantic Model Config.
@@ -462,35 +388,7 @@ Iriamos criar um novo objeto a cada requisição, e estaríamos lendo o arquivo
Mas como estamos utilizando o decorador `@lru_cache` acima, o objeto `Settings` é criado apenas uma vez, na primeira vez que a função é chamada. ✔️
-//// tab | Python 3.9+
-
-```Python hl_lines="1 11"
-{!> ../../../docs_src/settings/app03_an_py39/main.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="1 11"
-{!> ../../../docs_src/settings/app03_an/main.py!}
-```
-
-////
-
-//// tab | Python 3.8+ non-Annotated
-
-/// dica
-
-Utilize a versão com `Annotated` se possível.
-
-///
-
-```Python hl_lines="1 10"
-{!> ../../../docs_src/settings/app03/main.py!}
-```
-
-////
+{* ../../docs_src/settings/app03_an_py39/main.py hl[1,11] *}
Dessa forma, todas as chamadas da função `get_settings()` nas dependências das próximas requisições, em vez de executar o código interno de `get_settings()` e instanciar um novo objeto `Settings`, irão retornar o mesmo objeto que foi retornado na primeira chamada, de novo e de novo.
diff --git a/docs/pt/docs/advanced/sub-applications.md b/docs/pt/docs/advanced/sub-applications.md
index 8149edc5aa..efc6bef647 100644
--- a/docs/pt/docs/advanced/sub-applications.md
+++ b/docs/pt/docs/advanced/sub-applications.md
@@ -10,9 +10,7 @@ Se você precisar ter duas aplicações FastAPI independentes, cada uma com seu
Primeiro, crie a aplicação principal, de nível superior, **FastAPI**, e suas *operações de rota*:
-```Python hl_lines="3 6-8"
-{!../../../docs_src/sub_applications/tutorial001.py!}
-```
+{* ../../docs_src/sub_applications/tutorial001.py hl[3,6:8] *}
### Sub-aplicação
@@ -20,9 +18,7 @@ Em seguida, crie sua sub-aplicação e suas *operações de rota*.
Essa sub-aplicação é apenas outra aplicação FastAPI padrão, mas esta é a que será "montada":
-```Python hl_lines="11 14-16"
-{!../../../docs_src/sub_applications/tutorial001.py!}
-```
+{* ../../docs_src/sub_applications/tutorial001.py hl[11,14:16] *}
### Monte a sub-aplicação
@@ -30,9 +26,7 @@ Na sua aplicação de nível superior, `app`, monte a sub-aplicação, `subapi`.
Neste caso, ela será montada no caminho `/subapi`:
-```Python hl_lines="11 19"
-{!../../../docs_src/sub_applications/tutorial001.py!}
-```
+{* ../../docs_src/sub_applications/tutorial001.py hl[11,19] *}
### Verifique a documentação automática da API
diff --git a/docs/pt/docs/advanced/templates.md b/docs/pt/docs/advanced/templates.md
index 6d231b3c25..65ff89faee 100644
--- a/docs/pt/docs/advanced/templates.md
+++ b/docs/pt/docs/advanced/templates.md
@@ -25,9 +25,7 @@ $ pip install jinja2
* Declare um parâmetro `Request` no *path operation* que retornará um template.
* Use o `template` que você criou para renderizar e retornar uma `TemplateResponse`, passe o nome do template, o request object, e um "context" dict com pares chave-valor a serem usados dentro do template do Jinja2.
-```Python hl_lines="4 11 15-18"
-{!../../../docs_src/templates/tutorial001.py!}
-```
+{* ../../docs_src/templates/tutorial001.py hl[4,11,15:18] *}
/// note
@@ -37,13 +35,13 @@ Além disso, em versões anteriores, o objeto `request` era passado como parte d
///
-/// tip | "Dica"
+/// tip | Dica
Ao declarar `response_class=HTMLResponse`, a documentação entenderá que a resposta será HTML.
///
-/// note | "Detalhes Técnicos"
+/// note | Detalhes Técnicos
Você também poderia usar `from starlette.templating import Jinja2Templates`.
@@ -56,7 +54,7 @@ Você também poderia usar `from starlette.templating import Jinja2Templates`.
Então você pode escrever um template em `templates/item.html`, por exemplo:
```jinja hl_lines="7"
-{!../../../docs_src/templates/templates/item.html!}
+{!../../docs_src/templates/templates/item.html!}
```
### Interpolação de Valores no Template
@@ -110,17 +108,17 @@ Por exemplo, com um ID de `42`, isso renderizará:
Você também pode usar `url_for()` dentro do template e usá-lo, por examplo, com o `StaticFiles` que você montou com o `name="static"`.
```jinja hl_lines="4"
-{!../../../docs_src/templates/templates/item.html!}
+{!../../docs_src/templates/templates/item.html!}
```
Neste exemplo, ele seria vinculado a um arquivo CSS em `static/styles.css` com:
```CSS hl_lines="4"
-{!../../../docs_src/templates/static/styles.css!}
+{!../../docs_src/templates/static/styles.css!}
```
E como você está usando `StaticFiles`, este arquivo CSS será automaticamente servido pela sua aplicação FastAPI na URL `/static/styles.css`.
## Mais detalhes
-Para obter mais detalhes, incluindo como testar templates, consulte a documentação da Starlette sobre templates.
+Para obter mais detalhes, incluindo como testar templates, consulte a documentação da Starlette sobre templates.
diff --git a/docs/pt/docs/advanced/testing-dependencies.md b/docs/pt/docs/advanced/testing-dependencies.md
index 747dd7d06f..3ede4741d9 100644
--- a/docs/pt/docs/advanced/testing-dependencies.md
+++ b/docs/pt/docs/advanced/testing-dependencies.md
@@ -28,59 +28,9 @@ Para sobrepor a dependência para os testes, você coloca como chave a dependên
E então o **FastAPI** chamará a sobreposição no lugar da dependência original.
-//// tab | Python 3.10+
+{* ../../docs_src/dependency_testing/tutorial001_an_py310.py hl[26:27,30] *}
-```Python hl_lines="26-27 30"
-{!> ../../../docs_src/dependency_testing/tutorial001_an_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="28-29 32"
-{!> ../../../docs_src/dependency_testing/tutorial001_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="29-30 33"
-{!> ../../../docs_src/dependency_testing/tutorial001_an.py!}
-```
-
-////
-
-//// tab | Python 3.10+ non-Annotated
-
-/// tip | "Dica"
-
-Prefira utilizar a versão `Annotated` se possível.
-
-///
-
-```Python hl_lines="24-25 28"
-{!> ../../../docs_src/dependency_testing/tutorial001_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+ non-Annotated
-
-/// tip | "Dica"
-
-Prefira utilizar a versão `Annotated` se possível.
-
-///
-
-```Python hl_lines="28-29 32"
-{!> ../../../docs_src/dependency_testing/tutorial001.py!}
-```
-
-////
-
-/// tip | "Dica"
+/// tip | Dica
Você pode definir uma sobreposição de dependência para uma dependência que é utilizada em qualquer lugar da sua aplicação **FastAPI**.
@@ -96,7 +46,7 @@ E então você pode redefinir as suas sobreposições (removê-las) definindo o
app.dependency_overrides = {}
```
-/// tip | "Dica"
+/// tip | Dica
Se você quer sobrepor uma dependência apenas para alguns testes, você pode definir a sobreposição no início do testes (dentro da função de teste) e reiniciá-la ao final (no final da função de teste).
diff --git a/docs/pt/docs/advanced/testing-events.md b/docs/pt/docs/advanced/testing-events.md
new file mode 100644
index 0000000000..6113c99136
--- /dev/null
+++ b/docs/pt/docs/advanced/testing-events.md
@@ -0,0 +1,5 @@
+# Testando Eventos: inicialização - encerramento
+
+Quando você precisa que os seus manipuladores de eventos (`startup` e `shutdown`) sejam executados em seus testes, você pode utilizar o `TestClient` usando a instrução `with`:
+
+{* ../../docs_src/app_testing/tutorial003.py hl[9:12,20:24] *}
diff --git a/docs/pt/docs/advanced/testing-websockets.md b/docs/pt/docs/advanced/testing-websockets.md
index f458a05d4e..9b81936552 100644
--- a/docs/pt/docs/advanced/testing-websockets.md
+++ b/docs/pt/docs/advanced/testing-websockets.md
@@ -4,12 +4,10 @@ Você pode usar o mesmo `TestClient` para testar WebSockets.
Para isso, você utiliza o `TestClient` dentro de uma instrução `with`, conectando com o WebSocket:
-```Python hl_lines="27-31"
-{!../../../docs_src/app_testing/tutorial002.py!}
-```
+{* ../../docs_src/app_testing/tutorial002.py hl[27:31] *}
-/// note | "Nota"
+/// note | Nota
-Para mais detalhes, confira a documentação do Starlette para testar WebSockets.
+Para mais detalhes, confira a documentação do Starlette para testar WebSockets.
///
diff --git a/docs/pt/docs/advanced/using-request-directly.md b/docs/pt/docs/advanced/using-request-directly.md
index 3dd0a8aefe..f4fb0ed8f3 100644
--- a/docs/pt/docs/advanced/using-request-directly.md
+++ b/docs/pt/docs/advanced/using-request-directly.md
@@ -15,7 +15,7 @@ Porém há situações em que você possa precisar acessar o objeto `Request` di
## Detalhes sobre o objeto `Request`
-Como o **FastAPI** é na verdade o **Starlette** por baixo, com camadas de diversas funcionalidades por cima, você pode utilizar o objeto `Request` do Starlette diretamente quando precisar.
+Como o **FastAPI** é na verdade o **Starlette** por baixo, com camadas de diversas funcionalidades por cima, você pode utilizar o objeto `Request` do Starlette diretamente quando precisar.
Isso significaria também que se você obtiver informações do objeto `Request` diretamente (ler o corpo da requisição por exemplo), as informações não serão validadas, convertidas ou documentadas (com o OpenAPI, para a interface de usuário automática da API) pelo FastAPI.
@@ -29,13 +29,11 @@ Vamos imaginar que você deseja obter o endereço de IP/host do cliente dentro d
Para isso você precisa acessar a requisição diretamente.
-```Python hl_lines="1 7-8"
-{!../../../docs_src/using_request_directly/tutorial001.py!}
-```
+{* ../../docs_src/using_request_directly/tutorial001.py hl[1,7:8] *}
Ao declarar o parâmetro com o tipo sendo um `Request` em sua *função de operação de rota*, o **FastAPI** saberá como passar o `Request` neste parâmetro.
-/// tip | "Dica"
+/// tip | Dica
Note que neste caso, nós estamos declarando o parâmetro da rota ao lado do parâmetro da requisição.
@@ -47,9 +45,9 @@ Do mesmo jeito, você pode declarar qualquer outro parâmetro normalmente, e al
## Documentação do `Request`
-Você pode ler mais sobre os detalhes do objeto `Request` no site da documentação oficial do Starlette..
+Você pode ler mais sobre os detalhes do objeto `Request` no site da documentação oficial do Starlette..
-/// note | "Detalhes Técnicos"
+/// note | Detalhes Técnicos
Você também pode utilizar `from starlette.requests import Request`.
diff --git a/docs/pt/docs/advanced/websockets.md b/docs/pt/docs/advanced/websockets.md
new file mode 100644
index 0000000000..721c0b403a
--- /dev/null
+++ b/docs/pt/docs/advanced/websockets.md
@@ -0,0 +1,186 @@
+# WebSockets
+
+Você pode usar WebSockets com **FastAPI**.
+
+## Instalando `WebSockets`
+
+Garanta que você criou um [ambiente virtual](../virtual-environments.md){.internal-link target=_blank}, o ativou e instalou o `websockets`:
+
+
+
+Você pode digitar mensagens na caixa de entrada e enviá-las:
+
+
+
+E sua aplicação **FastAPI** com WebSockets responderá de volta:
+
+
+
+Você pode enviar (e receber) muitas mensagens:
+
+
+
+E todas elas usarão a mesma conexão WebSocket.
+
+## Usando `Depends` e outros
+
+Nos endpoints WebSocket você pode importar do `fastapi` e usar:
+
+* `Depends`
+* `Security`
+* `Cookie`
+* `Header`
+* `Path`
+* `Query`
+
+Eles funcionam da mesma forma que para outros endpoints FastAPI/*operações de rota*:
+
+{*../../docs_src/websockets/tutorial002_an_py310.py hl[68:69,82]*}
+
+/// info | Informação
+
+Como isso é um WebSocket, não faz muito sentido levantar uma `HTTPException`, em vez disso levantamos uma `WebSocketException`.
+
+Você pode usar um código de fechamento dos códigos válidos definidos na especificação.
+
+///
+
+### Tente os WebSockets com dependências
+
+Se seu arquivo for nomeado `main.py`, execute sua aplicação com:
+
+
+
+## Lidando com desconexões e múltiplos clientes
+
+Quando uma conexão WebSocket é fechada, o `await websocket.receive_text()` levantará uma exceção `WebSocketDisconnect`, que você pode então capturar e lidar como neste exemplo.
+
+{*../../docs_src/websockets/tutorial003_py39.py hl[79:81]*}
+
+Para testar:
+
+* Abrar o aplicativo com várias abas do navegador.
+* Escreva mensagens a partir delas.
+* Então feche uma das abas.
+
+Isso levantará a exceção `WebSocketDisconnect`, e todos os outros clientes receberão uma mensagem como:
+
+```
+Client #1596980209979 left the chat
+```
+
+/// tip | Dica
+
+O app acima é um exemplo mínimo e simples para demonstrar como lidar e transmitir mensagens para várias conexões WebSocket.
+
+Mas tenha em mente que, como tudo é manipulado na memória, em uma única lista, ele só funcionará enquanto o processo estiver em execução e só funcionará com um único processo.
+
+Se você precisa de algo fácil de integrar com o FastAPI, mas que seja mais robusto, suportado por Redis, PostgreSQL ou outros, verifique o encode/broadcaster.
+
+///
+
+## Mais informações
+
+Para aprender mais sobre as opções, verifique a documentação do Starlette para:
+
+* A classe `WebSocket`.
+* Manipulação de WebSockets baseada em classes.
diff --git a/docs/pt/docs/advanced/wsgi.md b/docs/pt/docs/advanced/wsgi.md
index 2c7ac1ffed..a36261e5ef 100644
--- a/docs/pt/docs/advanced/wsgi.md
+++ b/docs/pt/docs/advanced/wsgi.md
@@ -12,9 +12,7 @@ Em seguinda, encapsular a aplicação WSGI (e.g. Flask) com o middleware.
E então **"montar"** em um caminho de rota.
-```Python hl_lines="2-3 23"
-{!../../../docs_src/wsgi/tutorial001.py!}
-```
+{* ../../docs_src/wsgi/tutorial001.py hl[2:3,23] *}
## Conferindo
diff --git a/docs/pt/docs/alternatives.md b/docs/pt/docs/alternatives.md
index d3a304fa78..66cf3fe127 100644
--- a/docs/pt/docs/alternatives.md
+++ b/docs/pt/docs/alternatives.md
@@ -30,13 +30,13 @@ Ele é utilizado por muitas companhias incluindo Mozilla, Red Hat e Eventbrite.
Ele foi um dos primeiros exemplos de **documentação automática de API**, e essa foi especificamente uma das primeiras idéias que inspirou "a busca por" **FastAPI**.
-/// note | "Nota"
+/// note | Nota
Django REST Framework foi criado por Tom Christie. O mesmo criador de Starlette e Uvicorn, nos quais **FastAPI** é baseado.
///
-/// check | "**FastAPI** inspirado para"
+/// check | **FastAPI** inspirado para
Ter uma documentação automática da API em interface web.
@@ -56,7 +56,7 @@ Esse desacoplamento de partes, e sendo um "microframework" que pode ser extendid
Dada a simplicidade do Flask, parecia uma ótima opção para construção de APIs. A próxima coisa a procurar era um "Django REST Framework" para Flask.
-/// check | "**FastAPI** inspirado para"
+/// check | **FastAPI** inspirado para
Ser um microframework. Fazer ele fácil para misturar e combinar com ferramentas e partes necessárias.
@@ -98,7 +98,7 @@ def read_url():
Veja as similaridades em `requests.get(...)` e `@app.get(...)`.
-/// check | "**FastAPI** inspirado para"
+/// check | **FastAPI** inspirado para
* Ter uma API simples e intuitiva.
* Utilizar nomes de métodos HTTP (operações) diretamente, de um jeito direto e intuitivo.
@@ -118,7 +118,7 @@ Em algum ponto, Swagger foi dado para a Fundação Linux, e foi renomeado OpenAP
Isso acontece porquê quando alguém fala sobre a versão 2.0 é comum dizer "Swagger", e para a versão 3+, "OpenAPI".
-/// check | "**FastAPI** inspirado para"
+/// check | **FastAPI** inspirado para
Adotar e usar um padrão aberto para especificações API, ao invés de algum esquema customizado.
@@ -147,7 +147,7 @@ Esses recursos são o que Marshmallow foi construído para fornecer. Ele é uma
Mas ele foi criado antes da existência do _type hints_ do Python. Então, para definir todo o _schema_ você precisa utilizar específicas ferramentas e classes fornecidas pelo Marshmallow.
-/// check | "**FastAPI** inspirado para"
+/// check | **FastAPI** inspirado para
Usar código para definir "schemas" que forneçam, automaticamente, tipos de dados e validação.
@@ -169,7 +169,7 @@ Webargs foi criado pelos mesmos desenvolvedores do Marshmallow.
///
-/// check | "**FastAPI** inspirado para"
+/// check | **FastAPI** inspirado para
Ter validação automática de dados vindos de requisições.
@@ -199,7 +199,7 @@ APISpec foi criado pelos mesmos desenvolvedores do Marshmallow.
///
-/// check | "**FastAPI** inspirado para"
+/// check | **FastAPI** inspirado para
Dar suporte a padrões abertos para APIs, OpenAPI.
@@ -231,7 +231,7 @@ Flask-apispec foi criado pelos mesmos desenvolvedores do Marshmallow.
///
-/// check | "**FastAPI** inspirado para"
+/// check | **FastAPI** inspirado para
Gerar _schema_ OpenAPI automaticamente, a partir do mesmo código que define serialização e validação.
@@ -251,7 +251,7 @@ Mas como os dados TypeScript não são preservados após a compilação para o J
Ele também não controla modelos aninhados muito bem. Então, se o corpo JSON na requisição for um objeto JSON que contém campos internos que contém objetos JSON aninhados, ele não consegue ser validado e documentado apropriadamente.
-/// check | "**FastAPI** inspirado para"
+/// check | **FastAPI** inspirado para
Usar tipos Python para ter um ótimo suporte do editor.
@@ -263,7 +263,7 @@ Ter um sistema de injeção de dependência poderoso. Achar um jeito de minimiza
Ele foi um dos primeiros frameworks Python extremamente rápido baseado em `asyncio`. Ele foi feito para ser muito similar ao Flask.
-/// note | "Detalhes técnicos"
+/// note | Detalhes técnicos
Ele utiliza `uvloop` ao invés do '_loop_' `asyncio` padrão do Python. É isso que deixa ele tão rápido.
@@ -271,7 +271,7 @@ Ele claramente inspirou Uvicorn e Starlette, que são atualmente mais rápidos q
///
-/// check | "**FastAPI** inspirado para"
+/// check | **FastAPI** inspirado para
Achar um jeito de ter uma performance insana.
@@ -289,7 +289,7 @@ Ele é projetado para ter funções que recebem dois parâmetros, uma "requisiç
Então, validação de dados, serialização e documentação tem que ser feitos no código, não automaticamente. Ou eles terão que ser implementados como um framework acima do Falcon, como o Hug. Essa mesma distinção acontece em outros frameworks que são inspirados pelo design do Falcon, tendo um objeto de requisição e um objeto de resposta como parâmetros.
-/// check | "**FastAPI** inspirado para"
+/// check | **FastAPI** inspirado para
Achar jeitos de conseguir melhor performance.
@@ -315,7 +315,7 @@ O sistema de injeção de dependência exige pré-registro das dependências e a
Rotas são declaradas em um único lugar, usando funções declaradas em outros lugares (ao invés de usar decoradores que possam ser colocados diretamente acima da função que controla o _endpoint_). Isso é mais perto de como o Django faz isso do que como Flask (e Starlette) faz. Ele separa no código coisas que são relativamente amarradas.
-/// check | "**FastAPI** inspirado para"
+/// check | **FastAPI** inspirado para
Definir validações extras para tipos de dados usando valores "padrão" de atributos dos modelos. Isso melhora o suporte do editor, e não estava disponível no Pydantic antes.
@@ -323,7 +323,7 @@ Isso na verdade inspirou a atualização de partes do Pydantic, para dar suporte
///
-### Hug
+### Hug
Hug foi um dos primeiros frameworks a implementar a declaração de tipos de parâmetros usando Python _type hints_. Isso foi uma ótima idéia que inspirou outras ferramentas a fazer o mesmo.
@@ -343,7 +343,7 @@ Hug foi criado por Timothy Crosley, o mesmo criador do Starlette
+### Starlette
Starlette é um framework/caixa de ferramentas ASGI peso leve, o que é ideal para construir serviços assíncronos de alta performance.
@@ -447,7 +447,7 @@ Mas ele não fornece validação de dados automática, serialização e document
Essa é uma das principais coisas que **FastAPI** adiciona no topo, tudo baseado em Python _type hints_ (usando Pydantic). Isso, mais o sistema de injeção de dependência, utilidades de segurança, geração de _schema_ OpenAPI, etc.
-/// note | "Detalhes Técnicos"
+/// note | Detalhes Técnicos
ASGI é um novo "padrão" sendo desenvolvido pelos membros do time central do Django. Ele ainda não está como "Padrão Python" (PEP), embora eles estejam em processo de fazer isso.
@@ -455,7 +455,7 @@ No entanto, ele já está sendo utilizado como "padrão" por diversas ferramenta
///
-/// check | "**FastAPI** usa isso para"
+/// check | **FastAPI** usa isso para
Controlar todas as partes web centrais. Adiciona recursos no topo.
@@ -465,7 +465,7 @@ Então, qualquer coisa que você faz com Starlette, você pode fazer diretamente
///
-### Uvicorn
+### Uvicorn
Uvicorn é um servidor ASGI peso leve, construído com uvloop e httptools.
@@ -473,7 +473,7 @@ Ele não é um framework web, mas sim um servidor. Por exemplo, ele não fornece
Ele é o servidor recomendado para Starlette e **FastAPI**.
-/// check | "**FastAPI** recomenda isso para"
+/// check | **FastAPI** recomenda isso para
O principal servidor web para rodar aplicações **FastAPI**.
diff --git a/docs/pt/docs/async.md b/docs/pt/docs/async.md
index 0d6bdbf0e5..c70924ea52 100644
--- a/docs/pt/docs/async.md
+++ b/docs/pt/docs/async.md
@@ -40,7 +40,7 @@ def results():
---
-Se sua aplicação (de alguma forma) não tem que se comunicar com nada mais e tem que esperar que o respondam, use `async def`.
+Se sua aplicação (de alguma forma) não tem que se comunicar com nada mais e esperar que o respondam, use `async def`.
---
@@ -52,7 +52,7 @@ Se você simplesmente não sabe, use apenas `def`.
De qualquer forma, em ambos os casos acima, FastAPI irá trabalhar assincronamente e ser extremamente rápido.
-Seguindo os passos acima, ele será capaz de fazer algumas otimizações de performance.
+Mas, seguindo os passos acima, ele será capaz de fazer algumas otimizações de performance.
## Detalhes Técnicos
@@ -66,36 +66,36 @@ Vamos ver aquela frase por partes na seção abaixo:
## Código assíncrono
-Código assíncrono apenas significa que a linguagem 💬 tem um jeito de dizer para o computador / programa 🤖 que em certo ponto, ele 🤖 terá que esperar por *algo* para finalizar em outro lugar. Vamos dizer que esse *algo* seja chamado "arquivo lento" 📝.
+Código assíncrono apenas significa que a linguagem 💬 tem um jeito de dizer para o computador / programa 🤖 que em certo ponto do código, ele 🤖 terá que esperar *algo* finalizar em outro lugar. Vamos dizer que esse *algo* seja chamado "arquivo lento" 📝.
-Então, durante esse tempo, o computador pode ir e fazer outro trabalho, enquanto o "arquivo lento" 📝 termine.
+Então, durante esse tempo, o computador pode ir e fazer outro trabalho, enquanto o "arquivo lento" 📝 termina.
-Então o computador / programa 🤖 irá voltar toda hora que tiver uma chance porquê ele ainda está esperando o "arquivo lento", ou ele 🤖 nunca irá terminar todo o trabalho que tem até esse ponto. E ele 🤖 irá ver se alguma das tarefas que estava esperando já terminaram, fazendo o que quer que tinham que fazer.
+Então o computador / programa 🤖 irá voltar sempre que tiver uma chance, seja porque ele está esperando novamente, ou quando ele 🤖 terminar todo o trabalho que tem até esse ponto. E ele 🤖 irá ver se alguma das tarefas que estava esperando já terminaram de fazer o que quer que tinham que fazer.
-Depois, ele 🤖 pega a primeira tarefa para finalizar (vamos dizer, nosso "arquivo lento" 📝) e continua o que ele tem que fazer com isso.
+Depois, ele 🤖 pega a primeira tarefa para finalizar (vamos dizer, nosso "arquivo lento" 📝) e continua o que tem que fazer com ela.
-Esse "esperar por algo" normalmente se refere a operações I/O que são relativamente "lentas" (comparadas a velocidade do processador e da memória RAM), como esperar por:
+Esse "esperar por algo" normalmente se refere a operações I/O que são relativamente "lentas" (comparadas à velocidade do processador e da memória RAM), como esperar por:
* dados do cliente para serem enviados através da rede
-* dados enviados pelo seu programa para serem recebidos pelo clente através da rede
-* conteúdo de um arquivo no disco pra ser lido pelo sistema e entregar ao seu programa
+* dados enviados pelo seu programa serem recebidos pelo clente através da rede
+* conteúdo de um arquivo no disco ser lido pelo sistema e entregue ao seu programa
* conteúdo que seu programa deu ao sistema para ser escrito no disco
-* uma operação remota API
-* uma operação no banco de dados para finalizar
-* uma solicitação no banco de dados esperando o retorno do resultado
+* uma operação em uma API remota
+* uma operação no banco de dados finalizar
+* uma solicitação no banco de dados retornar o resultado
* etc.
-Enquanto o tempo de execução é consumido mais pela espera das operações I/O, essas operações são chamadas de operações "limitadas por I/O".
+Quanto o tempo de execução é consumido majoritariamente pela espera de operações I/O, essas operações são chamadas operações "limitadas por I/O".
-Isso é chamado de "assíncrono" porquê o computador / programa não tem que ser "sincronizado" com a tarefa lenta, esperando pelo exato momento que a tarefa finalize, enquanto não faz nada, para ser capaz de pegar o resultado da tarefa e dar continuidade ao trabalho.
+Isso é chamado de "assíncrono" porque o computador / programa não tem que ser "sincronizado" com a tarefa lenta, esperando pelo momento exato em que a tarefa finaliza, enquanto não faz nada, para ser capaz de pegar o resultado da tarefa e dar continuidade ao trabalho.
-Ao invés disso, sendo um sistema "assíncrono", uma vez finalizada, a tarefa pode esperar um pouco (alguns microssegundos) para que o computador / programa finalize o que quer que esteja fazendo,e então volte para pegar o resultado e continue trabalhando com ele.
+Ao invés disso, sendo um sistema "assíncrono", uma vez finalizada, a tarefa pode esperar na fila um pouco (alguns microssegundos) para que o computador / programa finalize o que quer que esteja fazendo, e então volte para pegar o resultado e continue trabalhando com ele.
-Para "síncrono" (contrário de "assíncrono") também é utilizado o termo "sequencial", porquê o computador / programa segue todos os passos, na sequência, antes de trocar para uma tarefa diferente, mesmo se alguns passos envolvam esperar.
+Para "síncrono" (contrário de "assíncrono") também é utilizado o termo "sequencial", porquê o computador / programa segue todos os passos, em sequência, antes de trocar para uma tarefa diferente, mesmo se alguns passos envolvam esperar.
### Concorrência e hambúrgueres
-Essa idéia de código **assíncrono** descrito acima é algo às vezes chamado de **"concorrência"**. E é diferente de **"paralelismo"**.
+Essa idéia de código **assíncrono** descrita acima é às vezes chamado de **"concorrência"**. Isso é diferente de **"paralelismo"**.
**Concorrência** e **paralelismo** ambos são relacionados a "diferentes coisas acontecendo mais ou menos ao mesmo tempo".
@@ -105,117 +105,115 @@ Para ver essa diferença, imagine a seguinte história sobre hambúrgueres:
### Hambúrgueres concorrentes
-Você vai com seu _crush_ :heart_eyes: na lanchonete, fica na fila enquanto o caixa pega os pedidos das pessoas na sua frente.
+Você vai com seu _crush_ na lanchonete, e fica na fila enquanto o caixa pega os pedidos das pessoas na sua frente. 😍
-Então chega a sua vez, você pede dois saborosos hambúrgueres para você e seu _crush_ :heart_eyes:.
+Então chega a sua vez, você pede dois saborosos hambúrgueres para você e seu _crush_. 🍔🍔
-Você paga.
+O caixa diz alguma coisa para o cozinheiro na cozinha para que eles saivam que têm que preparar seus hambúrgueres (mesmo que ele esteja atualmente preparando os lanches dos outros clientes).
-O caixa diz alguma coisa para o cara na cozinha para que ele tenha que preparar seus hambúrgueres (mesmo embora ele esteja preparando os lanches dos outros clientes).
+Você paga. 💸
O caixa te entrega seu número de chamada.
-Enquanto você espera, você vai com seu _crush_ :heart_eyes: e pega uma mesa, senta e conversa com seu _crush_ :heart_eyes: por um bom tempo (como seus hambúrgueres são muito saborosos, leva um tempo para serem preparados).
+Enquanto você espera, você vai com seu _crush_ e pega uma mesa, senta e conversa com seu _crush_ por um bom tempo (já que seus hambúrgueres são muito saborosos, e leva um tempo para serem preparados).
-Enquanto você está sentado na mesa com seu _crush_ :heart_eyes:, esperando os hambúrgueres, você pode gastar o tempo admirando como lindo, maravilhoso e esperto é seu _crush_ :heart_eyes:.
+Já que você está sentado na mesa com seu _crush_, esperando os hambúrgueres, você pode passar esse tempo admirando o quão lindo, maravilhoso e esperto é seu _crush_ ✨😍✨.
-Enquanto espera e conversa com seu _crush_ :heart_eyes:, de tempos em tempos, você verifica o número de chamada exibido no balcão para ver se já é sua vez.
+Enquanto espera e conversa com seu _crush_, de tempos em tempos, você verifica o número da chamada exibido no balcão para ver se já é sua vez.
-Então a certo ponto, é finalmente sua vez. Você vai no balcão, pega seus hambúrgueres e volta para a mesa.
+Então em algum momento, é finalmente sua vez. Você vai ao balcão, pega seus hambúrgueres e volta para a mesa.
-Você e seu _crush_ :heart_eyes: comem os hambúrgueres e aproveitam o tempo.
+Você e seu _crush_ comem os hambúrgueres e aproveitam o tempo. ✨
---
-Imagine que você seja o computador / programa nessa história.
+Imagine que você seja o computador / programa nessa história.
-Enquanto você está na fila, tranquilo, esperando por sua vez, não está fazendo nada "produtivo". Mas a fila é rápida porquê o caixa só está pegando os pedidos, então está tudo bem.
+Enquanto você está na fila, você está somente ocioso 😴, esperando por sua vez, sem fazer nada muito "produtivo". Mas a fila é rápida porque o caixa só está pegando os pedidos (não os preparando), então está tudo bem.
-Então, quando é sua vez, você faz o trabalho "produtivo" de verdade, você processa o menu, decide o que quer, pega a escolha de seu _crush_ :heart_eyes:, paga, verifica se entregou o valor correto em dinheiro ou cartão de crédito, verifica se foi cobrado corretamente, verifica se seu pedido está correto etc.
+Então, quando é sua vez, você faz trabalho realmente "produtivo", você processa o menu, decide o que quer, pega a escolha de seu _crush_, paga, verifica se entregou o cartão ou a cédula correta, verifica se foi cobrado corretamente, verifica se seu pedido está correto etc.
-Mas então, embora você ainda não tenha os hambúrgueres, seu trabalho no caixa está "pausado", porquê você tem que esperar seus hambúrgueres estarem prontos.
+Mas então, embora você ainda não tenha os hambúrgueres, seu trabalho no caixa está "pausado" ⏸, porque você tem que esperar 🕙 seus hambúrgueres ficarem prontos.
-Mas enquanto você se afasta do balcão e senta na mesa com o número da sua chamada, você pode trocar sua atenção para seu _crush_ :heart_eyes:, e "trabalhar" nisso. Então você está novamente fazendo algo muito "produtivo", como flertar com seu _crush_ :heart_eyes:.
+Contudo, à medida que você se afasta do balcão e senta na mesa, com um número para sua chamada, você pode trocar 🔀 sua atenção para seu _crush_, e "trabalhar" ⏯ 🤓 nisso. Então você está novamente fazendo algo muito "produtivo", como flertar com seu _crush_ 😍.
-Então o caixa diz que "seus hambúrgueres estão prontos" colocando seu número no balcão, mas você não corre que nem um maluco imediatamente quando o número exibido é o seu. Você sabe que ninguém irá roubar seus hambúrgueres porquê você tem o número de chamada, e os outros tem os números deles.
+Então o caixa 💁 diz que "seus hambúrgueres estão prontos" colocando seu número no balcão, mas você não corre que nem um maluco imediatamente quando o número exibido é o seu. Você sabe que ninguém irá roubar seus hambúrgueres porque você tem o seu número da chamada, e os outros têm os deles.
-Então você espera que seu _crush_ :heart_eyes: termine a história que estava contando (terminar o trabalho atual / tarefa sendo processada), sorri gentilmente e diz que você está indo buscar os hambúrgueres.
+Então você espera seu _crush_ terminar a história que estava contando (terminar o trabalho atual ⏯ / tarefa sendo processada 🤓), sorri gentilmente e diz que você está indo buscar os hambúrgueres.
-Então você vai no balcão, para a tarefa inicial que agora está finalizada, pega os hambúrgueres, e leva para a mesa. Isso finaliza esse passo / tarefa da interação com o balcão. Agora é criada uma nova tarefa, "comer hambúrgueres", mas a tarefa anterior, "pegar os hambúrgueres" já está finalizada.
+Então você vai ao balcão 🔀, para a tarefa inicial que agora está finalizada⏯, pega os hambúrgueres, agradece, e leva-os para a mesa. Isso finaliza esse passo / tarefa da interação com o balcão ⏹. Isso, por sua vez, cria uma nova tarefa, a de "comer hambúrgueres" 🔀 ⏯, mas a tarefa anterior de "pegar os hambúrgueres" já está finalizada ⏹.
### Hambúrgueres paralelos
-Você vai com seu _crush_ :heart_eyes: em uma lanchonete paralela.
+Agora vamos imaginar que esses não são "Hambúrgueres Concorrentes", e sim "Hambúrgueres Paralelos"
-Você fica na fila enquanto alguns (vamos dizer 8) caixas pegam os pedidos das pessoas na sua frente.
+Você vai com seu _crush_ na lanchonete paralela.
-Todo mundo antes de você está esperando pelos hambúrgueres estarem prontos antes de deixar o caixa porquê cada um dos 8 caixas vai e prepara o hambúrguer antes de pegar o próximo pedido.
+Você fica na fila enquanto vários (vamos dizer 8) caixas que também são cozinheiros pegam os pedidos das pessoas na sua frente.
-Então é finalmente sua vez, e pede 2 hambúrgueres muito saborosos para você e seu _crush_ :heart_eyes:.
+Todo mundo na sua frente está esperando seus hambúrgueres ficarem prontos antes de deixar o caixa porque cada um dos 8 caixas vai e prepara o hambúrguer logo após receber o pedido, antes de pegar o próximo pedido.
-Você paga.
+Então é finalmente sua vez, você pede 2 hambúrgueres muito saborosos para você e seu _crush_.
+
+Você paga 💸.
O caixa vai para a cozinha.
-Você espera, na frente do balcão, para que ninguém pegue seus hambúrgueres antes de você, já que não tem números de chamadas.
+Você espera, na frente do balcão 🕙, para que ninguém pegue seus hambúrgueres antes de você, já que não tem números de chamadas.
-Enquanto você e seu _crush_ :heart_eyes: estão ocupados não permitindo que ninguém passe a frente e pegue seus hambúrgueres assim que estiverem prontos, você não pode dar atenção ao seu _crush_ :heart_eyes:.
+Como você e seu _crush_ estão ocupados não permitindo que ninguém passe na frente e pegue seus hambúrgueres assim que estiverem prontos, você não pode dar atenção ao seu _crush_. 😞
-Isso é trabalho "síncrono", você está "sincronizado" com o caixa / cozinheiro. Você tem que esperar e estar lá no exato momento que o caixa / cozinheiro terminar os hambúrgueres e dá-los a você, ou então, outro alguém pode pegá-los.
+Isso é trabalho "síncrono", você está "sincronizado" com o caixa / cozinheiro👨🍳. Você tem que esperar 🕙 e estar lá no exato momento que o caixa / cozinheiro 👨🍳 terminar os hambúrgueres e os der a você, ou então, outro alguém pode pegá-los.
-Então seu caixa / cozinheiro finalmente volta com seus hambúrgueres, depois de um longo tempo esperando por eles em frente ao balcão.
+Então seu caixa / cozinheiro 👨🍳 finalmente volta com seus hambúrgueres, depois de um longo tempo esperando 🕙 por eles em frente ao balcão.
-Você pega seus hambúrgueres e vai para a mesa com seu _crush_ :heart_eyes:.
+Você pega seus hambúrgueres e vai para a mesa com seu _crush_.
-Vocês comem os hambúrgueres, e o trabalho está terminado.
+Vocês comem os hambúrgueres, e o trabalho está terminado. ⏹
-Não houve muita conversa ou flerte já que a maior parte do tempo foi gasto esperando os lanches na frente do balcão.
+Não houve muita conversa ou flerte já que a maior parte do tempo foi gasto esperando 🕙 na frente do balcão. 😞
---
-Nesse cenário dos hambúrgueres paralelos, você é um computador / programa com dois processadores (você e seu _crush_ :heart_eyes:), ambos esperando e dedicando a atenção de estar "esperando no balcão" por um bom tempo.
+Nesse cenário dos hambúrgueres paralelos, você é um computador / programa com dois processadores (você e seu _crush_), ambos esperando 🕙 e dedicando sua atenção ⏯ "esperando no balcão" 🕙 por um bom tempo.
-A lanchonete paralela tem 8 processadores (caixas / cozinheiros). Enquanto a lanchonete dos hambúrgueres concorrentes tinham apenas 2 (um caixa e um cozinheiro).
+A lanchonete paralela tem 8 processadores (caixas / cozinheiros), enquanto a lanchonete dos hambúrgueres concorrentes tinha apenas 2 (um caixa e um cozinheiro).
-Ainda assim, a última experiência não foi a melhor.
+Ainda assim, a experiência final não foi a melhor. 😞
---
-Essa poderia ser a história paralela equivalente aos hambúrgueres.
+Essa seria o equivalente paralelo à histório dos hambúrgueres. 🍔
Para um exemplo "mais real", imagine um banco.
-Até recentemente, a maioria dos bancos tinha muitos caixas e uma grande fila.
+Até recentemente, a maioria dos bancos tinham muitos caixas 👨💼👨💼👨💼👨💼 e uma grande fila 🕙🕙🕙🕙🕙🕙🕙🕙.
-Todos os caixas fazendo todo o trabalho, um cliente após o outro.
+Todos os caixas fazendo todo o trabalho, um cliente após o outro 👨💼⏯.
-E você tinha que esperar na fila por um longo tempo ou poderia perder a vez.
+E você tinha que esperar 🕙 na fila por um longo tempo ou poderia perder a vez.
-Você provavelmente não gostaria de levar seu _crush_ :heart_eyes: com você para um rolezinho no banco.
+Você provavelmente não gostaria de levar seu _crush_ 😍 com você para um rolezinho no banco 🏦.
### Conclusão dos hambúrgueres
-Nesse cenário dos "hambúrgueres com seu _crush_ :heart_eyes:", como tem muita espera, faz mais sentido ter um sistema concorrente.
+Nesse cenário dos "hambúrgueres com seu _crush_", como tem muita espera, faz mais sentido ter um sistema concorrente ⏸🔀⏯.
Esse é o caso da maioria das aplicações web.
-Geralmente são muitos usuários, e seu servidor está esperando pelas suas conexões não tão boas para enviar as requisições.
+Muitos, muitos usuários, mas seu servidor está esperando 🕙 pela sua conexão não tão boa enviar suas requisições.
-E então esperando novamente pelas respostas voltarem.
+E então esperando 🕙 novamente as respostas voltarem.
-Essa "espera" é medida em microssegundos, e ainda assim, somando tudo, é um monte de espera no final.
+Essa "espera" 🕙 é medida em microssegundos, mas ainda assim, somando tudo, é um monte de espera no final.
-Por isso que faz muito mais sentido utilizar código assíncrono para APIs web.
+Por isso que faz bastante sentido utilizar código assíncrono ⏸🔀⏯ para APIs web.
-A maioria dos frameworks Python existentes mais populares (incluindo Flask e Django) foram criados antes que os novos recursos assíncronos existissem em Python. Então, os meios que eles podem ser colocados em produção para suportar execução paralela mais a forma antiga de execução assíncrona não são tão poderosos quanto as novas capacidades.
-
-Mesmo embora a especificação principal para web assíncrono em Python (ASGI) foi desenvolvida no Django, para adicionar suporte para WebSockets.
-
-Esse tipo de assincronicidade é o que fez NodeJS popular (embora NodeJS não seja paralelo) e que essa seja a força do Go como uma linguagem de programa.
+Esse tipo de assincronicidade é o que fez NodeJS popular (embora NodeJS não seja paralelo) e essa é a força do Go como uma linguagem de programação.
E esse é o mesmo nível de performance que você tem com o **FastAPI**.
-E como você pode ter paralelismo e sincronicidade ao mesmo tempo, você tem uma maior performance do que a maioria dos frameworks NodeJS testados e lado a lado com Go, que é uma linguagem compilada próxima ao C (tudo graças ao Starlette).
+E como você pode ter paralelismo e assincronicidade ao mesmo tempo, você tem uma maior performance do que a maioria dos frameworks NodeJS testados e lado a lado com Go, que é uma linguagem compilada, mais próxima ao C (tudo graças ao Starlette).
### Concorrência é melhor que paralelismo?
@@ -225,64 +223,64 @@ Concorrência é diferente de paralelismo. E é melhor em cenários **específic
Então, para equilibrar tudo, imagine a seguinte historinha:
-> Você tem que limpar uma grande casa suja.
+> Você tem que limpar uma casa grande e suja.
*Sim, essa é toda a história*.
---
-Não há espera em lugar algum, apenas um monte de trabalho para ser feito, em múltiplos cômodos da casa.
+Não há espera 🕙 em lugar algum, apenas um monte de trabalho para ser feito, em múltiplos cômodos da casa.
-Você poderia ter chamadas como no exemplo dos hambúrgueres, primeiro a sala de estar, então a cozinha, mas você não está esperando por nada, apenas limpar e limpar, as chamadas não afetariam em nada.
+Você poderia ter turnos como no exemplo dos hambúrgueres, primeiro a sala de estar, então a cozinha, mas como você não está esperando por nada, apenas limpando e limpando, as chamadas não afetariam em nada.
-Levaria o mesmo tempo para finalizar com ou sem chamadas (concorrência) e você teria feito o mesmo tanto de trabalho.
+Levaria o mesmo tempo para finalizar com ou sem turnos (concorrência) e você teria feito o mesmo tanto de trabalho.
Mas nesse caso, se você trouxesse os 8 ex-caixas / cozinheiros / agora-faxineiros, e cada um deles (mais você) pudessem dividir a casa para limpá-la, vocês fariam toda a limpeza em **paralelo**, com a ajuda extra, e terminariam muito mais cedo.
Nesse cenário, cada um dos faxineiros (incluindo você) poderia ser um processador, fazendo a sua parte do trabalho.
-E a maior parte do tempo de execução é tomada por trabalho (ao invés de ficar esperando), e o trabalho em um computador é feito pela CPU, que podem gerar problemas que são chamados de "limite de CPU".
+E a maior parte do tempo de execução é tomada por trabalho real (ao invés de ficar esperando), e o trabalho em um computador é feito pela CPU. Eles chamam esses problemas de "limitados por CPU".
---
-Exemplos comuns de limite de CPU são coisas que exigem processamento matemático complexo.
+Exemplos comuns de operações limitadas por CPU são coisas que exigem processamento matemático complexo.
Por exemplo:
* **Processamento de áudio** ou **imagem**
-* **Visão do Computador**: uma imagem é composta por milhões de pixels, cada pixel tem 3 valores (cores, processamento que normalmente exige alguma computação em todos esses pixels ao mesmo tempo)
+* **Visão Computacional**: uma imagem é composta por milhões de pixels, cada pixel tem 3 valores / cores, processar isso normalmente exige alguma computação em todos esses pixels ao mesmo tempo
-* **Machine Learning**: Normalmente exige muita multiplicação de matrizes e vetores. Pense numa grande folha de papel com números e multiplicando todos eles juntos e ao mesmo tempo.
+* **Machine Learning**: Normalmente exige muita multiplicação de matrizes e vetores. Pense numa grande planilha com números e em multiplicar todos eles juntos e ao mesmo tempo.
-* **Deep Learning**: Esse é um subcampo do Machine Learning, então o mesmo se aplica. A diferença é que não há apenas uma grande folha de papel com números para multiplicar, mas um grande conjunto de folhas de papel, e em muitos casos, você utiliza um processador especial para construir e/ou usar modelos.
+* **Deep Learning**: Esse é um subcampo do Machine Learning, então, o mesmo se aplica. A diferença é que não há apenas uma grande planilha com números para multiplicar, mas um grande conjunto delas, e em muitos casos, você utiliza um processador especial para construir e/ou usar esses modelos.
### Concorrência + Paralelismo: Web + Machine learning
Com **FastAPI** você pode levar a vantagem da concorrência que é muito comum para desenvolvimento web (o mesmo atrativo de NodeJS).
-Mas você também pode explorar os benefícios do paralelismo e multiprocessamento (tendo múltiplos processadores rodando em paralelo) para trabalhos pesados que geram **limite de CPU** como aqueles em sistemas de Machine Learning.
+Mas você também pode explorar os benefícios do paralelismo e multiprocessamento (tendo múltiplos processadores rodando em paralelo) para trabalhos **limitados por CPU** como aqueles em sistemas de Machine Learning.
-Isso, mais o simples fato que Python é a principal linguagem para **Data Science**, Machine Learning e especialmente Deep Learning, faz do FastAPI uma ótima escolha para APIs web e aplicações com Data Science / Machine Learning (entre muitas outras).
+Isso, somado ao simples fato que Python é a principal linguagem para **Data Science**, Machine Learning e especialmente Deep Learning, faz do FastAPI uma ótima escolha para APIs web e aplicações com Data Science / Machine Learning (entre muitas outras).
Para ver como alcançar esse paralelismo em produção veja a seção sobre [Deployment](deployment/index.md){.internal-link target=_blank}.
## `async` e `await`
-Versões modernas do Python tem um modo muito intuitivo para definir código assíncrono. Isso faz parecer normal o código "sequencial" e fazer o "esperar" para você nos momentos certos.
+Versões modernas do Python têm um modo muito intuitivo para definir código assíncrono. Isso faz parecer do mesmo jeito do código normal "sequencial" e fazer a "espera" para você nos momentos certos.
-Quando tem uma operação que exigirá espera antes de dar os resultados e tem suporte para esses recursos Python, você pode escrever assim:
+Quando tem uma operação que exigirá espera antes de dar os resultados e tem suporte para esses novos recursos do Python, você pode escrever assim:
```Python
burgers = await get_burgers(2)
```
-A chave aqui é o `await`. Ele diz ao Python que ele tem que esperar por `get_burgers(2)` para finalizar suas coisas antes de armazenar os resultados em `burgers`. Com isso, o Python saberá que ele pode ir e fazer outras coisas nesse meio tempo (como receber outra requisição).
+A chave aqui é o `await`. Ele diz ao Python que ele tem que esperar por `get_burgers(2)` finalizar suas coisas 🕙 antes de armazenar os resultados em `burgers`. Com isso, o Python saberá que ele pode ir e fazer outras coisas 🔀 ⏯ nesse meio tempo (como receber outra requisição).
Para o `await` funcionar, tem que estar dentro de uma função que suporte essa assincronicidade. Para fazer isso, apenas declare a função com `async def`:
```Python hl_lines="1"
async def get_burgers(number: int):
- # Fazer alguma coisa assíncrona para criar os hambúrgueres
+ # Faz alguma coisa assíncrona para criar os hambúrgueres
return burgers
```
@@ -295,9 +293,9 @@ def get_sequential_burgers(number: int):
return burgers
```
-Com `async def`, o Python sabe que, dentro dessa função, tem que estar ciente das expressões `await`, e que isso pode "pausar" a execução dessa função, e poderá fazer outra coisa antes de voltar.
+Com `async def`, o Python sabe que, dentro dessa função, ele deve estar ciente das expressões `await`, e que isso poderá "pausar" ⏸ a execução dessa função, e ir fazer outra coisa 🔀 antes de voltar.
-Quando você quiser chamar uma função `async def`, você tem que "esperar". Então, isso não funcionará:
+Quando você quiser chamar uma função `async def`, você tem que "esperar" ela. Então, isso não funcionará:
```Python
# Isso não irá funcionar, porquê get_burgers foi definido com: async def
@@ -319,13 +317,24 @@ async def read_burgers():
Você deve ter observado que `await` pode ser usado somente dentro de funções definidas com `async def`.
-Mas ao mesmo tempo, funções definidas com `async def` tem que ser aguardadas. Então, funções com `async def` pdem ser chamadas somente dentro de funções definidas com `async def` também.
+Mas ao mesmo tempo, funções definidas com `async def` têm que ser "aguardadas". Então, funções com `async def` pdem ser chamadas somente dentro de funções definidas com `async def` também.
Então, sobre o ovo e a galinha, como você chama a primeira função async?
Se você estivar trabalhando com **FastAPI** não terá que se preocupar com isso, porquê essa "primeira" função será a sua *função de operação de rota*, e o FastAPI saberá como fazer a coisa certa.
-Mas se você quiser usar `async` / `await` sem FastAPI, verifique a documentação oficial Python.
+Mas se você quiser usar `async` / `await` sem FastAPI, você também pode fazê-lo.
+
+### Escreva seu próprio código assíncrono
+
+Starlette (e **FastAPI**) são baseados no AnyIO, o que o torna compatível com ambos o asyncio da biblioteca padrão do Python, e o Trio.
+
+Em particular, você pode usar diretamente o AnyIO para seus casos de uso avançados de concorrência que requerem padrões mais avançados no seu próprio código.
+
+E até se você não estiver utilizando FastAPI, você também pode escrever suas próprias aplicações assíncronas com o AnyIO por ser altamente compatível e ganhar seus benefícios (e.g. *concorrência estruturada*).
+
+Eu criei outra biblioteca em cima do AnyIO, como uma fina camada acima, para melhorar um pouco as anotações de tipo e obter melhor **autocompletar**, **erros de linha**, etc. Ela também possui uma introdução amigável e um tutorial para ajudar você a **entender** e escrever **seu próprio código async**: Asyncer. Seria particularmente útil se você precisar **combinar código async com código regular** (bloqueador/síncrono).
+
### Outras formas de código assíncrono
@@ -337,25 +346,25 @@ Essa mesma sintaxe (ou quase a mesma) foi também incluída recentemente em vers
Mas antes disso, controlar código assíncrono era bem mais complexo e difícil.
-Nas versões anteriores do Python, você poderia utilizar threads ou Gevent. Mas o código é um pouco mais complexo de entender, debugar, e pensar sobre.
+Nas versões anteriores do Python, você poderia utilizar threads ou Gevent. Mas o código é bem mais complexo de entender, debugar, e pensar sobre.
-Nas versões anteriores do NodeJS / Navegador JavaScript, você poderia utilizar "callbacks". O que leva ao inferno do callback.
+Nas versões anteriores do NodeJS / Navegador JavaScript, você utilizaria "callbacks". O que leva ao "inferno do callback".
## Corrotinas
-**Corrotina** é apenas um jeito bonitinho para a coisa que é retornada de uma função `async def`. O Python sabe que é uma função que pode começar e terminar em algum ponto, mas que pode ser pausada internamente também, sempre que tiver um `await` dentro dela.
+**Corrotina** é apenas um jeito bonitinho para a coisa que é retornada de uma função `async def`. O Python sabe que é algo como uma função, que pode começar e que vai terminar em algum ponto, mas que pode ser pausada ⏸ internamente também, sempre que tiver um `await` dentro dela.
-Mas toda essa funcionalidade de código assíncrono com `async` e `await` é muitas vezes resumida como "corrotina". É comparável ao principal recurso chave do Go, a "Gorotina".
+Mas toda essa funcionalidade de código assíncrono com `async` e `await` é muitas vezes resumida como usando "corrotinas". É comparável ao principal recurso chave do Go, a "Gorrotina".
## Conclusão
-Vamos ver a mesma frase com o conteúdo cima:
+Vamos ver a mesma frase de cima:
-> Versões modernas do Python tem suporte para **"código assíncrono"** usando algo chamado **"corrotinas"**, com sintaxe **`async` e `await`**.
+> Versões modernas do Python têm suporte para **"código assíncrono"** usando algo chamado **"corrotinas"**, com sintaxe **`async` e `await`**.
-Isso pode fazer mais sentido agora.
+Isso pode fazer mais sentido agora. ✨
-Tudo isso é o que deixa o FastAPI poderoso (através do Starlette) e que o faz ter uma performance impressionante.
+Tudo isso é o que empodera o FastAPI (através do Starlette) e que o faz ter uma performance tão impressionante.
## Detalhes muito técnicos
@@ -365,25 +374,25 @@ Você pode provavelmente pular isso.
Esses são detalhes muito técnicos de como **FastAPI** funciona por baixo do capô.
-Se você tem algum conhecimento técnico (corrotinas, threads, blocking etc) e está curioso sobre como o FastAPI controla o `async def` vs normal `def`, vá em frente.
+Se você tem certo conhecimento técnico (corrotinas, threads, blocking etc) e está curioso sobre como o FastAPI controla o `async def` vs normal `def`, vá em frente.
///
### Funções de operação de rota
-Quando você declara uma *função de operação de rota* com `def` normal ao invés de `async def`, ela é rodada em uma threadpool externa que então é aguardada, ao invés de ser chamada diretamente (ela poderia bloquear o servidor).
+Quando você declara uma *função de operação de rota* com `def` normal ao invés de `async def`, ela é rodada em uma threadpool externa que é então aguardada, ao invés de ser chamada diretamente (já que ela bloquearia o servidor).
-Se você está chegando de outro framework assíncrono que não faz o trabalho descrito acima e você está acostumado a definir triviais *funções de operação de rota* com simples `def` para ter um mínimo ganho de performance (cerca de 100 nanosegundos), por favor observe que no **FastAPI** o efeito pode ser bem o oposto. Nesses casos, é melhor usar `async def` a menos que suas *funções de operação de rota* utilizem código que performem bloqueamento IO.
+Se você está chegando de outro framework assíncrono que não funciona como descrito acima e você está acostumado a definir *funções de operação de rota* triviais somente de computação com simples `def` para ter um mínimo ganho de performance (cerca de 100 nanosegundos), por favor observe que no **FastAPI** o efeito pode ser bem o oposto. Nesses casos, é melhor usar `async def` a menos que suas *funções de operação de rota* utilizem código que performe bloqueamento IO.
-Ainda, em ambas as situações, as chances são que o **FastAPI** será [ainda mais rápido](index.md#performance){.internal-link target=_blank} do que (ou ao menos comparável a) seus frameworks antecessores.
+Ainda, em ambas as situações, as chances são que o **FastAPI** [ainda será mais rápido](index.md#performance){.internal-link target=_blank} do que (ou ao menos comparável a) seu framework anterior.
### Dependências
-O mesmo se aplica para as dependências. Se uma dependência tem as funções com padrão `def` ao invés de `async def`, ela é rodada no threadpool externo.
+O mesmo se aplica para as [dependências](tutorial/dependencies/index.md){.internal-link target=_blank}. Se uma dependência tem as funções com padrão `def` ao invés de `async def`, ela é rodada no threadpool externo.
### Sub-dependências
-Você pode ter múltiplas dependências e sub-dependências exigindo uma a outra (como parâmetros de definições de funções), algumas delas podem ser criadas com `async def` e algumas com `def` normal. Isso ainda poderia funcionar, e aquelas criadas com `def` podem ser chamadas em uma thread externa ao invés de serem "aguardadas".
+Você pode ter múltiplas dependências e [sub-dependências](tutorial/dependencies/sub-dependencies.md){.internal-link target=_blank} requisitando uma à outra (como parâmetros de definições de funções), algumas delas podem ser criadas com `async def` e algumas com `def` normal. Isso ainda funcionaria, e aquelas criadas com `def` normal seriam chamadas em uma thread externa (do threadpool) ao invés de serem "aguardadas".
### Outras funções de utilidade
@@ -395,6 +404,6 @@ Se sua função de utilidade é uma função normal com `def`, ela será chamada
---
-Novamente, esses são detalhes muito técnicos que provavelmente possam ser úteis caso você esteja procurando por eles.
+Novamente, esses são detalhes muito técnicos que provavelmente seriam úteis caso você esteja procurando por eles.
Caso contrário, você deve ficar bem com as dicas da seção acima: Com pressa?.
diff --git a/docs/pt/docs/contributing.md b/docs/pt/docs/contributing.md
deleted file mode 100644
index bb518a2fa0..0000000000
--- a/docs/pt/docs/contributing.md
+++ /dev/null
@@ -1,507 +0,0 @@
-# Desenvolvimento - Contribuindo
-
-Primeiramente, você deveria ver os meios básicos para [ajudar FastAPI e pedir ajuda](help-fastapi.md){.internal-link target=_blank}.
-
-## Desenvolvendo
-
-Se você já clonou o repositório e precisa mergulhar no código, aqui estão algumas orientações para configurar seu ambiente.
-
-### Ambiente virtual com `venv`
-
-Você pode criar um ambiente virtual em um diretório utilizando o módulo `venv` do Python:
-
-
+
+---
+
+Agora que sabemos a diferença entre os termos **processo** e **programa**, vamos continuar falando sobre implantações.
+
+## Executando na inicialização
+
+Na maioria dos casos, quando você cria uma API web, você quer que ela esteja **sempre em execução**, ininterrupta, para que seus clientes possam sempre acessá-la. Isso é claro, a menos que você tenha um motivo específico para querer que ela seja executada somente em certas situações, mas na maioria das vezes você quer que ela esteja constantemente em execução e **disponível**.
+
+### Em um servidor remoto
+
+Ao configurar um servidor remoto (um servidor em nuvem, uma máquina virtual, etc.), a coisa mais simples que você pode fazer é usar `fastapi run` (que usa Uvicorn) ou algo semelhante, manualmente, da mesma forma que você faz ao desenvolver localmente.
+
+E funcionará e será útil **durante o desenvolvimento**.
+
+Mas se sua conexão com o servidor for perdida, o **processo em execução** provavelmente morrerá.
+
+E se o servidor for reiniciado (por exemplo, após atualizações ou migrações do provedor de nuvem), você provavelmente **não notará**. E por causa disso, você nem saberá que precisa reiniciar o processo manualmente. Então, sua API simplesmente permanecerá inativa. 😱
+
+### Executar automaticamente na inicialização
+
+Em geral, você provavelmente desejará que o programa do servidor (por exemplo, Uvicorn) seja iniciado automaticamente na inicialização do servidor e, sem precisar de nenhuma **intervenção humana**, tenha um processo sempre em execução com sua API (por exemplo, Uvicorn executando seu aplicativo FastAPI).
+
+### Programa separado
+
+Para conseguir isso, você normalmente terá um **programa separado** que garantiria que seu aplicativo fosse executado na inicialização. E em muitos casos, ele também garantiria que outros componentes ou aplicativos também fossem executados, por exemplo, um banco de dados.
+
+### Ferramentas de exemplo para executar na inicialização
+
+Alguns exemplos de ferramentas que podem fazer esse trabalho são:
+
+* Docker
+* Kubernetes
+* Docker Compose
+* Docker em Modo Swarm
+* Systemd
+* Supervisor
+* Gerenciado internamente por um provedor de nuvem como parte de seus serviços
+* Outros...
+
+Darei exemplos mais concretos nos próximos capítulos.
+
+## Reinicializações
+
+Semelhante a garantir que seu aplicativo seja executado na inicialização, você provavelmente também deseja garantir que ele seja **reiniciado** após falhas.
+
+### Nós cometemos erros
+
+Nós, como humanos, cometemos **erros** o tempo todo. O software quase *sempre* tem **bugs** escondidos em lugares diferentes. 🐛
+
+E nós, como desenvolvedores, continuamos aprimorando o código à medida que encontramos esses bugs e implementamos novos recursos (possivelmente adicionando novos bugs também 😅).
+
+### Pequenos erros são tratados automaticamente
+
+Ao criar APIs da web com FastAPI, se houver um erro em nosso código, o FastAPI normalmente o conterá na única solicitação que acionou o erro. 🛡
+
+O cliente receberá um **Erro Interno do Servidor 500** para essa solicitação, mas o aplicativo continuará funcionando para as próximas solicitações em vez de travar completamente.
+
+### Erros maiores - Travamentos
+
+No entanto, pode haver casos em que escrevemos algum código que **trava todo o aplicativo**, fazendo com que o Uvicorn e o Python travem. 💥
+
+E ainda assim, você provavelmente não gostaria que o aplicativo permanecesse inativo porque houve um erro em um lugar, você provavelmente quer que ele **continue em execução** pelo menos para as *operações de caminho* que não estão quebradas.
+
+### Reiniciar após falha
+
+Mas nos casos com erros realmente graves que travam o **processo** em execução, você vai querer um componente externo que seja responsável por **reiniciar** o processo, pelo menos algumas vezes...
+
+/// tip | Dica
+
+...Embora se o aplicativo inteiro estiver **travando imediatamente**, provavelmente não faça sentido reiniciá-lo para sempre. Mas nesses casos, você provavelmente notará isso durante o desenvolvimento, ou pelo menos logo após a implantação.
+
+Então, vamos nos concentrar nos casos principais, onde ele pode travar completamente em alguns casos específicos **no futuro**, e ainda faz sentido reiniciá-lo.
+
+///
+
+Você provavelmente gostaria de ter a coisa responsável por reiniciar seu aplicativo como um **componente externo**, porque a essa altura, o mesmo aplicativo com Uvicorn e Python já havia travado, então não há nada no mesmo código do mesmo aplicativo que possa fazer algo a respeito.
+
+### Ferramentas de exemplo para reiniciar automaticamente
+
+Na maioria dos casos, a mesma ferramenta usada para **executar o programa na inicialização** também é usada para lidar com **reinicializações** automáticas.
+
+Por exemplo, isso poderia ser resolvido por:
+
+* Docker
+* Kubernetes
+* Docker Compose
+* Docker no Modo Swarm
+* Systemd
+* Supervisor
+* Gerenciado internamente por um provedor de nuvem como parte de seus serviços
+* Outros...
+
+## Replicação - Processos e Memória
+
+Com um aplicativo FastAPI, usando um programa de servidor como o comando `fastapi` que executa o Uvicorn, executá-lo uma vez em **um processo** pode atender a vários clientes simultaneamente.
+
+Mas em muitos casos, você desejará executar vários processos de trabalho ao mesmo tempo.
+
+### Processos Múltiplos - Trabalhadores
+
+Se você tiver mais clientes do que um único processo pode manipular (por exemplo, se a máquina virtual não for muito grande) e tiver **vários núcleos** na CPU do servidor, você poderá ter **vários processos** em execução com o mesmo aplicativo ao mesmo tempo e distribuir todas as solicitações entre eles.
+
+Quando você executa **vários processos** do mesmo programa de API, eles são comumente chamados de **trabalhadores**.
+
+### Processos do Trabalhador e Portas
+
+Lembra da documentação [Sobre HTTPS](https.md){.internal-link target=_blank} que diz que apenas um processo pode escutar em uma combinação de porta e endereço IP em um servidor?
+
+Isso ainda é verdade.
+
+Então, para poder ter **vários processos** ao mesmo tempo, tem que haver um **único processo escutando em uma porta** que então transmite a comunicação para cada processo de trabalho de alguma forma.
+
+### Memória por Processo
+
+Agora, quando o programa carrega coisas na memória, por exemplo, um modelo de aprendizado de máquina em uma variável, ou o conteúdo de um arquivo grande em uma variável, tudo isso **consome um pouco da memória (RAM)** do servidor.
+
+E vários processos normalmente **não compartilham nenhuma memória**. Isso significa que cada processo em execução tem suas próprias coisas, variáveis e memória. E se você estiver consumindo uma grande quantidade de memória em seu código, **cada processo** consumirá uma quantidade equivalente de memória.
+
+### Memória do servidor
+
+Por exemplo, se seu código carrega um modelo de Machine Learning com **1 GB de tamanho**, quando você executa um processo com sua API, ele consumirá pelo menos 1 GB de RAM. E se você iniciar **4 processos** (4 trabalhadores), cada um consumirá 1 GB de RAM. Então, no total, sua API consumirá **4 GB de RAM**.
+
+E se o seu servidor remoto ou máquina virtual tiver apenas 3 GB de RAM, tentar carregar mais de 4 GB de RAM causará problemas. 🚨
+
+### Processos Múltiplos - Um Exemplo
+
+Neste exemplo, há um **Processo Gerenciador** que inicia e controla dois **Processos de Trabalhadores**.
+
+Este Processo de Gerenciador provavelmente seria o que escutaria na **porta** no IP. E ele transmitiria toda a comunicação para os processos de trabalho.
+
+Esses processos de trabalho seriam aqueles que executariam seu aplicativo, eles executariam os cálculos principais para receber uma **solicitação** e retornar uma **resposta**, e carregariam qualquer coisa que você colocasse em variáveis na RAM.
+
+
+
+Mas você pode desabilitá-lo definindo `syntaxHighlight` como `False`:
+
+{* ../../docs_src/configure_swagger_ui/tutorial001.py hl[3] *}
+
+...e então o Swagger UI não mostrará mais o destaque de sintaxe:
+
+
+
+## Alterar o tema
+
+Da mesma forma que você pode definir o tema de destaque de sintaxe com a chave `"syntaxHighlight.theme"` (observe que há um ponto no meio):
+
+{* ../../docs_src/configure_swagger_ui/tutorial002.py hl[3] *}
+
+Essa configuração alteraria o tema de cores de destaque de sintaxe:
+
+
+
+## Alterar parâmetros de UI padrão do Swagger
+
+O FastAPI inclui alguns parâmetros de configuração padrão apropriados para a maioria dos casos de uso.
+
+Inclui estas configurações padrão:
+
+{* ../../fastapi/openapi/docs.py ln[7:23] *}
+
+Você pode substituir qualquer um deles definindo um valor diferente no argumento `swagger_ui_parameters`.
+
+Por exemplo, para desabilitar `deepLinking` você pode passar essas configurações para `swagger_ui_parameters`:
+
+{* ../../docs_src/configure_swagger_ui/tutorial003.py hl[3] *}
+
+## Outros parâmetros da UI do Swagger
+
+Para ver todas as outras configurações possíveis que você pode usar, leia a documentação oficial dos parâmetros da UI do Swagger.
+
+## Configurações somente JavaScript
+
+A interface do usuário do Swagger também permite que outras configurações sejam objetos **somente JavaScript** (por exemplo, funções JavaScript).
+
+O FastAPI também inclui estas configurações de `predefinições` somente para JavaScript:
+
+```JavaScript
+presets: [
+ SwaggerUIBundle.presets.apis,
+ SwaggerUIBundle.SwaggerUIStandalonePreset
+]
+```
+
+Esses são objetos **JavaScript**, não strings, então você não pode passá-los diretamente do código Python.
+
+Se você precisar usar configurações somente JavaScript como essas, você pode usar um dos métodos acima. Sobrescreva todas as *operações de rotas* do Swagger UI e escreva manualmente qualquer JavaScript que você precisar.
diff --git a/docs/pt/docs/how-to/custom-docs-ui-assets.md b/docs/pt/docs/how-to/custom-docs-ui-assets.md
new file mode 100644
index 0000000000..b7de6c8bdc
--- /dev/null
+++ b/docs/pt/docs/how-to/custom-docs-ui-assets.md
@@ -0,0 +1,185 @@
+# Recursos Estáticos Personalizados para a UI de Documentação (Hospedagem Própria)
+
+A documentação da API usa **Swagger UI** e **ReDoc**, e cada um deles precisa de alguns arquivos JavaScript e CSS.
+
+Por padrão, esses arquivos são fornecidos por um CDN.
+
+Mas é possível personalizá-los, você pode definir um CDN específico ou providenciar os arquivos você mesmo.
+
+## CDN Personalizado para JavaScript e CSS
+
+Vamos supor que você deseja usar um CDN diferente, por exemplo, você deseja usar `https://unpkg.com/`.
+
+Isso pode ser útil se, por exemplo, você mora em um país que restringe algumas URLs.
+
+### Desativar a documentação automática
+
+O primeiro passo é desativar a documentação automática, pois por padrão, ela usa o CDN padrão.
+
+Para desativá-los, defina suas URLs como `None` ao criar seu aplicativo `FastAPI`:
+
+{* ../../docs_src/custom_docs_ui/tutorial001.py hl[8] *}
+
+### Incluir a documentação personalizada
+
+Agora você pode criar as *operações de rota* para a documentação personalizada.
+
+Você pode reutilizar as funções internas do FastAPI para criar as páginas HTML para a documentação e passar os argumentos necessários:
+
+* `openapi_url`: a URL onde a página HTML para a documentação pode obter o esquema OpenAPI para a sua API. Você pode usar aqui o atributo `app.openapi_url`.
+* `title`: o título da sua API.
+* `oauth2_redirect_url`: você pode usar `app.swagger_ui_oauth2_redirect_url` aqui para usar o padrão.
+* `swagger_js_url`: a URL onde a página HTML para a sua documentação do Swagger UI pode obter o arquivo **JavaScript**. Este é o URL do CDN personalizado.
+* `swagger_css_url`: a URL onde a página HTML para a sua documentação do Swagger UI pode obter o arquivo **CSS**. Este é o URL do CDN personalizado.
+
+E de forma semelhante para o ReDoc...
+
+{* ../../docs_src/custom_docs_ui/tutorial001.py hl[2:6,11:19,22:24,27:33] *}
+
+/// tip | Dica
+
+A *operação de rota* para `swagger_ui_redirect` é um auxiliar para quando você usa OAuth2.
+
+Se você integrar sua API com um provedor OAuth2, você poderá autenticar e voltar para a documentação da API com as credenciais adquiridas. E interagir com ela usando a autenticação OAuth2 real.
+
+Swagger UI lidará com isso nos bastidores para você, mas ele precisa desse auxiliar de "redirecionamento".
+
+///
+
+### Criar uma *operação de rota* para testar
+
+Agora, para poder testar se tudo funciona, crie uma *operação de rota*:
+
+{* ../../docs_src/custom_docs_ui/tutorial001.py hl[36:38] *}
+
+### Teste
+
+Agora, você deve ser capaz de ir para a documentação em http://127.0.0.1:8000/docs, e recarregar a página, ela carregará esses recursos do novo CDN.
+
+## Hospedagem Própria de JavaScript e CSS para a documentação
+
+Hospedar o JavaScript e o CSS pode ser útil se, por exemplo, você precisa que seu aplicativo continue funcionando mesmo offline, sem acesso aberto à Internet, ou em uma rede local.
+
+Aqui você verá como providenciar esses arquivos você mesmo, no mesmo aplicativo FastAPI, e configurar a documentação para usá-los.
+
+### Estrutura de Arquivos do Projeto
+
+Vamos supor que a estrutura de arquivos do seu projeto se pareça com isso:
+
+```
+.
+├── app
+│ ├── __init__.py
+│ ├── main.py
+```
+
+Agora crie um diretório para armazenar esses arquivos estáticos.
+
+Sua nova estrutura de arquivos poderia se parecer com isso:
+
+```
+.
+├── app
+│ ├── __init__.py
+│ ├── main.py
+└── static/
+```
+
+### Baixe os arquivos
+
+Baixe os arquivos estáticos necessários para a documentação e coloque-os no diretório `static/`.
+
+Você provavelmente pode clicar com o botão direito em cada link e selecionar uma opção semelhante a `Salvar link como...`.
+
+**Swagger UI** usa os arquivos:
+
+* `swagger-ui-bundle.js`
+* `swagger-ui.css`
+
+E o **ReDoc** usa os arquivos:
+
+* `redoc.standalone.js`
+
+Depois disso, sua estrutura de arquivos deve se parecer com:
+
+```
+.
+├── app
+│ ├── __init__.py
+│ ├── main.py
+└── static
+ ├── redoc.standalone.js
+ ├── swagger-ui-bundle.js
+ └── swagger-ui.css
+```
+
+### Prover os arquivos estáticos
+
+* Importe `StaticFiles`.
+* "Monte" a instância `StaticFiles()` em um caminho específico.
+
+{* ../../docs_src/custom_docs_ui/tutorial002.py hl[7,11] *}
+
+### Teste os arquivos estáticos
+
+Inicialize seu aplicativo e vá para http://127.0.0.1:8000/static/redoc.standalone.js.
+
+Você deverá ver um arquivo JavaScript muito longo para o **ReDoc**.
+
+Esse arquivo pode começar com algo como:
+
+```JavaScript
+/*! For license information please see redoc.standalone.js.LICENSE.txt */
+!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("null")):
+...
+```
+
+Isso confirma que você está conseguindo fornecer arquivos estáticos do seu aplicativo e que você colocou os arquivos estáticos para a documentação no local correto.
+
+Agora, podemos configurar o aplicativo para usar esses arquivos estáticos para a documentação.
+
+### Desativar a documentação automática para arquivos estáticos
+
+Da mesma forma que ao usar um CDN personalizado, o primeiro passo é desativar a documentação automática, pois ela usa o CDN padrão.
+
+Para desativá-los, defina suas URLs como `None` ao criar seu aplicativo `FastAPI`:
+
+{* ../../docs_src/custom_docs_ui/tutorial002.py hl[9] *}
+
+### Incluir a documentação personalizada para arquivos estáticos
+
+E da mesma forma que com um CDN personalizado, agora você pode criar as *operações de rota* para a documentação personalizada.
+
+Novamente, você pode reutilizar as funções internas do FastAPI para criar as páginas HTML para a documentação e passar os argumentos necessários:
+
+* `openapi_url`: a URL onde a página HTML para a documentação pode obter o esquema OpenAPI para a sua API. Você pode usar aqui o atributo `app.openapi_url`.
+* `title`: o título da sua API.
+* `oauth2_redirect_url`: Você pode usar `app.swagger_ui_oauth2_redirect_url` aqui para usar o padrão.
+* `swagger_js_url`: a URL onde a página HTML para a sua documentação do Swagger UI pode obter o arquivo **JavaScript**. Este é o URL do CDN personalizado. **Este é o URL que seu aplicativo está fornecendo**.
+* `swagger_css_url`: a URL onde a página HTML para a sua documentação do Swagger UI pode obter o arquivo **CSS**. **Esse é o que seu aplicativo está fornecendo**.
+
+E de forma semelhante para o ReDoc...
+
+{* ../../docs_src/custom_docs_ui/tutorial002.py hl[2:6,14:22,25:27,30:36] *}
+
+/// tip | Dica
+
+A *operação de rota* para `swagger_ui_redirect` é um auxiliar para quando você usa OAuth2.
+
+Se você integrar sua API com um provedor OAuth2, você poderá autenticar e voltar para a documentação da API com as credenciais adquiridas. E, então, interagir com ela usando a autenticação OAuth2 real.
+
+Swagger UI lidará com isso nos bastidores para você, mas ele precisa desse auxiliar de "redirect".
+
+///
+
+### Criar uma *operação de rota* para testar arquivos estáticos
+
+Agora, para poder testar se tudo funciona, crie uma *operação de rota*:
+
+{* ../../docs_src/custom_docs_ui/tutorial002.py hl[39:41] *}
+
+### Teste a UI de Arquivos Estáticos
+
+Agora, você deve ser capaz de desconectar o WiFi, ir para a documentação em http://127.0.0.1:8000/docs, e recarregar a página.
+
+E mesmo sem Internet, você será capaz de ver a documentação da sua API e interagir com ela.
diff --git a/docs/pt/docs/how-to/custom-request-and-route.md b/docs/pt/docs/how-to/custom-request-and-route.md
new file mode 100644
index 0000000000..151a0f5d44
--- /dev/null
+++ b/docs/pt/docs/how-to/custom-request-and-route.md
@@ -0,0 +1,109 @@
+# Requisições Personalizadas e Classes da APIRoute
+
+Em algum casos, você pode querer sobreescrever a lógica usada pelas classes `Request`e `APIRoute`.
+
+Em particular, isso pode ser uma boa alternativa para uma lógica em um middleware
+
+Por exemplo, se você quiser ler ou manipular o corpo da requisição antes que ele seja processado pela sua aplicação.
+
+/// danger | Perigo
+
+Isso é um recurso "avançado".
+
+Se você for um iniciante em **FastAPI** você deve considerar pular essa seção.
+
+///
+
+## Casos de Uso
+
+Alguns casos de uso incluem:
+
+* Converter requisições não-JSON para JSON (por exemplo, `msgpack`).
+* Descomprimir corpos de requisição comprimidos com gzip.
+* Registrar automaticamente todos os corpos de requisição.
+
+## Manipulando codificações de corpo de requisição personalizadas
+
+Vamos ver como usar uma subclasse personalizada de `Request` para descomprimir requisições gzip.
+
+E uma subclasse de `APIRoute` para usar essa classe de requisição personalizada.
+
+### Criar uma classe `GzipRequest` personalizada
+
+/// tip | Dica
+
+Isso é um exemplo de brincadeira para demonstrar como funciona, se você precisar de suporte para Gzip, você pode usar o [`GzipMiddleware`](../advanced/middleware.md#gzipmiddleware){.internal-link target=_blank} fornecido.
+
+///
+
+Primeiro, criamos uma classe `GzipRequest`, que irá sobrescrever o método `Request.body()` para descomprimir o corpo na presença de um cabeçalho apropriado.
+
+Se não houver `gzip` no cabeçalho, ele não tentará descomprimir o corpo.
+
+Dessa forma, a mesma classe de rota pode lidar com requisições comprimidas ou não comprimidas.
+
+{* ../../docs_src/custom_request_and_route/tutorial001.py hl[8:15] *}
+
+### Criar uma classe `GzipRoute` personalizada
+
+Em seguida, criamos uma subclasse personalizada de `fastapi.routing.APIRoute` que fará uso do `GzipRequest`.
+
+Dessa vez, ele irá sobrescrever o método `APIRoute.get_route_handler()`.
+
+Esse método retorna uma função. E essa função é o que irá receber uma requisição e retornar uma resposta.
+
+Aqui nós usamos para criar um `GzipRequest` a partir da requisição original.
+
+{* ../../docs_src/custom_request_and_route/tutorial001.py hl[18:26] *}
+
+/// note | Detalhes Técnicos
+
+Um `Request` também tem um `request.receive`, que é uma função para "receber" o corpo da requisição.
+
+Um `Request` também tem um `request.receive`, que é uma função para "receber" o corpo da requisição.
+
+O dicionário `scope` e a função `receive` são ambos parte da especificação ASGI.
+
+E essas duas coisas, `scope` e `receive`, são o que é necessário para criar uma nova instância de `Request`.
+
+Para aprender mais sobre o `Request` confira a documentação do Starlette sobre Requests.
+
+///
+
+A única coisa que a função retornada por `GzipRequest.get_route_handler` faz de diferente é converter o `Request` para um `GzipRequest`.
+
+Fazendo isso, nosso `GzipRequest` irá cuidar de descomprimir os dados (se necessário) antes de passá-los para nossas *operações de rota*.
+
+Depois disso, toda a lógica de processamento é a mesma.
+
+Mas por causa das nossas mudanças em `GzipRequest.body`, o corpo da requisição será automaticamente descomprimido quando for carregado pelo **FastAPI** quando necessário.
+
+## Acessando o corpo da requisição em um manipulador de exceção
+
+/// tip | Dica
+
+Para resolver esse mesmo problema, é provavelmente muito mais fácil usar o `body` em um manipulador personalizado para `RequestValidationError` ([Tratando Erros](../tutorial/handling-errors.md#use-the-requestvalidationerror-body){.internal-link target=_blank}).
+
+Mas esse exemplo ainda é valido e mostra como interagir com os componentes internos.
+
+///
+
+Também podemos usar essa mesma abordagem para acessar o corpo da requisição em um manipulador de exceção.
+
+Tudo que precisamos fazer é manipular a requisição dentro de um bloco `try`/`except`:
+
+{* ../../docs_src/custom_request_and_route/tutorial002.py hl[13,15] *}
+
+Se uma exceção ocorrer, a instância `Request` ainda estará em escopo, então podemos ler e fazer uso do corpo da requisição ao lidar com o erro:
+
+{* ../../docs_src/custom_request_and_route/tutorial002.py hl[16:18] *}
+
+## Classe `APIRoute` personalizada em um router
+
+você também pode definir o parametro `route_class` de uma `APIRouter`;
+
+{* ../../docs_src/custom_request_and_route/tutorial003.py hl[26] *}
+
+Nesse exemplo, as *operações de rota* sob o `router` irão usar a classe `TimedRoute` personalizada, e terão um cabeçalho extra `X-Response-Time` na resposta com o tempo que levou para gerar a resposta:
+
+{* ../../docs_src/custom_request_and_route/tutorial003.py hl[13:20] *}
diff --git a/docs/pt/docs/how-to/extending-openapi.md b/docs/pt/docs/how-to/extending-openapi.md
new file mode 100644
index 0000000000..b4785edc15
--- /dev/null
+++ b/docs/pt/docs/how-to/extending-openapi.md
@@ -0,0 +1,80 @@
+# Extendendo o OpenAPI
+
+Existem alguns casos em que pode ser necessário modificar o esquema OpenAPI gerado.
+
+Nesta seção, você verá como fazer isso.
+
+## O processo normal
+
+O processo normal (padrão) é o seguinte:
+
+Uma aplicação (instância) do `FastAPI` possui um método `.openapi()` que deve retornar o esquema OpenAPI.
+
+Como parte da criação do objeto de aplicação, uma *operação de rota* para `/openapi.json` (ou para o que você definir como `openapi_url`) é registrada.
+
+Ela apenas retorna uma resposta JSON com o resultado do método `.openapi()` da aplicação.
+
+Por padrão, o que o método `.openapi()` faz é verificar se a propriedade `.openapi_schema` tem conteúdo e retorná-lo.
+
+Se não tiver, ele gera o conteúdo usando a função utilitária em `fastapi.openapi.utils.get_openapi`.
+
+E essa função `get_openapi()` recebe como parâmetros:
+
+* `title`: O título do OpenAPI, exibido na documentação.
+* `version`: A versão da sua API, por exemplo, `2.5.0`.
+* `openapi_version`: A versão da especificação OpenAPI utilizada. Por padrão, a mais recente: `3.1.0`.
+* `summary`: Um resumo curto da API.
+* `description`: A descrição da sua API, que pode incluir markdown e será exibida na documentação.
+* `routes`: Uma lista de rotas, que são cada uma das *operações de rota* registradas. Elas são obtidas de `app.routes`.
+
+/// info | Informação
+
+O parâmetro `summary` está disponível no OpenAPI 3.1.0 e superior, suportado pelo FastAPI 0.99.0 e superior.
+
+///
+
+## Sobrescrevendo os padrões
+
+Com as informações acima, você pode usar a mesma função utilitária para gerar o esquema OpenAPI e sobrescrever cada parte que precisar.
+
+Por exemplo, vamos adicionar Extensão OpenAPI do ReDoc para incluir um logo personalizado.
+
+### **FastAPI** Normal
+
+Primeiro, escreva toda a sua aplicação **FastAPI** normalmente:
+
+{* ../../docs_src/extending_openapi/tutorial001.py hl[1,4,7:9] *}
+
+### Gerar o esquema OpenAPI
+
+Em seguida, use a mesma função utilitária para gerar o esquema OpenAPI, dentro de uma função `custom_openapi()`:
+
+{* ../../docs_src/extending_openapi/tutorial001.py hl[2,15:21] *}
+
+### Modificar o esquema OpenAPI
+
+Agora, você pode adicionar a extensão do ReDoc, incluindo um `x-logo` personalizado ao "objeto" `info` no esquema OpenAPI:
+
+{* ../../docs_src/extending_openapi/tutorial001.py hl[22:24] *}
+
+### Armazenar em cache o esquema OpenAPI
+
+Você pode usar a propriedade `.openapi_schema` como um "cache" para armazenar o esquema gerado.
+
+Dessa forma, sua aplicação não precisará gerar o esquema toda vez que um usuário abrir a documentação da sua API.
+
+Ele será gerado apenas uma vez, e o mesmo esquema armazenado em cache será utilizado nas próximas requisições.
+
+{* ../../docs_src/extending_openapi/tutorial001.py hl[13:14,25:26] *}
+
+### Sobrescrever o método
+
+Agora, você pode substituir o método `.openapi()` pela sua nova função.
+
+{* ../../docs_src/extending_openapi/tutorial001.py hl[29] *}
+
+### Verificar
+
+Uma vez que você acessar http://127.0.0.1:8000/redoc, verá que está usando seu logo personalizado (neste exemplo, o logo do **FastAPI**):
+
+
diff --git a/docs/pt/docs/how-to/graphql.md b/docs/pt/docs/how-to/graphql.md
new file mode 100644
index 0000000000..ef0bad7f67
--- /dev/null
+++ b/docs/pt/docs/how-to/graphql.md
@@ -0,0 +1,60 @@
+# GraphQL
+
+Como o **FastAPI** é baseado no padrão **ASGI**, é muito fácil integrar qualquer biblioteca **GraphQL** também compatível com ASGI.
+
+Você pode combinar *operações de rota* normais do FastAPI com GraphQL na mesma aplicação.
+
+/// tip | Dica
+
+**GraphQL** resolve alguns casos de uso muito específicos.
+
+Ele tem **vantagens** e **desvantagens** quando comparado a **web APIs** comuns.
+
+Certifique-se de avaliar se os **benefícios** para o seu caso de uso compensam as **desvantagens**. 🤓
+
+///
+
+## Bibliotecas GraphQL
+
+Aqui estão algumas das bibliotecas **GraphQL** que têm suporte **ASGI**. Você pode usá-las com **FastAPI**:
+
+* Strawberry 🍓
+ * Com docs para FastAPI
+* Ariadne
+ * Com docs para FastAPI
+* Tartiflette
+ * Com Tartiflette ASGI para fornecer integração ASGI
+* Graphene
+ * Com starlette-graphene3
+
+## GraphQL com Strawberry
+
+Se você precisar ou quiser trabalhar com **GraphQL**, **Strawberry** é a biblioteca **recomendada** pois tem o design mais próximo ao design do **FastAPI**, ela é toda baseada em **type annotations**.
+
+Dependendo do seu caso de uso, você pode preferir usar uma biblioteca diferente, mas se você me perguntasse, eu provavelmente sugeriria que você experimentasse o **Strawberry**.
+
+Aqui está uma pequena prévia de como você poderia integrar Strawberry com FastAPI:
+
+{* ../../docs_src/graphql/tutorial001.py hl[3,22,25:26] *}
+
+Você pode aprender mais sobre Strawberry na documentação do Strawberry.
+
+E também na documentação sobre Strawberry com FastAPI.
+
+## Antigo `GraphQLApp` do Starlette
+
+Versões anteriores do Starlette incluiam uma classe `GraphQLApp` para integrar com Graphene.
+
+Ela foi descontinuada do Starlette, mas se você tem código que a utilizava, você pode facilmente **migrar** para starlette-graphene3, que cobre o mesmo caso de uso e tem uma **interface quase idêntica**.
+
+/// tip | Dica
+
+Se você precisa de GraphQL, eu ainda recomendaria que você desse uma olhada no Strawberry, pois ele é baseado em type annotations em vez de classes e tipos personalizados.
+
+///
+
+## Saiba Mais
+
+Você pode aprender mais sobre **GraphQL** na documentação oficial do GraphQL.
+
+Você também pode ler mais sobre cada uma das bibliotecas descritas acima em seus links.
diff --git a/docs/pt/docs/how-to/separate-openapi-schemas.md b/docs/pt/docs/how-to/separate-openapi-schemas.md
new file mode 100644
index 0000000000..291b0e1639
--- /dev/null
+++ b/docs/pt/docs/how-to/separate-openapi-schemas.md
@@ -0,0 +1,104 @@
+# Esquemas OpenAPI Separados para Entrada e Saída ou Não
+
+Ao usar **Pydantic v2**, o OpenAPI gerado é um pouco mais exato e **correto** do que antes. 😎
+
+Inclusive, em alguns casos, ele terá até **dois JSON Schemas** no OpenAPI para o mesmo modelo Pydantic, para entrada e saída, dependendo se eles possuem **valores padrão**.
+
+Vamos ver como isso funciona e como alterar se for necessário.
+
+## Modelos Pydantic para Entrada e Saída
+
+Digamos que você tenha um modelo Pydantic com valores padrão, como este:
+
+{* ../../docs_src/separate_openapi_schemas/tutorial001_py310.py ln[1:7] hl[7] *}
+
+### Modelo para Entrada
+
+Se você usar esse modelo como entrada, como aqui:
+
+{* ../../docs_src/separate_openapi_schemas/tutorial001_py310.py ln[1:15] hl[14] *}
+
+... então o campo `description` não será obrigatório. Porque ele tem um valor padrão de `None`.
+
+### Modelo de Entrada na Documentação
+
+Você pode confirmar que na documentação, o campo `description` não tem um **asterisco vermelho**, não é marcado como obrigatório:
+
+
+
+
+
+
+uvicorn main:app --reload...fastapi dev main.py...email-validator - para validação de email.
-Usados por Starlette:
+Utilizado pelo Starlette:
-* httpx - Necessário se você quiser utilizar o `TestClient`.
-* jinja2 - Necessário se você quiser utilizar a configuração padrão de templates.
-* python-multipart - Necessário se você quiser suporte com "parsing" de formulário, com `request.form()`.
-* itsdangerous - Necessário para suporte a `SessionMiddleware`.
-* pyyaml - Necessário para suporte a `SchemaGenerator` da Starlette (você provavelmente não precisará disso com o FastAPI).
-* graphene - Necessário para suporte a `GraphQLApp`.
+* httpx - Obrigatório caso você queira utilizar o `TestClient`.
+* jinja2 - Obrigatório se você quer utilizar a configuração padrão de templates.
+* python-multipart - Obrigatório se você deseja suporte a "parsing" de formulário, com `request.form()`.
-Usados por FastAPI / Starlette:
+Utilizado pelo FastAPI / Starlette:
-* uvicorn - para o servidor que carrega e serve sua aplicação.
-* orjson - Necessário se você quer utilizar `ORJSONResponse`.
-* ujson - Necessário se você quer utilizar `UJSONResponse`.
+* uvicorn - para o servidor que carrega e serve a sua aplicação. Isto inclui `uvicorn[standard]`, que inclui algumas dependências (e.g. `uvloop`) necessárias para servir em alta performance.
+* `fastapi-cli` - que disponibiliza o comando `fastapi`.
-Você pode instalar todas essas dependências com `pip install fastapi[all]`.
+### Sem as dependências `standard`
+
+Se você não deseja incluir as dependências opcionais `standard`, você pode instalar utilizando `pip install fastapi` ao invés de `pip install "fastapi[standard]"`.
+
+### Dpendências opcionais adicionais
+
+Existem algumas dependências adicionais que você pode querer instalar.
+
+Dependências opcionais adicionais do Pydantic:
+
+* pydantic-settings - para gerenciamento de configurações.
+* pydantic-extra-types - tipos extras para serem utilizados com o Pydantic.
+
+Dependências opcionais adicionais do FastAPI:
+
+* orjson - Obrigatório se você deseja utilizar o `ORJSONResponse`.
+* ujson - Obrigatório se você deseja utilizar o `UJSONResponse`.
## Licença
diff --git a/docs/pt/docs/project-generation.md b/docs/pt/docs/project-generation.md
index e5c935fd2d..e337ad762e 100644
--- a/docs/pt/docs/project-generation.md
+++ b/docs/pt/docs/project-generation.md
@@ -1,84 +1,28 @@
-# Geração de Projetos - Modelo
+# Full Stack FastAPI Template
-Você pode usar um gerador de projetos para começar, por já incluir configurações iniciais, segurança, banco de dados e os primeiros _endpoints_ API já feitos para você.
+_Templates_, embora tipicamente venham com alguma configuração específica, são desenhados para serem flexíveis e customizáveis. Isso permite que você os modifique e adapte para as especificações do seu projeto, fazendo-os um excelente ponto de partida. 🏁
-Um gerador de projetos sempre terá uma pré-configuração que você pode atualizar e adaptar para suas próprias necessidades, mas pode ser um bom ponto de partida para seu projeto.
+Você pode usar esse _template_ para começar, já que ele inclui várias configurações iniciais, segurança, banco de dados, e alguns _endpoints_ de API já feitos para você.
-## Full Stack FastAPI PostgreSQL
+Repositório GitHub: Full Stack FastAPI Template
-GitHub: https://github.com/tiangolo/full-stack-fastapi-postgresql
+## Full Stack FastAPI Template - Pilha de Tecnologias e Recursos
-### Full Stack FastAPI PostgreSQL - Recursos
-
-* Integração completa **Docker**.
-* Modo de implantação Docker Swarm.
-* Integração e otimização **Docker Compose** para desenvolvimento local.
-* **Pronto para Produção** com servidor _web_ usando Uvicorn e Gunicorn.
-* _Backend_ **FastAPI** Python:
- * **Rápido**: Alta performance, no nível de **NodeJS** e **Go** (graças ao Starlette e Pydantic).
- * **Intuitivo**: Ótimo suporte de editor. _Auto-Complete_ em todo lugar. Menos tempo _debugando_.
- * **Fácil**: Projetado para ser fácil de usar e aprender. Menos tempo lendo documentações.
- * **Curto**: Minimize duplicação de código. Múltiplos recursos para cada declaração de parâmetro.
- * **Robusto**: Tenha código pronto para produção. Com documentação interativa automática.
- * **Baseado em Padrões**: Baseado em (e completamente compatível com) padrões abertos para APIs: OpenAPI e JSON Schema.
- * **Muitos outros recursos** incluindo validação automática, serialização, documentação interativa, autenticação com _tokens_ OAuth2 JWT etc.
-* **Senha segura** _hashing_ por padrão.
-* Autenticação **Token JWT**.
-* Modelos **SQLAlchemy** (independente de extensões Flask, para que eles possam ser usados com _workers_ Celery diretamente).
-* Modelos básicos para usuários (modifique e remova conforme suas necessidades).
-* Migrações **Alembic**.
-* **CORS** (_Cross Origin Resource Sharing_ - Compartilhamento de Recursos Entre Origens).
-* _Worker_ **Celery** que pode importar e usar modelos e códigos do resto do _backend_ seletivamente.
-* Testes _backend_ _REST_ baseados no **Pytest**, integrados com Docker, então você pode testar a interação completa da API, independente do banco de dados. Como roda no Docker, ele pode construir um novo repositório de dados do zero toda vez (assim você pode usar ElasticSearch, MongoDB, CouchDB, ou o que quiser, e apenas testar que a API esteja funcionando).
-* Fácil integração com Python através dos **Kernels Jupyter** para desenvolvimento remoto ou no Docker com extensões como Atom Hydrogen ou Visual Studio Code Jupyter.
-* _Frontend_ **Vue**:
- * Gerado com Vue CLI.
- * Controle de **Autenticação JWT**.
- * Visualização de _login_.
- * Após o _login_, visualização do painel de controle principal.
- * Painel de controle principal com criação e edição de usuário.
- * Edição do próprio usuário.
- * **Vuex**.
- * **Vue-router**.
- * **Vuetify** para belos componentes _material design_.
- * **TypeScript**.
- * Servidor Docker baseado em **Nginx** (configurado para rodar "lindamente" com Vue-router).
- * Construção multi-estágio Docker, então você não precisa salvar ou _commitar_ código compilado.
- * Testes _frontend_ rodados na hora da construção (pode ser desabilitado também).
- * Feito tão modular quanto possível, então ele funciona fora da caixa, mas você pode gerar novamente com Vue CLI ou criar conforme você queira, e reutilizar o que quiser.
-* **PGAdmin** para banco de dados PostgreSQL, você pode modificar para usar PHPMyAdmin e MySQL facilmente.
-* **Flower** para monitoração de tarefas Celery.
-* Balanceamento de carga entre _frontend_ e _backend_ com **Traefik**, então você pode ter ambos sob o mesmo domínio, separados por rota, mas servidos por diferentes containers.
-* Integração Traefik, incluindo geração automática de certificados **HTTPS** Let's Encrypt.
-* GitLab **CI** (integração contínua), incluindo testes _frontend_ e _backend_.
-
-## Full Stack FastAPI Couchbase
-
-GitHub: https://github.com/tiangolo/full-stack-fastapi-couchbase
-
-⚠️ **WARNING** ⚠️
-
-Se você está iniciando um novo projeto do zero, verifique as alternativas aqui.
-
-Por exemplo, o gerador de projetos Full Stack FastAPI PostgreSQL pode ser uma alternativa melhor, como ele é ativamente mantido e utilizado. E ele inclui todos os novos recursos e melhorias.
-
-Você ainda é livre para utilizar o gerador baseado em Couchbase se quiser, ele provavelmente ainda funciona bem, e você já tem um projeto gerado com ele que roda bem também (e você provavelmente já atualizou ele para encaixar nas suas necessidades).
-
-Você pode ler mais sobre nas documentaçãoes do repositório.
-
-## Full Stack FastAPI MongoDB
-
-...pode demorar, dependendo do meu tempo disponível e outros fatores. 😅 🎉
-
-## Modelos de Aprendizado de Máquina com spaCy e FastAPI
-
-GitHub: https://github.com/microsoft/cookiecutter-spacy-fastapi
-
-### Modelos de Aprendizado de Máquina com spaCy e FastAPI - Recursos
-
-* Integração com modelo NER **spaCy**.
-* Formato de requisição **Busca Cognitiva Azure** acoplado.
-* Servidor Python _web_ **Pronto para Produção** usando Uvicorn e Gunicorn.
-* Implantação **Azure DevOps** Kubernetes (AKS) CI/CD acoplada.
-* **Multilingual** facilmente escolhido como uma das linguagens spaCy acopladas durante a configuração do projeto.
-* **Facilmente extensível** para outros modelos de _frameworks_ (Pytorch, Tensorflow), não apenas spaCy.
+- ⚡ [**FastAPI**](https://fastapi.tiangolo.com) para a API do backend em Python.
+ - 🧰 [SQLModel](https://sqlmodel.tiangolo.com) para as interações do Python com bancos de dados SQL (ORM).
+ - 🔍 [Pydantic](https://docs.pydantic.dev), usado pelo FastAPI, para validação de dados e gerenciamento de configurações.
+ - 💾 [PostgreSQL](https://www.postgresql.org) como banco de dados SQL.
+- 🚀 [React](https://react.dev) para o frontend.
+ - 💃 Usando TypeScript, hooks, [Vite](https://vitejs.dev), e outras partes de uma _stack_ frontend moderna.
+ - 🎨 [Chakra UI](https://chakra-ui.com) para os componentes de frontend.
+ - 🤖 Um cliente frontend automaticamente gerado.
+ - 🧪 [Playwright](https://playwright.dev) para testes Ponta-a-Ponta.
+ - 🦇 Suporte para modo escuro.
+- 🐋 [Docker Compose](https://www.docker.com) para desenvolvimento e produção.
+- 🔒 _Hash_ seguro de senhas por padrão.
+- 🔑 Autenticação por token JWT.
+- 📫 Recuperação de senhas baseada em email.
+- ✅ Testes com [Pytest](https://pytest.org).
+- 📞 [Traefik](https://traefik.io) como proxy reverso / balanceador de carga.
+- 🚢 Instruções de _deployment_ usando Docker Compose, incluindo como configurar um proxy frontend com Traefik para gerenciar automaticamente certificados HTTPS.
+- 🏭 CI (Integração Contínua) e CD (_Deploy_ Contínuo) baseado em GitHub Actions.
diff --git a/docs/pt/docs/python-types.md b/docs/pt/docs/python-types.md
index 86630cd2ad..90a361f403 100644
--- a/docs/pt/docs/python-types.md
+++ b/docs/pt/docs/python-types.md
@@ -1,18 +1,18 @@
# Introdução aos tipos Python
-**Python 3.6 +** tem suporte para "type hints" opcionais.
+O Python possui suporte para "dicas de tipo" ou "type hints" (também chamado de "anotações de tipo" ou "type annotations")
-Esses **"type hints"** são uma nova sintaxe (desde Python 3.6+) que permite declarar o tipo de uma variável.
+Esses **"type hints"** são uma sintaxe especial que permite declarar o tipo de uma variável.
Ao declarar tipos para suas variáveis, editores e ferramentas podem oferecer um melhor suporte.
-Este é apenas um **tutorial rápido / atualização** sobre type hints Python. Ele cobre apenas o mínimo necessário para usá-los com o **FastAPI** ... que é realmente muito pouco.
+Este é apenas um **tutorial rápido / atualização** sobre type hints do Python. Ele cobre apenas o mínimo necessário para usá-los com o **FastAPI**... que é realmente muito pouco.
O **FastAPI** é baseado nesses type hints, eles oferecem muitas vantagens e benefícios.
Mas mesmo que você nunca use o **FastAPI**, você se beneficiaria de aprender um pouco sobre eles.
-/// note | "Nota"
+/// note | Nota
Se você é um especialista em Python e já sabe tudo sobre type hints, pule para o próximo capítulo.
@@ -22,9 +22,7 @@ Se você é um especialista em Python e já sabe tudo sobre type hints, pule par
Vamos começar com um exemplo simples:
-```Python
-{!../../../docs_src/python_types/tutorial001.py!}
-```
+{* ../../docs_src/python_types/tutorial001.py *}
A chamada deste programa gera:
@@ -35,12 +33,10 @@ John Doe
A função faz o seguinte:
* Pega um `first_name` e `last_name`.
-* Converte a primeira letra de cada uma em maiúsculas com `title ()`.
-* Concatena com um espaço no meio.
+* Converte a primeira letra de cada uma em maiúsculas com `title()`.
+* Concatena com um espaço no meio.
-```Python hl_lines="2"
-{!../../../docs_src/python_types/tutorial001.py!}
-```
+{* ../../docs_src/python_types/tutorial001.py hl[2] *}
### Edite-o
@@ -48,7 +44,7 @@ A função faz o seguinte:
Mas agora imagine que você estava escrevendo do zero.
-Em algum momento você teria iniciado a definição da função, já tinha os parâmetros prontos ...
+Em algum momento você teria iniciado a definição da função, já tinha os parâmetros prontos...
Mas então você deve chamar "esse método que converte a primeira letra em maiúscula".
@@ -82,9 +78,7 @@ para:
Esses são os "type hints":
-```Python hl_lines="1"
-{!../../../docs_src/python_types/tutorial002.py!}
-```
+{* ../../docs_src/python_types/tutorial002.py hl[1] *}
Isso não é o mesmo que declarar valores padrão como seria com:
@@ -96,37 +90,33 @@ Isso não é o mesmo que declarar valores padrão como seria com:
Estamos usando dois pontos (`:`), não é igual a (`=`).
-E adicionar type hints normalmente não muda o que acontece do que aconteceria sem elas.
+E adicionar type hints normalmente não muda o que acontece do que aconteceria sem eles.
Mas agora, imagine que você está novamente no meio da criação dessa função, mas com type hints.
-No mesmo ponto, você tenta acionar o preenchimento automático com o `Ctrl Space` e vê:
+No mesmo ponto, você tenta acionar o preenchimento automático com o `Ctrl+Space` e vê:
-Com isso, você pode rolar, vendo as opções, até encontrar o que "toca uma campainha":
+Com isso, você pode rolar, vendo as opções, até encontrar o que "soa familiar":
## Mais motivação
-Marque esta função, ela já possui type hints:
+Verifique esta função, ela já possui type hints:
-```Python hl_lines="1"
-{!../../../docs_src/python_types/tutorial003.py!}
-```
+{* ../../docs_src/python_types/tutorial003.py hl[1] *}
-Como o editor conhece os tipos de variáveis, você não apenas obtém a conclusão, mas também as verificações de erro:
+Como o editor conhece os tipos de variáveis, você não obtém apenas o preenchimento automático, mas também as verificações de erro:
-Agora você sabe que precisa corrigí-lo, converta `age` em uma string com `str (age)`:
+Agora você sabe que precisa corrigí-lo, converta `age` em uma string com `str(age)`:
-```Python hl_lines="2"
-{!../../../docs_src/python_types/tutorial004.py!}
-```
+{* ../../docs_src/python_types/tutorial004.py hl[2] *}
-## Tipos de declaração
+## Declarando Tipos
Você acabou de ver o local principal para declarar type hints. Como parâmetros de função.
@@ -143,47 +133,83 @@ Você pode usar, por exemplo:
* `bool`
* `bytes`
-```Python hl_lines="1"
-{!../../../docs_src/python_types/tutorial005.py!}
-```
+{* ../../docs_src/python_types/tutorial005.py hl[1] *}
### Tipos genéricos com parâmetros de tipo
Existem algumas estruturas de dados que podem conter outros valores, como `dict`, `list`, `set` e `tuple`. E os valores internos também podem ter seu próprio tipo.
-Para declarar esses tipos e os tipos internos, você pode usar o módulo Python padrão `typing`.
+Estes tipos que possuem tipos internos são chamados de tipos "**genéricos**". E é possível declará-los mesmo com os seus tipos internos.
-Ele existe especificamente para suportar esses type hints.
+Para declarar esses tipos e os tipos internos, você pode usar o módulo Python padrão `typing`. Ele existe especificamente para suportar esses type hints.
-#### `List`
+#### Versões mais recentes do Python
-Por exemplo, vamos definir uma variável para ser uma `lista` de `str`.
+A sintaxe utilizando `typing` é **compatível** com todas as versões, desde o Python 3.6 até as últimas, incluindo o Python 3.9, 3.10, etc.
-Em `typing`, importe `List` (com um `L` maiúsculo):
+Conforme o Python evolui, **novas versões** chegam com suporte melhorado para esses type annotations, e em muitos casos, você não precisará nem importar e utilizar o módulo `typing` para declarar os type annotations.
+
+Se você pode escolher uma versão mais recente do Python para o seu projeto, você poderá aproveitar isso ao seu favor.
+
+Em todos os documentos existem exemplos compatíveis com cada versão do Python (quando existem diferenças).
+
+Por exemplo, "**Python 3.6+**" significa que é compatível com o Python 3.6 ou superior (incluindo o 3.7, 3.8, 3.9, 3.10, etc). E "**Python 3.9+**" significa que é compatível com o Python 3.9 ou mais recente (incluindo o 3.10, etc).
+
+Se você pode utilizar a **versão mais recente do Python**, utilize os exemplos para as últimas versões. Eles terão as **melhores e mais simples sintaxes**, como por exemplo, "**Python 3.10+**".
+
+#### List
+
+Por exemplo, vamos definir uma variável para ser uma `list` de `str`.
+
+//// tab | Python 3.9+
+
+Declare uma variável com a mesma sintaxe com dois pontos (`:`)
+
+Como tipo, coloque `list`.
+
+Como a lista é o tipo que contém algum tipo interno, você coloca o tipo dentro de colchetes:
```Python hl_lines="1"
-{!../../../docs_src/python_types/tutorial006.py!}
+{!> ../../docs_src/python_types/tutorial006_py39.py!}
```
-Declare a variável com a mesma sintaxe de dois pontos (`:`).
+////
-Como o tipo, coloque a `List`.
+//// tab | Python 3.8+
-Como a lista é um tipo que contém alguns tipos internos, você os coloca entre colchetes:
+De `typing`, importe `List` (com o `L` maiúsculo):
+
+```Python hl_lines="1"
+{!> ../../docs_src/python_types/tutorial006.py!}
+```
+
+Declare uma variável com a mesma sintaxe com dois pontos (`:`)
+
+Como tipo, coloque o `List` que você importou de `typing`.
+
+Como a lista é o tipo que contém algum tipo interno, você coloca o tipo dentro de colchetes:
```Python hl_lines="4"
-{!../../../docs_src/python_types/tutorial006.py!}
+{!> ../../docs_src/python_types/tutorial006.py!}
```
-/// tip | "Dica"
+////
-Esses tipos internos entre colchetes são chamados de "parâmetros de tipo".
+/// info | Informação
-Nesse caso, `str` é o parâmetro de tipo passado para `List`.
+Estes tipos internos dentro dos colchetes são chamados "parâmetros de tipo" (type parameters).
+
+Neste caso, `str` é o parâmetro de tipo passado para `List` (ou `list` no Python 3.9 ou superior).
///
-Isso significa que: "a variável `items` é uma `list`, e cada um dos itens desta lista é uma `str`".
+Isso significa: "a variável `items` é uma `list`, e cada um dos itens desta lista é uma `str`".
+
+/// tip | Dica
+
+Se você usa o Python 3.9 ou superior, você não precisa importar `List` de `typing`. Você pode utilizar o mesmo tipo `list` no lugar.
+
+///
Ao fazer isso, seu editor pode fornecer suporte mesmo durante o processamento de itens da lista:
@@ -195,20 +221,32 @@ Observe que a variável `item` é um dos elementos da lista `items`.
E, ainda assim, o editor sabe que é um `str` e fornece suporte para isso.
-#### `Tuple` e `Set`
+#### Tuple e Set
Você faria o mesmo para declarar `tuple`s e `set`s:
-```Python hl_lines="1 4"
-{!../../../docs_src/python_types/tutorial007.py!}
+//// tab | Python 3.9+
+
+```Python hl_lines="1"
+{!> ../../docs_src/python_types/tutorial007_py39.py!}
```
+////
+
+//// tab | Python 3.8+
+
+```Python hl_lines="1 4"
+{!> ../../docs_src/python_types/tutorial007.py!}
+```
+
+////
+
Isso significa que:
* A variável `items_t` é uma `tuple` com 3 itens, um `int`, outro `int` e uma `str`.
* A variável `items_s` é um `set`, e cada um de seus itens é do tipo `bytes`.
-#### `Dict`
+#### Dict
Para definir um `dict`, você passa 2 parâmetros de tipo, separados por vírgulas.
@@ -216,38 +254,181 @@ O primeiro parâmetro de tipo é para as chaves do `dict`.
O segundo parâmetro de tipo é para os valores do `dict`:
-```Python hl_lines="1 4"
-{!../../../docs_src/python_types/tutorial008.py!}
+//// tab | Python 3.9+
+
+```Python hl_lines="1"
+{!> ../../docs_src/python_types/tutorial008_py39.py!}
```
+////
+
+//// tab | Python 3.8+
+
+```Python hl_lines="1 4"
+{!> ../../docs_src/python_types/tutorial008.py!}
+```
+
+////
+
Isso significa que:
* A variável `prices` é um dict`:
* As chaves deste `dict` são do tipo `str` (digamos, o nome de cada item).
* Os valores deste `dict` são do tipo `float` (digamos, o preço de cada item).
-#### `Opcional`
+#### Union
-Você também pode usar o `Opcional` para declarar que uma variável tem um tipo, como `str`, mas que é "opcional", o que significa que também pode ser `None`:
+Você pode declarar que uma variável pode ser de qualquer um dentre **diversos tipos**. Por exemplo, um `int` ou um `str`.
-```Python hl_lines="1 4"
-{!../../../docs_src/python_types/tutorial009.py!}
+No Python 3.6 e superior (incluindo o Python 3.10), você pode utilizar o tipo `Union` de `typing`, e colocar dentro dos colchetes os possíveis tipos aceitáveis.
+
+No Python 3.10 também existe uma **nova sintaxe** onde você pode colocar os possívels tipos separados por uma barra vertical (`|`).
+
+//// tab | Python 3.10+
+
+```Python hl_lines="1"
+{!> ../../docs_src/python_types/tutorial008b_py310.py!}
```
-O uso de `Opcional [str]` em vez de apenas `str` permitirá que o editor o ajude a detectar erros, onde você pode estar assumindo que um valor é sempre um `str`, quando na verdade também pode ser `None`.
+////
+
+//// tab | Python 3.8+
+
+```Python hl_lines="1 4"
+{!> ../../docs_src/python_types/tutorial008b.py!}
+```
+
+////
+
+Em ambos os casos, isso significa que `item` poderia ser um `int` ou um `str`.
+
+
+#### Possívelmente `None`
+
+Você pode declarar que um valor pode ter um tipo, como `str`, mas que ele também pode ser `None`.
+
+No Python 3.6 e superior (incluindo o Python 3.10) você pode declará-lo importando e utilizando `Optional` do módulo `typing`.
+
+```Python hl_lines="1 4"
+{!../../docs_src/python_types/tutorial009.py!}
+```
+
+O uso de `Optional[str]` em vez de apenas `str` permitirá que o editor o ajude a detectar erros, onde você pode estar assumindo que um valor é sempre um `str`, quando na verdade também pode ser `None`.
+
+`Optional[Something]` é na verdade um atalho para `Union[Something, None]`, eles são equivalentes.
+
+Isso também significa que no Python 3.10, você pode utilizar `Something | None`:
+
+//// tab | Python 3.10+
+
+```Python hl_lines="1"
+{!> ../../docs_src/python_types/tutorial009_py310.py!}
+```
+
+////
+
+//// tab | Python 3.8+
+
+```Python hl_lines="1 4"
+{!> ../../docs_src/python_types/tutorial009.py!}
+```
+
+////
+
+//// tab | Python 3.8+ alternative
+
+```Python hl_lines="1 4"
+{!> ../../docs_src/python_types/tutorial009b.py!}
+```
+
+////
+
+#### Utilizando `Union` ou `Optional`
+
+Se você está utilizando uma versão do Python abaixo da 3.10, aqui vai uma dica do meu ponto de vista bem **subjetivo**:
+
+* 🚨 Evite utilizar `Optional[SomeType]`
+* No lugar, ✨ **use `Union[SomeType, None]`** ✨.
+
+Ambos são equivalentes, e no final das contas, eles são o mesmo. Mas eu recomendaria o `Union` ao invés de `Optional` porque a palavra **Optional** parece implicar que o valor é opcional, quando na verdade significa "isso pode ser `None`", mesmo que ele não seja opcional e ainda seja obrigatório.
+
+Eu penso que `Union[SomeType, None]` é mais explícito sobre o que ele significa.
+
+Isso é apenas sobre palavras e nomes. Mas estas palavras podem afetar como os seus colegas de trabalho pensam sobre o código.
+
+Por exemplo, vamos pegar esta função:
+
+{* ../../docs_src/python_types/tutorial009c.py hl[1,4] *}
+
+O paâmetro `name` é definido como `Optional[str]`, mas ele **não é opcional**, você não pode chamar a função sem o parâmetro:
+
+```Python
+say_hi() # Oh, no, this throws an error! 😱
+```
+
+O parâmetro `name` **ainda é obrigatório** (não *opicional*) porque ele não possui um valor padrão. Mesmo assim, `name` aceita `None` como valor:
+
+```Python
+say_hi(name=None) # This works, None is valid 🎉
+```
+
+A boa notícia é, quando você estiver no Python 3.10 você não precisará se preocupar mais com isso, pois você poderá simplesmente utilizar o `|` para definir uniões de tipos:
+
+{* ../../docs_src/python_types/tutorial009c_py310.py hl[1,4] *}
+
+E então você não precisará mais se preocupar com nomes como `Optional` e `Union`. 😎
#### Tipos genéricos
-Esses tipos que usam parâmetros de tipo entre colchetes, como:
+Esses tipos que usam parâmetros de tipo entre colchetes são chamados **tipos genéricos** ou **genéricos**. Por exemplo:
+
+//// tab | Python 3.10+
+
+Você pode utilizar os mesmos tipos internos como genéricos (com colchetes e tipos dentro):
+
+* `list`
+* `tuple`
+* `set`
+* `dict`
+
+E o mesmo como no Python 3.8, do módulo `typing`:
+
+* `Union`
+* `Optional` (o mesmo que com o 3.8)
+* ...entro outros.
+
+No Python 3.10, como uma alternativa para a utilização dos genéricos `Union` e `Optional`, você pode usar a barra vertical (`|`) para declarar uniões de tipos. Isso é muito melhor e mais simples.
+
+////
+
+//// tab | Python 3.9+
+
+Você pode utilizar os mesmos tipos internos como genéricos (com colchetes e tipos dentro):
+
+* `list`
+* `tuple`
+* `set`
+* `dict`
+
+E o mesmo como no Python 3.8, do módulo `typing`:
+
+* `Union`
+* `Optional`
+* ...entro outros.
+
+////
+
+//// tab | Python 3.8+
* `List`
* `Tuple`
* `Set`
* `Dict`
-* `Opcional`
-* ...e outros.
+* `Union`
+* `Optional`
+* ...entro outros.
-são chamados **tipos genéricos** ou **genéricos**.
+////
### Classes como tipos
@@ -255,23 +436,23 @@ Você também pode declarar uma classe como o tipo de uma variável.
Digamos que você tenha uma classe `Person`, com um nome:
-```Python hl_lines="1 2 3"
-{!../../../docs_src/python_types/tutorial010.py!}
-```
+{* ../../docs_src/python_types/tutorial010.py hl[1:3] *}
Então você pode declarar que uma variável é do tipo `Person`:
-```Python hl_lines="6"
-{!../../../docs_src/python_types/tutorial010.py!}
-```
+{* ../../docs_src/python_types/tutorial010.py hl[6] *}
E então, novamente, você recebe todo o suporte do editor:
+Perceba que isso significa que "`one_person` é uma **instância** da classe `Person`".
+
+Isso não significa que "`one_person` é a **classe** chamada `Person`".
+
## Modelos Pydantic
- Pydantic é uma biblioteca Python para executar a validação de dados.
+O Pydantic é uma biblioteca Python para executar a validação de dados.
Você declara a "forma" dos dados como classes com atributos.
@@ -283,21 +464,93 @@ E você recebe todo o suporte do editor com esse objeto resultante.
Retirado dos documentos oficiais dos Pydantic:
+//// tab | Python 3.10+
+
```Python
-{!../../../docs_src/python_types/tutorial011.py!}
+{!> ../../docs_src/python_types/tutorial011_py310.py!}
```
-/// info | "Informação"
+////
-Para saber mais sobre o Pydantic, verifique seus documentos .
+//// tab | Python 3.9+
+
+```Python
+{!> ../../docs_src/python_types/tutorial011_py39.py!}
+```
+
+////
+
+//// tab | Python 3.8+
+
+```Python
+{!> ../../docs_src/python_types/tutorial011.py!}
+```
+
+////
+
+/// info | Informação
+
+Para saber mais sobre o Pydantic, verifique a sua documentação.
///
-**FastAPI** é todo baseado em Pydantic.
+O **FastAPI** é todo baseado em Pydantic.
Você verá muito mais disso na prática no [Tutorial - Guia do usuário](tutorial/index.md){.internal-link target=_blank}.
-## Type hints em **FastAPI**
+/// tip | Dica
+
+O Pydantic tem um comportamento especial quando você usa `Optional` ou `Union[Something, None]` sem um valor padrão. Você pode ler mais sobre isso na documentação do Pydantic sobre campos Opcionais Obrigatórios.
+
+///
+
+
+## Type Hints com Metadados de Anotações
+
+O Python possui uma funcionalidade que nos permite incluir **metadados adicionais** nos type hints utilizando `Annotated`.
+
+//// tab | Python 3.9+
+
+No Python 3.9, `Annotated` é parte da biblioteca padrão, então você pode importá-lo de `typing`.
+
+```Python hl_lines="1 4"
+{!> ../../docs_src/python_types/tutorial013_py39.py!}
+```
+
+////
+
+//// tab | Python 3.8+
+
+Em versões abaixo do Python 3.9, você importa `Annotated` de `typing_extensions`.
+
+Ele já estará instalado com o **FastAPI**.
+
+```Python hl_lines="1 4"
+{!> ../../docs_src/python_types/tutorial013.py!}
+```
+
+////
+
+O Python em si não faz nada com este `Annotated`. E para editores e outras ferramentas, o tipo ainda é `str`.
+
+Mas você pode utilizar este espaço dentro do `Annotated` para fornecer ao **FastAPI** metadata adicional sobre como você deseja que a sua aplicação se comporte.
+
+O importante aqui de se lembrar é que **o primeiro *type parameter*** que você informar ao `Annotated` é o **tipo de fato**. O resto é apenas metadado para outras ferramentas.
+
+Por hora, você precisa apenas saber que o `Annotated` existe, e que ele é Python padrão. 😎
+
+Mais tarde você verá o quão **poderoso** ele pode ser.
+
+/// tip | Dica
+
+O fato de que isso é **Python padrão** significa que você ainda obtém a **melhor experiência de desenvolvedor possível** no seu editor, com as ferramentas que você utiliza para analisar e refatorar o seu código, etc. ✨
+
+E também que o seu código será muito compatível com diversas outras ferramentas e bibliotecas Python. 🚀
+
+///
+
+
+## Type hints no **FastAPI**
O **FastAPI** aproveita esses type hints para fazer várias coisas.
@@ -306,20 +559,20 @@ Com o **FastAPI**, você declara parâmetros com type hints e obtém:
* **Suporte ao editor**.
* **Verificações de tipo**.
-... e **FastAPI** usa as mesmas declarações para:
+... e o **FastAPI** usa as mesmas declarações para:
-* **Definir requisitos**: dos parâmetros do caminho da solicitação, parâmetros da consulta, cabeçalhos, corpos, dependências, etc.
+* **Definir requisitos**: dos parâmetros de rota, parâmetros da consulta, cabeçalhos, corpos, dependências, etc.
* **Converter dados**: da solicitação para o tipo necessário.
* **Validar dados**: provenientes de cada solicitação:
- * A geração de **erros automáticos** retornou ao cliente quando os dados são inválidos.
-* **Documente** a API usando OpenAPI:
+ * Gerando **erros automáticos** retornados ao cliente quando os dados são inválidos.
+* **Documentar** a API usando OpenAPI:
* que é usado pelas interfaces de usuário da documentação interativa automática.
Tudo isso pode parecer abstrato. Não se preocupe. Você verá tudo isso em ação no [Tutorial - Guia do usuário](tutorial/index.md){.internal-link target=_blank}.
O importante é que, usando tipos padrão de Python, em um único local (em vez de adicionar mais classes, decoradores, etc.), o **FastAPI** fará muito trabalho para você.
-/// info | "Informação"
+/// info | Informação
Se você já passou por todo o tutorial e voltou para ver mais sobre os tipos, um bom recurso é a "cheat sheet" do `mypy` .
diff --git a/docs/pt/docs/tutorial/background-tasks.md b/docs/pt/docs/tutorial/background-tasks.md
index 625fa2b111..b8ab58cda3 100644
--- a/docs/pt/docs/tutorial/background-tasks.md
+++ b/docs/pt/docs/tutorial/background-tasks.md
@@ -15,9 +15,7 @@ Isso inclui, por exemplo:
Primeiro, importe `BackgroundTasks` e defina um parâmetro em sua _função de operação de caminho_ com uma declaração de tipo de `BackgroundTasks`:
-```Python hl_lines="1 13"
-{!../../../docs_src/background_tasks/tutorial001.py!}
-```
+{* ../../docs_src/background_tasks/tutorial001.py hl[1,13] *}
O **FastAPI** criará o objeto do tipo `BackgroundTasks` para você e o passará como esse parâmetro.
@@ -33,17 +31,13 @@ Nesse caso, a função de tarefa gravará em um arquivo (simulando o envio de um
E como a operação de gravação não usa `async` e `await`, definimos a função com `def` normal:
-```Python hl_lines="6-9"
-{!../../../docs_src/background_tasks/tutorial001.py!}
-```
+{* ../../docs_src/background_tasks/tutorial001.py hl[6:9] *}
## Adicionar a tarefa em segundo plano
Dentro de sua _função de operação de caminho_, passe sua função de tarefa para o objeto _tarefas em segundo plano_ com o método `.add_task()`:
-```Python hl_lines="14"
-{!../../../docs_src/background_tasks/tutorial001.py!}
-```
+{* ../../docs_src/background_tasks/tutorial001.py hl[14] *}
`.add_task()` recebe como argumentos:
@@ -57,9 +51,7 @@ Usar `BackgroundTasks` também funciona com o sistema de injeção de dependênc
O **FastAPI** sabe o que fazer em cada caso e como reutilizar o mesmo objeto, de forma que todas as tarefas em segundo plano sejam mescladas e executadas em segundo plano posteriormente:
-```Python hl_lines="13 15 22 25"
-{!../../../docs_src/background_tasks/tutorial002.py!}
-```
+{* ../../docs_src/background_tasks/tutorial002.py hl[13,15,22,25] *}
Neste exemplo, as mensagens serão gravadas no arquivo `log.txt` _após_ o envio da resposta.
@@ -69,7 +61,7 @@ E então outra tarefa em segundo plano gerada na _função de operação de cami
## Detalhes técnicos
-A classe `BackgroundTasks` vem diretamente de `starlette.background`.
+A classe `BackgroundTasks` vem diretamente de `starlette.background`.
Ela é importada/incluída diretamente no FastAPI para que você possa importá-la do `fastapi` e evitar a importação acidental da alternativa `BackgroundTask` (sem o `s` no final) de `starlette.background`.
@@ -77,7 +69,7 @@ Usando apenas `BackgroundTasks` (e não `BackgroundTask`), é então possível u
Ainda é possível usar `BackgroundTask` sozinho no FastAPI, mas você deve criar o objeto em seu código e retornar uma Starlette `Response` incluindo-o.
-Você pode ver mais detalhes na documentação oficiais da Starlette para tarefas em segundo plano .
+Você pode ver mais detalhes na documentação oficiais da Starlette para tarefas em segundo plano .
## Ressalva
@@ -85,8 +77,6 @@ Se você precisa realizar cálculos pesados em segundo plano e não necess
Eles tendem a exigir configurações mais complexas, um gerenciador de fila de mensagens/tarefas, como RabbitMQ ou Redis, mas permitem que você execute tarefas em segundo plano em vários processos e, especialmente, em vários servidores.
-Para ver um exemplo, verifique os [Geradores de projeto](../project-generation.md){.internal-link target=\_blank}, todos incluem celery já configurado.
-
Mas se você precisa acessar variáveis e objetos do mesmo aplicativo **FastAPI**, ou precisa realizar pequenas tarefas em segundo plano (como enviar uma notificação por e-mail), você pode simplesmente usar `BackgroundTasks`.
## Recapitulando
diff --git a/docs/pt/docs/tutorial/bigger-applications.md b/docs/pt/docs/tutorial/bigger-applications.md
index 7137bf865d..b621f3c726 100644
--- a/docs/pt/docs/tutorial/bigger-applications.md
+++ b/docs/pt/docs/tutorial/bigger-applications.md
@@ -4,7 +4,7 @@ Se você está construindo uma aplicação ou uma API web, é raro que você pos
**FastAPI** oferece uma ferramenta conveniente para estruturar sua aplicação, mantendo toda a flexibilidade.
-/// info | "Informação"
+/// info | Informação
Se você vem do Flask, isso seria o equivalente aos Blueprints do Flask.
@@ -29,7 +29,7 @@ Digamos que você tenha uma estrutura de arquivos como esta:
│ └── admin.py
```
-/// tip | "Dica"
+/// tip | Dica
Existem vários arquivos `__init__.py` presentes em cada diretório ou subdiretório.
@@ -52,7 +52,7 @@ from app.routers import items
* Há também um subdiretório `app/internal/` com outro arquivo `__init__.py`, então ele é outro "subpacote Python":`app.internal`.
* E o arquivo `app/internal/admin.py` é outro submódulo: `app.internal.admin`.
-
+
+
+---
+
+Se você usar o Pycharm, você pode:
+
+* Abrir o menu "Executar".
+* Selecionar a opção "Depurar...".
+* Então um menu de contexto aparece.
+* Selecionar o arquivo para depurar (neste caso, `main.py`).
+
+Em seguida, ele iniciará o servidor com seu código **FastAPI**, parará em seus pontos de interrupção, etc.
+
+Veja como pode parecer:
+
+
diff --git a/docs/pt/docs/tutorial/dependencies/classes-as-dependencies.md b/docs/pt/docs/tutorial/dependencies/classes-as-dependencies.md
index 420503b878..ef67aa979e 100644
--- a/docs/pt/docs/tutorial/dependencies/classes-as-dependencies.md
+++ b/docs/pt/docs/tutorial/dependencies/classes-as-dependencies.md
@@ -6,57 +6,7 @@ Antes de nos aprofundarmos no sistema de **Injeção de Dependência**, vamos me
No exemplo anterior, nós retornávamos um `dict` da nossa dependência ("injetável"):
-//// tab | Python 3.10+
-
-```Python hl_lines="9"
-{!> ../../../docs_src/dependencies/tutorial001_an_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="11"
-{!> ../../../docs_src/dependencies/tutorial001_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="12"
-{!> ../../../docs_src/dependencies/tutorial001_an.py!}
-```
-
-////
-
-//// tab | Python 3.10+ non-Annotated
-
-/// tip | "Dica"
-
-Utilize a versão com `Annotated` se possível.
-
-///
-
-```Python hl_lines="7"
-{!> ../../../docs_src/dependencies/tutorial001_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+ non-Annotated
-
-/// tip | "Dica"
-
-Utilize a versão com `Annotated` se possível.
-
-///
-
-```Python hl_lines="11"
-{!> ../../../docs_src/dependencies/tutorial001.py!}
-```
-
-////
+{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[9] *}
Mas assim obtemos um `dict` como valor do parâmetro `commons` na *função de operação de rota*.
@@ -119,165 +69,15 @@ Isso também se aplica a objetos chamáveis que não recebem nenhum parâmetro.
Então, podemos mudar o "injetável" na dependência `common_parameters` acima para a classe `CommonQueryParams`:
-//// tab | Python 3.10+
-
-```Python hl_lines="11-15"
-{!> ../../../docs_src/dependencies/tutorial002_an_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="11-15"
-{!> ../../../docs_src/dependencies/tutorial002_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="12-16"
-{!> ../../../docs_src/dependencies/tutorial002_an.py!}
-```
-
-////
-
-//// tab | Python 3.10+ non-Annotated
-
-/// tip | "Dica"
-
-Utilize a versão com `Annotated` se possível.
-
-///
-
-```Python hl_lines="9-13"
-{!> ../../../docs_src/dependencies/tutorial002_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+ non-Annotated
-
-/// tip | "Dica"
-
-Utilize a versão com `Annotated` se possível.
-
-///
-
-```Python hl_lines="11-15"
-{!> ../../../docs_src/dependencies/tutorial002.py!}
-```
-
-////
+{* ../../docs_src/dependencies/tutorial002_an_py310.py hl[11:15] *}
Observe o método `__init__` usado para criar uma instância da classe:
-//// tab | Python 3.10+
-
-```Python hl_lines="12"
-{!> ../../../docs_src/dependencies/tutorial002_an_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="12"
-{!> ../../../docs_src/dependencies/tutorial002_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="13"
-{!> ../../../docs_src/dependencies/tutorial002_an.py!}
-```
-
-////
-
-//// tab | Python 3.10+ non-Annotated
-
-/// tip | "Dica"
-
-Utilize a versão com `Annotated` se possível.
-
-///
-
-```Python hl_lines="10"
-{!> ../../../docs_src/dependencies/tutorial002_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+ non-Annotated
-
-/// tip | "Dica"
-
-Utilize a versão com `Annotated` se possível.
-
-///
-
-```Python hl_lines="12"
-{!> ../../../docs_src/dependencies/tutorial002.py!}
-```
-
-////
+{* ../../docs_src/dependencies/tutorial002_an_py310.py hl[12] *}
...ele possui os mesmos parâmetros que nosso `common_parameters` anterior:
-//// tab | Python 3.10+
-
-```Python hl_lines="8"
-{!> ../../../docs_src/dependencies/tutorial001_an_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="9"
-{!> ../../../docs_src/dependencies/tutorial001_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="10"
-{!> ../../../docs_src/dependencies/tutorial001_an.py!}
-```
-
-////
-
-//// tab | Python 3.10+ non-Annotated
-
-/// tip | "Dica"
-
-Utilize a versão com `Annotated` se possível.
-
-///
-
-```Python hl_lines="6"
-{!> ../../../docs_src/dependencies/tutorial001_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+ non-Annotated
-
-/// tip | "Dica"
-
-Utilize a versão com `Annotated` se possível.
-
-///
-
-```Python hl_lines="9"
-{!> ../../../docs_src/dependencies/tutorial001.py!}
-```
-
-////
+{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[8] *}
Esses parâmetros são utilizados pelo **FastAPI** para "definir" a dependência.
@@ -293,57 +93,7 @@ Os dados serão convertidos, validados, documentados no esquema da OpenAPI e etc
Agora você pode declarar sua dependência utilizando essa classe.
-//// tab | Python 3.10+
-
-```Python hl_lines="19"
-{!> ../../../docs_src/dependencies/tutorial002_an_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="19"
-{!> ../../../docs_src/dependencies/tutorial002_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="20"
-{!> ../../../docs_src/dependencies/tutorial002_an.py!}
-```
-
-////
-
-//// tab | Python 3.10+ non-Annotated
-
-/// tip | "Dica"
-
-Utilize a versão com `Annotated` se possível.
-
-///
-
-```Python hl_lines="17"
-{!> ../../../docs_src/dependencies/tutorial002_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+ non-Annotated
-
-/// tip | "Dica"
-
-Utilize a versão com `Annotated` se possível.
-
-///
-
-```Python hl_lines="19"
-{!> ../../../docs_src/dependencies/tutorial002.py!}
-```
-
-////
+{* ../../docs_src/dependencies/tutorial002_an_py310.py hl[19] *}
O **FastAPI** chama a classe `CommonQueryParams`. Isso cria uma "instância" dessa classe e é a instância que será passada para o parâmetro `commons` na sua função.
@@ -361,7 +111,7 @@ commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
//// tab | Python 3.8+ non-Annotated
-/// tip | "Dica"
+/// tip | Dica
Utilize a versão com `Annotated` se possível.
@@ -397,7 +147,7 @@ commons: Annotated[CommonQueryParams, ...
//// tab | Python 3.8+ non-Annotated
-/// tip | "Dica"
+/// tip | Dica
Utilize a versão com `Annotated` se possível.
@@ -423,7 +173,7 @@ commons: Annotated[Any, Depends(CommonQueryParams)]
//// tab | Python 3.8+ non-Annotated
-/// tip | "Dica"
+/// tip | Dica
Utilize a versão com `Annotated` se possível.
@@ -437,57 +187,7 @@ commons = Depends(CommonQueryParams)
...como em:
-//// tab | Python 3.10+
-
-```Python hl_lines="19"
-{!> ../../../docs_src/dependencies/tutorial003_an_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="19"
-{!> ../../../docs_src/dependencies/tutorial003_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="20"
-{!> ../../../docs_src/dependencies/tutorial003_an.py!}
-```
-
-////
-
-//// tab | Python 3.10+ non-Annotated
-
-/// tip | "Dica"
-
-Utilize a versão com `Annotated` se possível.
-
-///
-
-```Python hl_lines="17"
-{!> ../../../docs_src/dependencies/tutorial003_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+ non-Annotated
-
-/// tip | "Dica"
-
-Utilize a versão com `Annotated` se possível.
-
-///
-
-```Python hl_lines="19"
-{!> ../../../docs_src/dependencies/tutorial003.py!}
-```
-
-////
+{* ../../docs_src/dependencies/tutorial003_an_py310.py hl[19] *}
Mas declarar o tipo é encorajado por que é a forma que o seu editor de texto sabe o que será passado como valor do parâmetro `commons`.
@@ -507,7 +207,7 @@ commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
//// tab | Python 3.8+ non-Annotated
-/// tip | "Dica"
+/// tip | Dica
Utilize a versão com `Annotated` se possível.
@@ -535,7 +235,7 @@ commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
//// tab | Python 3.8+ non-Annotated
-/// tip | "Dica"
+/// tip | Dica
Utilize a versão com `Annotated` se possível.
@@ -559,7 +259,7 @@ commons: Annotated[CommonQueryParams, Depends()]
//// tab | Python 3.8 non-Annotated
-/// tip | "Dica"
+/// tip | Dica
Utilize a versão com `Annotated` se possível.
@@ -575,61 +275,11 @@ Você declara a dependência como o tipo do parâmetro, e utiliza `Depends()` se
O mesmo exemplo ficaria então dessa forma:
-//// tab | Python 3.10+
-
-```Python hl_lines="19"
-{!> ../../../docs_src/dependencies/tutorial004_an_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="19"
-{!> ../../../docs_src/dependencies/tutorial004_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="20"
-{!> ../../../docs_src/dependencies/tutorial004_an.py!}
-```
-
-////
-
-//// tab | Python 3.10+ non-Annotated
-
-/// tip | "Dica"
-
-Utilize a versão com `Annotated` se possível.
-
-///
-
-```Python hl_lines="17"
-{!> ../../../docs_src/dependencies/tutorial004_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+ non-Annotated
-
-/// tip | "Dica"
-
-Utilize a versão com `Annotated` se possível.
-
-///
-
-```Python hl_lines="19"
-{!> ../../../docs_src/dependencies/tutorial004.py!}
-```
-
-////
+{* ../../docs_src/dependencies/tutorial004_an_py310.py hl[19] *}
...e o **FastAPI** saberá o que fazer.
-/// tip | "Dica"
+/// tip | Dica
Se isso parece mais confuso do que útil, não utilize, você não *precisa* disso.
diff --git a/docs/pt/docs/tutorial/dependencies/dependencies-in-path-operation-decorators.md b/docs/pt/docs/tutorial/dependencies/dependencies-in-path-operation-decorators.md
index 4a7a293901..d7d31bb45e 100644
--- a/docs/pt/docs/tutorial/dependencies/dependencies-in-path-operation-decorators.md
+++ b/docs/pt/docs/tutorial/dependencies/dependencies-in-path-operation-decorators.md
@@ -14,39 +14,11 @@ O *decorador da operação de rota* recebe um argumento opcional `dependencies`.
Ele deve ser uma lista de `Depends()`:
-//// tab | Python 3.9+
-
-```Python hl_lines="19"
-{!> ../../../docs_src/dependencies/tutorial006_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="18"
-{!> ../../../docs_src/dependencies/tutorial006_an.py!}
-```
-
-////
-
-//// tab | Python 3.8 non-Annotated
-
-/// tip | "Dica"
-
-Utilize a versão com `Annotated` se possível
-
-///
-
-```Python hl_lines="17"
-{!> ../../../docs_src/dependencies/tutorial006.py!}
-```
-
-////
+{* ../../docs_src/dependencies/tutorial006_an_py39.py hl[19] *}
Essas dependências serão executadas/resolvidas da mesma forma que dependências comuns. Mas o valor delas (se existir algum) não será passado para a sua *função de operação de rota*.
-/// tip | "Dica"
+/// tip | Dica
Alguns editores de texto checam parâmetros de funções não utilizados, e os mostram como erros.
@@ -56,7 +28,7 @@ Isso também pode ser útil para evitar confundir novos desenvolvedores que ao v
///
-/// info | "Informação"
+/// info | Informação
Neste exemplo utilizamos cabeçalhos personalizados inventados `X-Keys` e `X-Token`.
@@ -72,69 +44,13 @@ Você pode utilizar as mesmas *funções* de dependências que você usaria norm
Dependências podem declarar requisitos de requisições (como cabeçalhos) ou outras subdependências:
-//// tab | Python 3.9+
-
-```Python hl_lines="8 13"
-{!> ../../../docs_src/dependencies/tutorial006_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="7 12"
-{!> ../../../docs_src/dependencies/tutorial006_an.py!}
-```
-
-////
-
-//// tab | Python 3.8 non-Annotated
-
-/// tip | "Dica"
-
-Utilize a versão com `Annotated` se possível
-
-///
-
-```Python hl_lines="6 11"
-{!> ../../../docs_src/dependencies/tutorial006.py!}
-```
-
-////
+{* ../../docs_src/dependencies/tutorial006_an_py39.py hl[8,13] *}
### Levantando exceções
Essas dependências podem levantar exceções, da mesma forma que dependências comuns:
-//// tab | Python 3.9+
-
-```Python hl_lines="10 15"
-{!> ../../../docs_src/dependencies/tutorial006_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="9 14"
-{!> ../../../docs_src/dependencies/tutorial006_an.py!}
-```
-
-////
-
-//// tab | Python 3.8 non-Annotated
-
-/// tip | "Dica"
-
-Utilize a versão com `Annotated` se possível
-
-///
-
-```Python hl_lines="8 13"
-{!> ../../../docs_src/dependencies/tutorial006.py!}
-```
-
-////
+{* ../../docs_src/dependencies/tutorial006_an_py39.py hl[10,15] *}
### Valores de retorno
@@ -142,37 +58,7 @@ E elas também podem ou não retornar valores, eles não serão utilizados.
Então, você pode reutilizar uma dependência comum (que retorna um valor) que já seja utilizada em outro lugar, e mesmo que o valor não seja utilizado, a dependência será executada:
-//// tab | Python 3.9+
-
-```Python hl_lines="11 16"
-{!> ../../../docs_src/dependencies/tutorial006_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="10 15"
-{!> ../../../docs_src/dependencies/tutorial006_an.py!}
-```
-
-////
-
-//// tab | Python 3.8 non-Annotated
-
-/// tip | "Dica"
-
-
-
-///
-
- Utilize a versão com `Annotated` se possível
-
-```Python hl_lines="9 14"
-{!> ../../../docs_src/dependencies/tutorial006.py!}
-```
-
-////
+{* ../../docs_src/dependencies/tutorial006_an_py39.py hl[11,16] *}
## Dependências para um grupo de *operações de rota*
diff --git a/docs/pt/docs/tutorial/dependencies/dependencies-with-yield.md b/docs/pt/docs/tutorial/dependencies/dependencies-with-yield.md
index 16c2cf8997..eaf711197f 100644
--- a/docs/pt/docs/tutorial/dependencies/dependencies-with-yield.md
+++ b/docs/pt/docs/tutorial/dependencies/dependencies-with-yield.md
@@ -4,13 +4,13 @@ O FastAPI possui suporte para dependências que realizam .
-Mas se você tiver cabeçalhos personalizados desejando que um cliente em um navegador esteja apto a ver, você precisa adicioná-los às suas configurações CORS ([CORS (Cross-Origin Resource Sharing)](cors.md){.internal-link target=_blank}) usando o parâmetro `expose_headers` documentado em Documentos CORS da Starlette.
+Mas se você tiver cabeçalhos personalizados desejando que um cliente em um navegador esteja apto a ver, você precisa adicioná-los às suas configurações CORS ([CORS (Cross-Origin Resource Sharing)](cors.md){.internal-link target=_blank}) usando o parâmetro `expose_headers` documentado em Documentos CORS da Starlette.
///
-/// note | "Detalhes técnicos"
+/// note | Detalhes técnicos
Você também pode usar `from starlette.requests import Request`.
@@ -59,9 +57,7 @@ E também depois que a `response` é gerada, antes de retorná-la.
Por exemplo, você pode adicionar um cabeçalho personalizado `X-Process-Time` contendo o tempo em segundos que levou para processar a solicitação e gerar uma resposta:
-```Python hl_lines="10 12-13"
-{!../../../docs_src/middleware/tutorial001.py!}
-```
+{* ../../docs_src/middleware/tutorial001.py hl[10,12:13] *}
## Outros middlewares
diff --git a/docs/pt/docs/tutorial/path-operation-configuration.md b/docs/pt/docs/tutorial/path-operation-configuration.md
index c578137804..f183c9d23b 100644
--- a/docs/pt/docs/tutorial/path-operation-configuration.md
+++ b/docs/pt/docs/tutorial/path-operation-configuration.md
@@ -2,7 +2,7 @@
Existem vários parâmetros que você pode passar para o seu *decorador de operação de rota* para configurá-lo.
-/// warning | "Aviso"
+/// warning | Aviso
Observe que esses parâmetros são passados diretamente para o *decorador de operação de rota*, não para a sua *função de operação de rota*.
@@ -16,33 +16,11 @@ Você pode passar diretamente o código `int`, como `404`.
Mas se você não se lembrar o que cada código numérico significa, pode usar as constantes de atalho em `status`:
-//// tab | Python 3.8 and above
-
-```Python hl_lines="3 17"
-{!> ../../../docs_src/path_operation_configuration/tutorial001.py!}
-```
-
-////
-
-//// tab | Python 3.9 and above
-
-```Python hl_lines="3 17"
-{!> ../../../docs_src/path_operation_configuration/tutorial001_py39.py!}
-```
-
-////
-
-//// tab | Python 3.10 and above
-
-```Python hl_lines="1 15"
-{!> ../../../docs_src/path_operation_configuration/tutorial001_py310.py!}
-```
-
-////
+{* ../../docs_src/path_operation_configuration/tutorial001.py hl[3,17] *}
Esse código de status será usado na resposta e será adicionado ao esquema OpenAPI.
-/// note | "Detalhes Técnicos"
+/// note | Detalhes Técnicos
Você também poderia usar `from starlette import status`.
@@ -54,29 +32,7 @@ Você também poderia usar `from starlette import status`.
Você pode adicionar tags para sua *operação de rota*, passe o parâmetro `tags` com uma `list` de `str` (comumente apenas um `str`):
-//// tab | Python 3.8 and above
-
-```Python hl_lines="17 22 27"
-{!> ../../../docs_src/path_operation_configuration/tutorial002.py!}
-```
-
-////
-
-//// tab | Python 3.9 and above
-
-```Python hl_lines="17 22 27"
-{!> ../../../docs_src/path_operation_configuration/tutorial002_py39.py!}
-```
-
-////
-
-//// tab | Python 3.10 and above
-
-```Python hl_lines="15 20 25"
-{!> ../../../docs_src/path_operation_configuration/tutorial002_py310.py!}
-```
-
-////
+{* ../../docs_src/path_operation_configuration/tutorial002.py hl[17,22,27] *}
Eles serão adicionados ao esquema OpenAPI e usados pelas interfaces de documentação automática:
@@ -90,37 +46,13 @@ Nestes casos, pode fazer sentido armazenar as tags em um `Enum`.
**FastAPI** suporta isso da mesma maneira que com strings simples:
-```Python hl_lines="1 8-10 13 18"
-{!../../../docs_src/path_operation_configuration/tutorial002b.py!}
-```
+{* ../../docs_src/path_operation_configuration/tutorial002b.py hl[1,8:10,13,18] *}
## Resumo e descrição
Você pode adicionar um `summary` e uma `description`:
-//// tab | Python 3.8 and above
-
-```Python hl_lines="20-21"
-{!> ../../../docs_src/path_operation_configuration/tutorial003.py!}
-```
-
-////
-
-//// tab | Python 3.9 and above
-
-```Python hl_lines="20-21"
-{!> ../../../docs_src/path_operation_configuration/tutorial003_py39.py!}
-```
-
-////
-
-//// tab | Python 3.10 and above
-
-```Python hl_lines="18-19"
-{!> ../../../docs_src/path_operation_configuration/tutorial003_py310.py!}
-```
-
-////
+{* ../../docs_src/path_operation_configuration/tutorial003.py hl[20:21] *}
## Descrição do docstring
@@ -128,29 +60,7 @@ Como as descrições tendem a ser longas e cobrir várias linhas, você pode dec
Você pode escrever Markdown na docstring, ele será interpretado e exibido corretamente (levando em conta a indentação da docstring).
-//// tab | Python 3.8 and above
-
-```Python hl_lines="19-27"
-{!> ../../../docs_src/path_operation_configuration/tutorial004.py!}
-```
-
-////
-
-//// tab | Python 3.9 and above
-
-```Python hl_lines="19-27"
-{!> ../../../docs_src/path_operation_configuration/tutorial004_py39.py!}
-```
-
-////
-
-//// tab | Python 3.10 and above
-
-```Python hl_lines="17-25"
-{!> ../../../docs_src/path_operation_configuration/tutorial004_py310.py!}
-```
-
-////
+{* ../../docs_src/path_operation_configuration/tutorial004.py hl[19:27] *}
Ela será usada nas documentações interativas:
@@ -161,31 +71,9 @@ Ela será usada nas documentações interativas:
Você pode especificar a descrição da resposta com o parâmetro `response_description`:
-//// tab | Python 3.8 and above
+{* ../../docs_src/path_operation_configuration/tutorial005.py hl[21] *}
-```Python hl_lines="21"
-{!> ../../../docs_src/path_operation_configuration/tutorial005.py!}
-```
-
-////
-
-//// tab | Python 3.9 and above
-
-```Python hl_lines="21"
-{!> ../../../docs_src/path_operation_configuration/tutorial005_py39.py!}
-```
-
-////
-
-//// tab | Python 3.10 and above
-
-```Python hl_lines="19"
-{!> ../../../docs_src/path_operation_configuration/tutorial005_py310.py!}
-```
-
-////
-
-/// info | "Informação"
+/// info | Informação
Note que `response_description` se refere especificamente à resposta, a `description` se refere à *operação de rota* em geral.
@@ -205,9 +93,7 @@ Então, se você não fornecer uma, o **FastAPI** irá gerar automaticamente uma
Se você precisar marcar uma *operação de rota* como descontinuada, mas sem removê-la, passe o parâmetro `deprecated`:
-```Python hl_lines="16"
-{!../../../docs_src/path_operation_configuration/tutorial006.py!}
-```
+{* ../../docs_src/path_operation_configuration/tutorial006.py hl[16] *}
Ela será claramente marcada como descontinuada nas documentações interativas:
diff --git a/docs/pt/docs/tutorial/path-params-numeric-validations.md b/docs/pt/docs/tutorial/path-params-numeric-validations.md
index 08ed03f756..3aea1188dd 100644
--- a/docs/pt/docs/tutorial/path-params-numeric-validations.md
+++ b/docs/pt/docs/tutorial/path-params-numeric-validations.md
@@ -6,21 +6,7 @@ Do mesmo modo que você pode declarar mais validações e metadados para parâme
Primeiro, importe `Path` de `fastapi`:
-//// tab | Python 3.10+
-
-```Python hl_lines="1"
-{!> ../../../docs_src/path_params_numeric_validations/tutorial001_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="3"
-{!> ../../../docs_src/path_params_numeric_validations/tutorial001.py!}
-```
-
-////
+{* ../../docs_src/path_params_numeric_validations/tutorial001_py310.py hl[1] *}
## Declare metadados
@@ -28,23 +14,9 @@ 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:
-//// tab | Python 3.10+
+{* ../../docs_src/path_params_numeric_validations/tutorial001_py310.py hl[8] *}
-```Python hl_lines="8"
-{!> ../../../docs_src/path_params_numeric_validations/tutorial001_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="10"
-{!> ../../../docs_src/path_params_numeric_validations/tutorial001.py!}
-```
-
-////
-
-/// note | "Nota"
+/// note | Nota
Um parâmetro de rota é sempre obrigatório, como se fizesse parte da rota.
@@ -70,9 +42,7 @@ Isso não faz diferença para o **FastAPI**. Ele vai detectar os parâmetros pel
Então, você pode declarar sua função assim:
-```Python hl_lines="7"
-{!../../../docs_src/path_params_numeric_validations/tutorial002.py!}
-```
+{* ../../docs_src/path_params_numeric_validations/tutorial002.py hl[7] *}
## Ordene os parâmetros de a acordo com sua necessidade, truques
@@ -82,9 +52,7 @@ Passe `*`, como o primeiro parâmetro da função.
O Python não vai fazer nada com esse `*`, mas ele vai saber que a partir dali os parâmetros seguintes deverão ser chamados argumentos nomeados (pares chave-valor), também conhecidos como kwargs. Mesmo que eles não possuam um valor padrão.
-```Python hl_lines="7"
-{!../../../docs_src/path_params_numeric_validations/tutorial003.py!}
-```
+{* ../../docs_src/path_params_numeric_validations/tutorial003.py hl[7] *}
## Validações numéricas: maior que ou igual
@@ -92,9 +60,7 @@ Com `Query` e `Path` (e outras que você verá mais tarde) você pode declarar r
Aqui, com `ge=1`, `item_id` precisará ser um número inteiro maior que ("`g`reater than") ou igual ("`e`qual") a 1.
-```Python hl_lines="8"
-{!../../../docs_src/path_params_numeric_validations/tutorial004.py!}
-```
+{* ../../docs_src/path_params_numeric_validations/tutorial004.py hl[8] *}
## Validações numéricas: maior que e menor que ou igual
@@ -103,9 +69,7 @@ O mesmo se aplica para:
* `gt`: maior que (`g`reater `t`han)
* `le`: menor que ou igual (`l`ess than or `e`qual)
-```Python hl_lines="9"
-{!../../../docs_src/path_params_numeric_validations/tutorial005.py!}
-```
+{* ../../docs_src/path_params_numeric_validations/tutorial005.py hl[9] *}
## Validações numéricas: valores do tipo float, maior que e menor que
@@ -117,9 +81,7 @@ Assim, `0.5` seria um valor válido. Mas `0.0` ou `0` não seria.
E o mesmo para lt.
-```Python hl_lines="11"
-{!../../../docs_src/path_params_numeric_validations/tutorial006.py!}
-```
+{* ../../docs_src/path_params_numeric_validations/tutorial006.py hl[11] *}
## Recapitulando
@@ -132,7 +94,7 @@ E você também pode declarar validações numéricas:
* `lt`: menor que (`l`ess `t`han)
* `le`: menor que ou igual (`l`ess than or `e`qual)
-/// info | "Informação"
+/// info | Informação
`Query`, `Path` e outras classes que você verá a frente são subclasses de uma classe comum `Param`.
@@ -140,7 +102,7 @@ Todas elas compartilham os mesmos parâmetros para validação adicional e metad
///
-/// note | "Detalhes Técnicos"
+/// note | Detalhes Técnicos
Quando você importa `Query`, `Path` e outras de `fastapi`, elas são na verdade funções.
diff --git a/docs/pt/docs/tutorial/path-params.md b/docs/pt/docs/tutorial/path-params.md
index fb872e4f52..ecf77d676d 100644
--- a/docs/pt/docs/tutorial/path-params.md
+++ b/docs/pt/docs/tutorial/path-params.md
@@ -2,9 +2,7 @@
Você pode declarar os "parâmetros" ou "variáveis" com a mesma sintaxe utilizada pelo formato de strings do Python:
-```Python hl_lines="6-7"
-{!../../../docs_src/path_params/tutorial001.py!}
-```
+{* ../../docs_src/path_params/tutorial001.py hl[6:7] *}
O valor do parâmetro que foi passado à `item_id` será passado para a sua função como o argumento `item_id`.
@@ -18,13 +16,11 @@ Então, se você rodar este exemplo e for até http://127.0.0.1:8000/items/4.2
-/// check | "Verifique"
+/// check | Verifique
@@ -91,7 +87,7 @@ Quando você abrir o seu navegador em
-/// check | "Verifique"
+/// check | Verifique
@@ -129,9 +125,7 @@ E então você pode ter também uma rota `/users/{user_id}` para pegar dados sob
Porque as operações de rota são avaliadas em ordem, você precisa ter certeza que a rota para `/users/me` está sendo declarado antes da rota `/users/{user_id}`:
-```Python hl_lines="6 11"
-{!../../../docs_src/path_params/tutorial003.py!}
-```
+{* ../../docs_src/path_params/tutorial003.py hl[6,11] *}
Caso contrário, a rota para `/users/{user_id}` coincidiria também para `/users/me`, "pensando" que estaria recebendo o parâmetro `user_id` com o valor de `"me"`.
@@ -147,17 +141,15 @@ Por herdar de `str` a documentação da API vai ser capaz de saber que os valore
Assim, crie atributos de classe com valores fixos, que serão os valores válidos disponíveis.
-```Python hl_lines="1 6-9"
-{!../../../docs_src/path_params/tutorial005.py!}
-```
+{* ../../docs_src/path_params/tutorial005.py hl[1,6:9] *}
-/// info | "informação"
+/// info | informação
Enumerations (ou enums) estão disponíveis no Python desde a versão 3.4.
///
-/// tip | "Dica"
+/// tip | Dica
@@ -169,9 +161,7 @@ Assim, crie atributos de classe com valores fixos, que serão os valores válido
Logo, crie um *parâmetro de rota* com anotações de tipo usando a classe enum que você criou (`ModelName`):
-```Python hl_lines="16"
-{!../../../docs_src/path_params/tutorial005.py!}
-```
+{* ../../docs_src/path_params/tutorial005.py hl[16] *}
### Revise a documentação
@@ -187,19 +177,15 @@ O valor do *parâmetro da rota* será um *membro de enumeration*.
Você pode comparar eles com o *membro de enumeration* no enum `ModelName` que você criou:
-```Python hl_lines="17"
-{!../../../docs_src/path_params/tutorial005.py!}
-```
+{* ../../docs_src/path_params/tutorial005.py hl[17] *}
#### Obtenha o *valor de enumerate*
Você pode ter o valor exato de enumerate (um `str` nesse caso) usando `model_name.value`, ou em geral, `your_enum_member.value`:
-```Python hl_lines="20"
-{!../../../docs_src/path_params/tutorial005.py!}
-```
+{* ../../docs_src/path_params/tutorial005.py hl[20] *}
-/// tip | "Dica"
+/// tip | Dica
@@ -213,9 +199,7 @@ Você pode retornar *membros de enum* da sua *rota de operação*, em um corpo J
Eles serão convertidos para o seus valores correspondentes (strings nesse caso) antes de serem retornados ao cliente:
-```Python hl_lines="18 21 23"
-{!../../../docs_src/path_params/tutorial005.py!}
-```
+{* ../../docs_src/path_params/tutorial005.py hl[18,21,23] *}
No seu cliente você vai obter uma resposta JSON como:
@@ -254,11 +238,9 @@ Nesse caso, o nome do parâmetro é `file_path`, e a última parte, `:path`, diz
Então, você poderia usar ele com:
-```Python hl_lines="6"
-{!../../../docs_src/path_params/tutorial004.py!}
-```
+{* ../../docs_src/path_params/tutorial004.py hl[6] *}
-/// tip | "Dica"
+/// tip | Dica
diff --git a/docs/pt/docs/tutorial/query-param-models.md b/docs/pt/docs/tutorial/query-param-models.md
new file mode 100644
index 0000000000..01a6e462f1
--- /dev/null
+++ b/docs/pt/docs/tutorial/query-param-models.md
@@ -0,0 +1,69 @@
+# Modelos de Parâmetros de Consulta
+
+Se você possui um grupo de **parâmetros de consultas** que são relacionados, você pode criar um **modelo Pydantic** para declará-los.
+
+Isso permitiria que você **reutilizasse o modelo** em **diversos lugares**, e também declarasse validações e metadados de todos os parâmetros de uma única vez. 😎
+
+/// note | Nota
+
+Isso é suportado desde o FastAPI versão `0.115.0`. 🤓
+
+///
+
+## Parâmetros de Consulta com um Modelo Pydantic
+
+Declare os **parâmetros de consulta** que você precisa em um **modelo Pydantic**, e então declare o parâmetro como `Query`:
+
+{* ../../docs_src/query_param_models/tutorial001_an_py310.py hl[9:13,17] *}
+
+O **FastAPI** **extrairá** os dados para **cada campo** dos **parâmetros de consulta** presentes na requisição, e fornecerá o modelo Pydantic que você definiu.
+
+
+## Verifique os Documentos
+
+Você pode ver os parâmetros de consulta nos documentos de IU em `/docs`:
+
+
+POST.
+
+///
+
+/// warning | Aviso
+
+Você pode declarar múltiplos parâmetros `File` e `Form` em uma *operação de rota*, mas você não pode declarar campos `Body` que você espera receber como JSON, pois a requisição terá o corpo codificado usando `multipart/form-data` ao invés de `application/json`.
+
+Isso não é uma limitação do **FastAPI**, é parte do protocolo HTTP.
+
+///
+
+## Upload de Arquivo Opcional
+
+Você pode tornar um arquivo opcional usando anotações de tipo padrão e definindo um valor padrão de `None`:
+
+{* ../../docs_src/request_files/tutorial001_02_an_py310.py hl[9,17] *}
+
+## `UploadFile` com Metadados Adicionais
+
+Você também pode usar `File()` com `UploadFile`, por exemplo, para definir metadados adicionais:
+
+{* ../../docs_src/request_files/tutorial001_03_an_py39.py hl[9,15] *}
+
+## Uploads de Múltiplos Arquivos
+
+É possível realizar o upload de vários arquivos ao mesmo tempo.
+
+Eles serão associados ao mesmo "campo de formulário" enviado usando "dados de formulário".
+
+Para usar isso, declare uma lista de `bytes` ou `UploadFile`:
+
+{* ../../docs_src/request_files/tutorial002_an_py39.py hl[10,15] *}
+
+Você receberá, tal como declarado, uma `list` de `bytes` ou `UploadFile`.
+
+/// note | Detalhes Técnicos
+
+Você pode também pode usar `from starlette.responses import HTMLResponse`.
+
+**FastAPI** providencia o mesmo `starlette.responses` que `fastapi.responses` apenas como uma conveniência para você, o desenvolvedor. Mas a maioria das respostas disponíveis vem diretamente do Starlette.
+
+///
+
+### Uploads de Múltiplos Arquivos com Metadados Adicionais
+
+Da mesma forma de antes, você pode usar `File()` para definir parâmetros adicionais, mesmo para `UploadFile`:
+
+{* ../../docs_src/request_files/tutorial003_an_py39.py hl[11,18:20] *}
+
+## Recapitulando
+
+Utilize `File`, `bytes` e `UploadFile` para declarar arquivos a serem enviados na requisição, enviados como dados de formulário.
diff --git a/docs/pt/docs/tutorial/request-form-models.md b/docs/pt/docs/tutorial/request-form-models.md
new file mode 100644
index 0000000000..ea0e63d381
--- /dev/null
+++ b/docs/pt/docs/tutorial/request-form-models.md
@@ -0,0 +1,78 @@
+# Modelos de Formulários
+
+Você pode utilizar **Modelos Pydantic** para declarar **campos de formulários** no FastAPI.
+
+/// info | Informação
+
+Para utilizar formulários, instale primeiramente o `python-multipart`.
+
+Certifique-se de criar um [ambiente virtual](../virtual-environments.md){.internal-link target=_blank}, ativá-lo, e então instalar. Por exemplo:
+
+```console
+$ pip install python-multipart
+```
+
+///
+
+/// note | Nota
+
+Isto é suportado desde a versão `0.113.0` do FastAPI. 🤓
+
+///
+
+## Modelos Pydantic para Formulários
+
+Você precisa apenas declarar um **modelo Pydantic** com os campos que deseja receber como **campos de formulários**, e então declarar o parâmetro como um `Form`:
+
+{* ../../docs_src/request_form_models/tutorial001_an_py39.py hl[9:11,15] *}
+
+O **FastAPI** irá **extrair** as informações para **cada campo** dos **dados do formulário** na requisição e dar para você o modelo Pydantic que você definiu.
+
+## Confira os Documentos
+
+Você pode verificar na UI de documentação em `/docs`:
+
+
+
+
+E ambos os modelos serão usados para a documentação interativa da API:
+
+
+
+## Outras anotações de tipo de retorno
+
+Pode haver casos em que você retorna algo que não é um campo Pydantic válido e anota na função, apenas para obter o suporte fornecido pelas ferramentas (o editor, mypy, etc).
+
+### Retornar uma resposta diretamente
+
+O caso mais comum seria [retornar uma resposta diretamente, conforme explicado posteriormente na documentação avançada](../advanced/response-directly.md){.internal-link target=_blank}.
+
+{* ../../docs_src/response_model/tutorial003_02.py hl[8,10:11] *}
+
+Este caso simples é tratado automaticamente pelo FastAPI porque a anotação do tipo de retorno é a classe (ou uma subclasse de) `Response`.
+
+E as ferramentas também ficarão felizes porque `RedirectResponse` e `JSONResponse` são subclasses de `Response`, então a anotação de tipo está correta.
+
+### Anotar uma subclasse de resposta
+
+Você também pode usar uma subclasse de `Response` na anotação de tipo:
+
+{* ../../docs_src/response_model/tutorial003_03.py hl[8:9] *}
+
+Isso também funcionará porque `RedirectResponse` é uma subclasse de `Response`, e o FastAPI tratará automaticamente este caso simples.
+
+### Anotações de Tipo de Retorno Inválido
+
+Mas quando você retorna algum outro objeto arbitrário que não é um tipo Pydantic válido (por exemplo, um objeto de banco de dados) e você o anota dessa forma na função, o FastAPI tentará criar um modelo de resposta Pydantic a partir dessa anotação de tipo e falhará.
+
+O mesmo aconteceria se você tivesse algo como uma união entre tipos diferentes onde um ou mais deles não são tipos Pydantic válidos, por exemplo, isso falharia 💥:
+
+{* ../../docs_src/response_model/tutorial003_04_py310.py hl[8] *}
+
+... isso falha porque a anotação de tipo não é um tipo Pydantic e não é apenas uma única classe ou subclasse `Response`, é uma união (qualquer uma das duas) entre um `Response` e um `dict`.
+
+### Desabilitar modelo de resposta
+
+Continuando com o exemplo acima, você pode não querer ter a validação de dados padrão, documentação, filtragem, etc. que é realizada pelo FastAPI.
+
+Mas você pode querer manter a anotação do tipo de retorno na função para obter o suporte de ferramentas como editores e verificadores de tipo (por exemplo, mypy).
+
+Neste caso, você pode desabilitar a geração do modelo de resposta definindo `response_model=None`:
+
+{* ../../docs_src/response_model/tutorial003_05_py310.py hl[7] *}
+
+Isso fará com que o FastAPI pule a geração do modelo de resposta e, dessa forma, você pode ter quaisquer anotações de tipo de retorno que precisar sem afetar seu aplicativo FastAPI. 🤓
+
+## Parâmetros de codificação do modelo de resposta
+
+Seu modelo de resposta pode ter valores padrão, como:
+
+{* ../../docs_src/response_model/tutorial004_py310.py hl[9,11:12] *}
+
+* `description: Union[str, None] = None` (ou `str | None = None` no Python 3.10) tem um padrão de `None`.
+* `tax: float = 10.5` tem um padrão de `10.5`.
+* `tags: List[str] = []` tem um padrão de uma lista vazia: `[]`.
+
+mas você pode querer omiti-los do resultado se eles não foram realmente armazenados.
+
+Por exemplo, se você tem modelos com muitos atributos opcionais em um banco de dados NoSQL, mas não quer enviar respostas JSON muito longas cheias de valores padrão.
+
+### Usar o parâmetro `response_model_exclude_unset`
+
+Você pode definir o parâmetro `response_model_exclude_unset=True` do *decorador de operação de rota* :
+
+{* ../../docs_src/response_model/tutorial004_py310.py hl[22] *}
+
+e esses valores padrão não serão incluídos na resposta, apenas os valores realmente definidos.
+
+Então, se você enviar uma solicitação para essa *operação de rota* para o item com ID `foo`, a resposta (sem incluir valores padrão) será:
+
+```JSON
+{
+"name": "Foo",
+"price": 50.2
+}
+```
+
+/// info | Informação
+
+No Pydantic v1, o método era chamado `.dict()`, ele foi descontinuado (mas ainda suportado) no Pydantic v2 e renomeado para `.model_dump()`.
+
+Os exemplos aqui usam `.dict()` para compatibilidade com Pydantic v1, mas você deve usar `.model_dump()` em vez disso se puder usar Pydantic v2.
+
+///
+
+/// info | Informação
+
+O FastAPI usa `.dict()` do modelo Pydantic com seu parâmetro `exclude_unset` para chegar a isso.
+
+///
+
+/// info | Informação
+
+Você também pode usar:
+
+* `response_model_exclude_defaults=True`
+* `response_model_exclude_none=True`
+
+conforme descrito na documentação do Pydantic para `exclude_defaults` e `exclude_none`.
+
+///
+
+#### Dados com valores para campos com padrões
+
+Mas se seus dados tiverem valores para os campos do modelo com valores padrões, como o item com ID `bar`:
+
+```Python hl_lines="3 5"
+{
+"name": "Bar",
+"description": "The bartenders",
+"price": 62,
+"tax": 20.2
+}
+```
+
+eles serão incluídos na resposta.
+
+#### Dados com os mesmos valores que os padrões
+
+Se os dados tiverem os mesmos valores que os padrões, como o item com ID `baz`:
+
+```Python hl_lines="3 5-6"
+{
+"name": "Baz",
+"description": None,
+"price": 50.2,
+"tax": 10.5,
+"tags": []
+}
+```
+
+O FastAPI é inteligente o suficiente (na verdade, o Pydantic é inteligente o suficiente) para perceber que, embora `description`, `tax` e `tags` tenham os mesmos valores que os padrões, eles foram definidos explicitamente (em vez de retirados dos padrões).
+
+Portanto, eles serão incluídos na resposta JSON.
+
+/// tip | Dica
+
+Observe que os valores padrão podem ser qualquer coisa, não apenas `None`.
+
+Eles podem ser uma lista (`[]`), um `float` de `10.5`, etc.
+
+///
+
+### `response_model_include` e `response_model_exclude`
+
+Você também pode usar os parâmetros `response_model_include` e `response_model_exclude` do *decorador de operação de rota*.
+
+Eles pegam um `set` de `str` com o nome dos atributos para incluir (omitindo o resto) ou para excluir (incluindo o resto).
+
+Isso pode ser usado como um atalho rápido se você tiver apenas um modelo Pydantic e quiser remover alguns dados da saída.
+
+/// tip | Dica
+
+Mas ainda é recomendado usar as ideias acima, usando várias classes, em vez desses parâmetros.
+
+Isso ocorre porque o Schema JSON gerado no OpenAPI do seu aplicativo (e a documentação) ainda será o único para o modelo completo, mesmo que você use `response_model_include` ou `response_model_exclude` para omitir alguns atributos.
+
+Isso também se aplica ao `response_model_by_alias` que funciona de forma semelhante.
+
+///
+
+{* ../../docs_src/response_model/tutorial005_py310.py hl[29,35] *}
+
+/// tip | Dica
+
+A sintaxe `{"nome", "descrição"}` cria um `conjunto` com esses dois valores.
+
+É equivalente a `set(["nome", "descrição"])`.
+
+///
+
+#### Usando `list`s em vez de `set`s
+
+Se você esquecer de usar um `set` e usar uma `lista` ou `tupla` em vez disso, o FastAPI ainda o converterá em um `set` e funcionará corretamente:
+
+{* ../../docs_src/response_model/tutorial006_py310.py hl[29,35] *}
+
+## Recapitulação
+
+Use o parâmetro `response_model` do *decorador de operação de rota* para definir modelos de resposta e, especialmente, para garantir que dados privados sejam filtrados.
+
+Use `response_model_exclude_unset` para retornar apenas os valores definidos explicitamente.
diff --git a/docs/pt/docs/tutorial/response-status-code.md b/docs/pt/docs/tutorial/response-status-code.md
index dc8e120485..48957f67ac 100644
--- a/docs/pt/docs/tutorial/response-status-code.md
+++ b/docs/pt/docs/tutorial/response-status-code.md
@@ -8,11 +8,9 @@ Da mesma forma que você pode especificar um modelo de resposta, você também p
* `@app.delete()`
* etc.
-```Python hl_lines="6"
-{!../../../docs_src/response_status_code/tutorial001.py!}
-```
+{* ../../docs_src/response_status_code/tutorial001.py hl[6] *}
-/// note | "Nota"
+/// note | Nota
Observe que `status_code` é um parâmetro do método "decorador" (get, post, etc). Não da sua função de *operação de caminho*, como todos os parâmetros e corpo.
@@ -20,7 +18,7 @@ Observe que `status_code` é um parâmetro do método "decorador" (get, post, et
O parâmetro `status_code` recebe um número com o código de status HTTP.
-/// info | "Informação"
+/// info | Informação
`status_code` também pode receber um `IntEnum`, como o do Python `http.HTTPStatus`.
@@ -33,7 +31,7 @@ Dessa forma:
-/// note | "Nota"
+/// note | Nota
Alguns códigos de resposta (consulte a próxima seção) indicam que a resposta não possui um corpo.
@@ -43,7 +41,7 @@ O FastAPI sabe disso e produzirá documentos OpenAPI informando que não há cor
## Sobre os códigos de status HTTP
-/// note | "Nota"
+/// note | Nota
Se você já sabe o que são códigos de status HTTP, pule para a próxima seção.
@@ -67,7 +65,7 @@ Resumidamente:
* Para erros genéricos do cliente, você pode usar apenas `400`.
* `500` e acima são para erros do servidor. Você quase nunca os usa diretamente. Quando algo der errado em alguma parte do código do seu aplicativo ou servidor, ele retornará automaticamente um desses códigos de status.
-/// tip | "Dica"
+/// tip | Dica
Para saber mais sobre cada código de status e qual código serve para quê, verifique o MDN documentação sobre códigos de status HTTP.
@@ -77,9 +75,7 @@ Para saber mais sobre cada código de status e qual código serve para quê, ver
Vamos ver o exemplo anterior novamente:
-```Python hl_lines="6"
-{!../../../docs_src/response_status_code/tutorial001.py!}
-```
+{* ../../docs_src/response_status_code/tutorial001.py hl[6] *}
`201` é o código de status para "Criado".
@@ -87,15 +83,13 @@ Mas você não precisa memorizar o que cada um desses códigos significa.
Você pode usar as variáveis de conveniência de `fastapi.status`.
-```Python hl_lines="1 6"
-{!../../../docs_src/response_status_code/tutorial002.py!}
-```
+{* ../../docs_src/response_status_code/tutorial002.py hl[1,6] *}
Eles são apenas uma conveniência, eles possuem o mesmo número, mas dessa forma você pode usar o autocomplete do editor para encontrá-los:
-/// note | "Detalhes técnicos"
+/// note | Detalhes técnicos
Você também pode usar `from starlette import status`.
diff --git a/docs/pt/docs/tutorial/schema-extra-example.md b/docs/pt/docs/tutorial/schema-extra-example.md
index a291db045b..5d3498d7de 100644
--- a/docs/pt/docs/tutorial/schema-extra-example.md
+++ b/docs/pt/docs/tutorial/schema-extra-example.md
@@ -8,13 +8,11 @@ Aqui estão várias formas de se fazer isso.
Você pode declarar um `example` para um modelo Pydantic usando `Config` e `schema_extra`, conforme descrito em Documentação do Pydantic: Schema customization:
-```Python hl_lines="15-23"
-{!../../../docs_src/schema_extra_example/tutorial001.py!}
-```
+{* ../../docs_src/schema_extra_example/tutorial001.py hl[15:23] *}
Essas informações extras serão adicionadas como se encontram no **JSON Schema** de resposta desse modelo e serão usadas na documentação da API.
-/// tip | "Dica"
+/// tip | Dica
Você pode usar a mesma técnica para estender o JSON Schema e adicionar suas próprias informações extras de forma personalizada.
@@ -28,11 +26,9 @@ Ao usar `Field ()` com modelos Pydantic, você também pode declarar informaçõ
Você pode usar isso para adicionar um `example` para cada campo:
-```Python hl_lines="4 10-13"
-{!../../../docs_src/schema_extra_example/tutorial002.py!}
-```
+{* ../../docs_src/schema_extra_example/tutorial002.py hl[4,10:13] *}
-/// warning | "Atenção"
+/// warning | Atenção
Lembre-se de que esses argumentos extras passados não adicionarão nenhuma validação, apenas informações extras, para fins de documentação.
@@ -56,9 +52,7 @@ você também pode declarar um dado `example` ou um grupo de `examples` com info
Aqui nós passamos um `example` dos dados esperados por `Body()`:
-```Python hl_lines="21-26"
-{!../../../docs_src/schema_extra_example/tutorial003.py!}
-```
+{* ../../docs_src/schema_extra_example/tutorial003.py hl[21:26] *}
### Exemplo na UI da documentação
@@ -79,9 +73,7 @@ Cada `dict` de exemplo específico em `examples` pode conter:
* `value`: O próprio exemplo mostrado, ex: um `dict`.
* `externalValue`: alternativa ao `value`, uma URL apontando para o exemplo. Embora isso possa não ser suportado por tantas ferramentas quanto `value`.
-```Python hl_lines="22-48"
-{!../../../docs_src/schema_extra_example/tutorial004.py!}
-```
+{* ../../docs_src/schema_extra_example/tutorial004.py hl[22:48] *}
### Exemplos na UI da documentação
@@ -91,7 +83,7 @@ Com `examples` adicionado a `Body()`, os `/docs` vão ficar assim:
## Detalhes técnicos
-/// warning | "Atenção"
+/// warning | Atenção
Esses são detalhes muito técnicos sobre os padrões **JSON Schema** e **OpenAPI**.
diff --git a/docs/pt/docs/tutorial/security/first-steps.md b/docs/pt/docs/tutorial/security/first-steps.md
index 007fefcb96..f4dea8e14b 100644
--- a/docs/pt/docs/tutorial/security/first-steps.md
+++ b/docs/pt/docs/tutorial/security/first-steps.md
@@ -19,13 +19,11 @@ Vamos primeiro usar o código e ver como funciona, e depois voltaremos para ente
## Crie um `main.py`
Copie o exemplo em um arquivo `main.py`:
-```Python
-{!../../../docs_src/security/tutorial001.py!}
-```
+{* ../../docs_src/security/tutorial001.py *}
## Execute-o
-/// info | "informação"
+/// info | informação
@@ -57,7 +55,7 @@ Você verá algo deste tipo:
-/// check | "Botão de Autorizar!"
+/// check | Botão de Autorizar!
@@ -71,7 +69,7 @@ E se você clicar, você terá um pequeno formulário de autorização para digi
-/// note | "Nota"
+/// note | Nota
@@ -119,7 +117,7 @@ Então, vamos rever de um ponto de vista simplificado:
Neste exemplo, nós vamos usar o **OAuth2** com o fluxo de **Senha**, usando um token **Bearer**. Fazemos isso usando a classe `OAuth2PasswordBearer`.
-/// info | "informação"
+/// info | informação
@@ -135,11 +133,9 @@ Neste exemplo, nós vamos usar o **OAuth2** com o fluxo de **Senha**, usando um
Quando nós criamos uma instância da classe `OAuth2PasswordBearer`, nós passamos pelo parâmetro `tokenUrl` Esse parâmetro contém a URL que o client (o frontend rodando no browser do usuário) vai usar para mandar o `username` e `senha` para obter um token.
-```Python hl_lines="6"
-{!../../../docs_src/security/tutorial001.py!}
-```
+{* ../../docs_src/security/tutorial001.py hl[6] *}
-/// tip | "Dica"
+/// tip | Dica
@@ -155,7 +151,7 @@ Esse parâmetro não cria um endpoint / *path operation*, mas declara que a URL
Em breve também criaremos o atual path operation.
-/// info | "informação"
+/// info | informação
@@ -179,15 +175,13 @@ Então, pode ser usado com `Depends`.
Agora você pode passar aquele `oauth2_scheme` em uma dependência com `Depends`.
-```Python hl_lines="10"
-{!../../../docs_src/security/tutorial001.py!}
-```
+{* ../../docs_src/security/tutorial001.py hl[10] *}
Esse dependência vai fornecer uma `str` que é atribuído ao parâmetro `token da *função do path operation*
A **FastAPI** saberá que pode usar essa dependência para definir um "esquema de segurança" no esquema da OpenAPI (e na documentação da API automática).
-/// info | "Detalhes técnicos"
+/// info | Detalhes técnicos
diff --git a/docs/pt/docs/tutorial/security/get-current-user.md b/docs/pt/docs/tutorial/security/get-current-user.md
new file mode 100644
index 0000000000..1a2badb83b
--- /dev/null
+++ b/docs/pt/docs/tutorial/security/get-current-user.md
@@ -0,0 +1,105 @@
+# Obter Usuário Atual
+
+No capítulo anterior, o sistema de segurança (que é baseado no sistema de injeção de dependências) estava fornecendo à *função de operação de rota* um `token` como uma `str`:
+
+{* ../../docs_src/security/tutorial001_an_py39.py hl[12] *}
+
+Mas isso ainda não é tão útil.
+
+Vamos fazer com que ele nos forneça o usuário atual.
+
+## Criar um modelo de usuário
+
+Primeiro, vamos criar um modelo de usuário com Pydantic.
+
+Da mesma forma que usamos o Pydantic para declarar corpos, podemos usá-lo em qualquer outro lugar:
+
+{* ../../docs_src/security/tutorial002_an_py310.py hl[5,12:6] *}
+
+## Criar uma dependência `get_current_user`
+
+Vamos criar uma dependência chamada `get_current_user`.
+
+Lembra que as dependências podem ter subdependências?
+
+`get_current_user` terá uma dependência com o mesmo `oauth2_scheme` que criamos antes.
+
+Da mesma forma que estávamos fazendo antes diretamente na *operação de rota*, a nossa nova dependência `get_current_user` receberá um `token` como uma `str` da subdependência `oauth2_scheme`:
+
+{* ../../docs_src/security/tutorial002_an_py310.py hl[25] *}
+
+## Obter o usuário
+
+`get_current_user` usará uma função utilitária (falsa) que criamos, que recebe um token como uma `str` e retorna nosso modelo Pydantic `User`:
+
+{* ../../docs_src/security/tutorial002_an_py310.py hl[19:22,26:27] *}
+
+## Injetar o usuário atual
+
+Então agora nós podemos usar o mesmo `Depends` com nosso `get_current_user` na *operação de rota*:
+
+{* ../../docs_src/security/tutorial002_an_py310.py hl[31] *}
+
+Observe que nós declaramos o tipo de `current_user` como o modelo Pydantic `User`.
+
+Isso nos ajudará dentro da função com todo o preenchimento automático e verificações de tipo.
+
+/// tip | Dica
+
+Você pode se lembrar que corpos de requisição também são declarados com modelos Pydantic.
+
+Aqui, o **FastAPI** não ficará confuso porque você está usando `Depends`.
+
+///
+
+/// check | Verifique
+
+A forma como esse sistema de dependências foi projetado nos permite ter diferentes dependências (diferentes "dependables") que retornam um modelo `User`.
+
+Não estamos restritos a ter apenas uma dependência que possa retornar esse tipo de dado.
+
+///
+
+## Outros modelos
+
+Agora você pode obter o usuário atual diretamente nas *funções de operação de rota* e lidar com os mecanismos de segurança no nível da **Injeção de Dependências**, usando `Depends`.
+
+E você pode usar qualquer modelo ou dado para os requisitos de segurança (neste caso, um modelo Pydantic `User`).
+
+Mas você não está restrito a usar um modelo de dados, classe ou tipo específico.
+
+Você quer ter apenas um `id` e `email`, sem incluir nenhum `username` no modelo? Claro. Você pode usar essas mesmas ferramentas.
+
+Você quer ter apenas uma `str`? Ou apenas um `dict`? Ou uma instância de modelo de classe de banco de dados diretamente? Tudo funciona da mesma forma.
+
+Na verdade, você não tem usuários que fazem login no seu aplicativo, mas sim robôs, bots ou outros sistemas, que possuem apenas um token de acesso? Novamente, tudo funciona da mesma forma.
+
+Apenas use qualquer tipo de modelo, qualquer tipo de classe, qualquer tipo de banco de dados que você precise para a sua aplicação. O **FastAPI** cobre tudo com o sistema de injeção de dependências.
+
+## Tamanho do código
+
+Este exemplo pode parecer verboso. Lembre-se de que estamos misturando segurança, modelos de dados, funções utilitárias e *operações de rota* no mesmo arquivo.
+
+Mas aqui está o ponto principal.
+
+O código relacionado à segurança e à injeção de dependências é escrito apenas uma vez.
+
+E você pode torná-lo tão complexo quanto quiser. E ainda assim, tê-lo escrito apenas uma vez, em um único lugar. Com toda a flexibilidade.
+
+Mas você pode ter milhares de endpoints (*operações de rota*) usando o mesmo sistema de segurança.
+
+E todos eles (ou qualquer parte deles que você desejar) podem aproveitar o reuso dessas dependências ou de quaisquer outras dependências que você criar.
+
+E todos esses milhares de *operações de rota* podem ter apenas 3 linhas:
+
+{* ../../docs_src/security/tutorial002_an_py310.py hl[30:32] *}
+
+## Recapitulação
+
+Agora você pode obter o usuário atual diretamente na sua *função de operação de rota*.
+
+Já estamos na metade do caminho.
+
+Só precisamos adicionar uma *operação de rota* para que o usuário/cliente realmente envie o `username` e `password`.
+
+Isso vem a seguir.
diff --git a/docs/pt/docs/tutorial/security/index.md b/docs/pt/docs/tutorial/security/index.md
index 2f23aa47ee..2ebb87fcd8 100644
--- a/docs/pt/docs/tutorial/security/index.md
+++ b/docs/pt/docs/tutorial/security/index.md
@@ -22,7 +22,7 @@ Ela é bastante extensiva na especificação e cobre casos de uso muito complexo
Ela inclui uma forma para autenticação usando “third party”/aplicações de terceiros.
-Isso é o que todos os sistemas com “Login with Facebook, Google, Twitter, GitHub” usam por baixo.
+Isso é o que todos os sistemas com “Login with Facebook, Google, X (Twitter), GitHub” usam por baixo.
### OAuth 1
@@ -32,7 +32,7 @@ Não é muito popular ou usado nos dias atuais.
OAuth2 não especifica como criptografar a comunicação, ele espera que você tenha sua aplicação em um servidor HTTPS.
-/// tip | "Dica"
+/// tip | Dica
Na seção sobre **deployment** você irá ver como configurar HTTPS de modo gratuito, usando Traefik e Let’s Encrypt.
@@ -79,7 +79,7 @@ OpenAPI define os seguintes esquemas de segurança:
* HTTP Basic authentication.
* HTTP Digest, etc.
* `oauth2`: todas as formas do OAuth2 para lidar com segurança (chamados "fluxos").
- * Vários desses fluxos são apropriados para construir um provedor de autenticação OAuth2 (como Google, Facebook, Twitter, GitHub, etc):
+ * Vários desses fluxos são apropriados para construir um provedor de autenticação OAuth2 (como Google, Facebook, X (Twitter), GitHub, etc):
* `implicit`
* `clientCredentials`
* `authorizationCode`
@@ -89,9 +89,9 @@ OpenAPI define os seguintes esquemas de segurança:
* Essa descoberta automática é o que é definido na especificação OpenID Connect.
-/// tip | "Dica"
+/// tip | Dica
-Integração com outros provedores de autenticação/autorização como Google, Facebook, Twitter, GitHub, etc. é bem possível e relativamente fácil.
+Integração com outros provedores de autenticação/autorização como Google, Facebook, X (Twitter), GitHub, etc. é bem possível e relativamente fácil.
O problema mais complexo é criar um provedor de autenticação/autorização como eles, mas o FastAPI dá a você ferramentas para fazer isso facilmente, enquanto faz o trabalho pesado para você.
diff --git a/docs/pt/docs/tutorial/security/oauth2-jwt.md b/docs/pt/docs/tutorial/security/oauth2-jwt.md
new file mode 100644
index 0000000000..7d80d12fab
--- /dev/null
+++ b/docs/pt/docs/tutorial/security/oauth2-jwt.md
@@ -0,0 +1,274 @@
+# OAuth2 com Senha (e hashing), Bearer com tokens JWT
+
+Agora que temos todo o fluxo de segurança, vamos tornar a aplicação realmente segura, usando tokens JWT e hashing de senhas seguras.
+
+Este código é algo que você pode realmente usar na sua aplicação, salvar os hashes das senhas no seu banco de dados, etc.
+
+Vamos começar de onde paramos no capítulo anterior e incrementá-lo.
+
+## Sobre o JWT
+
+JWT significa "JSON Web Tokens".
+
+É um padrão para codificar um objeto JSON em uma string longa e densa sem espaços. Ele se parece com isso:
+
+```
+eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
+```
+
+Ele não é criptografado, então qualquer pessoa pode recuperar as informações do seu conteúdo.
+
+Mas ele é assinado. Assim, quando você recebe um token que você emitiu, você pode verificar que foi realmente você quem o emitiu.
+
+Dessa forma, você pode criar um token com um prazo de expiração, digamos, de 1 semana. E então, quando o usuário voltar no dia seguinte com o token, você sabe que ele ainda está logado no seu sistema.
+
+Depois de uma semana, o token expirará e o usuário não estará autorizado, precisando fazer login novamente para obter um novo token. E se o usuário (ou uma terceira parte) tentar modificar o token para alterar a expiração, você seria capaz de descobrir isso, pois as assinaturas não iriam corresponder.
+
+Se você quiser brincar com tokens JWT e ver como eles funcionam, visite https://jwt.io.
+
+## Instalar `PyJWT`
+
+Nós precisamos instalar o `PyJWT` para criar e verificar os tokens JWT em Python.
+
+Certifique-se de criar um [ambiente virtual](../../virtual-environments.md){.internal-link target=_blank}, ativá-lo e então instalar o `pyjwt`:
+
+
+
+Autorize a aplicação da mesma maneira que antes.
+
+Usando as credenciais:
+
+Username: `johndoe`
+Password: `secret`
+
+/// check | Verifique
+
+Observe que em nenhuma parte do código está a senha em texto puro "`secret`", nós temos apenas o hash.
+
+///
+
+
+
+Chame o endpoint `/users/me/`, você receberá o retorno como:
+
+```JSON
+{
+ "username": "johndoe",
+ "email": "johndoe@example.com",
+ "full_name": "John Doe",
+ "disabled": false
+}
+```
+
+
+
+Se você abrir as ferramentas de desenvolvedor, poderá ver que os dados enviados incluem apenas o token. A senha é enviada apenas na primeira requisição para autenticar o usuário e obter o token de acesso, mas não é enviada nas próximas requisições:
+
+
+
+/// note | Nota
+
+Perceba que o cabeçalho `Authorization`, com o valor que começa com `Bearer `.
+
+///
+
+## Uso avançado com `scopes`
+
+O OAuth2 tem a noção de "scopes" (escopos).
+
+Você pode usá-los para adicionar um conjunto específico de permissões a um token JWT.
+
+Então, você pode dar este token diretamente a um usuário ou a uma terceira parte para interagir com sua API com um conjunto de restrições.
+
+Você pode aprender como usá-los e como eles são integrados ao **FastAPI** mais adiante no **Guia Avançado do Usuário**.
+
+
+## Recapitulação
+
+Com o que você viu até agora, você pode configurar uma aplicação **FastAPI** segura usando padrões como OAuth2 e JWT.
+
+Em quase qualquer framework, lidar com a segurança se torna rapidamente um assunto bastante complexo.
+
+Muitos pacotes que simplificam bastante isso precisam fazer muitas concessões com o modelo de dados, o banco de dados e os recursos disponíveis. E alguns desses pacotes que simplificam demais na verdade têm falhas de segurança subjacentes.
+
+---
+
+O **FastAPI** não faz nenhuma concessão com nenhum banco de dados, modelo de dados ou ferramenta.
+
+Ele oferece toda a flexibilidade para você escolher as opções que melhor se ajustam ao seu projeto.
+
+E você pode usar diretamente muitos pacotes bem mantidos e amplamente utilizados, como `passlib` e `PyJWT`, porque o **FastAPI** não exige mecanismos complexos para integrar pacotes externos.
+
+Mas ele fornece as ferramentas para simplificar o processo o máximo possível, sem comprometer a flexibilidade, robustez ou segurança.
+
+E você pode usar e implementar protocolos padrão seguros, como o OAuth2, de uma maneira relativamente simples.
+
+Você pode aprender mais no **Guia Avançado do Usuário** sobre como usar os "scopes" do OAuth2 para um sistema de permissões mais refinado, seguindo esses mesmos padrões. O OAuth2 com scopes é o mecanismo usado por muitos provedores grandes de autenticação, como o Facebook, Google, GitHub, Microsoft, X (Twitter), etc. para autorizar aplicativos de terceiros a interagir com suas APIs em nome de seus usuários.
diff --git a/docs/pt/docs/tutorial/security/simple-oauth2.md b/docs/pt/docs/tutorial/security/simple-oauth2.md
new file mode 100644
index 0000000000..1cf05785ee
--- /dev/null
+++ b/docs/pt/docs/tutorial/security/simple-oauth2.md
@@ -0,0 +1,289 @@
+# Simples OAuth2 com senha e Bearer
+
+Agora vamos construir a partir do capítulo anterior e adicionar as partes que faltam para ter um fluxo de segurança completo.
+
+## Pegue o `username` (nome de usuário) e `password` (senha)
+
+É utilizado o utils de segurança da **FastAPI** para obter o `username` e a `password`.
+
+OAuth2 especifica que ao usar o "password flow" (fluxo de senha), que estamos usando, o cliente/usuário deve enviar os campos `username` e `password` como dados do formulário.
+
+E a especificação diz que os campos devem ser nomeados assim. Portanto, `user-name` ou `email` não funcionariam.
+
+Mas não se preocupe, você pode mostrá-lo como quiser aos usuários finais no frontend.
+
+E seus modelos de banco de dados podem usar qualquer outro nome que você desejar.
+
+Mas para a *operação de rota* de login, precisamos usar esses nomes para serem compatíveis com a especificação (e poder, por exemplo, usar o sistema integrado de documentação da API).
+
+A especificação também afirma que o `username` e a `password` devem ser enviados como dados de formulário (portanto, não há JSON aqui).
+
+### `scope`
+
+A especificação também diz que o cliente pode enviar outro campo de formulário "`scope`" (Escopo).
+
+O nome do campo do formulário é `scope` (no singular), mas na verdade é uma longa string com "escopos" separados por espaços.
+
+Cada “scope” é apenas uma string (sem espaços).
+
+Normalmente são usados para declarar permissões de segurança específicas, por exemplo:
+
+* `users:read` ou `users:write` são exemplos comuns.
+* `instagram_basic` é usado pelo Facebook e Instagram.
+* `https://www.googleapis.com/auth/drive` é usado pelo Google.
+
+/// info | Informação
+
+No OAuth2, um "scope" é apenas uma string que declara uma permissão específica necessária.
+
+Não importa se tem outros caracteres como `:` ou se é uma URL.
+
+Esses detalhes são específicos da implementação.
+
+Para OAuth2 são apenas strings.
+
+///
+
+## Código para conseguir o `username` e a `password`
+
+Agora vamos usar os utilitários fornecidos pelo **FastAPI** para lidar com isso.
+
+### `OAuth2PasswordRequestForm`
+
+Primeiro, importe `OAuth2PasswordRequestForm` e use-o como uma dependência com `Depends` na *operação de rota* para `/token`:
+
+{* ../../docs_src/security/tutorial003_an_py310.py hl[4,78] *}
+
+`OAuth2PasswordRequestForm` é uma dependência de classe que declara um corpo de formulário com:
+
+* O `username`.
+* A `password`.
+* Um campo `scope` opcional como uma string grande, composta de strings separadas por espaços.
+* Um `grant_type` (tipo de concessão) opcional.
+
+/// tip | Dica
+
+A especificação OAuth2 na verdade *requer* um campo `grant_type` com um valor fixo de `password`, mas `OAuth2PasswordRequestForm` não o impõe.
+
+Se você precisar aplicá-lo, use `OAuth2PasswordRequestFormStrict` em vez de `OAuth2PasswordRequestForm`.
+
+///
+
+* Um `client_id` opcional (não precisamos dele em nosso exemplo).
+* Um `client_secret` opcional (não precisamos dele em nosso exemplo).
+
+/// info | Informação
+
+O `OAuth2PasswordRequestForm` não é uma classe especial para **FastAPI** como é `OAuth2PasswordBearer`.
+
+`OAuth2PasswordBearer` faz com que **FastAPI** saiba que é um esquema de segurança. Portanto, é adicionado dessa forma ao OpenAPI.
+
+Mas `OAuth2PasswordRequestForm` é apenas uma dependência de classe que você mesmo poderia ter escrito ou poderia ter declarado os parâmetros do `Form` (formulário) diretamente.
+
+Mas como é um caso de uso comum, ele é fornecido diretamente pelo **FastAPI**, apenas para facilitar.
+
+///
+
+### Use os dados do formulário
+
+/// tip | Dica
+
+A instância da classe de dependência `OAuth2PasswordRequestForm` não terá um atributo `scope` com a string longa separada por espaços, em vez disso, terá um atributo `scopes` com a lista real de strings para cada escopo enviado.
+
+Não estamos usando `scopes` neste exemplo, mas a funcionalidade está disponível se você precisar.
+
+///
+
+Agora, obtenha os dados do usuário do banco de dados (falso), usando o `username` do campo do formulário.
+
+Se não existir tal usuário, retornaremos um erro dizendo "Incorrect username or password" (Nome de usuário ou senha incorretos).
+
+Para o erro, usamos a exceção `HTTPException`:
+
+{* ../../docs_src/security/tutorial003_an_py310.py hl[3,79:81] *}
+
+### Confira a password (senha)
+
+Neste ponto temos os dados do usuário do nosso banco de dados, mas não verificamos a senha.
+
+Vamos colocar esses dados primeiro no modelo `UserInDB` do Pydantic.
+
+Você nunca deve salvar senhas em texto simples, portanto, usaremos o sistema de hashing de senhas (falsas).
+
+Se as senhas não corresponderem, retornaremos o mesmo erro.
+
+#### Hashing de senha
+
+"Hashing" significa: converter algum conteúdo (uma senha neste caso) em uma sequência de bytes (apenas uma string) que parece algo sem sentido.
+
+Sempre que você passa exatamente o mesmo conteúdo (exatamente a mesma senha), você obtém exatamente a mesma sequência aleatória de caracteres.
+
+Mas você não pode converter a sequência aleatória de caracteres de volta para a senha.
+
+##### Porque usar hashing de senha
+
+Se o seu banco de dados for roubado, o ladrão não terá as senhas em texto simples dos seus usuários, apenas os hashes.
+
+Assim, o ladrão não poderá tentar usar essas mesmas senhas em outro sistema (como muitos usuários usam a mesma senha em todos os lugares, isso seria perigoso).
+
+{* ../../docs_src/security/tutorial003_an_py310.py hl[82:85] *}
+
+#### Sobre `**user_dict`
+
+`UserInDB(**user_dict)` significa:
+
+*Passe as keys (chaves) e values (valores) de `user_dict` diretamente como argumentos de valor-chave, equivalente a:*
+
+```Python
+UserInDB(
+ username = user_dict["username"],
+ email = user_dict["email"],
+ full_name = user_dict["full_name"],
+ disabled = user_dict["disabled"],
+ hashed_password = user_dict["hashed_password"],
+)
+```
+
+/// info | Informação
+
+Para uma explicação mais completa de `**user_dict`, verifique [a documentação para **Extra Models**](../extra-models.md#about-user_indict){.internal-link target=_blank}.
+
+///
+
+## Retorne o token
+
+A resposta do endpoint `token` deve ser um objeto JSON.
+
+Deve ter um `token_type`. No nosso caso, como estamos usando tokens "Bearer", o tipo de token deve ser "`bearer`".
+
+E deve ter um `access_token`, com uma string contendo nosso token de acesso.
+
+Para este exemplo simples, seremos completamente inseguros e retornaremos o mesmo `username` do token.
+
+/// tip | Dica
+
+No próximo capítulo, você verá uma implementação realmente segura, com hash de senha e tokens JWT.
+
+Mas, por enquanto, vamos nos concentrar nos detalhes específicos de que precisamos.
+
+///
+
+{* ../../docs_src/security/tutorial003_an_py310.py hl[87] *}
+
+/// tip | Dica
+
+Pela especificação, você deve retornar um JSON com um `access_token` e um `token_type`, o mesmo que neste exemplo.
+
+Isso é algo que você mesmo deve fazer em seu código e certifique-se de usar essas chaves JSON.
+
+É quase a única coisa que você deve se lembrar de fazer corretamente, para estar em conformidade com as especificações.
+
+De resto, **FastAPI** cuida disso para você.
+
+///
+
+## Atualize as dependências
+
+Agora vamos atualizar nossas dependências.
+
+Queremos obter o `user_user` *somente* se este usuário estiver ativo.
+
+Portanto, criamos uma dependência adicional `get_current_active_user` que por sua vez usa `get_current_user` como dependência.
+
+Ambas as dependências retornarão apenas um erro HTTP se o usuário não existir ou se estiver inativo.
+
+Portanto, em nosso endpoint, só obteremos um usuário se o usuário existir, tiver sido autenticado corretamente e estiver ativo:
+
+{* ../../docs_src/security/tutorial003_an_py310.py hl[58:66,69:74,94] *}
+
+/// info | Informação
+
+O cabeçalho adicional `WWW-Authenticate` com valor `Bearer` que estamos retornando aqui também faz parte da especificação.
+
+Qualquer código de status HTTP (erro) 401 "UNAUTHORIZED" também deve retornar um cabeçalho `WWW-Authenticate`.
+
+No caso de tokens ao portador (nosso caso), o valor desse cabeçalho deve ser `Bearer`.
+
+Na verdade, você pode pular esse cabeçalho extra e ainda funcionaria.
+
+Mas é fornecido aqui para estar em conformidade com as especificações.
+
+Além disso, pode haver ferramentas que esperam e usam isso (agora ou no futuro) e que podem ser úteis para você ou seus usuários, agora ou no futuro.
+
+Esse é o benefício dos padrões...
+
+///
+
+## Veja em ação
+
+Abra o docs interativo: http://127.0.0.1:8000/docs.
+
+### Autenticação
+
+Clique no botão "Authorize".
+
+Use as credenciais:
+
+User: `johndoe`
+
+Password: `secret`
+
+
+
+Após autenticar no sistema, você verá assim:
+
+
+
+### Obtenha seus próprios dados de usuário
+
+Agora use a operação `GET` com o caminho `/users/me`.
+
+Você obterá os dados do seu usuário, como:
+
+```JSON
+{
+ "username": "johndoe",
+ "email": "johndoe@example.com",
+ "full_name": "John Doe",
+ "disabled": false,
+ "hashed_password": "fakehashedsecret"
+}
+```
+
+
+
+Se você clicar no ícone de cadeado, sair e tentar a mesma operação novamente, receberá um erro HTTP 401 de:
+
+```JSON
+{
+ "detail": "Not authenticated"
+}
+```
+
+### Usuário inativo
+
+Agora tente com um usuário inativo, autentique-se com:
+
+User: `alice`
+
+Password: `secret2`
+
+E tente usar a operação `GET` com o caminho `/users/me`.
+
+Você receberá um erro "Usuário inativo", como:
+
+```JSON
+{
+ "detail": "Inactive user"
+}
+```
+
+## Recaptulando
+
+Agora você tem as ferramentas para implementar um sistema de segurança completo baseado em `username` e `password` para sua API.
+
+Usando essas ferramentas, você pode tornar o sistema de segurança compatível com qualquer banco de dados e com qualquer usuário ou modelo de dados.
+
+O único detalhe que falta é que ainda não é realmente "seguro".
+
+No próximo capítulo você verá como usar uma biblioteca de hashing de senha segura e tokens JWT.
diff --git a/docs/pt/docs/tutorial/sql-databases.md b/docs/pt/docs/tutorial/sql-databases.md
new file mode 100644
index 0000000000..3d76a532cc
--- /dev/null
+++ b/docs/pt/docs/tutorial/sql-databases.md
@@ -0,0 +1,359 @@
+# Bancos de Dados SQL (Relacionais)
+
+**FastAPI** não exige que você use um banco de dados SQL (relacional). Mas você pode usar **qualquer banco de dados** que quiser.
+
+Aqui veremos um exemplo usando SQLModel.
+
+**SQLModel** é construído sobre SQLAlchemy e Pydantic. Ele foi criado pelo mesmo autor do **FastAPI** para ser o par perfeito para aplicações **FastAPI** que precisam usar **bancos de dados SQL**.
+
+/// tip | Dica
+
+Você pode usar qualquer outra biblioteca de banco de dados SQL ou NoSQL que quiser (em alguns casos chamadas de "ORMs"), o FastAPI não obriga você a usar nada. 😎
+
+///
+
+Como o SQLModel é baseado no SQLAlchemy, você pode facilmente usar **qualquer banco de dados suportado** pelo SQLAlchemy (o que também os torna suportados pelo SQLModel), como:
+
+* PostgreSQL
+* MySQL
+* SQLite
+* Oracle
+* Microsoft SQL Server, etc.
+
+Neste exemplo, usaremos **SQLite**, porque ele usa um único arquivo e o Python tem suporte integrado. Assim, você pode copiar este exemplo e executá-lo como está.
+
+Mais tarde, para sua aplicação em produção, você pode querer usar um servidor de banco de dados como o **PostgreSQL**.
+
+/// tip | Dica
+
+Existe um gerador de projetos oficial com **FastAPI** e **PostgreSQL** incluindo um frontend e mais ferramentas: https://github.com/fastapi/full-stack-fastapi-template
+
+///
+
+Este é um tutorial muito simples e curto, se você quiser aprender sobre bancos de dados em geral, sobre SQL ou recursos mais avançados, acesse a documentação do SQLModel.
+
+## Instalar o `SQLModel`
+
+Primeiro, certifique-se de criar seu [ambiente virtual](../virtual-environments.md){.internal-link target=_blank}, ativá-lo e, em seguida, instalar o `sqlmodel`:
+
+
+
+lt
+* XWT
+* PSGI
+
+### abbr даёт объяснение { #the-abbr-gives-an-explanation }
+
+* кластер
+* Глубокое обучение
+
+### abbr даёт полную расшифровку и объяснение { #the-abbr-gives-a-full-phrase-and-an-explanation }
+
+* MDN
+* I/O.
+
+////
+
+//// tab | Информация
+
+Атрибуты "title" элементов "abbr" переводятся по определённым правилам.
+
+Переводы могут добавлять свои собственные элементы "abbr", которые LLM не должна удалять. Например, чтобы объяснить английские слова.
+
+См. раздел `### HTML abbr elements` в общем промпте в `scripts/translate.py`.
+
+////
+
+## Заголовки { #headings }
+
+//// tab | Тест
+
+### Разработка веб‑приложения — руководство { #develop-a-webapp-a-tutorial }
+
+Привет.
+
+### Аннотации типов и -аннотации { #type-hints-and-annotations }
+
+Снова привет.
+
+### Супер- и подклассы { #super-and-subclasses }
+
+Снова привет.
+
+////
+
+//// tab | Информация
+
+Единственное жёсткое правило для заголовков — LLM должна оставить часть хеша в фигурных скобках без изменений, чтобы ссылки не ломались.
+
+См. раздел `### Headings` в общем промпте в `scripts/translate.py`.
+
+Для некоторых языковых инструкций см., например, раздел `### Headings` в `docs/de/llm-prompt.md`.
+
+////
+
+## Термины, используемые в документации { #terms-used-in-the-docs }
+
+//// tab | Тест
+
+* вы
+* ваш
+
+* например
+* и т.д.
+
+* `foo` как `int`
+* `bar` как `str`
+* `baz` как `list`
+
+* Учебник — Руководство пользователя
+* Расширенное руководство пользователя
+* Документация по SQLModel
+* Документация API
+* Автоматическая документация
+
+* Наука о данных
+* Глубокое обучение
+* Машинное обучение
+* Внедрение зависимостей
+* Аутентификация HTTP Basic
+* HTTP Digest
+* формат ISO
+* стандарт JSON Schema
+* JSON-схема
+* определение схемы
+* password flow
+* Мобильный
+
+* устаревший
+* спроектированный
+* некорректный
+* на лету
+* стандарт
+* по умолчанию
+* чувствительный к регистру
+* нечувствительный к регистру
+
+* обслуживать приложение
+* отдавать страницу
+
+* приложение
+* приложение
+
+* HTTP-запрос
+* HTTP-ответ
+* ответ с ошибкой
+
+* операция пути
+* декоратор операции пути
+* функция-обработчик пути
+
+* тело
+* тело запроса
+* тело ответа
+* JSON-тело
+* тело формы
+* тело файла
+* тело функции
+
+* параметр
+* body-параметр
+* path-параметр
+* query-параметр
+* cookie-параметр
+* параметр заголовка
+* параметр формы
+* параметр функции
+
+* событие
+* событие запуска
+* запуск сервера
+* событие остановки
+* событие lifespan
+
+* обработчик
+* обработчик события
+* обработчик исключений
+* обрабатывать
+
+* модель
+* Pydantic-модель
+* модель данных
+* модель базы данных
+* модель формы
+* объект модели
+
+* класс
+* базовый класс
+* родительский класс
+* подкласс
+* дочерний класс
+* родственный класс
+* метод класса
+
+* заголовок
+* HTTP-заголовки
+* заголовок авторизации
+* заголовок `Authorization`
+* заголовок `Forwarded`
+
+* система внедрения зависимостей
+* зависимость
+* зависимый объект
+* зависимый
+
+* ограниченный вводом/выводом
+* ограниченный процессором
+* конкурентность
+* параллелизм
+* многопроцессность
+
+* переменная окружения
+* переменная окружения
+* `PATH`
+* переменная `PATH`
+
+* аутентификация
+* провайдер аутентификации
+* авторизация
+* форма авторизации
+* провайдер авторизации
+* пользователь аутентифицируется
+* система аутентифицирует пользователя
+
+* CLI
+* интерфейс командной строки
+
+* сервер
+* клиент
+
+* облачный провайдер
+* облачный сервис
+
+* разработка
+* этапы разработки
+
+* dict
+* словарь
+* перечисление
+* enum
+* член перечисления
+
+* кодировщик
+* декодировщик
+* кодировать
+* декодировать
+
+* исключение
+* вызвать
+
+* выражение
+* оператор
+
+* фронтенд
+* бэкенд
+
+* обсуждение на GitHub
+* Issue на GitHub (тикет/обращение)
+
+* производительность
+* оптимизация производительности
+
+* тип возвращаемого значения
+* возвращаемое значение
+
+* безопасность
+* схема безопасности
+
+* задача
+* фоновая задача
+* функция задачи
+
+* шаблон
+* шаблонизатор
+
+* аннотация типов
+* аннотация типов
+
+* воркер сервера
+* воркер Uvicorn
+* воркер Gunicorn
+* воркер-процесс
+* класс воркера
+* рабочая нагрузка
+
+* деплой
+* развернуть
+
+* SDK
+* набор средств разработки ПО
+
+* `APIRouter`
+* `requirements.txt`
+* токен Bearer
+* несовместимое изменение
+* баг
+* кнопка
+* вызываемый объект
+* код
+* коммит
+* менеджер контекста
+* корутина
+* сессия базы данных
+* диск
+* домен
+* движок
+* фиктивный X
+* метод HTTP GET
+* элемент
+* библиотека
+* lifespan
+* блокировка
+* middleware (Промежуточный слой)
+* мобильное приложение
+* модуль
+* монтирование
+* сеть
+* origin (источник)
+* переопределение
+* полезная нагрузка
+* процессор
+* свойство
+* прокси
+* пулл-реквест (запрос на изменение)
+* запрос
+* ОЗУ
+* удалённая машина
+* статус-код
+* строка
+* тег
+* веб‑фреймворк
+* подстановочный знак
+* вернуть
+* валидировать
+
+////
+
+//// tab | Информация
+
+Это неполный и ненормативный список (в основном) технических терминов, встречающихся в документации. Он может помочь автору промпта понять, по каким терминам LLM нужна подсказка. Например, когда она продолжает возвращать действительно хороший перевод к неоптимальному. Или когда у неё возникают проблемы со склонением/спряжением термина на вашем языке.
+
+См., например, раздел `### List of English terms and their preferred German translations` в `docs/de/llm-prompt.md`.
+
+////
diff --git a/docs/ru/docs/about/index.md b/docs/ru/docs/about/index.md
index 1015b667a8..4f48266a7a 100644
--- a/docs/ru/docs/about/index.md
+++ b/docs/ru/docs/about/index.md
@@ -1,3 +1,3 @@
-# О проекте
+# О проекте { #about }
-FastAPI: внутреннее устройство, повлиявшие технологии и всё такое прочее. 🤓
+О FastAPI, его дизайне, источниках вдохновения и многом другом. 🤓
diff --git a/docs/ru/docs/advanced/additional-responses.md b/docs/ru/docs/advanced/additional-responses.md
new file mode 100644
index 0000000000..c63c0c08b0
--- /dev/null
+++ b/docs/ru/docs/advanced/additional-responses.md
@@ -0,0 +1,247 @@
+# Дополнительные ответы в OpenAPI { #additional-responses-in-openapi }
+
+/// warning | Предупреждение
+
+Это довольно продвинутая тема.
+
+Если вы только начинаете работать с **FastAPI**, возможно, вам это пока не нужно.
+
+///
+
+Вы можете объявлять дополнительные ответы с дополнительными статус-кодами, типами содержимого, описаниями и т.д.
+
+Эти дополнительные ответы будут включены в схему OpenAPI, и поэтому появятся в документации API.
+
+Но для таких дополнительных ответов убедитесь, что вы возвращаете `Response`, например `JSONResponse`, напрямую, со своим статус-кодом и содержимым.
+
+## Дополнительный ответ с `model` { #additional-response-with-model }
+
+Вы можете передать вашим декораторам операции пути параметр `responses`.
+
+Он принимает `dict`: ключи — это статус-коды для каждого ответа (например, `200`), а значения — другие `dict` с информацией для каждого из них.
+
+Каждый из этих `dict` для ответа может иметь ключ `model`, содержащий Pydantic-модель, аналогично `response_model`.
+
+**FastAPI** возьмёт эту модель, сгенерирует для неё JSON‑схему и включит её в нужное место в OpenAPI.
+
+Например, чтобы объявить ещё один ответ со статус-кодом `404` и Pydantic-моделью `Message`, можно написать:
+
+{* ../../docs_src/additional_responses/tutorial001.py hl[18,22] *}
+
+/// note | Примечание
+
+Имейте в виду, что необходимо возвращать `JSONResponse` напрямую.
+
+///
+
+/// info | Информация
+
+Ключ `model` не является частью OpenAPI.
+
+**FastAPI** возьмёт Pydantic-модель оттуда, сгенерирует JSON‑схему и поместит её в нужное место.
+
+Нужное место:
+
+* В ключе `content`, значением которого является другой JSON‑объект (`dict`), содержащий:
+ * Ключ с типом содержимого, например `application/json`, значением которого является другой JSON‑объект, содержащий:
+ * Ключ `schema`, значением которого является JSON‑схема из модели — вот нужное место.
+ * **FastAPI** добавляет здесь ссылку на глобальные JSON‑схемы в другом месте вашего OpenAPI вместо того, чтобы включать схему напрямую. Так другие приложения и клиенты смогут использовать эти JSON‑схемы напрямую, предоставлять лучшие инструменты генерации кода и т.д.
+
+///
+
+Сгенерированные в OpenAPI ответы для этой операции пути будут такими:
+
+```JSON hl_lines="3-12"
+{
+ "responses": {
+ "404": {
+ "description": "Additional Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Message"
+ }
+ }
+ }
+ },
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Item"
+ }
+ }
+ }
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ }
+ }
+ }
+}
+```
+
+Схемы даны как ссылки на другое место внутри схемы OpenAPI:
+
+```JSON hl_lines="4-16"
+{
+ "components": {
+ "schemas": {
+ "Message": {
+ "title": "Message",
+ "required": [
+ "message"
+ ],
+ "type": "object",
+ "properties": {
+ "message": {
+ "title": "Message",
+ "type": "string"
+ }
+ }
+ },
+ "Item": {
+ "title": "Item",
+ "required": [
+ "id",
+ "value"
+ ],
+ "type": "object",
+ "properties": {
+ "id": {
+ "title": "Id",
+ "type": "string"
+ },
+ "value": {
+ "title": "Value",
+ "type": "string"
+ }
+ }
+ },
+ "ValidationError": {
+ "title": "ValidationError",
+ "required": [
+ "loc",
+ "msg",
+ "type"
+ ],
+ "type": "object",
+ "properties": {
+ "loc": {
+ "title": "Location",
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "msg": {
+ "title": "Message",
+ "type": "string"
+ },
+ "type": {
+ "title": "Error Type",
+ "type": "string"
+ }
+ }
+ },
+ "HTTPValidationError": {
+ "title": "HTTPValidationError",
+ "type": "object",
+ "properties": {
+ "detail": {
+ "title": "Detail",
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/ValidationError"
+ }
+ }
+ }
+ }
+ }
+ }
+}
+```
+
+## Дополнительные типы содержимого для основного ответа { #additional-media-types-for-the-main-response }
+
+Вы можете использовать этот же параметр `responses`, чтобы добавить разные типы содержимого для того же основного ответа.
+
+Например, вы можете добавить дополнительный тип содержимого `image/png`, объявив, что ваша операция пути может возвращать JSON‑объект (с типом содержимого `application/json`) или PNG‑изображение:
+
+{* ../../docs_src/additional_responses/tutorial002.py hl[19:24,28] *}
+
+/// note | Примечание
+
+Учтите, что изображение нужно возвращать напрямую, используя `FileResponse`.
+
+///
+
+/// info | Информация
+
+Если вы явно не укажете другой тип содержимого в параметре `responses`, FastAPI будет считать, что ответ имеет тот же тип содержимого, что и основной класс ответа (по умолчанию `application/json`).
+
+Но если вы указали пользовательский класс ответа с `None` в качестве его типа содержимого, FastAPI использует `application/json` для любого дополнительного ответа, у которого есть связанная модель.
+
+///
+
+## Комбинирование информации { #combining-information }
+
+Вы также можете комбинировать информацию об ответах из нескольких мест, включая параметры `response_model`, `status_code` и `responses`.
+
+Вы можете объявить `response_model`, используя статус-код по умолчанию `200` (или свой, если нужно), а затем объявить дополнительную информацию для этого же ответа в `responses`, напрямую в схеме OpenAPI.
+
+**FastAPI** сохранит дополнительную информацию из `responses` и объединит её с JSON‑схемой из вашей модели.
+
+Например, вы можете объявить ответ со статус-кодом `404`, который использует Pydantic-модель и имеет пользовательское `description`.
+
+А также ответ со статус-кодом `200`, который использует ваш `response_model`, но включает пользовательский `example`:
+
+{* ../../docs_src/additional_responses/tutorial003.py hl[20:31] *}
+
+Всё это будет объединено и включено в ваш OpenAPI и отображено в документации API:
+
+
+
+## Комбинирование предопределённых и пользовательских ответов { #combine-predefined-responses-and-custom-ones }
+
+Возможно, вы хотите иметь некоторые предопределённые ответы, применимые ко многим операциям пути, но при этом комбинировать их с пользовательскими ответами, необходимыми для каждой конкретной операции пути.
+
+В таких случаях вы можете использовать приём Python «распаковки» `dict` с помощью `**dict_to_unpack`:
+
+```Python
+old_dict = {
+ "old key": "old value",
+ "second old key": "second old value",
+}
+new_dict = {**old_dict, "new key": "new value"}
+```
+
+Здесь `new_dict` будет содержать все пары ключ-значение из `old_dict` плюс новую пару ключ-значение:
+
+```Python
+{
+ "old key": "old value",
+ "second old key": "second old value",
+ "new key": "new value",
+}
+```
+
+Вы можете использовать этот приём, чтобы переиспользовать некоторые предопределённые ответы в ваших операциях пути и комбинировать их с дополнительными пользовательскими.
+
+Например:
+
+{* ../../docs_src/additional_responses/tutorial004.py hl[13:17,26] *}
+
+## Дополнительная информация об ответах OpenAPI { #more-information-about-openapi-responses }
+
+Чтобы увидеть, что именно можно включать в ответы, посмотрите эти разделы спецификации OpenAPI:
+
+* Объект Responses OpenAPI, он включает `Response Object`.
+* Объект Response OpenAPI, вы можете включить всё из этого объекта напрямую в каждый ответ внутри вашего параметра `responses`. Включая `description`, `headers`, `content` (внутри него вы объявляете разные типы содержимого и JSON‑схемы) и `links`.
diff --git a/docs/ru/docs/advanced/additional-status-codes.md b/docs/ru/docs/advanced/additional-status-codes.md
new file mode 100644
index 0000000000..7c73cf5d5d
--- /dev/null
+++ b/docs/ru/docs/advanced/additional-status-codes.md
@@ -0,0 +1,41 @@
+# Дополнительные статус-коды { #additional-status-codes }
+
+По умолчанию **FastAPI** будет возвращать ответы, используя `JSONResponse`, помещая содержимое, которое вы возвращаете из вашей *операции пути*, внутрь этого `JSONResponse`.
+
+Он будет использовать статус-код по умолчанию или тот, который вы укажете в вашей *операции пути*.
+
+## Дополнительные статус-коды { #additional-status-codes_1 }
+
+Если вы хотите возвращать дополнительные статус-коды помимо основного, вы можете сделать это, возвращая `Response` напрямую, например `JSONResponse`, и устанавливая дополнительный статус-код напрямую.
+
+Например, предположим, что вы хотите иметь *операцию пути*, которая позволяет обновлять элементы и возвращает HTTP статус-код 200 «OK» при успешном выполнении.
+
+Но вы также хотите, чтобы она принимала новые элементы. И если элементы ранее не существовали, она создаёт их и возвращает HTTP статус-код 201 «Created».
+
+Чтобы добиться этого, импортируйте `JSONResponse` и верните туда свой контент напрямую, установив нужный вам `status_code`:
+
+{* ../../docs_src/additional_status_codes/tutorial001_an_py310.py hl[4,25] *}
+
+/// warning | Внимание
+
+Когда вы возвращаете `Response` напрямую, как в примере выше, он будет возвращён как есть.
+
+Он не будет сериализован с помощью модели и т.п.
+
+Убедитесь, что в нём именно те данные, которые вы хотите, и что значения являются валидным JSON (если вы используете `JSONResponse`).
+
+///
+
+/// note | Технические детали
+
+Вы также можете использовать `from starlette.responses import JSONResponse`.
+
+**FastAPI** предоставляет тот же `starlette.responses` через `fastapi.responses` просто для вашего удобства как разработчика. Но большинство доступных Response-классов приходят напрямую из Starlette. То же самое со `status`.
+
+///
+
+## OpenAPI и документация API { #openapi-and-api-docs }
+
+Если вы возвращаете дополнительные статус-коды и ответы напрямую, они не будут включены в схему OpenAPI (документацию API), потому что у FastAPI нет способа заранее знать, что вы собираетесь вернуть.
+
+Но вы можете задокументировать это в своём коде, используя: [Дополнительные ответы](additional-responses.md){.internal-link target=_blank}.
diff --git a/docs/ru/docs/advanced/advanced-dependencies.md b/docs/ru/docs/advanced/advanced-dependencies.md
new file mode 100644
index 0000000000..75a6f0d1fa
--- /dev/null
+++ b/docs/ru/docs/advanced/advanced-dependencies.md
@@ -0,0 +1,153 @@
+# Продвинутые зависимости { #advanced-dependencies }
+
+## Параметризованные зависимости { #parameterized-dependencies }
+
+Все зависимости, которые мы видели, — это конкретная функция или класс.
+
+Но бывают случаи, когда нужно задавать параметры зависимости, не объявляя много разных функций или классов.
+
+Представим, что нам нужна зависимость, которая проверяет, содержит ли query-параметр `q` некоторое фиксированное содержимое.
+
+Но при этом мы хотим иметь возможность параметризовать это фиксированное содержимое.
+
+## «Вызываемый» экземпляр { #a-callable-instance }
+
+В Python есть способ сделать экземпляр класса «вызываемым» объектом.
+
+Не сам класс (он уже является вызываемым), а экземпляр этого класса.
+
+Для этого объявляем метод `__call__`:
+
+{* ../../docs_src/dependencies/tutorial011_an_py39.py hl[12] *}
+
+В этом случае именно `__call__` **FastAPI** использует для проверки дополнительных параметров и подзависимостей, и именно он будет вызван, чтобы позже передать значение параметру в вашей *функции-обработчике пути*.
+
+## Параметризуем экземпляр { #parameterize-the-instance }
+
+Теперь мы можем использовать `__init__`, чтобы объявить параметры экземпляра, с помощью которых будем «параметризовать» зависимость:
+
+{* ../../docs_src/dependencies/tutorial011_an_py39.py hl[9] *}
+
+В этом случае **FastAPI** вовсе не трогает `__init__` и не зависит от него — мы используем его напрямую в нашем коде.
+
+## Создаём экземпляр { #create-an-instance }
+
+Мы можем создать экземпляр этого класса так:
+
+{* ../../docs_src/dependencies/tutorial011_an_py39.py hl[18] *}
+
+Так мы «параметризуем» нашу зависимость: теперь внутри неё хранится "bar" в атрибуте `checker.fixed_content`.
+
+## Используем экземпляр как зависимость { #use-the-instance-as-a-dependency }
+
+Затем мы можем использовать этот `checker` в `Depends(checker)` вместо `Depends(FixedContentQueryChecker)`, потому что зависимостью является экземпляр `checker`, а не сам класс.
+
+И при разрешении зависимости **FastAPI** вызовет `checker` примерно так:
+
+```Python
+checker(q="somequery")
+```
+
+…и передаст возвращённое значение как значение зависимости в нашу *функцию-обработчике пути* в параметр `fixed_content_included`:
+
+{* ../../docs_src/dependencies/tutorial011_an_py39.py hl[22] *}
+
+/// tip | Совет
+
+Все это может показаться притянутым за уши. И пока может быть не совсем понятно, чем это полезно.
+
+Эти примеры намеренно простые, но они показывают, как всё устроено.
+
+В главах про безопасность есть вспомогательные функции, реализованные тем же способом.
+
+Если вы поняли всё выше, вы уже знаете, как «под капотом» работают эти утилиты для безопасности.
+
+///
+
+## Зависимости с `yield`, `HTTPException`, `except` и фоновыми задачами { #dependencies-with-yield-httpexception-except-and-background-tasks }
+
+/// warning | Предупреждение
+
+Скорее всего, вам не понадобятся эти технические детали.
+
+Они полезны главным образом, если у вас было приложение FastAPI версии ниже 0.118.0 и вы столкнулись с проблемами зависимостей с `yield`.
+
+///
+
+Зависимости с `yield` со временем изменялись, чтобы учитывать разные случаи применения и исправлять проблемы. Ниже — краткое резюме изменений.
+
+### Зависимости с `yield` и `StreamingResponse`, технические детали { #dependencies-with-yield-and-streamingresponse-technical-details }
+
+До FastAPI 0.118.0, если вы использовали зависимость с `yield`, код после `yield` выполнялся после возврата из *функции-обработчика пути*, но прямо перед отправкой ответа.
+
+Идея состояла в том, чтобы не удерживать ресурсы дольше необходимого, пока ответ «путешествует» по сети.
+
+Это изменение также означало, что если вы возвращали `StreamingResponse`, код после `yield` в зависимости уже успевал выполниться.
+
+Например, если у вас была сессия базы данных в зависимости с `yield`, `StreamingResponse` не смог бы использовать эту сессию во время стриминга данных, потому что сессия уже была закрыта в коде после `yield`.
+
+В версии 0.118.0 это поведение было возвращено к тому, что код после `yield` выполняется после отправки ответа.
+
+/// info | Информация
+
+Как вы увидите ниже, это очень похоже на поведение до версии 0.106.0, но с несколькими улучшениями и исправлениями краевых случаев.
+
+///
+
+#### Сценарии с ранним выполнением кода после `yield` { #use-cases-with-early-exit-code }
+
+Есть некоторые сценарии со специфическими условиями, которым могло бы помочь старое поведение — выполнение кода после `yield` перед отправкой ответа.
+
+Например, представьте, что вы используете сессию базы данных в зависимости с `yield` только для проверки пользователя, а в самой *функции-обработчике пути* эта сессия больше не используется, и при этом ответ отправляется долго, например, это `StreamingResponse`, который медленно отправляет данные и по какой-то причине не использует базу данных.
+
+В таком случае сессия базы данных будет удерживаться до завершения отправки ответа, хотя если вы её не используете, удерживать её не требуется.
+
+Это могло бы выглядеть так:
+
+{* ../../docs_src/dependencies/tutorial013_an_py310.py *}
+
+Код после `yield`, автоматическое закрытие `Session` в:
+
+{* ../../docs_src/dependencies/tutorial013_an_py310.py ln[19:21] *}
+
+…будет выполнен после того, как ответ закончит отправку медленных данных:
+
+{* ../../docs_src/dependencies/tutorial013_an_py310.py ln[30:38] hl[31:33] *}
+
+Но поскольку `generate_stream()` не использует сессию базы данных, нет реальной необходимости держать сессию открытой во время отправки ответа.
+
+Если у вас именно такой сценарий с SQLModel (или SQLAlchemy), вы можете явно закрыть сессию, когда она больше не нужна:
+
+{* ../../docs_src/dependencies/tutorial014_an_py310.py ln[24:28] hl[28] *}
+
+Так сессия освободит подключение к базе данных, и другие запросы смогут его использовать.
+
+Если у вас есть другой сценарий, где нужно раннее завершение зависимости с `yield`, пожалуйста, создайте вопрос в GitHub Discussions с описанием конкретного кейса и почему вам было бы полезно иметь раннее закрытие для зависимостей с `yield`.
+
+Если появятся веские причины для раннего закрытия в зависимостях с `yield`, я рассмотрю добавление нового способа опционально включать раннее закрытие.
+
+### Зависимости с `yield` и `except`, технические детали { #dependencies-with-yield-and-except-technical-details }
+
+До FastAPI 0.110.0, если вы использовали зависимость с `yield`, затем перехватывали исключение с `except` в этой зависимости и не пробрасывали исключение снова, исключение автоматически пробрасывалось дальше к обработчикам исключений или к обработчику внутренней ошибки сервера.
+
+В версии 0.110.0 это было изменено, чтобы исправить неконтролируемое потребление памяти из‑за проброшенных исключений без обработчика (внутренние ошибки сервера) и привести поведение в соответствие с обычным поведением Python-кода.
+
+### Фоновые задачи и зависимости с `yield`, технические детали { #background-tasks-and-dependencies-with-yield-technical-details }
+
+До FastAPI 0.106.0 вызывать исключения после `yield` было невозможно: код после `yield` в зависимостях выполнялся уже после отправки ответа, поэтому [Обработчики исключений](../handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank} к тому моменту уже отработали.
+
+Так было сделано в основном для того, чтобы можно было использовать те же объекты, «отданные» зависимостями через `yield`, внутри фоновых задач, потому что код после `yield` выполнялся после завершения фоновых задач.
+
+В FastAPI 0.106.0 это изменили, чтобы не удерживать ресурсы, пока ответ передаётся по сети.
+
+/// tip | Совет
+
+Кроме того, фоновая задача обычно — это самостоятельный фрагмент логики, который следует обрабатывать отдельно, со своими ресурсами (например, со своим подключением к базе данных).
+
+Так код, скорее всего, будет чище.
+
+///
+
+Если вы полагались на прежнее поведение, теперь ресурсы для фоновых задач следует создавать внутри самой фоновой задачи и использовать внутри неё только данные, которые не зависят от ресурсов зависимостей с `yield`.
+
+Например, вместо использования той же сессии базы данных, создайте новую сессию в фоновой задаче и получите объекты из базы данных с помощью этой новой сессии. И затем, вместо передачи объекта из базы данных параметром в функцию фоновой задачи, передавайте идентификатор этого объекта и заново получайте объект внутри функции фоновой задачи.
diff --git a/docs/ru/docs/advanced/async-tests.md b/docs/ru/docs/advanced/async-tests.md
new file mode 100644
index 0000000000..5062bc52e7
--- /dev/null
+++ b/docs/ru/docs/advanced/async-tests.md
@@ -0,0 +1,99 @@
+# Асинхронное тестирование { #async-tests }
+
+Вы уже видели как тестировать **FastAPI** приложение, используя имеющийся класс `TestClient`. К этому моменту вы видели только как писать тесты в синхронном стиле без использования `async` функций.
+
+Возможность использования асинхронных функций в ваших тестах может быть полезнa, когда, например, вы асинхронно обращаетесь к вашей базе данных. Представьте, что вы хотите отправить запросы в ваше FastAPI приложение, а затем при помощи асинхронной библиотеки для работы с базой данных удостовериться, что ваш бекэнд корректно записал данные в базу данных.
+
+Давайте рассмотрим, как мы можем это реализовать.
+
+## pytest.mark.anyio { #pytest-mark-anyio }
+
+Если мы хотим вызывать асинхронные функции в наших тестах, то наши тестовые функции должны быть асинхронными. AnyIO предоставляет для этого отличный плагин, который позволяет нам указывать, какие тестовые функции должны вызываться асинхронно.
+
+## HTTPX { #httpx }
+
+Даже если **FastAPI** приложение использует обычные функции `def` вместо `async def`, это все равно `async` приложение 'под капотом'.
+
+Чтобы работать с асинхронным FastAPI приложением в ваших обычных тестовых функциях `def`, используя стандартный pytest, `TestClient` внутри себя делает некоторую магию. Но эта магия перестает работать, когда мы используем его внутри асинхронных функций. Запуская наши тесты асинхронно, мы больше не можем использовать `TestClient` внутри наших тестовых функций.
+
+`TestClient` основан на HTTPX, и, к счастью, мы можем использовать его (`HTTPX`) напрямую для тестирования API.
+
+## Пример { #example }
+
+В качестве простого примера, давайте рассмотрим файловую структуру, схожую с описанной в [Большие приложения](../tutorial/bigger-applications.md){.internal-link target=_blank} и [Тестирование](../tutorial/testing.md){.internal-link target=_blank}:
+
+```
+.
+├── app
+│ ├── __init__.py
+│ ├── main.py
+│ └── test_main.py
+```
+
+Файл `main.py`:
+
+{* ../../docs_src/async_tests/main.py *}
+
+Файл `test_main.py` содержит тесты для `main.py`, теперь он может выглядеть так:
+
+{* ../../docs_src/async_tests/test_main.py *}
+
+## Запуск тестов { #run-it }
+
+Вы можете запустить свои тесты как обычно:
+
+
+
+А вот если открыть интерфейс документации по «официальному» URL через прокси на порту `9999`, по `/api/v1/docs`, всё работает корректно! 🎉
+
+Проверьте по адресу http://127.0.0.1:9999/api/v1/docs:
+
+
+
+Именно как и хотелось. ✔️
+
+Это потому, что FastAPI использует `root_path`, чтобы создать в OpenAPI сервер по умолчанию с URL из `root_path`.
+
+## Дополнительные серверы { #additional-servers }
+
+/// warning | Предупреждение
+
+Это более продвинутый сценарий. Можно пропустить.
+
+///
+
+По умолчанию FastAPI создаёт в схеме OpenAPI `server` с URL из `root_path`.
+
+Но вы также можете указать дополнительные `servers`, например, если хотите, чтобы один и тот же интерфейс документации работал и со стейджингом, и с продакшн.
+
+Если вы передадите свой список `servers` и при этом задан `root_path` (потому что ваш API работает за прокси), FastAPI вставит «server» с этим `root_path` в начало списка.
+
+Например:
+
+{* ../../docs_src/behind_a_proxy/tutorial003.py hl[4:7] *}
+
+Будет сгенерирована схема OpenAPI примерно такая:
+
+```JSON hl_lines="5-7"
+{
+ "openapi": "3.1.0",
+ // Здесь ещё что-то
+ "servers": [
+ {
+ "url": "/api/v1"
+ },
+ {
+ "url": "https://stag.example.com",
+ "description": "Staging environment"
+ },
+ {
+ "url": "https://prod.example.com",
+ "description": "Production environment"
+ }
+ ],
+ "paths": {
+ // Здесь ещё что-то
+ }
+}
+```
+
+/// tip | Совет
+
+Обратите внимание на автоматически добавленный сервер с `url` равным `/api/v1`, взятым из `root_path`.
+
+///
+
+В интерфейсе документации по адресу http://127.0.0.1:9999/api/v1/docs это будет выглядеть так:
+
+
+
+/// tip | Совет
+
+Интерфейс документации будет взаимодействовать с сервером, который вы выберете.
+
+///
+
+### Отключить автоматическое добавление сервера из `root_path` { #disable-automatic-server-from-root-path }
+
+Если вы не хотите, чтобы FastAPI добавлял автоматический сервер, используя `root_path`, укажите параметр `root_path_in_servers=False`:
+
+{* ../../docs_src/behind_a_proxy/tutorial004.py hl[9] *}
+
+и тогда этот сервер не будет добавлен в схему OpenAPI.
+
+## Монтирование вложенного приложения { #mounting-a-sub-application }
+
+Если вам нужно смонтировать вложенное приложение (как описано в [Вложенные приложения — монтирование](sub-applications.md){.internal-link target=_blank}), и при этом вы используете прокси с `root_path`, делайте это обычным образом — всё будет работать, как ожидается.
+
+FastAPI умно использует `root_path` внутри, так что всё просто работает. ✨
diff --git a/docs/ru/docs/advanced/custom-response.md b/docs/ru/docs/advanced/custom-response.md
new file mode 100644
index 0000000000..2c238bd95b
--- /dev/null
+++ b/docs/ru/docs/advanced/custom-response.md
@@ -0,0 +1,312 @@
+# Кастомные ответы — HTML, поток, файл и другие { #custom-response-html-stream-file-others }
+
+По умолчанию **FastAPI** возвращает ответы с помощью `JSONResponse`.
+
+Вы можете переопределить это, вернув `Response` напрямую, как показано в разделе [Вернуть Response напрямую](response-directly.md){.internal-link target=_blank}.
+
+Но если вы возвращаете `Response` напрямую (или любой его подкласс, например `JSONResponse`), данные не будут автоматически преобразованы (даже если вы объявили `response_model`), и документация не будет автоматически сгенерирована (например, со специфичным «типом содержимого» в HTTP-заголовке `Content-Type` как частью сгенерированного OpenAPI).
+
+Но вы можете также объявить `Response`, который хотите использовать (например, любой подкласс `Response`), в декораторе операции пути, используя параметр `response_class`.
+
+Содержимое, которое вы возвращаете из своей функции-обработчика пути, будет помещено внутрь этого `Response`.
+
+И если у этого `Response` тип содержимого JSON (`application/json`), как в случае с `JSONResponse` и `UJSONResponse`, данные, которые вы возвращаете, будут автоматически преобразованы (и отфильтрованы) любым объявленным вами в декораторе операции пути Pydantic `response_model`.
+
+/// note | Примечание
+
+Если вы используете класс ответа без типа содержимого, FastAPI будет ожидать, что у вашего ответа нет содержимого, поэтому он не будет документировать формат ответа в сгенерированной документации OpenAPI.
+
+///
+
+## Используйте `ORJSONResponse` { #use-orjsonresponse }
+
+Например, если вы выжимаете максимум производительности, вы можете установить и использовать `orjson` и задать ответ как `ORJSONResponse`.
+
+Импортируйте класс (подкласс) `Response`, который вы хотите использовать, и объявите его в декораторе операции пути.
+
+Для больших ответов возвращать `Response` напрямую значительно быстрее, чем возвращать словарь.
+
+Это потому, что по умолчанию FastAPI проверяет каждый элемент внутри и убеждается, что он сериализуем в JSON, используя тот же [JSON Compatible Encoder](../tutorial/encoder.md){.internal-link target=_blank}, объяснённый в руководстве. Это позволяет возвращать **произвольные объекты**, например модели из базы данных.
+
+Но если вы уверены, что содержимое, которое вы возвращаете, **сериализуемо в JSON**, вы можете передать его напрямую в класс ответа и избежать дополнительных накладных расходов, которые FastAPI понёс бы, пропуская возвращаемое содержимое через `jsonable_encoder` перед передачей в класс ответа.
+
+{* ../../docs_src/custom_response/tutorial001b.py hl[2,7] *}
+
+/// info | Информация
+
+Параметр `response_class` также используется для указания «типа содержимого» ответа.
+
+В этом случае HTTP-заголовок `Content-Type` будет установлен в `application/json`.
+
+И это будет задокументировано как таковое в OpenAPI.
+
+///
+
+/// tip | Совет
+
+`ORJSONResponse` доступен только в FastAPI, а не в Starlette.
+
+///
+
+## HTML-ответ { #html-response }
+
+Чтобы вернуть ответ с HTML напрямую из **FastAPI**, используйте `HTMLResponse`.
+
+- Импортируйте `HTMLResponse`.
+- Передайте `HTMLResponse` в параметр `response_class` вашего декоратора операции пути.
+
+{* ../../docs_src/custom_response/tutorial002.py hl[2,7] *}
+
+/// info | Информация
+
+Параметр `response_class` также используется для указания «типа содержимого» ответа.
+
+В этом случае HTTP-заголовок `Content-Type` будет установлен в `text/html`.
+
+И это будет задокументировано как таковое в OpenAPI.
+
+///
+
+### Вернуть `Response` { #return-a-response }
+
+Как показано в разделе [Вернуть Response напрямую](response-directly.md){.internal-link target=_blank}, вы также можете переопределить ответ прямо в своей операции пути, просто вернув его.
+
+Тот же пример сверху, возвращающий `HTMLResponse`, может выглядеть так:
+
+{* ../../docs_src/custom_response/tutorial003.py hl[2,7,19] *}
+
+/// warning | Предупреждение
+
+`Response`, возвращённый напрямую вашей функцией-обработчиком пути, не будет задокументирован в OpenAPI (например, `Content-Type` нне будет задокументирова) и не будет виден в автоматически сгенерированной интерактивной документации.
+
+///
+
+/// info | Информация
+
+Разумеется, фактические заголовок `Content-Type`, статус-код и т.д. возьмутся из объекта `Response`, который вы вернули.
+
+///
+
+### Задокументировать в OpenAPI и переопределить `Response` { #document-in-openapi-and-override-response }
+
+Если вы хотите переопределить ответ внутри функции, но при этом задокументировать «тип содержимого» в OpenAPI, вы можете использовать параметр `response_class` И вернуть объект `Response`.
+
+Тогда `response_class` будет использоваться только для документирования *операции пути* в OpenAPI, а ваш `Response` будет использован как есть.
+
+#### Вернуть `HTMLResponse` напрямую { #return-an-htmlresponse-directly }
+
+Например, это может быть что-то вроде:
+
+{* ../../docs_src/custom_response/tutorial004.py hl[7,21,23] *}
+
+В этом примере функция `generate_html_response()` уже генерирует и возвращает `Response` вместо возврата HTML в `str`.
+
+Возвращая результат вызова `generate_html_response()`, вы уже возвращаете `Response`, который переопределит поведение **FastAPI** по умолчанию.
+
+Но поскольку вы также передали `HTMLResponse` в `response_class`, **FastAPI** будет знать, как задокументировать это в OpenAPI и интерактивной документации как HTML с `text/html`:
+
+
+
+## Доступные ответы { #available-responses }
+
+Ниже перечислены некоторые доступные классы ответов.
+
+Учтите, что вы можете использовать `Response`, чтобы вернуть что угодно ещё, или даже создать собственный подкласс.
+
+/// note | Технические детали
+
+Вы также могли бы использовать `from starlette.responses import HTMLResponse`.
+
+**FastAPI** предоставляет те же `starlette.responses` как `fastapi.responses` для вашего удобства как разработчика. Но большинство доступных классов ответов приходят непосредственно из Starlette.
+
+///
+
+### `Response` { #response }
+
+Базовый класс `Response`, от него наследуются все остальные ответы.
+
+Его можно возвращать напрямую.
+
+Он принимает следующие параметры:
+
+- `content` — `str` или `bytes`.
+- `status_code` — целое число, HTTP статус-код.
+- `headers` — словарь строк.
+- `media_type` — строка, задающая тип содержимого. Например, `"text/html"`.
+
+FastAPI (фактически Starlette) автоматически добавит заголовок Content-Length. Также будет добавлен заголовок Content-Type, основанный на `media_type` и с добавлением charset для текстовых типов.
+
+{* ../../docs_src/response_directly/tutorial002.py hl[1,18] *}
+
+### `HTMLResponse` { #htmlresponse }
+
+Принимает текст или байты и возвращает HTML-ответ, как описано выше.
+
+### `PlainTextResponse` { #plaintextresponse }
+
+Принимает текст или байты и возвращает ответ в виде простого текста.
+
+{* ../../docs_src/custom_response/tutorial005.py hl[2,7,9] *}
+
+### `JSONResponse` { #jsonresponse }
+
+Принимает данные и возвращает ответ, кодированный как `application/json`.
+
+Это ответ по умолчанию, используемый в **FastAPI**, как было сказано выше.
+
+### `ORJSONResponse` { #orjsonresponse }
+
+Быстрая альтернативная реализация JSON-ответа с использованием `orjson`, как было сказано выше.
+
+/// info | Информация
+
+Требуется установка `orjson`, например командой `pip install orjson`.
+
+///
+
+### `UJSONResponse` { #ujsonresponse }
+
+Альтернативная реализация JSON-ответа с использованием `ujson`.
+
+/// info | Информация
+
+Требуется установка `ujson`, например командой `pip install ujson`.
+
+///
+
+/// warning | Предупреждение
+
+`ujson` менее аккуратен, чем встроенная реализация Python, в обработке некоторых крайних случаев.
+
+///
+
+{* ../../docs_src/custom_response/tutorial001.py hl[2,7] *}
+
+/// tip | Совет
+
+Возможно, `ORJSONResponse` окажется более быстрым вариантом.
+
+///
+
+### `RedirectResponse` { #redirectresponse }
+
+Возвращает HTTP-редирект. По умолчанию использует статус-код 307 (Temporary Redirect — временное перенаправление).
+
+Вы можете вернуть `RedirectResponse` напрямую:
+
+{* ../../docs_src/custom_response/tutorial006.py hl[2,9] *}
+
+---
+
+Или можно использовать его в параметре `response_class`:
+
+{* ../../docs_src/custom_response/tutorial006b.py hl[2,7,9] *}
+
+Если вы сделаете так, то сможете возвращать URL напрямую из своей функции-обработчика пути.
+
+В этом случае будет использован статус-код по умолчанию для `RedirectResponse`, то есть `307`.
+
+---
+
+Также вы можете использовать параметр `status_code` в сочетании с параметром `response_class`:
+
+{* ../../docs_src/custom_response/tutorial006c.py hl[2,7,9] *}
+
+### `StreamingResponse` { #streamingresponse }
+
+Принимает асинхронный генератор или обычный генератор/итератор и отправляет тело ответа потоково.
+
+{* ../../docs_src/custom_response/tutorial007.py hl[2,14] *}
+
+#### Использование `StreamingResponse` с файлоподобными объектами { #using-streamingresponse-with-file-like-objects }
+
+Если у вас есть файлоподобный объект (например, объект, возвращаемый `open()`), вы можете создать функцию-генератор для итерации по этому файлоподобному объекту.
+
+Таким образом, вам не нужно сначала читать всё в память, вы можете передать эту функцию-генератор в `StreamingResponse` и вернуть его.
+
+Это включает многие библиотеки для работы с облачным хранилищем, обработки видео и т.д.
+
+{* ../../docs_src/custom_response/tutorial008.py hl[2,10:12,14] *}
+
+1. Это функция-генератор. Она является «функцией-генератором», потому что содержит оператор(ы) `yield` внутри.
+2. Используя блок `with`, мы гарантируем, что файлоподобный объект будет закрыт после завершения работы функции-генератора. То есть после того, как она закончит отправку ответа.
+3. Этот `yield from` говорит функции итерироваться по объекту с именем `file_like`. И затем, для каждой итерации, отдавать эту часть как исходящую из этой функции-генератора (`iterfile`).
+
+ Таким образом, это функция-генератор, которая внутренне передаёт работу по «генерации» чему-то другому.
+
+ Делая это таким образом, мы можем поместить её в блок `with` и тем самым гарантировать, что файлоподобный объект будет закрыт после завершения.
+
+/// tip | Совет
+
+Заметьте, что здесь мы используем стандартный `open()`, который не поддерживает `async` и `await`, поэтому объявляем операцию пути обычной `def`.
+
+///
+
+### `FileResponse` { #fileresponse }
+
+Асинхронно отправляет файл как ответ.
+
+Для создания экземпляра принимает иной набор аргументов, чем другие типы ответов:
+
+- `path` — путь к файлу, который будет отправлен.
+- `headers` — любые дополнительные заголовки для включения, в виде словаря.
+- `media_type` — строка, задающая тип содержимого. Если не задан, для определения типа содержимого будет использовано имя файла или путь.
+- `filename` — если задан, будет включён в заголовок ответа `Content-Disposition`.
+
+Файловые ответы будут содержать соответствующие заголовки `Content-Length`, `Last-Modified` и `ETag`.
+
+{* ../../docs_src/custom_response/tutorial009.py hl[2,10] *}
+
+Вы также можете использовать параметр `response_class`:
+
+{* ../../docs_src/custom_response/tutorial009b.py hl[2,8,10] *}
+
+В этом случае вы можете возвращать путь к файлу напрямую из своей функции-обработчика пути.
+
+## Пользовательский класс ответа { #custom-response-class }
+
+Вы можете создать собственный класс ответа, унаследовавшись от `Response`, и использовать его.
+
+Например, предположим, что вы хотите использовать `orjson`, но с некоторыми пользовательскими настройками, которые не используются во встроенном классе `ORJSONResponse`.
+
+Скажем, вы хотите, чтобы возвращался отформатированный JSON с отступами, то есть хотите использовать опцию orjson `orjson.OPT_INDENT_2`.
+
+Вы могли бы создать `CustomORJSONResponse`. Главное, что вам нужно сделать — реализовать метод `Response.render(content)`, который возвращает содержимое как `bytes`:
+
+{* ../../docs_src/custom_response/tutorial009c.py hl[9:14,17] *}
+
+Теперь вместо того, чтобы возвращать:
+
+```json
+{"message": "Hello World"}
+```
+
+...этот ответ вернёт:
+
+```json
+{
+ "message": "Hello World"
+}
+```
+
+Разумеется, вы наверняка найдёте гораздо более полезные способы воспользоваться этим, чем просто форматирование JSON. 😉
+
+## Класс ответа по умолчанию { #default-response-class }
+
+При создании экземпляра класса **FastAPI** или `APIRouter` вы можете указать, какой класс ответа использовать по умолчанию.
+
+Параметр, который это определяет, — `default_response_class`.
+
+В примере ниже **FastAPI** будет использовать `ORJSONResponse` по умолчанию во всех операциях пути вместо `JSONResponse`.
+
+{* ../../docs_src/custom_response/tutorial010.py hl[2,4] *}
+
+/// tip | Совет
+
+Вы по-прежнему можете переопределять `response_class` в операциях пути, как и раньше.
+
+///
+
+## Дополнительная документация { #additional-documentation }
+
+Вы также можете объявить тип содержимого и многие другие детали в OpenAPI с помощью `responses`: [Дополнительные ответы в OpenAPI](additional-responses.md){.internal-link target=_blank}.
diff --git a/docs/ru/docs/advanced/dataclasses.md b/docs/ru/docs/advanced/dataclasses.md
new file mode 100644
index 0000000000..816f744048
--- /dev/null
+++ b/docs/ru/docs/advanced/dataclasses.md
@@ -0,0 +1,95 @@
+# Использование dataclasses { #using-dataclasses }
+
+FastAPI построен поверх **Pydantic**, и я показывал вам, как использовать Pydantic-модели для объявления HTTP-запросов и HTTP-ответов.
+
+Но FastAPI также поддерживает использование `dataclasses` тем же способом:
+
+{* ../../docs_src/dataclasses/tutorial001.py hl[1,7:12,19:20] *}
+
+Это по-прежнему поддерживается благодаря **Pydantic**, так как в нём есть встроенная поддержка `dataclasses`.
+
+Так что даже если в коде выше Pydantic не используется явно, FastAPI использует Pydantic, чтобы конвертировать стандартные dataclasses в собственный вариант dataclasses от Pydantic.
+
+И, конечно, поддерживаются те же возможности:
+
+- валидация данных
+- сериализация данных
+- документирование данных и т.д.
+
+Это работает так же, как с Pydantic-моделями. И на самом деле под капотом это достигается тем же образом, с использованием Pydantic.
+
+/// info | Информация
+
+Помните, что dataclasses не умеют всего того, что умеют Pydantic-модели.
+
+Поэтому вам всё ещё может потребоваться использовать Pydantic-модели.
+
+Но если у вас уже есть набор dataclasses, это полезный приём — задействовать их для веб-API на FastAPI. 🤓
+
+///
+
+## Dataclasses в `response_model` { #dataclasses-in-response-model }
+
+Вы также можете использовать `dataclasses` в параметре `response_model`:
+
+{* ../../docs_src/dataclasses/tutorial002.py hl[1,7:13,19] *}
+
+Этот dataclass будет автоматически преобразован в Pydantic dataclass.
+
+Таким образом, его схема появится в интерфейсе документации API:
+
+
+
+## Dataclasses во вложенных структурах данных { #dataclasses-in-nested-data-structures }
+
+Вы также можете комбинировать `dataclasses` с другими аннотациями типов, чтобы создавать вложенные структуры данных.
+
+В некоторых случаях вам всё же может понадобиться использовать версию `dataclasses` из Pydantic. Например, если у вас возникают ошибки с автоматически генерируемой документацией API.
+
+В таком случае вы можете просто заменить стандартные `dataclasses` на `pydantic.dataclasses`, которая является полностью совместимой заменой (drop-in replacement):
+
+{* ../../docs_src/dataclasses/tutorial003.py hl[1,5,8:11,14:17,23:25,28] *}
+
+1. Мы по-прежнему импортируем `field` из стандартных `dataclasses`.
+
+2. `pydantic.dataclasses` — полностью совместимая замена (drop-in replacement) для `dataclasses`.
+
+3. Dataclass `Author` содержит список dataclass `Item`.
+
+4. Dataclass `Author` используется в параметре `response_model`.
+
+5. Вы можете использовать и другие стандартные аннотации типов вместе с dataclasses в качестве тела запроса.
+
+ В этом случае это список dataclass `Item`.
+
+6. Здесь мы возвращаем словарь, содержащий `items`, который является списком dataclass.
+
+ FastAPI по-прежнему способен сериализовать данные в JSON.
+
+7. Здесь `response_model` использует аннотацию типа — список dataclass `Author`.
+
+ Снова, вы можете комбинировать `dataclasses` со стандартными аннотациями типов.
+
+8. Обратите внимание, что эта *функция-обработчик пути* использует обычный `def` вместо `async def`.
+
+ Как и всегда в FastAPI, вы можете сочетать `def` и `async def` по необходимости.
+
+ Если хотите освежить в памяти, когда что использовать, посмотрите раздел _"Нет времени?"_ в документации про [`async` и `await`](../async.md#in-a-hurry){.internal-link target=_blank}.
+
+9. Эта *функция-обработчик пути* возвращает не dataclasses (хотя могла бы), а список словарей с внутренними данными.
+
+ FastAPI использует параметр `response_model` (в котором заданы dataclasses), чтобы преобразовать HTTP-ответ.
+
+Вы можете комбинировать `dataclasses` с другими аннотациями типов множеством способов, чтобы формировать сложные структуры данных.
+
+Смотрите подсказки в коде выше, чтобы увидеть более конкретные детали.
+
+## Узнать больше { #learn-more }
+
+Вы также можете комбинировать `dataclasses` с другими Pydantic-моделями, наследоваться от них, включать их в свои модели и т.д.
+
+Чтобы узнать больше, посмотрите документацию Pydantic о dataclasses.
+
+## Версия { #version }
+
+Доступно начиная с версии FastAPI `0.67.0`. 🔖
diff --git a/docs/ru/docs/advanced/events.md b/docs/ru/docs/advanced/events.md
new file mode 100644
index 0000000000..20d1df98a9
--- /dev/null
+++ b/docs/ru/docs/advanced/events.md
@@ -0,0 +1,165 @@
+# События lifespan { #lifespan-events }
+
+Вы можете определить логику (код), которую нужно выполнить перед тем, как приложение начнет запускаться. Это означает, что этот код будет выполнен один раз, перед тем как приложение начнет получать HTTP-запросы.
+
+Аналогично, вы можете определить логику (код), которую нужно выполнить, когда приложение завершает работу. В этом случае код будет выполнен один раз, после обработки, возможно, многих запросов.
+
+Поскольку этот код выполняется до того, как приложение начинает принимать запросы, и сразу после того, как оно заканчивает их обрабатывать, он охватывает весь lifespan (жизненный цикл) приложения (слово «lifespan» станет важным через секунду 😉).
+
+Это может быть очень полезно для настройки ресурсов, которые нужны для всего приложения, которые разделяются между запросами и/или которые нужно затем очистить. Например, пул подключений к базе данных или загрузка общей модели Машинного обучения.
+
+## Вариант использования { #use-case }
+
+Начнем с примера варианта использования, а затем посмотрим, как это решить.
+
+Представим, что у вас есть несколько моделей Машинного обучения, которые вы хотите использовать для обработки запросов. 🤖
+
+Эти же модели разделяются между запросами, то есть это не одна модель на запрос, не одна на пользователя и т.п.
+
+Представим, что загрузка модели может занимать довольно много времени, потому что ей нужно прочитать много данных с диска. Поэтому вы не хотите делать это для каждого запроса.
+
+Вы могли бы загрузить её на верхнем уровне модуля/файла, но это означало бы, что модель загружается даже если вы просто запускаете простой автоматический тест; тогда этот тест будет медленным, так как ему придется ждать загрузки модели перед запуском независимой части кода.
+
+Именно это мы и решим: давайте загружать модель перед тем, как начнётся обработка запросов, но только непосредственно перед тем, как приложение начнет принимать запросы, а не во время загрузки кода.
+
+## Lifespan { #lifespan }
+
+Вы можете определить логику для startup и shutdown, используя параметр `lifespan` приложения `FastAPI` и «менеджер контекста» (через секунду покажу что это).
+
+Начнем с примера, а затем разберём его подробнее.
+
+Мы создаём асинхронную функцию `lifespan()` с `yield` примерно так:
+
+{* ../../docs_src/events/tutorial003.py hl[16,19] *}
+
+Здесь мы симулируем дорогую операцию startup по загрузке модели, помещая (фиктивную) функцию модели в словарь с моделями Машинного обучения до `yield`. Этот код будет выполнен до того, как приложение начнет принимать запросы, во время startup.
+
+А затем сразу после `yield` мы выгружаем модель. Этот код будет выполнен после того, как приложение закончит обрабатывать запросы, непосредственно перед shutdown. Это может, например, освободить ресурсы, такие как память или GPU.
+
+/// tip | Совет
+
+`shutdown` произойдёт, когда вы останавливаете приложение.
+
+Возможно, вам нужно запустить новую версию, или вы просто устали от него. 🤷
+
+///
+
+### Функция lifespan { #lifespan-function }
+
+Первое, на что стоит обратить внимание, — мы определяем асинхронную функцию с `yield`. Это очень похоже на Зависимости с `yield`.
+
+{* ../../docs_src/events/tutorial003.py hl[14:19] *}
+
+Первая часть функции, до `yield`, будет выполнена до запуска приложения.
+
+А часть после `yield` будет выполнена после завершения работы приложения.
+
+### Асинхронный менеджер контекста { #async-context-manager }
+
+Если присмотреться, функция декорирована `@asynccontextmanager`.
+
+Это превращает функцию в «асинхронный менеджер контекста».
+
+{* ../../docs_src/events/tutorial003.py hl[1,13] *}
+
+Менеджер контекста в Python — это то, что можно использовать в операторе `with`. Например, `open()` можно использовать как менеджер контекста:
+
+```Python
+with open("file.txt") as file:
+ file.read()
+```
+
+В последних версиях Python есть также асинхронный менеджер контекста. Его используют с `async with`:
+
+```Python
+async with lifespan(app):
+ await do_stuff()
+```
+
+Когда вы создаёте менеджер контекста или асинхронный менеджер контекста, как выше, он перед входом в блок `with` выполнит код до `yield`, а после выхода из блока `with` выполнит код после `yield`.
+
+В нашем примере выше мы не используем его напрямую, а передаём его в FastAPI, чтобы он использовал его сам.
+
+Параметр `lifespan` приложения `FastAPI` принимает асинхронный менеджер контекста, поэтому мы можем передать ему наш новый асинхронный менеджер контекста `lifespan`.
+
+{* ../../docs_src/events/tutorial003.py hl[22] *}
+
+## Альтернативные события (устаревшие) { #alternative-events-deprecated }
+
+/// warning | Предупреждение
+
+Рекомендуемый способ обрабатывать startup и shutdown — использовать параметр `lifespan` приложения `FastAPI`, как описано выше. Если вы укажете параметр `lifespan`, обработчики событий `startup` и `shutdown` больше вызываться не будут. Либо всё через `lifespan`, либо всё через события — не одновременно.
+
+Эту часть, скорее всего, можно пропустить.
+
+///
+
+Есть альтернативный способ определить логику, которую нужно выполнить во время startup и во время shutdown.
+
+Вы можете определить обработчики событий (функции), которые нужно выполнить до старта приложения или при его завершении.
+
+Эти функции можно объявить с `async def` или обычным `def`.
+
+### Событие `startup` { #startup-event }
+
+Чтобы добавить функцию, которую нужно запустить до старта приложения, объявите её как обработчик события `"startup"`:
+
+{* ../../docs_src/events/tutorial001.py hl[8] *}
+
+В этом случае функция-обработчик события `startup` инициализирует «базу данных» items (это просто `dict`) некоторыми значениями.
+
+Вы можете добавить более одного обработчика события.
+
+И ваше приложение не начнет принимать запросы, пока все обработчики события `startup` не завершатся.
+
+### Событие `shutdown` { #shutdown-event }
+
+Чтобы добавить функцию, которую нужно запустить при завершении работы приложения, объявите её как обработчик события `"shutdown"`:
+
+{* ../../docs_src/events/tutorial002.py hl[6] *}
+
+Здесь функция-обработчик события `shutdown` запишет строку текста `"Application shutdown"` в файл `log.txt`.
+
+/// info | Информация
+
+В функции `open()` параметр `mode="a"` означает «добавление» (append), то есть строка будет добавлена в конец файла, без перезаписи предыдущего содержимого.
+
+///
+
+/// tip | Совет
+
+Обратите внимание, что в этом случае мы используем стандартную Python-функцию `open()`, которая взаимодействует с файлом.
+
+То есть это I/O (ввод/вывод), требующий «ожидания» записи на диск.
+
+Но `open()` не использует `async` и `await`.
+
+Поэтому мы объявляем обработчик события обычным `def` вместо `async def`.
+
+///
+
+### `startup` и `shutdown` вместе { #startup-and-shutdown-together }
+
+С высокой вероятностью логика для вашего startup и shutdown связана: вы можете хотеть что-то запустить, а затем завершить, получить ресурс, а затем освободить его и т.д.
+
+Делать это в отдельных функциях, которые не разделяют общую логику или переменные, сложнее, так как придётся хранить значения в глобальных переменных или использовать похожие приёмы.
+
+Поэтому теперь рекомендуется использовать `lifespan`, как описано выше.
+
+## Технические детали { #technical-details }
+
+Немного технических подробностей для любопытных умников. 🤓
+
+Под капотом, в ASGI-технической спецификации, это часть Протокола Lifespan, и он определяет события `startup` и `shutdown`.
+
+/// info | Информация
+
+Вы можете прочитать больше про обработчики `lifespan` в Starlette в документации Starlette по Lifespan.
+
+Включая то, как работать с состоянием lifespan, которое можно использовать в других частях вашего кода.
+
+///
+
+## Подприложения { #sub-applications }
+
+🚨 Имейте в виду, что эти события lifespan (startup и shutdown) будут выполнены только для основного приложения, а не для [Подприложения — Mounts](sub-applications.md){.internal-link target=_blank}.
diff --git a/docs/ru/docs/advanced/generate-clients.md b/docs/ru/docs/advanced/generate-clients.md
new file mode 100644
index 0000000000..ee52412c6d
--- /dev/null
+++ b/docs/ru/docs/advanced/generate-clients.md
@@ -0,0 +1,208 @@
+# Генерация SDK { #generating-sdks }
+
+Поскольку **FastAPI** основан на спецификации **OpenAPI**, его API можно описать в стандартном формате, понятном множеству инструментов.
+
+Это упрощает генерацию актуальной **документации**, клиентских библиотек (**SDKs**) на разных языках, а также **тестирования** или **воркфлоу автоматизации**, которые остаются синхронизированными с вашим кодом.
+
+В этом руководстве вы узнаете, как сгенерировать **TypeScript SDK** для вашего бэкенда на FastAPI.
+
+## Генераторы SDK с открытым исходным кодом { #open-source-sdk-generators }
+
+Гибкий вариант — OpenAPI Generator, который поддерживает **многие языки программирования** и умеет генерировать SDK из вашей спецификации OpenAPI.
+
+Для **TypeScript‑клиентов** Hey API — специализированное решение, обеспечивающее оптимальный опыт для экосистемы TypeScript.
+
+Больше генераторов SDK можно найти на OpenAPI.Tools.
+
+/// tip | Совет
+
+FastAPI автоматически генерирует спецификации **OpenAPI 3.1**, поэтому любой используемый инструмент должен поддерживать эту версию.
+
+///
+
+## Генераторы SDK от спонсоров FastAPI { #sdk-generators-from-fastapi-sponsors }
+
+В этом разделе представлены решения с **венчурной поддержкой** и **поддержкой компаний** от компаний, которые спонсируют FastAPI. Эти продукты предоставляют **дополнительные возможности** и **интеграции** сверх высококачественно генерируемых SDK.
+
+Благодаря ✨ [**спонсорству FastAPI**](../help-fastapi.md#sponsor-the-author){.internal-link target=_blank} ✨ эти компании помогают обеспечивать, чтобы фреймворк и его **экосистема** оставались здоровыми и **устойчивыми**.
+
+Их спонсорство также демонстрирует серьёзную приверженность **сообществу** FastAPI (вам), показывая, что им важно не только предоставлять **отличный сервис**, но и поддерживать **надёжный и процветающий фреймворк** FastAPI. 🙇
+
+Например, вы можете попробовать:
+
+* Speakeasy
+* Stainless
+* liblab
+
+Некоторые из этих решений также могут быть open source или иметь бесплатные тарифы, так что вы сможете попробовать их без финансовых затрат. Другие коммерческие генераторы SDK доступны и их можно найти онлайн. 🤓
+
+## Создать TypeScript SDK { #create-a-typescript-sdk }
+
+Начнём с простого приложения FastAPI:
+
+{* ../../docs_src/generate_clients/tutorial001_py39.py hl[7:9,12:13,16:17,21] *}
+
+Обратите внимание, что *операции пути (обработчики пути)* определяют модели, которые они используют для полезной нагрузки запроса и полезной нагрузки ответа, с помощью моделей `Item` и `ResponseMessage`.
+
+### Документация API { #api-docs }
+
+Если перейти на `/docs`, вы увидите **схемы** данных, отправляемых в запросах и принимаемых в ответах:
+
+
+
+Вы видите эти схемы, потому что они были объявлены с моделями в приложении.
+
+Эта информация доступна в **схеме OpenAPI** приложения и затем отображается в документации API.
+
+Та же информация из моделей, включённая в OpenAPI, может использоваться для **генерации клиентского кода**.
+
+### Hey API { #hey-api }
+
+Как только у нас есть приложение FastAPI с моделями, мы можем использовать Hey API для генерации TypeScript‑клиента. Самый быстрый способ сделать это — через npx.
+
+```sh
+npx @hey-api/openapi-ts -i http://localhost:8000/openapi.json -o src/client
+```
+
+Это сгенерирует TypeScript SDK в `./src/client`.
+
+Вы можете узнать, как установить `@hey-api/openapi-ts` и почитать о сгенерированном результате на их сайте.
+
+### Использование SDK { #using-the-sdk }
+
+Теперь вы можете импортировать и использовать клиентский код. Это может выглядеть так, обратите внимание, что вы получаете автозавершение для методoв:
+
+
+
+Вы также получите автозавершение для отправляемой полезной нагрузки:
+
+
+
+/// tip | Совет
+
+Обратите внимание на автозавершение для `name` и `price`, это было определено в приложении FastAPI, в модели `Item`.
+
+///
+
+Вы получите ошибки прямо в редакторе для отправляемых данных:
+
+
+
+Объект ответа также будет иметь автозавершение:
+
+
+
+## Приложение FastAPI с тегами { #fastapi-app-with-tags }
+
+Во многих случаях ваше приложение FastAPI будет больше, и вы, вероятно, будете использовать теги, чтобы разделять разные группы *операций пути*.
+
+Например, у вас может быть раздел для **items** и другой раздел для **users**, и они могут быть разделены тегами:
+
+{* ../../docs_src/generate_clients/tutorial002_py39.py hl[21,26,34] *}
+
+### Генерация TypeScript‑клиента с тегами { #generate-a-typescript-client-with-tags }
+
+Если вы генерируете клиент для приложения FastAPI с использованием тегов, обычно клиентский код также будет разделён по тегам.
+
+Таким образом вы сможете иметь всё правильно упорядоченным и сгруппированным в клиентском коде:
+
+
+
+В этом случае у вас есть:
+
+* `ItemsService`
+* `UsersService`
+
+### Имена методов клиента { #client-method-names }
+
+Сейчас сгенерированные имена методов вроде `createItemItemsPost` выглядят не очень аккуратно:
+
+```TypeScript
+ItemsService.createItemItemsPost({name: "Plumbus", price: 5})
+```
+
+...это потому, что генератор клиента использует внутренний **ID операции** OpenAPI для каждой *операции пути*.
+
+OpenAPI требует, чтобы каждый ID операции был уникален среди всех *операций пути*, поэтому FastAPI использует **имя функции**, **путь** и **HTTP‑метод/операцию** для генерации этого ID операции, так как таким образом можно гарантировать уникальность ID операций.
+
+Но далее я покажу, как это улучшить. 🤓
+
+## Пользовательские ID операций и лучшие имена методов { #custom-operation-ids-and-better-method-names }
+
+Вы можете **изменить** способ **генерации** этих ID операций, чтобы сделать их проще, а имена методов в клиентах — **более простыми**.
+
+В этом случае вам нужно будет обеспечить, чтобы каждый ID операции был **уникальным** другим способом.
+
+Например, вы можете гарантировать, что у каждой *операции пути* есть тег, и затем генерировать ID операции на основе **тега** и **имени** *операции пути* (имени функции).
+
+### Пользовательская функция генерации уникального ID { #custom-generate-unique-id-function }
+
+FastAPI использует **уникальный ID** для каждой *операции пути*, который применяется для **ID операции**, а также для имён любых необходимых пользовательских моделей запросов или ответов.
+
+Вы можете кастомизировать эту функцию. Она принимает `APIRoute` и возвращает строку.
+
+Например, здесь берётся первый тег (скорее всего у вас один тег) и имя *операции пути* (имя функции).
+
+Затем вы можете передать эту пользовательскую функцию в **FastAPI** через параметр `generate_unique_id_function`:
+
+{* ../../docs_src/generate_clients/tutorial003_py39.py hl[6:7,10] *}
+
+### Генерация TypeScript‑клиента с пользовательскими ID операций { #generate-a-typescript-client-with-custom-operation-ids }
+
+Теперь, если снова сгенерировать клиент, вы увидите, что имена методов улучшились:
+
+
+
+Как видите, теперь имена методов содержат тег, а затем имя функции; больше они не включают информацию из URL‑пути и HTTP‑операции.
+
+### Предобработка спецификации OpenAPI для генератора клиента { #preprocess-the-openapi-specification-for-the-client-generator }
+
+Сгенерированном коде всё ещё есть **дублирующаяся информация**.
+
+Мы уже знаем, что этот метод относится к **items**, потому что это слово есть в `ItemsService` (взято из тега), но при этом имя тега всё ещё добавлено префиксом к имени метода. 😕
+
+Скорее всего мы захотим оставить это в OpenAPI в целом, так как это гарантирует, что ID операций будут **уникальны**.
+
+Но для сгенерированного клиента мы можем **модифицировать** ID операций OpenAPI непосредственно перед генерацией клиентов, чтобы сделать имена методов более приятными и **чистыми**.
+
+Мы можем скачать OpenAPI JSON в файл `openapi.json`, а затем **убрать этот префикс‑тег** таким скриптом:
+
+{* ../../docs_src/generate_clients/tutorial004.py *}
+
+//// tab | Node.js
+
+```Javascript
+{!> ../../docs_src/generate_clients/tutorial004.js!}
+```
+
+////
+
+После этого ID операций будут переименованы с чего‑то вроде `items-get_items` просто в `get_items`, и генератор клиента сможет создавать более простые имена методов.
+
+### Генерация TypeScript‑клиента с предобработанным OpenAPI { #generate-a-typescript-client-with-the-preprocessed-openapi }
+
+Так как конечный результат теперь в файле `openapi.json`, нужно обновить входное расположение:
+
+```sh
+npx @hey-api/openapi-ts -i ./openapi.json -o src/client
+```
+
+После генерации нового клиента у вас будут **чистые имена методов**, со всем **автозавершением**, **ошибками прямо в редакторе** и т.д.:
+
+
+
+## Преимущества { #benefits }
+
+При использовании автоматически сгенерированных клиентов вы получите **автозавершение** для:
+
+* Методов.
+* Данных запроса — в теле запроса, query‑параметрах и т.д.
+* Данных ответа.
+
+У вас также будут **ошибки прямо в редакторе** для всего.
+
+И каждый раз, когда вы обновляете код бэкенда и **перегенерируете** фронтенд, в нём появятся новые *операции пути* как методы, старые будут удалены, а любые другие изменения отразятся в сгенерированном коде. 🤓
+
+Это также означает, что если что‑то изменилось, это будет **отражено** в клиентском коде автоматически. И если вы **соберёте** клиент, он завершится с ошибкой, если где‑то есть **несоответствие** в используемых данных.
+
+Таким образом, вы **обнаружите многие ошибки** очень рано в цикле разработки, вместо того чтобы ждать, когда ошибки проявятся у конечных пользователей в продакшн, и затем пытаться отладить, в чём проблема. ✨
diff --git a/docs/ru/docs/advanced/index.md b/docs/ru/docs/advanced/index.md
new file mode 100644
index 0000000000..c0a52c6c14
--- /dev/null
+++ b/docs/ru/docs/advanced/index.md
@@ -0,0 +1,21 @@
+# Расширенное руководство пользователя { #advanced-user-guide }
+
+## Дополнительные возможности { #additional-features }
+
+Основное [Учебник - Руководство пользователя](../tutorial/index.md){.internal-link target=_blank} должно быть достаточно, чтобы познакомить вас со всеми основными функциями **FastAPI**.
+
+В следующих разделах вы увидите другие варианты, конфигурации и дополнительные возможности.
+
+/// tip | Совет
+
+Следующие разделы **не обязательно являются "продвинутыми"**.
+
+И вполне возможно, что для вашего случая использования решение находится в одном из них.
+
+///
+
+## Сначала прочитайте Учебник - Руководство пользователя { #read-the-tutorial-first }
+
+Вы все еще можете использовать большинство функций **FastAPI** со знаниями из [Учебник - Руководство пользователя](../tutorial/index.md){.internal-link target=_blank}.
+
+И следующие разделы предполагают, что вы уже прочитали его, и предполагают, что вы знаете эти основные идеи.
diff --git a/docs/ru/docs/advanced/middleware.md b/docs/ru/docs/advanced/middleware.md
new file mode 100644
index 0000000000..82c86b2317
--- /dev/null
+++ b/docs/ru/docs/advanced/middleware.md
@@ -0,0 +1,97 @@
+# Расширенное использование middleware { #advanced-middleware }
+
+В основном руководстве вы читали, как добавить [пользовательское middleware](../tutorial/middleware.md){.internal-link target=_blank} в ваше приложение.
+
+А затем — как работать с [CORS с помощью `CORSMiddleware`](../tutorial/cors.md){.internal-link target=_blank}.
+
+В этом разделе посмотрим, как использовать другие middleware.
+
+## Добавление ASGI middleware { #adding-asgi-middlewares }
+
+Так как **FastAPI** основан на Starlette и реализует спецификацию ASGI, вы можете использовать любое ASGI middleware.
+
+Middleware не обязательно должно быть сделано специально для FastAPI или Starlette — достаточно, чтобы оно соответствовало спецификации ASGI.
+
+В общем случае ASGI middleware — это классы, которые ожидают получить ASGI‑приложение первым аргументом.
+
+Поэтому в документации к сторонним ASGI middleware, скорее всего, вы увидите что‑то вроде:
+
+```Python
+from unicorn import UnicornMiddleware
+
+app = SomeASGIApp()
+
+new_app = UnicornMiddleware(app, some_config="rainbow")
+```
+
+Но FastAPI (точнее, Starlette) предоставляет более простой способ, который гарантирует корректную обработку внутренних ошибок сервера и корректную работу пользовательских обработчиков исключений.
+
+Для этого используйте `app.add_middleware()` (как в примере с CORS).
+
+```Python
+from fastapi import FastAPI
+from unicorn import UnicornMiddleware
+
+app = FastAPI()
+
+app.add_middleware(UnicornMiddleware, some_config="rainbow")
+```
+
+`app.add_middleware()` принимает класс middleware в качестве первого аргумента и любые дополнительные аргументы, которые будут переданы этому middleware.
+
+## Встроенные middleware { #integrated-middlewares }
+
+**FastAPI** включает несколько middleware для распространённых сценариев. Ниже рассмотрим, как их использовать.
+
+/// note | Технические детали
+
+В следующих примерах вы также можете использовать `from starlette.middleware.something import SomethingMiddleware`.
+
+**FastAPI** предоставляет несколько middleware в `fastapi.middleware` для удобства разработчика. Но большинство доступных middleware приходит напрямую из Starlette.
+
+///
+
+## `HTTPSRedirectMiddleware` { #httpsredirectmiddleware }
+
+Гарантирует, что все входящие запросы должны использовать либо `https`, либо `wss`.
+
+Любой входящий запрос по `http` или `ws` будет перенаправлен на безопасную схему.
+
+{* ../../docs_src/advanced_middleware/tutorial001.py hl[2,6] *}
+
+## `TrustedHostMiddleware` { #trustedhostmiddleware }
+
+Гарантирует, что во всех входящих запросах корректно установлен `Host`‑заголовок, чтобы защититься от атак на HTTP‑заголовок Host.
+
+{* ../../docs_src/advanced_middleware/tutorial002.py hl[2,6:8] *}
+
+Поддерживаются следующие аргументы:
+
+- `allowed_hosts` — список доменных имён, которые следует разрешить как имена хостов. Подстановки вида `*.example.com` поддерживаются для сопоставления поддоменов. Чтобы разрешить любой хост, используйте либо `allowed_hosts=["*"]`, либо не добавляйте это middleware.
+- `www_redirect` — если установлено в True, запросы к не‑www версиям разрешённых хостов будут перенаправляться на их www‑аналоги. По умолчанию — `True`.
+
+Если входящий запрос не проходит валидацию, будет отправлен ответ `400`.
+
+## `GZipMiddleware` { #gzipmiddleware }
+
+Обрабатывает GZip‑ответы для любых запросов, которые включают `"gzip"` в заголовке `Accept-Encoding`.
+
+Это middleware обрабатывает как обычные, так и потоковые ответы.
+
+{* ../../docs_src/advanced_middleware/tutorial003.py hl[2,6] *}
+
+Поддерживаются следующие аргументы:
+
+- `minimum_size` — не сжимать GZip‑ом ответы, размер которых меньше этого минимального значения в байтах. По умолчанию — `500`.
+- `compresslevel` — уровень GZip‑сжатия. Целое число от 1 до 9. По умолчанию — `9`. Более низкое значение — быстреее сжатие, но больший размер файла; более высокое значение — более медленное сжатие, но меньший размер файла.
+
+## Другие middleware { #other-middlewares }
+
+Существует много других ASGI middleware.
+
+Например:
+
+- `ProxyHeadersMiddleware` от Uvicorn
+- MessagePack
+
+Чтобы увидеть другие доступные middleware, посмотрите документацию по middleware в Starlette и список ASGI Awesome.
diff --git a/docs/ru/docs/advanced/openapi-callbacks.md b/docs/ru/docs/advanced/openapi-callbacks.md
new file mode 100644
index 0000000000..faf58370be
--- /dev/null
+++ b/docs/ru/docs/advanced/openapi-callbacks.md
@@ -0,0 +1,186 @@
+# Обратные вызовы в OpenAPI { #openapi-callbacks }
+
+Вы можете создать API с *операцией пути* (обработчиком пути), которая будет инициировать HTTP-запрос к *внешнему API*, созданному кем-то другим (скорее всего тем же разработчиком, который будет использовать ваш API).
+
+Процесс, происходящий, когда ваше приложение API обращается к *внешнему API*, называется «callback» (обратный вызов). Программное обеспечение, написанное внешним разработчиком, отправляет HTTP-запрос вашему API, а затем ваш API выполняет обратный вызов, отправляя HTTP-запрос во *внешний API* (который, вероятно, тоже создал тот же разработчик).
+
+В этом случае вам может понадобиться задокументировать, как должно выглядеть это внешнее API: какую *операцию пути* оно должно иметь, какое тело запроса ожидать, какой ответ возвращать и т.д.
+
+## Приложение с обратными вызовами { #an-app-with-callbacks }
+
+Давайте рассмотрим это на примере.
+
+Представьте, что вы разрабатываете приложение, позволяющее создавать счета.
+
+Эти счета будут иметь `id`, `title` (необязательный), `customer` и `total`.
+
+Пользователь вашего API (внешний разработчик) создаст счет в вашем API с помощью POST-запроса.
+
+Затем ваш API (предположим) сделает следующее:
+
+* Отправит счет клиенту внешнего разработчика.
+* Получит оплату.
+* Отправит уведомление обратно пользователю API (внешнему разработчику).
+ * Это будет сделано отправкой POST-запроса (из *вашего API*) в *внешний API*, предоставленный этим внешним разработчиком (это и есть «callback»).
+
+## Обычное приложение **FastAPI** { #the-normal-fastapi-app }
+
+Сначала посмотрим, как будет выглядеть обычное приложение API до добавления обратного вызова.
+
+В нём будет *операция пути*, которая получит тело запроса `Invoice`, и query-параметр `callback_url`, содержащий URL для обратного вызова.
+
+Эта часть вполне обычна, большая часть кода вам уже знакома:
+
+{* ../../docs_src/openapi_callbacks/tutorial001.py hl[9:13,36:53] *}
+
+/// tip | Совет
+
+Query-параметр `callback_url` использует тип Pydantic Url.
+
+///
+
+Единственное новое — это `callbacks=invoices_callback_router.routes` в качестве аргумента *декоратора операции пути*. Далее разберёмся, что это такое.
+
+## Документирование обратного вызова { #documenting-the-callback }
+
+Реальный код обратного вызова будет сильно зависеть от вашего приложения API.
+
+И, вероятно, он будет заметно отличаться от одного приложения к другому.
+
+Это могут быть буквально одна-две строки кода, например:
+
+```Python
+callback_url = "https://example.com/api/v1/invoices/events/"
+httpx.post(callback_url, json={"description": "Invoice paid", "paid": True})
+```
+
+Но, возможно, самая важная часть обратного вызова — это убедиться, что пользователь вашего API (внешний разработчик) правильно реализует *внешний API* в соответствии с данными, которые *ваш API* будет отправлять в теле запроса обратного вызова и т.п.
+
+Поэтому далее мы добавим код, документирующий, как должен выглядеть этот *внешний API*, чтобы получать обратный вызов от *вашего API*.
+
+Эта документация отобразится в Swagger UI по адресу `/docs` в вашем API и позволит внешним разработчикам понять, как построить *внешний API*.
+
+В этом примере сам обратный вызов не реализуется (это может быть всего одна строка кода), реализуется только часть с документацией.
+
+/// tip | Совет
+
+Сам обратный вызов — это всего лишь HTTP-запрос.
+
+Реализуя обратный вызов, вы можете использовать, например, HTTPX или Requests.
+
+///
+
+## Напишите код документации обратного вызова { #write-the-callback-documentation-code }
+
+Этот код не будет выполняться в вашем приложении, он нужен только для *документирования* того, как должен выглядеть *внешний API*.
+
+Но вы уже знаете, как легко получить автоматическую документацию для API с **FastAPI**.
+
+Мы используем те же знания, чтобы задокументировать, как должен выглядеть *внешний API*... создав *операции пути*, которые внешний API должен реализовать (те, которые ваш API будет вызывать).
+
+/// tip | Совет
+
+Когда вы пишете код для документирования обратного вызова, полезно представить, что вы — тот самый *внешний разработчик*. И что вы сейчас реализуете *внешний API*, а не *свой API*.
+
+Временное принятие этой точки зрения (внешнего разработчика) поможет интуитивно понять, куда поместить параметры, какую Pydantic-модель использовать для тела запроса, для ответа и т.д. во *внешнем API*.
+
+///
+
+### Создайте `APIRouter` для обратного вызова { #create-a-callback-apirouter }
+
+Сначала создайте новый `APIRouter`, который будет содержать один или несколько обратных вызовов.
+
+{* ../../docs_src/openapi_callbacks/tutorial001.py hl[3,25] *}
+
+### Создайте *операцию пути* для обратного вызова { #create-the-callback-path-operation }
+
+Чтобы создать *операцию пути* для обратного вызова, используйте тот же `APIRouter`, который вы создали выше.
+
+Она должна выглядеть как обычная *операция пути* FastAPI:
+
+* Вероятно, в ней должно быть объявление тела запроса, например `body: InvoiceEvent`.
+* А также может быть объявление модели ответа, например `response_model=InvoiceEventReceived`.
+
+{* ../../docs_src/openapi_callbacks/tutorial001.py hl[16:18,21:22,28:32] *}
+
+Есть 2 основных отличия от обычной *операции пути*:
+
+* Ей не нужен реальный код, потому что ваше приложение никогда не будет вызывать эту функцию. Она используется только для документирования *внешнего API*. Поэтому в функции может быть просто `pass`.
+* *Путь* может содержать выражение OpenAPI 3 (подробнее ниже), где можно использовать переменные с параметрами и части исходного HTTP-запроса, отправленного *вашему API*.
+
+### Выражение пути для обратного вызова { #the-callback-path-expression }
+
+*Путь* обратного вызова может содержать выражение OpenAPI 3, которое может включать части исходного запроса, отправленного *вашему API*.
+
+В нашем случае это `str`:
+
+```Python
+"{$callback_url}/invoices/{$request.body.id}"
+```
+
+Итак, если пользователь вашего API (внешний разработчик) отправляет HTTP-запрос вашему API по адресу:
+
+```
+https://yourapi.com/invoices/?callback_url=https://www.external.org/events
+```
+
+с телом JSON:
+
+```JSON
+{
+ "id": "2expen51ve",
+ "customer": "Mr. Richie Rich",
+ "total": "9999"
+}
+```
+
+то *ваш API* обработает счёт и, в какой-то момент позже, отправит запрос обратного вызова на `callback_url` (*внешний API*):
+
+```
+https://www.external.org/events/invoices/2expen51ve
+```
+
+с телом JSON примерно такого вида:
+
+```JSON
+{
+ "description": "Payment celebration",
+ "paid": true
+}
+```
+
+и будет ожидать от *внешнего API* ответ с телом JSON вида:
+
+```JSON
+{
+ "ok": true
+}
+```
+
+/// tip | Совет
+
+Обратите внимание, что используемый URL обратного вызова содержит URL, полученный как query-параметр в `callback_url` (`https://www.external.org/events`), а также `id` счёта из тела JSON (`2expen51ve`).
+
+///
+
+### Подключите маршрутизатор обратного вызова { #add-the-callback-router }
+
+К этому моменту у вас есть необходимые *операции пути* обратного вызова (те, которые *внешний разработчик* должен реализовать во *внешнем API*) в созданном выше маршрутизаторе обратных вызовов.
+
+Теперь используйте параметр `callbacks` в *декораторе операции пути вашего API*, чтобы передать атрибут `.routes` (это, по сути, просто `list` маршрутов/*операций пути*) из этого маршрутизатора обратных вызовов:
+
+{* ../../docs_src/openapi_callbacks/tutorial001.py hl[35] *}
+
+/// tip | Совет
+
+Обратите внимание, что вы передаёте не сам маршрутизатор (`invoices_callback_router`) в `callback=`, а его атрибут `.routes`, то есть `invoices_callback_router.routes`.
+
+///
+
+### Проверьте документацию { #check-the-docs }
+
+Теперь вы можете запустить приложение и перейти по адресу http://127.0.0.1:8000/docs.
+
+Вы увидите документацию, включающую раздел «Callbacks» для вашей *операции пути*, который показывает, как должен выглядеть *внешний API*:
+
+
diff --git a/docs/ru/docs/advanced/openapi-webhooks.md b/docs/ru/docs/advanced/openapi-webhooks.md
new file mode 100644
index 0000000000..d38cf315f7
--- /dev/null
+++ b/docs/ru/docs/advanced/openapi-webhooks.md
@@ -0,0 +1,55 @@
+# Вебхуки OpenAPI { #openapi-webhooks }
+
+Бывают случаи, когда вы хотите сообщить пользователям вашего API, что ваше приложение может вызвать их приложение (отправив HTTP-запрос) с некоторыми данными, обычно чтобы уведомить о каком-то событии.
+
+Это означает, что вместо обычного процесса, когда пользователи отправляют запросы вашему API, ваш API (или ваше приложение) может отправлять запросы в их систему (в их API, их приложение).
+
+Обычно это называется вебхуком.
+
+## Шаги вебхуков { #webhooks-steps }
+
+Обычно процесс таков: вы определяете в своем коде, какое сообщение вы будете отправлять, то есть тело запроса.
+
+Вы также определяете, в какие моменты (при каких событиях) ваше приложение будет отправлять эти запросы.
+
+А ваши пользователи каким-то образом (например, в веб‑панели) указывают URL-адрес, на который ваше приложение должно отправлять эти запросы.
+
+Вся логика регистрации URL-адресов для вебхуков и код, который реально отправляет эти запросы, целиком на вашей стороне. Вы пишете это так, как вам нужно, в своем собственном коде.
+
+## Документирование вебхуков с помощью FastAPI и OpenAPI { #documenting-webhooks-with-fastapi-and-openapi }
+
+С FastAPI, используя OpenAPI, вы можете определить имена этих вебхуков, типы HTTP-операций, которые ваше приложение может отправлять (например, `POST`, `PUT` и т.д.), а также тела запросов, которые ваше приложение будет отправлять.
+
+Это значительно упростит вашим пользователям реализацию их API для приема ваших вебхук-запросов; возможно, они даже смогут автоматически сгенерировать часть кода своего API.
+
+/// info | Информация
+
+Вебхуки доступны в OpenAPI 3.1.0 и выше, поддерживаются в FastAPI `0.99.0` и новее.
+
+///
+
+## Приложение с вебхуками { #an-app-with-webhooks }
+
+При создании приложения на **FastAPI** есть атрибут `webhooks`, с помощью которого можно объявлять вебхуки так же, как вы объявляете операции пути (обработчики пути), например с `@app.webhooks.post()`.
+
+{* ../../docs_src/openapi_webhooks/tutorial001.py hl[9:13,36:53] *}
+
+Определенные вами вебхуки попадут в схему **OpenAPI** и в автоматический **интерфейс документации**.
+
+/// info | Информация
+
+Объект `app.webhooks` на самом деле — это обычный `APIRouter`, тот же тип, который вы используете при структурировании приложения по нескольким файлам.
+
+///
+
+Обратите внимание: в случае с вебхуками вы на самом деле не объявляете путь (например, `/items/`), передаваемый туда текст — это лишь идентификатор вебхука (имя события). Например, в `@app.webhooks.post("new-subscription")` имя вебхука — `new-subscription`.
+
+Это связано с тем, что предполагается: фактический URL‑путь, по которому они хотят получать запрос вебхука, ваши пользователи укажут каким-то другим образом (например, в веб‑панели).
+
+### Посмотрите документацию { #check-the-docs }
+
+Теперь вы можете запустить приложение и перейти по ссылке http://127.0.0.1:8000/docs.
+
+Вы увидите, что в документации есть обычные операции пути, а также появились вебхуки:
+
+
diff --git a/docs/ru/docs/advanced/path-operation-advanced-configuration.md b/docs/ru/docs/advanced/path-operation-advanced-configuration.md
new file mode 100644
index 0000000000..fcb3cd47f2
--- /dev/null
+++ b/docs/ru/docs/advanced/path-operation-advanced-configuration.md
@@ -0,0 +1,204 @@
+# Расширенная конфигурация операций пути { #path-operation-advanced-configuration }
+
+## OpenAPI operationId { #openapi-operationid }
+
+/// warning | Предупреждение
+
+Если вы не «эксперт» по OpenAPI, скорее всего, это вам не нужно.
+
+///
+
+Вы можете задать OpenAPI `operationId`, который будет использоваться в вашей *операции пути*, с помощью параметра `operation_id`.
+
+Нужно убедиться, что он уникален для каждой операции.
+
+{* ../../docs_src/path_operation_advanced_configuration/tutorial001.py hl[6] *}
+
+### Использование имени функции-обработчика пути как operationId { #using-the-path-operation-function-name-as-the-operationid }
+
+Если вы хотите использовать имена функций ваших API в качестве `operationId`, вы можете пройти по всем из них и переопределить `operation_id` каждой *операции пути* с помощью их `APIRoute.name`.
+
+Делать это следует после добавления всех *операций пути*.
+
+{* ../../docs_src/path_operation_advanced_configuration/tutorial002.py hl[2, 12:21, 24] *}
+
+/// tip | Совет
+
+Если вы вызываете `app.openapi()` вручную, обновите `operationId` до этого.
+
+///
+
+/// warning | Предупреждение
+
+Если вы делаете это, убедитесь, что каждая из ваших *функций-обработчиков пути* имеет уникальное имя.
+
+Даже если они находятся в разных модулях (файлах Python).
+
+///
+
+## Исключить из OpenAPI { #exclude-from-openapi }
+
+Чтобы исключить *операцию пути* из генерируемой схемы OpenAPI (а значит, и из автоматической документации), используйте параметр `include_in_schema` и установите его в `False`:
+
+{* ../../docs_src/path_operation_advanced_configuration/tutorial003.py hl[6] *}
+
+## Расширенное описание из docstring { #advanced-description-from-docstring }
+
+Вы можете ограничить количество строк из docstring *функции-обработчика пути*, используемых для OpenAPI.
+
+Добавление `\f` (экранированного символа «form feed») заставит **FastAPI** обрезать текст, используемый для OpenAPI, в этой точке.
+
+Эта часть не попадёт в документацию, но другие инструменты (например, Sphinx) смогут использовать остальное.
+
+{* ../../docs_src/path_operation_advanced_configuration/tutorial004.py hl[19:29] *}
+
+## Дополнительные ответы { #additional-responses }
+
+Вы, вероятно, уже видели, как объявлять `response_model` и `status_code` для *операции пути*.
+
+Это определяет метаданные об основном ответе *операции пути*.
+
+Также можно объявлять дополнительные ответы с их моделями, статус-кодами и т.д.
+
+В документации есть целая глава об этом — [Дополнительные ответы в OpenAPI](additional-responses.md){.internal-link target=_blank}.
+
+## Дополнительные данные OpenAPI { #openapi-extra }
+
+Когда вы объявляете *операцию пути* в своём приложении, **FastAPI** автоматически генерирует соответствующие метаданные об этой *операции пути* для включения в схему OpenAPI.
+
+/// note | Технические детали
+
+В спецификации OpenAPI это называется Объект операции.
+
+///
+
+Он содержит всю информацию об *операции пути* и используется для генерации автоматической документации.
+
+Там есть `tags`, `parameters`, `requestBody`, `responses` и т.д.
+
+Эта спецификация OpenAPI, специфичная для *операции пути*, обычно генерируется автоматически **FastAPI**, но вы также можете её расширить.
+
+/// tip | Совет
+
+Это низкоуровневая возможность расширения.
+
+Если вам нужно лишь объявить дополнительные ответы, удобнее сделать это через [Дополнительные ответы в OpenAPI](additional-responses.md){.internal-link target=_blank}.
+
+///
+
+Вы можете расширить схему OpenAPI для *операции пути* с помощью параметра `openapi_extra`.
+
+### Расширения OpenAPI { #openapi-extensions }
+
+`openapi_extra` может пригодиться, например, чтобы объявить [Расширения OpenAPI](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#specificationExtensions):
+
+{* ../../docs_src/path_operation_advanced_configuration/tutorial005.py hl[6] *}
+
+Если вы откроете автоматическую документацию API, ваше расширение появится внизу страницы конкретной *операции пути*.
+
+
+
+И если вы посмотрите на итоговый OpenAPI (по адресу `/openapi.json` вашего API), вы также увидите своё расширение в составе описания соответствующей *операции пути*:
+
+```JSON hl_lines="22"
+{
+ "openapi": "3.1.0",
+ "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"
+ }
+ }
+ }
+}
+```
+
+### Пользовательская схема OpenAPI для операции пути { #custom-openapi-path-operation-schema }
+
+Словарь в `openapi_extra` будет объединён с автоматически сгенерированной схемой OpenAPI для *операции пути*.
+
+Таким образом, вы можете добавить дополнительные данные к автоматически сгенерированной схеме.
+
+Например, вы можете решить читать и валидировать запрос своим кодом, не используя автоматические возможности FastAPI и Pydantic, но при этом захотите описать запрос в схеме OpenAPI.
+
+Это можно сделать с помощью `openapi_extra`:
+
+{* ../../docs_src/path_operation_advanced_configuration/tutorial006.py hl[19:36, 39:40] *}
+
+В этом примере мы не объявляли никакую Pydantic-модель. Фактически тело запроса даже не распарсено как JSON, оно читается напрямую как `bytes`, а функция `magic_data_reader()` будет отвечать за его парсинг каким-то способом.
+
+Тем не менее, мы можем объявить ожидаемую схему для тела запроса.
+
+### Пользовательский тип содержимого в OpenAPI { #custom-openapi-content-type }
+
+Используя тот же приём, вы можете воспользоваться Pydantic-моделью, чтобы определить JSON Schema, которая затем будет включена в пользовательский раздел схемы OpenAPI для *операции пути*.
+
+И вы можете сделать это, даже если тип данных в запросе — не JSON.
+
+Например, в этом приложении мы не используем встроенную функциональность FastAPI для извлечения JSON Schema из моделей Pydantic, равно как и автоматическую валидацию JSON. Мы объявляем тип содержимого запроса как YAML, а не JSON:
+
+//// tab | Pydantic v2
+
+{* ../../docs_src/path_operation_advanced_configuration/tutorial007.py hl[17:22, 24] *}
+
+////
+
+//// tab | Pydantic v1
+
+{* ../../docs_src/path_operation_advanced_configuration/tutorial007_pv1.py hl[17:22, 24] *}
+
+////
+
+/// info | Информация
+
+В Pydantic версии 1 метод для получения JSON Schema модели назывался `Item.schema()`, в Pydantic версии 2 метод называется `Item.model_json_schema()`.
+
+///
+
+Тем не менее, хотя мы не используем встроенную функциональность по умолчанию, мы всё равно используем Pydantic-модель, чтобы вручную сгенерировать JSON Schema для данных, которые мы хотим получить в YAML.
+
+Затем мы работаем с запросом напрямую и извлекаем тело как `bytes`. Это означает, что FastAPI даже не попытается распарсить полезную нагрузку запроса как JSON.
+
+А затем в нашем коде мы напрямую парсим этот YAML и снова используем ту же Pydantic-модель для валидации YAML-содержимого:
+
+//// tab | Pydantic v2
+
+{* ../../docs_src/path_operation_advanced_configuration/tutorial007.py hl[26:33] *}
+
+////
+
+//// tab | Pydantic v1
+
+{* ../../docs_src/path_operation_advanced_configuration/tutorial007_pv1.py hl[26:33] *}
+
+////
+
+/// info | Информация
+
+В Pydantic версии 1 метод для парсинга и валидации объекта назывался `Item.parse_obj()`, в Pydantic версии 2 метод называется `Item.model_validate()`.
+
+///
+
+/// tip | Совет
+
+Здесь мы переиспользуем ту же Pydantic-модель.
+
+Но аналогично мы могли бы валидировать данные и каким-то другим способом.
+
+///
diff --git a/docs/ru/docs/advanced/response-change-status-code.md b/docs/ru/docs/advanced/response-change-status-code.md
new file mode 100644
index 0000000000..e9e1c9470f
--- /dev/null
+++ b/docs/ru/docs/advanced/response-change-status-code.md
@@ -0,0 +1,31 @@
+# Response - Изменение статус-кода { #response-change-status-code }
+
+Вы, вероятно, уже читали о том, что можно установить [статус-код ответа по умолчанию](../tutorial/response-status-code.md){.internal-link target=_blank}.
+
+Но в некоторых случаях нужно вернуть другой статус-код, отличный от значения по умолчанию.
+
+## Пример использования { #use-case }
+
+Например, представьте, что вы хотите по умолчанию возвращать HTTP статус-код «OK» `200`.
+
+Но если данные не существовали, вы хотите создать их и вернуть HTTP статус-код «CREATED» `201`.
+
+При этом вы всё ещё хотите иметь возможность фильтровать и преобразовывать возвращаемые данные с помощью `response_model`.
+
+Для таких случаев вы можете использовать параметр `Response`.
+
+## Использование параметра `Response` { #use-a-response-parameter }
+
+Вы можете объявить параметр типа `Response` в вашей *функции обработки пути* (как и для cookies и HTTP-заголовков).
+
+И затем вы можете установить `status_code` в этом *временном* объекте ответа.
+
+{* ../../docs_src/response_change_status_code/tutorial001.py hl[1,9,12] *}
+
+После этого вы можете вернуть любой объект, который вам нужен, как обычно (`dict`, модель базы данных и т.д.).
+
+И если вы объявили `response_model`, он всё равно будет использоваться для фильтрации и преобразования возвращаемого объекта.
+
+**FastAPI** будет использовать этот *временный* ответ для извлечения статус-кода (а также cookies и HTTP-заголовков) и поместит их в финальный ответ, который содержит возвращаемое вами значение, отфильтрованное любым `response_model`.
+
+Вы также можете объявить параметр `Response` в зависимостях и установить в них статус-код. Но помните, что последнее установленное значение будет иметь приоритет.
diff --git a/docs/ru/docs/advanced/response-cookies.md b/docs/ru/docs/advanced/response-cookies.md
new file mode 100644
index 0000000000..9319aba6e2
--- /dev/null
+++ b/docs/ru/docs/advanced/response-cookies.md
@@ -0,0 +1,51 @@
+# Cookies в ответе { #response-cookies }
+
+## Использование параметра `Response` { #use-a-response-parameter }
+
+Вы можете объявить параметр типа `Response` в вашей функции-обработчике пути.
+
+Затем установить cookies в этом временном объекте ответа.
+
+{* ../../docs_src/response_cookies/tutorial002.py hl[1, 8:9] *}
+
+После этого можно вернуть любой объект, как и раньше (например, `dict`, объект модели базы данных и так далее).
+
+Если вы указали `response_model`, он всё равно будет использоваться для фильтрации и преобразования возвращаемого объекта.
+
+**FastAPI** извлечет cookies (а также HTTP-заголовки и статус-код) из временного ответа и включит их в окончательный ответ, содержащий ваше возвращаемое значение, отфильтрованное через `response_model`.
+
+Вы также можете объявить параметр типа `Response` в зависимостях и устанавливать cookies (и HTTP-заголовки) там.
+
+## Возвращение `Response` напрямую { #return-a-response-directly }
+
+Вы также можете установить Cookies, если возвращаете `Response` напрямую в вашем коде.
+
+Для этого создайте объект `Response`, как описано в разделе [Возвращение ответа напрямую](response-directly.md){.internal-link target=_blank}.
+
+Затем установите cookies и верните этот объект:
+
+{* ../../docs_src/response_cookies/tutorial001.py hl[10:12] *}
+
+/// tip | Совет
+
+Имейте в виду, что если вы возвращаете ответ напрямую, вместо использования параметра `Response`, FastAPI вернёт его напрямую.
+
+Убедитесь, что ваши данные имеют корректный тип. Например, они должны быть совместимы с JSON, если вы возвращаете `JSONResponse`.
+
+Также убедитесь, что вы не отправляете данные, которые должны были быть отфильтрованы через `response_model`.
+
+///
+
+### Дополнительная информация { #more-info }
+
+/// note | Технические детали
+
+Вы также можете использовать `from starlette.responses import Response` или `from starlette.responses import JSONResponse`.
+
+**FastAPI** предоставляет `fastapi.responses`, которые являются теми же объектами, что и `starlette.responses`, просто для удобства. Однако большинство доступных типов ответов поступает непосредственно из **Starlette**.
+
+И так как `Response` часто используется для установки HTTP-заголовков и cookies, **FastAPI** также предоставляет его как `fastapi.Response`.
+
+///
+
+Чтобы увидеть все доступные параметры и настройки, ознакомьтесь с документацией Starlette.
diff --git a/docs/ru/docs/advanced/response-directly.md b/docs/ru/docs/advanced/response-directly.md
new file mode 100644
index 0000000000..febd40ed4a
--- /dev/null
+++ b/docs/ru/docs/advanced/response-directly.md
@@ -0,0 +1,65 @@
+# Возврат ответа напрямую { #return-a-response-directly }
+
+Когда вы создаёте **FastAPI** *операцию пути*, вы можете возвращать из неё любые данные: `dict`, `list`, Pydantic-модель, модель базы данных и т.д.
+
+По умолчанию **FastAPI** автоматически преобразует возвращаемое значение в JSON с помощью `jsonable_encoder`, как описано в [JSON кодировщик](../tutorial/encoder.md){.internal-link target=_blank}.
+
+Затем "под капотом" эти данные, совместимые с JSON (например `dict`), помещаются в `JSONResponse`, который используется для отправки ответа клиенту.
+
+Но вы можете возвращать `JSONResponse` напрямую из ваших *операций пути*.
+
+Это может быть полезно, например, если нужно вернуть пользовательские HTTP-заголовки или cookie.
+
+## Возврат `Response` { #return-a-response }
+
+На самом деле, вы можете возвращать любой объект `Response` или его подкласс.
+
+/// tip | Подсказка
+
+`JSONResponse` сам по себе является подклассом `Response`.
+
+///
+
+И когда вы возвращаете `Response`, **FastAPI** передаст его напрямую.
+
+Это не приведет к преобразованию данных с помощью Pydantic-моделей, содержимое не будет преобразовано в какой-либо тип и т.д.
+
+Это даёт вам большую гибкость. Вы можете возвращать любые типы данных, переопределять любые объявления или валидацию данных и т.д.
+
+## Использование `jsonable_encoder` в `Response` { #using-the-jsonable-encoder-in-a-response }
+
+Поскольку **FastAPI** не изменяет объект `Response`, который вы возвращаете, вы должны убедиться, что его содержимое готово к отправке.
+
+Например, вы не можете поместить Pydantic-модель в `JSONResponse`, не преобразовав её сначала в `dict` с помощью преобразования всех типов данных (таких как `datetime`, `UUID` и т.д.) в совместимые с JSON типы.
+
+В таких случаях вы можете использовать `jsonable_encoder` для преобразования данных перед передачей их в ответ:
+
+{* ../../docs_src/response_directly/tutorial001.py hl[6:7,21:22] *}
+
+/// note | Технические детали
+
+Вы также можете использовать `from starlette.responses import JSONResponse`.
+
+**FastAPI** предоставляет `starlette.responses` через `fastapi.responses` просто для вашего удобства, как разработчика. Но большинство доступных Response-классов поступают напрямую из Starlette.
+
+///
+
+## Возврат пользовательского `Response` { #returning-a-custom-response }
+
+Пример выше показывает все необходимые части, но он пока не очень полезен, так как вы могли бы просто вернуть `item` напрямую, и **FastAPI** поместил бы его в `JSONResponse`, преобразовав в `dict` и т.д. Всё это происходит по умолчанию.
+
+Теперь давайте посмотрим, как можно использовать это для возврата пользовательского ответа.
+
+Допустим, вы хотите вернуть ответ в формате XML.
+
+Вы можете поместить ваш XML-контент в строку, поместить её в `Response` и вернуть:
+
+{* ../../docs_src/response_directly/tutorial002.py hl[1,18] *}
+
+## Примечания { #notes }
+
+Когда вы возвращаете объект `Response` напрямую, его данные не валидируются, не преобразуются (не сериализуются) и не документируются автоматически.
+
+Но вы всё равно можете задокументировать это, как описано в [Дополнительные ответы в OpenAPI](additional-responses.md){.internal-link target=_blank}.
+
+В следующих разделах вы увидите, как использовать/объявлять такие кастомные `Response`, при этом сохраняя автоматическое преобразование данных, документацию и т.д.
diff --git a/docs/ru/docs/advanced/response-headers.md b/docs/ru/docs/advanced/response-headers.md
new file mode 100644
index 0000000000..1c9360b31d
--- /dev/null
+++ b/docs/ru/docs/advanced/response-headers.md
@@ -0,0 +1,41 @@
+# HTTP-заголовки ответа { #response-headers }
+
+## Использовать параметр `Response` { #use-a-response-parameter }
+
+Вы можете объявить параметр типа `Response` в вашей функции-обработчике пути (как можно сделать и для cookie).
+
+А затем вы можете устанавливать HTTP-заголовки в этом *временном* объекте ответа.
+
+{* ../../docs_src/response_headers/tutorial002.py hl[1, 7:8] *}
+
+После этого вы можете вернуть любой нужный объект, как обычно (например, `dict`, модель из базы данных и т.д.).
+
+И, если вы объявили `response_model`, он всё равно будет использован для фильтрации и преобразования возвращённого объекта.
+
+**FastAPI** использует этот *временный* ответ, чтобы извлечь HTTP-заголовки (а также cookie и статус-код) и поместит их в финальный HTTP-ответ, который содержит возвращённое вами значение, отфильтрованное согласно `response_model`.
+
+Вы также можете объявлять параметр `Response` в зависимостях и устанавливать в них заголовки (и cookie).
+
+## Вернуть `Response` напрямую { #return-a-response-directly }
+
+Вы также можете добавить HTTP-заголовки, когда возвращаете `Response` напрямую.
+
+Создайте ответ, как описано в [Вернуть Response напрямую](response-directly.md){.internal-link target=_blank}, и передайте заголовки как дополнительный параметр:
+
+{* ../../docs_src/response_headers/tutorial001.py hl[10:12] *}
+
+/// note | Технические детали
+
+Вы также можете использовать `from starlette.responses import Response` или `from starlette.responses import JSONResponse`.
+
+**FastAPI** предоставляет те же самые `starlette.responses` как `fastapi.responses` — для вашего удобства как разработчика. Но большинство доступных классов ответов поступают напрямую из Starlette.
+
+И поскольку `Response` часто используется для установки заголовков и cookie, **FastAPI** также предоставляет его как `fastapi.Response`.
+
+///
+
+## Пользовательские HTTP-заголовки { #custom-headers }
+
+Помните, что собственные проприетарные заголовки можно добавлять, используя префикс `X-`.
+
+Но если у вас есть пользовательские заголовки, которые вы хотите показывать клиенту в браузере, вам нужно добавить их в настройки CORS (подробнее см. в [CORS (Cross-Origin Resource Sharing)](../tutorial/cors.md){.internal-link target=_blank}), используя параметр `expose_headers`, описанный в документации Starlette по CORS.
diff --git a/docs/ru/docs/advanced/security/http-basic-auth.md b/docs/ru/docs/advanced/security/http-basic-auth.md
new file mode 100644
index 0000000000..41e62d4bf3
--- /dev/null
+++ b/docs/ru/docs/advanced/security/http-basic-auth.md
@@ -0,0 +1,107 @@
+# HTTP Basic Auth { #http-basic-auth }
+
+Для самых простых случаев можно использовать HTTP Basic Auth.
+
+При HTTP Basic Auth приложение ожидает HTTP-заголовок, который содержит имя пользователя и пароль.
+
+Если его нет, возвращается ошибка HTTP 401 «Unauthorized».
+
+Также возвращается заголовок `WWW-Authenticate` со значением `Basic` и необязательным параметром `realm`.
+
+Это говорит браузеру показать встроенное окно запроса имени пользователя и пароля.
+
+Затем, когда вы вводите эти данные, браузер автоматически отправляет их в заголовке.
+
+## Простой HTTP Basic Auth { #simple-http-basic-auth }
+
+* Импортируйте `HTTPBasic` и `HTTPBasicCredentials`.
+* Создайте «схему» `security` с помощью `HTTPBasic`.
+* Используйте эту `security` как зависимость в вашей *операции пути*.
+* Она возвращает объект типа `HTTPBasicCredentials`:
+ * Он содержит отправленные `username` и `password`.
+
+{* ../../docs_src/security/tutorial006_an_py39.py hl[4,8,12] *}
+
+Когда вы впервые откроете URL (или нажмёте кнопку «Execute» в документации), браузер попросит ввести имя пользователя и пароль:
+
+
+
+## Проверка имени пользователя { #check-the-username }
+
+Вот более полный пример.
+
+Используйте зависимость, чтобы проверить, корректны ли имя пользователя и пароль.
+
+Для этого используйте стандартный модуль Python `secrets` для проверки имени пользователя и пароля.
+
+`secrets.compare_digest()` должен получать `bytes` или `str`, который содержит только символы ASCII (английские символы). Это значит, что он не будет работать с символами вроде `á`, как в `Sebastián`.
+
+Чтобы это обработать, сначала преобразуем `username` и `password` в `bytes`, закодировав их в UTF-8.
+
+Затем можно использовать `secrets.compare_digest()`, чтобы убедиться, что `credentials.username` равен `"stanleyjobson"`, а `credentials.password` — `"swordfish"`.
+
+{* ../../docs_src/security/tutorial007_an_py39.py hl[1,12:24] *}
+
+Это было бы похоже на:
+
+```Python
+if not (credentials.username == "stanleyjobson") or not (credentials.password == "swordfish"):
+ # Вернуть ошибку
+ ...
+```
+
+Но используя `secrets.compare_digest()`, вы защитите код от атак типа «тайминговая атака» (атака по времени).
+
+### Тайминговые атаки { #timing-attacks }
+
+Что такое «тайминговая атака»?
+
+Представим, что злоумышленники пытаются угадать имя пользователя и пароль.
+
+И они отправляют запрос с именем пользователя `johndoe` и паролем `love123`.
+
+Тогда Python-код в вашем приложении будет эквивалентен чему-то вроде:
+
+```Python
+if "johndoe" == "stanleyjobson" and "love123" == "swordfish":
+ ...
+```
+
+Но в момент, когда Python сравнит первую `j` в `johndoe` с первой `s` в `stanleyjobson`, он вернёт `False`, потому что уже ясно, что строки не совпадают, решив, что «нет смысла тратить ресурсы на сравнение остальных букв». И ваше приложение ответит «Неверное имя пользователя или пароль».
+
+Затем злоумышленники попробуют имя пользователя `stanleyjobsox` и пароль `love123`.
+
+И ваш код сделает что-то вроде:
+
+```Python
+if "stanleyjobsox" == "stanleyjobson" and "love123" == "swordfish":
+ ...
+```
+
+Pythonу придётся сравнить весь общий префикс `stanleyjobso` в `stanleyjobsox` и `stanleyjobson`, прежде чем понять, что строки отличаются. Поэтому на ответ «Неверное имя пользователя или пароль» уйдёт на несколько микросекунд больше.
+
+#### Время ответа помогает злоумышленникам { #the-time-to-answer-helps-the-attackers }
+
+Замечая, что сервер прислал «Неверное имя пользователя или пароль» на несколько микросекунд позже, злоумышленники поймут, что какая-то часть была угадана — начальные буквы верны.
+
+Тогда они могут попробовать снова, зная, что правильнее что-то ближе к `stanleyjobsox`, чем к `johndoe`.
+
+#### «Профессиональная» атака { #a-professional-attack }
+
+Конечно, злоумышленники не будут делать всё это вручную — они напишут программу, возможно, с тысячами или миллионами попыток в секунду. И будут подбирать по одной дополнительной верной букве за раз.
+
+Так за минуты или часы они смогут угадать правильные имя пользователя и пароль — с «помощью» нашего приложения — используя лишь время, затраченное на ответ.
+
+#### Исправление с помощью `secrets.compare_digest()` { #fix-it-with-secrets-compare-digest }
+
+Но в нашем коде мы используем `secrets.compare_digest()`.
+
+Вкратце: сравнение `stanleyjobsox` с `stanleyjobson` займёт столько же времени, сколько и сравнение `johndoe` с `stanleyjobson`. То же относится и к паролю.
+
+Таким образом, используя `secrets.compare_digest()` в коде приложения, вы защитите его от всего этого класса атак на безопасность.
+
+### Возврат ошибки { #return-the-error }
+
+После того как обнаружено, что учётные данные некорректны, верните `HTTPException` со статус-кодом ответа 401 (тем же, что и при отсутствии учётных данных) и добавьте HTTP-заголовок `WWW-Authenticate`, чтобы браузер снова показал окно входа:
+
+{* ../../docs_src/security/tutorial007_an_py39.py hl[26:30] *}
diff --git a/docs/ru/docs/advanced/security/index.md b/docs/ru/docs/advanced/security/index.md
new file mode 100644
index 0000000000..912e4812a5
--- /dev/null
+++ b/docs/ru/docs/advanced/security/index.md
@@ -0,0 +1,19 @@
+# Расширенная безопасность { #advanced-security }
+
+## Дополнительные возможности { #additional-features }
+
+Есть дополнительные возможности для работы с безопасностью помимо тех, что описаны в [Учебник — Руководство пользователя: Безопасность](../../tutorial/security/index.md){.internal-link target=_blank}.
+
+/// tip | Совет
+
+Следующие разделы **не обязательно являются «продвинутыми»**.
+
+И возможно, что решение для вашего варианта использования находится в одном из них.
+
+///
+
+## Сначала прочитайте руководство { #read-the-tutorial-first }
+
+В следующих разделах предполагается, что вы уже прочитали основной [Учебник — Руководство пользователя: Безопасность](../../tutorial/security/index.md){.internal-link target=_blank}.
+
+Все они основаны на тех же концепциях, но предоставляют дополнительные возможности.
diff --git a/docs/ru/docs/advanced/security/oauth2-scopes.md b/docs/ru/docs/advanced/security/oauth2-scopes.md
new file mode 100644
index 0000000000..8788df1991
--- /dev/null
+++ b/docs/ru/docs/advanced/security/oauth2-scopes.md
@@ -0,0 +1,274 @@
+# OAuth2 scopes { #oauth2-scopes }
+
+Вы можете использовать OAuth2 scopes (scope - область, рамки) напрямую с **FastAPI** — они интегрированы и работают бесшовно.
+
+Это позволит вам иметь более детальную систему разрешений по стандарту OAuth2, интегрированную в ваше OpenAPI‑приложение (и документацию API).
+
+OAuth2 со scopes — это механизм, который используют многие крупные провайдеры аутентификации: Facebook, Google, GitHub, Microsoft, X (Twitter) и т.д. Они применяют его, чтобы предоставлять конкретные разрешения пользователям и приложениям.
+
+Каждый раз, когда вы «входите через» Facebook, Google, GitHub, Microsoft, X (Twitter), это приложение использует OAuth2 со scopes.
+
+В этом разделе вы увидите, как управлять аутентификацией и авторизацией с теми же OAuth2 scopes в вашем приложении на **FastAPI**.
+
+/// warning | Предупреждение
+
+Это более-менее продвинутый раздел. Если вы только начинаете, можете пропустить его.
+
+Вам не обязательно нужны OAuth2 scopes — аутентификацию и авторизацию можно реализовать любым нужным вам способом.
+
+Но OAuth2 со scopes можно красиво интегрировать в ваш API (через OpenAPI) и документацию API.
+
+Так или иначе, вы все равно будете применять эти scopes или какие-то другие требования безопасности/авторизации, как вам нужно, в вашем коде.
+
+Во многих случаях OAuth2 со scopes может быть избыточным.
+
+Но если вы знаете, что это нужно, или вам просто интересно — продолжайте чтение.
+
+///
+
+## OAuth2 scopes и OpenAPI { #oauth2-scopes-and-openapi }
+
+Спецификация OAuth2 определяет «scopes» как список строк, разделённых пробелами.
+
+Содержимое каждой такой строки может иметь любой формат, но не должно содержать пробелов.
+
+Эти scopes представляют «разрешения».
+
+В OpenAPI (например, в документации API) можно определить «схемы безопасности» (security schemes).
+
+Когда одна из таких схем безопасности использует OAuth2, вы также можете объявлять и использовать scopes.
+
+Каждый «scope» — это просто строка (без пробелов).
+
+Обычно они используются для объявления конкретных разрешений безопасности, например:
+
+- `users:read` или `users:write` — распространённые примеры.
+- `instagram_basic` используется Facebook / Instagram.
+- `https://www.googleapis.com/auth/drive` используется Google.
+
+/// info | Информация
+
+В OAuth2 «scope» — это просто строка, объявляющая требуемое конкретное разрешение.
+
+Неважно, есть ли там другие символы, такие как `:`, или это URL.
+
+Эти детали зависят от реализации.
+
+Для OAuth2 это просто строки.
+
+///
+
+## Взгляд издалека { #global-view }
+
+Сначала быстро посмотрим, что изменилось по сравнению с примерами из основного раздела **Учебник - Руководство пользователя** — [OAuth2 с паролем (и хешированием), Bearer с JWT-токенами](../../tutorial/security/oauth2-jwt.md){.internal-link target=_blank}. Теперь — с использованием OAuth2 scopes:
+
+{* ../../docs_src/security/tutorial005_an_py310.py hl[5,9,13,47,65,106,108:116,122:126,130:136,141,157] *}
+
+Теперь рассмотрим эти изменения шаг за шагом.
+
+## OAuth2 схема безопасности { #oauth2-security-scheme }
+
+Первое изменение — мы объявляем схему безопасности OAuth2 с двумя доступными scopes: `me` и `items`.
+
+Параметр `scopes` получает `dict`, где каждый scope — это ключ, а описание — значение:
+
+{* ../../docs_src/security/tutorial005_an_py310.py hl[63:66] *}
+
+Так как теперь мы объявляем эти scopes, они появятся в документации API при входе/авторизации.
+
+И вы сможете выбрать, какие scopes вы хотите выдать доступ: `me` и `items`.
+
+Это тот же механизм, когда вы даёте разрешения при входе через Facebook, Google, GitHub и т.д.:
+
+
+
+## JWT-токены со scopes { #jwt-token-with-scopes }
+
+Теперь измените операцию пути, выдающую токен, чтобы возвращать запрошенные scopes.
+
+Мы всё ещё используем тот же `OAuth2PasswordRequestForm`. Он включает свойство `scopes` с `list` из `str` — каждый scope, полученный в запросе.
+
+И мы возвращаем scopes как часть JWT‑токена.
+
+/// danger | Опасность
+
+Для простоты здесь мы просто добавляем полученные scopes прямо в токен.
+
+Но в вашем приложении, в целях безопасности, следует убедиться, что вы добавляете только те scopes, которые пользователь действительно может иметь, или те, которые вы заранее определили.
+
+///
+
+{* ../../docs_src/security/tutorial005_an_py310.py hl[157] *}
+
+## Объявление scopes в *обработчиках путей* и зависимостях { #declare-scopes-in-path-operations-and-dependencies }
+
+Теперь объявим, что операция пути для `/users/me/items/` требует scope `items`.
+
+Для этого импортируем и используем `Security` из `fastapi`.
+
+Вы можете использовать `Security` для объявления зависимостей (как `Depends`), но `Security` также принимает параметр `scopes` со списком scopes (строк).
+
+В этом случае мы передаём функцию‑зависимость `get_current_active_user` в `Security` (точно так же, как сделали бы с `Depends`).
+
+Но мы также передаём `list` scopes — в данном случае только один scope: `items` (их могло быть больше).
+
+И функция‑зависимость `get_current_active_user` тоже может объявлять подзависимости не только через `Depends`, но и через `Security`, объявляя свою подзависимость (`get_current_user`) и дополнительные требования по scopes.
+
+В данном случае требуется scope `me` (их также могло быть больше одного).
+
+/// note | Примечание
+
+Вам не обязательно добавлять разные scopes в разных местах.
+
+Мы делаем это здесь, чтобы показать, как **FastAPI** обрабатывает scopes, объявленные на разных уровнях.
+
+///
+
+{* ../../docs_src/security/tutorial005_an_py310.py hl[5,141,172] *}
+
+/// info | Технические детали
+
+`Security` на самом деле является подклассом `Depends` и имеет всего один дополнительный параметр, который мы рассмотрим позже.
+
+Но используя `Security` вместо `Depends`, **FastAPI** будет знать, что можно объявлять security scopes, использовать их внутри и документировать API в OpenAPI.
+
+Однако когда вы импортируете `Query`, `Path`, `Depends`, `Security` и другие из `fastapi`, это на самом деле функции, возвращающие специальные классы.
+
+///
+
+## Использование `SecurityScopes` { #use-securityscopes }
+
+Теперь обновим зависимость `get_current_user`.
+
+Именно её используют зависимости выше.
+
+Здесь мы используем ту же схему OAuth2, созданную ранее, объявляя её как зависимость: `oauth2_scheme`.
+
+Поскольку у этой функции‑зависимости нет собственных требований по scopes, мы можем использовать `Depends` с `oauth2_scheme` — нам не нужно использовать `Security`, если не требуется указывать security scopes.
+
+Мы также объявляем специальный параметр типа `SecurityScopes`, импортированный из `fastapi.security`.
+
+Класс `SecurityScopes` похож на `Request` (через `Request` мы получали сам объект запроса).
+
+{* ../../docs_src/security/tutorial005_an_py310.py hl[9,106] *}
+
+## Использование `scopes` { #use-the-scopes }
+
+Параметр `security_scopes` будет типа `SecurityScopes`.
+
+У него есть свойство `scopes` со списком, содержащим все scopes, требуемые им самим и всеми зависимостями, использующими его как подзависимость. То есть всеми «зависящими»… это может звучать запутанно, ниже есть дополнительное объяснение.
+
+Объект `security_scopes` (класс `SecurityScopes`) также предоставляет атрибут `scope_str` — это одна строка с этими scopes, разделёнными пробелами (мы будем её использовать).
+
+Мы создаём `HTTPException`, который можем переиспользовать (`raise`) в нескольких местах.
+
+В этом исключении мы включаем требуемые scopes (если есть) в виде строки, разделённой пробелами (используя `scope_str`). Эту строку со scopes мы помещаем в HTTP‑заголовок `WWW-Authenticate` (это часть спецификации).
+
+{* ../../docs_src/security/tutorial005_an_py310.py hl[106,108:116] *}
+
+## Проверка `username` и формата данных { #verify-the-username-and-data-shape }
+
+Мы проверяем, что получили `username`, и извлекаем scopes.
+
+Затем валидируем эти данные с помощью Pydantic‑модели (перехватывая исключение `ValidationError`), и если возникает ошибка при чтении JWT‑токена или при валидации данных с Pydantic, мы вызываем `HTTPException`, созданное ранее.
+
+Для этого мы обновляем Pydantic‑модель `TokenData`, добавляя новое свойство `scopes`.
+
+Валидируя данные с помощью Pydantic, мы можем удостовериться, что у нас, например, именно `list` из `str` со scopes и `str` с `username`.
+
+А не, скажем, `dict` или что‑то ещё — ведь это могло бы где‑то позже сломать приложение и создать риск для безопасности.
+
+Мы также проверяем, что существует пользователь с таким именем, и если нет — вызываем то же исключение, созданное ранее.
+
+{* ../../docs_src/security/tutorial005_an_py310.py hl[47,117:129] *}
+
+## Проверка `scopes` { #verify-the-scopes }
+
+Теперь проверяем, что все требуемые scopes — этой зависимостью и всеми зависящими (включая операции пути) — присутствуют среди scopes, предоставленных в полученном токене, иначе вызываем `HTTPException`.
+
+Для этого используем `security_scopes.scopes`, содержащий `list` со всеми этими scopes как `str`.
+
+{* ../../docs_src/security/tutorial005_an_py310.py hl[130:136] *}
+
+## Дерево зависимостей и scopes { #dependency-tree-and-scopes }
+
+Ещё раз рассмотрим дерево зависимостей и scopes.
+
+Так как у зависимости `get_current_active_user` есть подзависимость `get_current_user`, scope `"me"`, объявленный в `get_current_active_user`, будет включён в список требуемых scopes в `security_scopes.scopes`, передаваемый в `get_current_user`.
+
+Сама операция пути тоже объявляет scope — `"items"`, поэтому он также будет в списке `security_scopes.scopes`, передаваемом в `get_current_user`.
+
+Иерархия зависимостей и scopes выглядит так:
+
+- Операция пути `read_own_items`:
+ - Запрашивает scopes `["items"]` с зависимостью:
+ - `get_current_active_user`:
+ - Функция‑зависимость `get_current_active_user`:
+ - Запрашивает scopes `["me"]` с зависимостью:
+ - `get_current_user`:
+ - Функция‑зависимость `get_current_user`:
+ - Собственных scopes не запрашивает.
+ - Имеет зависимость, использующую `oauth2_scheme`.
+ - Имеет параметр `security_scopes` типа `SecurityScopes`:
+ - Этот параметр `security_scopes` имеет свойство `scopes` с `list`, содержащим все объявленные выше scopes, то есть:
+ - `security_scopes.scopes` будет содержать `["me", "items"]` для операции пути `read_own_items`.
+ - `security_scopes.scopes` будет содержать `["me"]` для операции пути `read_users_me`, потому что он объявлен в зависимости `get_current_active_user`.
+ - `security_scopes.scopes` будет содержать `[]` (ничего) для операции пути `read_system_status`, потому что там не объявлялся `Security` со `scopes`, и его зависимость `get_current_user` тоже не объявляет `scopes`.
+
+/// tip | Совет
+
+Важный и «магический» момент здесь в том, что `get_current_user` будет иметь разный список `scopes` для проверки для каждой операции пути.
+
+Всё это зависит от `scopes`, объявленных в каждой операции пути и в каждой зависимости в дереве зависимостей конкретной операции пути.
+
+///
+
+## Больше деталей о `SecurityScopes` { #more-details-about-securityscopes }
+
+Вы можете использовать `SecurityScopes` в любой точке и в нескольких местах — необязательно в «корневой» зависимости.
+
+Он всегда будет содержать security scopes, объявленные в текущих зависимостях `Security`, и всеми зависящими — для этой конкретной операции пути и этого конкретного дерева зависимостей.
+
+Поскольку `SecurityScopes` будет содержать все scopes, объявленные зависящими, вы можете использовать его, чтобы централизованно проверять наличие требуемых scopes в токене в одной функции‑зависимости, а затем объявлять разные требования по scopes в разных операциях пути.
+
+Они будут проверяться независимо для каждой операции пути.
+
+## Проверим это { #check-it }
+
+Откройте документацию API — вы сможете аутентифицироваться и указать, какие scopes вы хотите авторизовать.
+
+
+
+Если вы не выберете ни один scope, вы будете «аутентифицированы», но при попытке доступа к `/users/me/` или `/users/me/items/` получите ошибку о недостаточных разрешениях. При этом доступ к `/status/` будет возможен.
+
+Если вы выберете scope `me`, но не `items`, вы сможете получить доступ к `/users/me/`, но не к `/users/me/items/`.
+
+Так и будет происходить со сторонним приложением, которое попытается обратиться к одной из этих операций пути с токеном, предоставленным пользователем, — в зависимости от того, сколько разрешений пользователь дал приложению.
+
+## О сторонних интеграциях { #about-third-party-integrations }
+
+В этом примере мы используем OAuth2 «password flow» (аутентификация по паролю).
+
+Это уместно, когда мы входим в наше собственное приложение, вероятно, с нашим собственным фронтендом.
+
+Мы можем ему доверять при получении `username` и `password`, потому что он под нашим контролем.
+
+Но если вы создаёте OAuth2‑приложение, к которому будут подключаться другие (т.е. вы строите провайдера аутентификации наподобие Facebook, Google, GitHub и т.п.), вам следует использовать один из других «flows».
+
+Самый распространённый — «implicit flow».
+
+Самый безопасный — «code flow», но он сложнее в реализации, так как требует больше шагов. Из‑за сложности многие провайдеры в итоге рекомендуют «implicit flow».
+
+/// note | Примечание
+
+Часто каждый провайдер аутентификации называет свои «flows» по‑разному — как часть бренда.
+
+Но в итоге они реализуют один и тот же стандарт OAuth2.
+
+///
+
+FastAPI включает утилиты для всех этих OAuth2‑flows в `fastapi.security.oauth2`.
+
+## `Security` в параметре `dependencies` декоратора { #security-in-decorator-dependencies }
+
+Точно так же, как вы можете определить `list` из `Depends` в параметре `dependencies` декоратора (см. [Зависимости в декораторах операции пути](../../tutorial/dependencies/dependencies-in-path-operation-decorators.md){.internal-link target=_blank}), вы можете использовать там и `Security` со `scopes`.
diff --git a/docs/ru/docs/advanced/settings.md b/docs/ru/docs/advanced/settings.md
new file mode 100644
index 0000000000..a335548c3c
--- /dev/null
+++ b/docs/ru/docs/advanced/settings.md
@@ -0,0 +1,346 @@
+# Настройки и переменные окружения { #settings-and-environment-variables }
+
+Во многих случаях вашему приложению могут понадобиться внешние настройки или конфигурации, например секретные ключи, учетные данные для базы данных, учетные данные для email‑сервисов и т.д.
+
+Большинство таких настроек являются изменяемыми (могут меняться), например URL базы данных. И многие из них могут быть «чувствительными», например секреты.
+
+По этой причине обычно их передают через переменные окружения, которые считываются приложением.
+
+/// tip | Совет
+
+Чтобы понять, что такое переменные окружения, вы можете прочитать [Переменные окружения](../environment-variables.md){.internal-link target=_blank}.
+
+///
+
+## Типы и валидация { #types-and-validation }
+
+Переменные окружения могут содержать только текстовые строки, так как они внешние по отношению к Python и должны быть совместимы с другими программами и остальной системой (и даже с разными операционными системами, такими как Linux, Windows, macOS).
+
+Это означает, что любое значение, прочитанное в Python из переменной окружения, будет `str`, а любые преобразования к другим типам или любая валидация должны выполняться в коде.
+
+## Pydantic `Settings` { #pydantic-settings }
+
+К счастью, Pydantic предоставляет отличную утилиту для работы с этими настройками, поступающими из переменных окружения, — Pydantic: управление настройками.
+
+### Установка `pydantic-settings` { #install-pydantic-settings }
+
+Сначала убедитесь, что вы создали [виртуальное окружение](../virtual-environments.md){.internal-link target=_blank}, активировали его, а затем установили пакет `pydantic-settings`:
+
+
+
+Затем откройте документацию для подприложения по адресу http://127.0.0.1:8000/subapi/docs.
+
+Вы увидите автоматическую документацию API для подприложения, включающую только его собственные _операции пути_, все под корректным префиксом подпути `/subapi`:
+
+
+
+Если вы попробуете взаимодействовать с любым из двух интерфейсов, всё будет работать корректно, потому что браузер сможет обращаться к каждому конкретному приложению и подприложению.
+
+### Технические подробности: `root_path` { #technical-details-root-path }
+
+Когда вы монтируете подприложение, как описано выше, FastAPI позаботится о передаче пути монтирования для подприложения, используя механизм из спецификации ASGI под названием `root_path`.
+
+Таким образом подприложение будет знать, что для интерфейса документации нужно использовать этот префикс пути.
+
+У подприложения также могут быть свои собственные смонтированные подприложения, и всё будет работать корректно, потому что FastAPI автоматически обрабатывает все эти `root_path`.
+
+Вы узнаете больше о `root_path` и о том, как использовать его явно, в разделе [За прокси](behind-a-proxy.md){.internal-link target=_blank}.
diff --git a/docs/ru/docs/advanced/templates.md b/docs/ru/docs/advanced/templates.md
new file mode 100644
index 0000000000..204e887605
--- /dev/null
+++ b/docs/ru/docs/advanced/templates.md
@@ -0,0 +1,126 @@
+# Шаблоны { #templates }
+
+Вы можете использовать любой шаблонизатор вместе с **FastAPI**.
+
+Часто выбирают Jinja2 — тот же, что используется во Flask и других инструментах.
+
+Есть утилиты для простой настройки, которые вы можете использовать прямо в своем приложении **FastAPI** (предоставляются Starlette).
+
+## Установка зависимостей { #install-dependencies }
+
+Убедитесь, что вы создали [виртуальное окружение](../virtual-environments.md){.internal-link target=_blank}, активировали его и установили `jinja2`:
+
+
+
+Вы можете набирать сообщения в поле ввода и отправлять их:
+
+
+
+И ваше **FastAPI** приложение с веб-сокетами ответит:
+
+
+
+Вы можете отправлять и получать множество сообщений:
+
+
+
+И все они будут использовать одно и то же веб-сокет соединение.
+
+## Использование `Depends` и не только { #using-depends-and-others }
+
+Вы можете импортировать из `fastapi` и использовать в эндпоинте вебсокета:
+
+* `Depends`
+* `Security`
+* `Cookie`
+* `Header`
+* `Path`
+* `Query`
+
+Они работают так же, как и в других FastAPI эндпоинтах/*операциях пути*:
+
+{* ../../docs_src/websockets/tutorial002_an_py310.py hl[68:69,82] *}
+
+/// info | Примечание
+
+В веб-сокете вызывать `HTTPException` не имеет смысла. Вместо этого нужно использовать `WebSocketException`.
+
+Закрывающий статус код можно использовать из valid codes defined in the specification.
+
+///
+
+### Веб-сокеты с зависимостями: проверка в действии { #try-the-websockets-with-dependencies }
+
+Если ваш файл называется `main.py`, то запустите приложение командой:
+
+
+
+## Обработка отключений и работа с несколькими клиентами { #handling-disconnections-and-multiple-clients }
+
+Если веб-сокет соединение закрыто, то `await websocket.receive_text()` вызовет исключение `WebSocketDisconnect`, которое можно поймать и обработать как в этом примере:
+
+{* ../../docs_src/websockets/tutorial003_py39.py hl[79:81] *}
+
+Чтобы воспроизвести пример:
+
+* Откройте приложение в нескольких вкладках браузера.
+* Отправьте из них сообщения.
+* Затем закройте одну из вкладок.
+
+Это вызовет исключение `WebSocketDisconnect`, и все остальные клиенты получат следующее сообщение:
+
+```
+Client #1596980209979 left the chat
+```
+
+/// tip | Подсказка
+
+Приложение выше - это всего лишь простой минимальный пример, демонстрирующий обработку и передачу сообщений нескольким веб-сокет соединениям.
+
+Но имейте в виду, что это будет работать только в одном процессе и только пока он активен, так как всё обрабатывается в простом списке в оперативной памяти.
+
+Если нужно что-то легко интегрируемое с FastAPI, но более надежное и с поддержкой Redis, PostgreSQL или другого, то можно воспользоваться encode/broadcaster.
+
+///
+
+## Дополнительная информация { #more-info }
+
+Для более глубокого изучения темы воспользуйтесь документацией Starlette:
+
+* The `WebSocket` class.
+* Class-based WebSocket handling.
diff --git a/docs/ru/docs/advanced/wsgi.md b/docs/ru/docs/advanced/wsgi.md
new file mode 100644
index 0000000000..1c5bf0a626
--- /dev/null
+++ b/docs/ru/docs/advanced/wsgi.md
@@ -0,0 +1,35 @@
+# Подключение WSGI — Flask, Django и другие { #including-wsgi-flask-django-others }
+
+Вы можете монтировать WSGI‑приложения, как вы видели в [Подприложения — Mounts](sub-applications.md){.internal-link target=_blank}, [За прокси‑сервером](behind-a-proxy.md){.internal-link target=_blank}.
+
+Для этого вы можете использовать `WSGIMiddleware` и обернуть им ваше WSGI‑приложение, например Flask, Django и т.д.
+
+## Использование `WSGIMiddleware` { #using-wsgimiddleware }
+
+Нужно импортировать `WSGIMiddleware`.
+
+Затем оберните WSGI‑приложение (например, Flask) в middleware (Промежуточный слой).
+
+После этого смонтируйте его на путь.
+
+{* ../../docs_src/wsgi/tutorial001.py hl[2:3,3] *}
+
+## Проверьте { #check-it }
+
+Теперь каждый HTTP‑запрос по пути `/v1/` будет обрабатываться приложением Flask.
+
+А всё остальное будет обрабатываться **FastAPI**.
+
+Если вы запустите это и перейдёте по http://localhost:8000/v1/, вы увидите HTTP‑ответ от Flask:
+
+```txt
+Hello, World from Flask!
+```
+
+А если вы перейдёте по http://localhost:8000/v2, вы увидите HTTP‑ответ от FastAPI:
+
+```JSON
+{
+ "message": "Hello World"
+}
+```
diff --git a/docs/ru/docs/alternatives.md b/docs/ru/docs/alternatives.md
index 413bf70b2d..17b54aad2f 100644
--- a/docs/ru/docs/alternatives.md
+++ b/docs/ru/docs/alternatives.md
@@ -1,104 +1,94 @@
-# Альтернативы, источники вдохновения и сравнения
+# Альтернативы, источники вдохновения и сравнения { #alternatives-inspiration-and-comparisons }
-Что вдохновило на создание **FastAPI**, сравнение его с альтернативами и чему он научился у них.
+Что вдохновило **FastAPI**, сравнение с альтернативами и чему он у них научился.
-## Введение
+## Введение { #intro }
-**FastAPI** не существовал бы, если б не было более ранних работ других людей.
+**FastAPI** не существовал бы без предыдущих работ других людей.
-Они создали большое количество инструментов, которые вдохновили меня на создание **FastAPI**.
+Было создано множество инструментов, которые вдохновили на его появление.
-Я всячески избегал создания нового фреймворка в течение нескольких лет.
-Сначала я пытался собрать все нужные функции, которые ныне есть в **FastAPI**, используя множество различных фреймворков, плагинов и инструментов.
+Я несколько лет избегал создания нового фреймворка. Сначала пытался закрыть все возможности, которые сейчас предоставляет **FastAPI**, с помощью множества разных фреймворков, плагинов и инструментов.
-Но в какой-то момент не осталось другого выбора, кроме как создать что-то, что предоставляло бы все эти функции сразу.
-Взять самые лучшие идеи из предыдущих инструментов и, используя новые возможности Python (которых не было до версии 3.6, то есть подсказки типов), объединить их.
+Но в какой-то момент не осталось другого варианта, кроме как создать что-то, что включает все эти возможности, взяв лучшие идеи из прежних инструментов и совместив их максимально удачным образом, используя возможности языка, которых прежде не было (аннотации типов в Python 3.6+).
-## Предшествующие инструменты
+## Предшествующие инструменты { #previous-tools }
-### Django
+### Django { #django }
-Это самый популярный Python-фреймворк, и он пользуется доверием.
-Он используется для создания проектов типа Instagram.
+Это самый популярный Python-фреймворк, ему широко доверяют. Он используется для построения систем вроде Instagram.
-Django довольно тесно связан с реляционными базами данных (такими как MySQL или PostgreSQL), потому использовать NoSQL базы данных (например, Couchbase, MongoDB, Cassandra и т.п.) в качестве основного хранилища данных - непросто.
+Он относительно тесно связан с реляционными базами данных (например, MySQL или PostgreSQL), поэтому использовать NoSQL-базу данных (например, Couchbase, MongoDB, Cassandra и т. п.) в качестве основного хранилища не очень просто.
-Он был создан для генерации HTML-страниц на сервере, а не для создания API, используемых современными веб-интерфейсами (React, Vue.js, Angular и т.п.) или другими системами (например, IoT) взаимодействующими с сервером.
+Он был создан для генерации HTML на бэкенде, а не для создания API, используемых современным фронтендом (например, React, Vue.js и Angular) или другими системами (например, устройствами IoT), которые с ним общаются.
-### Django REST Framework
+### Django REST Framework { #django-rest-framework }
-Фреймворк Django REST был создан, как гибкий инструментарий для создания веб-API на основе Django.
+Django REST Framework был создан как гибкий набор инструментов для построения веб-API поверх Django, чтобы улучшить его возможности в части API.
-DRF использовался многими компаниями, включая Mozilla, Red Hat и Eventbrite.
+Он используется многими компаниями, включая Mozilla, Red Hat и Eventbrite.
-Это был один из первых примеров **автоматического документирования API** и это была одна из первых идей, вдохновивших на создание **FastAPI**.
+Это был один из первых примеров **автоматической документации API**, и именно эта идея одной из первых вдохновила на «поиск» **FastAPI**.
-/// note | "Заметка"
+/// note | Заметка
-Django REST Framework был создан Tom Christie.
-Он же создал Starlette и Uvicorn, на которых основан **FastAPI**.
+Django REST Framework был создан Томом Кристи. Он же создал Starlette и Uvicorn, на которых основан **FastAPI**.
///
-/// check | "Идея для **FastAPI**"
+/// check | Вдохновило **FastAPI** на
-Должно быть автоматическое создание документации API с пользовательским веб-интерфейсом.
+Наличие пользовательского веб-интерфейса с автоматической документацией API.
///
-### Flask
+### Flask { #flask }
-Flask - это "микрофреймворк", в нём нет интеграции с базами данных и многих других вещей, которые предустановлены в Django.
+Flask — это «микрофреймворк», он не включает интеграции с базами данных и многие другие вещи, которые в Django идут «из коробки».
-Его простота и гибкость дают широкие возможности, такие как использование баз данных NoSQL в качестве основной системы хранения данных.
+Эта простота и гибкость позволяет, например, использовать NoSQL-базы в качестве основной системы хранения данных.
-Он очень прост, его изучение интуитивно понятно, хотя в некоторых местах документация довольно техническая.
+Он очень прост, его относительно легко интуитивно освоить, хотя местами документация довольно техническая.
-Flask часто используется и для приложений, которым не нужна база данных, настройки прав доступа для пользователей и прочие из множества функций, предварительно встроенных в Django.
-Хотя многие из этих функций могут быть добавлены с помощью плагинов.
+Его также часто используют для приложений, которым не нужна база данных, управление пользователями или многие другие функции, предварительно встроенные в Django. Хотя многие из этих возможностей можно добавить плагинами.
-Такое разделение на части и то, что это "микрофреймворк", который можно расширить, добавляя необходимые возможности, было ключевой особенностью, которую я хотел сохранить.
+Такое разбиение на части и то, что это «микрофреймворк», который можно расширять ровно под нужды, — ключевая особенность, которую хотелось сохранить.
-Простота Flask, показалась мне подходящей для создания API.
-Но ещё нужно было найти "Django REST Framework" для Flask.
+С учётом простоты Flask он казался хорошим вариантом для создания API. Следующим было найти «Django REST Framework» для Flask.
-/// check | "Идеи для **FastAPI**"
+/// check | Вдохновило **FastAPI** на
-Это будет микрофреймворк. К нему легко будет добавить необходимые инструменты и части.
+Быть микро-фреймворком. Облегчить комбинирование необходимых инструментов и компонентов.
-Должна быть простая и лёгкая в использовании система маршрутизации запросов.
+Иметь простую и удобную систему маршрутизации.
///
-### Requests
+### Requests { #requests }
-На самом деле **FastAPI** не является альтернативой **Requests**.
-Их область применения очень разная.
+**FastAPI** на самом деле не альтернатива **Requests**. Их области применения очень различны.
-В принципе, можно использовать Requests *внутри* приложения FastAPI.
+Обычно Requests используют даже внутри приложения FastAPI.
-Но всё же я использовал в FastAPI некоторые идеи из Requests.
+И всё же **FastAPI** во многом вдохновлялся Requests.
-**Requests** - это библиотека для взаимодействия с API в качестве клиента,
-в то время как **FastAPI** - это библиотека для *создания* API (то есть сервера).
+**Requests** — это библиотека для взаимодействия с API (как клиент), а **FastAPI** — библиотека для создания API (как сервер).
-Они, так или иначе, диаметрально противоположны и дополняют друг друга.
+Они, в каком-то смысле, находятся на противоположных концах и дополняют друг друга.
-Requests имеет очень простой и понятный дизайн, очень прост в использовании и имеет разумные значения по умолчанию.
-И в то же время он очень мощный и настраиваемый.
+Requests имеет очень простой и понятный дизайн, им очень легко пользоваться, есть разумные значения по умолчанию. И при этом он очень мощный и настраиваемый.
-Вот почему на официальном сайте написано:
+Именно поэтому на официальном сайте сказано:
-> Requests - один из самых загружаемых пакетов Python всех времен
+> Requests — один из самых загружаемых Python-пакетов всех времён
-
-Использовать его очень просто. Например, чтобы выполнить запрос `GET`, Вы бы написали:
+Пользоваться им очень просто. Например, чтобы сделать запрос `GET`, вы бы написали:
```Python
response = requests.get("http://example.com/some/url")
```
-Противоположная *операция пути* в FastAPI может выглядеть следующим образом:
+Соответствующая в FastAPI API-операция пути могла бы выглядеть так:
```Python hl_lines="1"
@app.get("/some/url")
@@ -106,428 +96,390 @@ def read_url():
return {"message": "Hello World"}
```
-Глядите, как похоже `requests.get(...)` и `@app.get(...)`.
+Посмотрите, насколько похожи `requests.get(...)` и `@app.get(...)`.
-/// check | "Идеи для **FastAPI**"
+/// check | Вдохновило **FastAPI** на
-* Должен быть простой и понятный API.
-* Нужно использовать названия HTTP-методов (операций) для упрощения понимания происходящего.
-* Должны быть разумные настройки по умолчанию и широкие возможности их кастомизации.
+* Иметь простой и понятный API.
+* Использовать названия HTTP-методов (операций) напрямую, простым и интуитивным образом.
+* Иметь разумные значения по умолчанию, но и мощные настройки.
///
-### Swagger / OpenAPI
+### Swagger / OpenAPI { #swagger-openapi }
-Главной функцией, которую я хотел унаследовать от Django REST Framework, была автоматическая документация API.
+Главной возможностью, которую хотелось взять из Django REST Framework, была автоматическая документация API.
-Но потом я обнаружил, что существует стандарт документирования API, использующий JSON (или YAML, расширение JSON) под названием Swagger.
+Затем я обнаружил, что есть стандарт для документирования API с использованием JSON (или YAML — расширения JSON), под названием Swagger.
-И к нему уже был создан пользовательский веб-интерфейс.
-Таким образом, возможность генерировать документацию Swagger для API позволила бы использовать этот интерфейс.
+И уже существовал веб-интерфейс для Swagger API. Поэтому возможность генерировать документацию Swagger для API позволила бы автоматически использовать этот веб-интерфейс.
В какой-то момент Swagger был передан Linux Foundation и переименован в OpenAPI.
-Вот почему, когда говорят о версии 2.0, обычно говорят "Swagger", а для версии 3+ "OpenAPI".
+Вот почему, говоря о версии 2.0, обычно говорят «Swagger», а о версии 3+ — «OpenAPI».
-/// check | "Идеи для **FastAPI**"
+/// check | Вдохновило **FastAPI** на
-Использовать открытые стандарты для спецификаций API вместо самодельных схем.
+Использовать открытый стандарт для спецификаций API вместо самодельной схемы.
-Совместимость с основанными на стандартах пользовательскими интерфейсами:
+И интегрировать основанные на стандартах инструменты пользовательского интерфейса:
* Swagger UI
* ReDoc
-Они были выбраны за популярность и стабильность.
-Но сделав беглый поиск, Вы можете найти десятки альтернативных пользовательских интерфейсов для OpenAPI, которые Вы можете использовать с **FastAPI**.
+Эти два инструмента выбраны за популярность и стабильность, но даже при беглом поиске можно найти десятки альтернативных интерфейсов для OpenAPI (которые можно использовать с **FastAPI**).
///
-### REST фреймворки для Flask
+### REST-фреймворки для Flask { #flask-rest-frameworks }
-Существует несколько REST фреймворков для Flask, но потратив время и усилия на их изучение, я обнаружил, что многие из них не обновляются или заброшены и имеют нерешённые проблемы из-за которых они непригодны к использованию.
+Существует несколько REST-фреймворков для Flask, но, вложив время и усилия в исследование, я обнаружил, что многие из них прекращены или заброшены, с несколькими нерешёнными Issue (тикет\обращение), из-за которых они непригодны.
-### Marshmallow
+### Marshmallow { #marshmallow }
-Одной из основных функций, необходимых системам API, является "сериализация" данных, то есть преобразование данных из кода (Python) во что-то, что может быть отправлено по сети.
-Например, превращение объекта содержащего данные из базы данных в объект JSON, конвертация объекта `datetime` в строку и т.п.
+Одна из основных возможностей, нужных системам API, — «сериализация» данных, то есть преобразование данных из кода (Python) во что-то, что можно отправить по сети. Например, преобразование объекта с данными из базы в JSON-объект. Преобразование объектов `datetime` в строки и т. п.
-Еще одна важная функция, необходимая API — проверка данных, позволяющая убедиться, что данные действительны и соответствуют заданным параметрам.
-Как пример, можно указать, что ожидаются данные типа `int`, а не какая-то произвольная строка.
-Это особенно полезно для входящих данных.
+Ещё одна важная возможность, востребованная API, — валидация данных: убеждаться, что данные валидны с учётом заданных параметров. Например, что какое-то поле — `int`, а не произвольная строка. Это особенно полезно для входящих данных.
-Без системы проверки данных Вам пришлось бы прописывать все проверки вручную.
+Без системы валидации данных вам пришлось бы выполнять все проверки вручную в коде.
-Именно для обеспечения этих функций и была создана Marshmallow.
-Это отличная библиотека и я много раз пользовался ею раньше.
+Именно для этих возможностей и был создан Marshmallow. Это отличная библиотека, я много ей пользовался раньше.
-Но она была создана до того, как появились подсказки типов Python.
-Итак, чтобы определить каждую схему,
-Вам нужно использовать определенные утилиты и классы, предоставляемые Marshmallow.
+Но она появилась до того, как в Python появились аннотации типов. Поэтому для определения каждой схемы нужно использовать специальные утилиты и классы, предоставляемые Marshmallow.
-/// check | "Идея для **FastAPI**"
+/// check | Вдохновило **FastAPI** на
-Использовать код программы для автоматического создания "схем", определяющих типы данных и их проверку.
+Использовать код для автоматического определения «схем», задающих типы данных и их валидацию.
///
-### Webargs
+### Webargs { #webargs }
-Другая немаловажная функция API - парсинг данных из входящих запросов.
+Ещё одна важная возможность для API — парсинг данных из входящих HTTP-запросов.
-Webargs - это инструмент, который был создан для этого и поддерживает несколько фреймворков, включая Flask.
+Webargs — это инструмент, созданный для этого поверх нескольких фреймворков, включая Flask.
-Для проверки данных он использует Marshmallow и создан теми же авторами.
+Он использует Marshmallow для валидации данных. И создан теми же разработчиками.
-Это превосходный инструмент и я тоже часто пользовался им до **FastAPI**.
+Это отличный инструмент, и я тоже много им пользовался до появления **FastAPI**.
-/// info | "Информация"
+/// info | Информация
-Webargs бы создан разработчиками Marshmallow.
+Webargs был создан теми же разработчиками, что и Marshmallow.
///
-/// check | "Идея для **FastAPI**"
+/// check | Вдохновило **FastAPI** на
-Должна быть автоматическая проверка входных данных.
+Автоматическую валидацию входящих данных HTTP-запроса.
///
-### APISpec
+### APISpec { #apispec }
-Marshmallow и Webargs осуществляют проверку, анализ и сериализацию данных как плагины.
+Marshmallow и Webargs предоставляют валидацию, парсинг и сериализацию как плагины.
-Но документации API всё ещё не было. Тогда был создан APISpec.
+Но документации всё ещё не было. Тогда появился APISpec.
-Это плагин для множества фреймворков, в том числе и для Starlette.
+Это плагин для многих фреймворков (есть плагин и для Starlette).
-Он работает так - Вы записываете определение схем, используя формат YAML, внутри докстринга каждой функции, обрабатывающей маршрут.
+Он работает так: вы пишете определение схемы в формате YAML внутри докстринга каждой функции, обрабатывающей маршрут.
-Используя эти докстринги, он генерирует схему OpenAPI.
+И он генерирует схемы OpenAPI.
-Так это работает для Flask, Starlette, Responder и т.п.
+Так это работает во Flask, Starlette, Responder и т. д.
-Но теперь у нас возникает новая проблема - наличие постороннего микро-синтаксиса внутри кода Python (большие YAML).
+Но у нас снова возникает проблема: появляется микро-синтаксис внутри строки Python (большой YAML).
-Редактор кода не особо может помочь в такой парадигме.
-А изменив какие-то параметры или схемы для Marshmallow можно забыть отредактировать докстринг с YAML и сгенерированная схема становится недействительной.
+Редактор кода мало чем может помочь. И если мы изменим параметры или схемы Marshmallow и забудем также изменить YAML в докстринге, сгенерированная схема устареет.
-/// info | "Информация"
+/// info | Информация
-APISpec тоже был создан авторами Marshmallow.
+APISpec был создан теми же разработчиками, что и Marshmallow.
///
-/// check | "Идея для **FastAPI**"
+/// check | Вдохновило **FastAPI** на
-Необходима поддержка открытого стандарта для API - OpenAPI.
+Поддержку открытого стандарта для API — OpenAPI.
///
-### Flask-apispec
+### Flask-apispec { #flask-apispec }
-Это плагин для Flask, который связан с Webargs, Marshmallow и APISpec.
+Это плагин для Flask, который связывает Webargs, Marshmallow и APISpec.
-Он получает информацию от Webargs и Marshmallow, а затем использует APISpec для автоматического создания схемы OpenAPI.
+Он использует информацию из Webargs и Marshmallow, чтобы автоматически генерировать схемы OpenAPI с помощью APISpec.
-Это отличный, но крайне недооценённый инструмент.
-Он должен быть более популярен, чем многие плагины для Flask.
-Возможно, это связано с тем, что его документация слишком скудна и абстрактна.
+Отличный и недооценённый инструмент. Он заслуживает большей популярности, чем многие плагины для Flask. Возможно, из-за слишком краткой и абстрактной документации.
-Он избавил от необходимости писать чужеродный синтаксис YAML внутри докстрингов.
+Это решило проблему необходимости писать YAML (ещё один синтаксис) в докстрингах Python.
-Такое сочетание Flask, Flask-apispec, Marshmallow и Webargs было моим любимым стеком при построении бэкенда до появления **FastAPI**.
+Комбинация Flask, Flask-apispec с Marshmallow и Webargs была моим любимым бэкенд-стеком до создания **FastAPI**.
-Использование этого стека привело к созданию нескольких генераторов проектов. Я и некоторые другие команды до сих пор используем их:
+Его использование привело к созданию нескольких full-stack генераторов на Flask. Это основные стеки, которые я (и несколько внешних команд) использовали до сих пор:
* https://github.com/tiangolo/full-stack
* https://github.com/tiangolo/full-stack-flask-couchbase
* https://github.com/tiangolo/full-stack-flask-couchdb
-Эти генераторы проектов также стали основой для [Генераторов проектов с **FastAPI**](project-generation.md){.internal-link target=_blank}.
+И эти же full-stack генераторы стали основой для [Генераторов проектов **FastAPI**](project-generation.md){.internal-link target=_blank}.
-/// info | "Информация"
+/// info | Информация
-Как ни странно, но Flask-apispec тоже создан авторами Marshmallow.
+Flask-apispec был создан теми же разработчиками, что и Marshmallow.
///
-/// check | "Идея для **FastAPI**"
+/// check | Вдохновило **FastAPI** на
-Схема OpenAPI должна создаваться автоматически и использовать тот же код, который осуществляет сериализацию и проверку данных.
+Автоматическую генерацию схемы OpenAPI из того же кода, который определяет сериализацию и валидацию.
///
-### NestJS (и Angular)
+### NestJS (и Angular) { #nestjs-and-angular }
-Здесь даже не используется Python. NestJS - этот фреймворк написанный на JavaScript (TypeScript), основанный на NodeJS и вдохновлённый Angular.
+Это даже не Python. NestJS — это JavaScript/TypeScript-фреймворк на NodeJS, вдохновлённый Angular.
-Он позволяет получить нечто похожее на то, что можно сделать с помощью Flask-apispec.
+Он достигает чего-то отчасти похожего на то, что можно сделать с Flask-apispec.
-В него встроена система внедрения зависимостей, ещё одна идея взятая от Angular.
-Однако требуется предварительная регистрация "внедрений" (как и во всех других известных мне системах внедрения зависимостей), что увеличивает количество и повторяемость кода.
+В нём встроена система внедрения зависимостей, вдохновлённая Angular 2. Требуется предварительная регистрация «инжектируемых» компонентов (как и во всех известных мне системах внедрения зависимостей), что добавляет многословности и повторяемости кода.
-Так как параметры в нём описываются с помощью типов TypeScript (аналогично подсказкам типов в Python), поддержка редактора работает довольно хорошо.
+Поскольку параметры описываются с помощью типов TypeScript (аналог аннотаций типов в Python), поддержка редактора весьма хороша.
-Но поскольку данные из TypeScript не сохраняются после компиляции в JavaScript, он не может полагаться на подсказки типов для определения проверки данных, сериализации и документации.
-Из-за этого и некоторых дизайнерских решений, для валидации, сериализации и автоматической генерации схем, приходится во многих местах добавлять декораторы.
-Таким образом, это становится довольно многословным.
+Но так как данные о типах TypeScript не сохраняются после компиляции в JavaScript, он не может полагаться на типы для одновременного определения валидации, сериализации и документации. Из‑за этого и некоторых проектных решений для получения валидации, сериализации и автоматической генерации схем приходится добавлять декораторы во многих местах. В итоге это становится довольно многословным.
-Кроме того, он не очень хорошо справляется с вложенными моделями.
-Если в запросе имеется объект JSON, внутренние поля которого, в свою очередь, являются вложенными объектами JSON, это не может быть должным образом задокументировано и проверено.
+Он плохо справляется с вложенными моделями. Если JSON-тело запроса — это объект JSON, содержащий внутренние поля, которые сами являются вложенными объектами JSON, это нельзя как следует задокументировать и провалидировать.
-/// check | "Идеи для **FastAPI** "
+/// check | Вдохновило **FastAPI** на
-Нужно использовать подсказки типов, чтоб воспользоваться поддержкой редактора кода.
+Использовать типы Python для отличной поддержки в редакторе кода.
-Нужна мощная система внедрения зависимостей. Необходим способ для уменьшения повторов кода.
+Иметь мощную систему внедрения зависимостей. Найти способ минимизировать повторение кода.
///
-### Sanic
+### Sanic { #sanic }
-Sanic был одним из первых чрезвычайно быстрых Python-фреймворков основанных на `asyncio`.
-Он был сделан очень похожим на Flask.
+Это был один из первых чрезвычайно быстрых Python-фреймворков на основе `asyncio`. Он был сделан очень похожим на Flask.
-/// note | "Технические детали"
+/// note | Технические детали
-В нём использован `uvloop` вместо стандартного цикла событий `asyncio`, что и сделало его таким быстрым.
+Он использовал `uvloop` вместо стандартного цикла `asyncio` в Python. Это и сделало его таким быстрым.
-Он явно вдохновил создателей Uvicorn и Starlette, которые в настоящее время быстрее Sanic в открытых бенчмарках.
+Он явно вдохновил Uvicorn и Starlette, которые сейчас быстрее Sanic в открытых бенчмарках.
///
-/// check | "Идеи для **FastAPI**"
+/// check | Вдохновило **FastAPI** на
-Должна быть сумасшедшая производительность.
+Поиск способа достичь сумасшедшей производительности.
-Для этого **FastAPI** основан на Starlette, самом быстром из доступных фреймворков (по замерам незаинтересованных лиц).
+Именно поэтому **FastAPI** основан на Starlette, так как это самый быстрый доступный фреймворк (по данным сторонних бенчмарков).
///
-### Falcon
+### Falcon { #falcon }
-Falcon - ещё один высокопроизводительный Python-фреймворк.
-В нём минимум функций и он создан, чтоб быть основой для других фреймворков, например, Hug.
+Falcon — ещё один высокопроизводительный Python-фреймворк, он минималистичен и служит основой для других фреймворков, таких как Hug.
-Функции в нём получают два параметра - "запрос к серверу" и "ответ сервера".
-Затем Вы "читаете" часть запроса и "пишите" часть ответа.
-Из-за такой конструкции невозможно объявить параметры запроса и тела сообщения со стандартными подсказками типов Python в качестве параметров функции.
+Он спроектирован так, что функции получают два параметра: «request» и «response». Затем вы «читаете» части из запроса и «пишете» части в ответ. Из‑за такого дизайна невозможно объявить параметры запроса и тело запроса стандартными аннотациями типов Python как параметры функции.
-Таким образом, и валидацию данных, и их сериализацию, и документацию нужно прописывать вручную.
-Либо эти функции должны быть встроены во фреймворк, сконструированный поверх Falcon, как в Hug.
-Такая же особенность присутствует и в других фреймворках, вдохновлённых идеей Falcon, использовать только один объект запроса и один объект ответа.
+Поэтому валидация данных, сериализация и документация должны выполняться в коде вручную, не автоматически. Либо должны быть реализованы во фреймворке поверх Falcon, как в Hug. Та же особенность есть и в других фреймворках, вдохновлённых дизайном Falcon — с одним объектом запроса и одним объектом ответа в параметрах.
-/// check | "Идея для **FastAPI**"
+/// check | Вдохновило **FastAPI** на
-Найдите способы добиться отличной производительности.
+Поиск способов получить отличную производительность.
-Объявлять параметры `ответа сервера` в функциях, как в Hug.
+Вместе с Hug (так как Hug основан на Falcon) вдохновило **FastAPI** объявлять параметр `response` в функциях.
-Хотя в FastAPI это необязательно и используется в основном для установки заголовков, куки и альтернативных кодов состояния.
+Хотя в FastAPI это необязательно, и используется в основном для установки HTTP-заголовков, cookie и альтернативных статус-кодов.
///
-### Molten
+### Molten { #molten }
-Molten мне попался на начальной стадии написания **FastAPI**. В нём были похожие идеи:
+Я обнаружил Molten на ранних этапах создания **FastAPI**. И у него были очень похожие идеи:
-* Использование подсказок типов.
-* Валидация и документация исходя из этих подсказок.
+* Основан на аннотациях типов Python.
+* Валидация и документация из этих типов.
* Система внедрения зависимостей.
-В нём не используются сторонние библиотеки (такие, как Pydantic) для валидации, сериализации и документации.
-Поэтому переиспользовать эти определения типов непросто.
+Он не использует стороннюю библиотеку для валидации, сериализации и документации, такую как Pydantic, — у него своя. Поэтому такие определения типов данных будет сложнее переиспользовать.
-Также требуется более подробная конфигурация и используется стандарт WSGI, который не предназначен для использования с высокопроизводительными инструментами, такими как Uvicorn, Starlette и Sanic, в отличие от ASGI.
+Требуются более многословные конфигурации. И так как он основан на WSGI (вместо ASGI), он не предназначен для использования преимуществ высокой производительности инструментов вроде Uvicorn, Starlette и Sanic.
-Его система внедрения зависимостей требует предварительной регистрации, и зависимости определяются, как объявления типов.
-Из-за этого невозможно объявить более одного "компонента" (зависимости), который предоставляет определенный тип.
+Система внедрения зависимостей требует предварительной регистрации зависимостей, а зависимости разрешаются по объявленным типам. Поэтому невозможно объявить более одного «компонента», предоставляющего определённый тип.
-Маршруты объявляются в единственном месте с использованием функций, объявленных в других местах (вместо использования декораторов, в которые могут быть обёрнуты функции, обрабатывающие конкретные ресурсы).
-Это больше похоже на Django, чем на Flask и Starlette.
-Он разделяет в коде вещи, которые довольно тесно связаны.
+Маршруты объявляются в одном месте, используя функции, объявленные в других местах (вместо декораторов, которые можно разместить прямо над функцией, обрабатывающей эндпоинт). Это ближе к тому, как это делает Django, чем Flask (и Starlette). Это разделяет в коде вещи, которые довольно тесно связаны.
-/// check | "Идея для **FastAPI**"
+/// check | Вдохновило **FastAPI** на
-Определить дополнительные проверки типов данных, используя значения атрибутов модели "по умолчанию".
-Это улучшает помощь редактора и раньше это не было доступно в Pydantic.
+Определять дополнительные проверки типов данных, используя значение «по умолчанию» атрибутов модели. Это улучшает поддержку в редакторе кода, и раньше этого не было в Pydantic.
-Фактически это подтолкнуло на обновление Pydantic для поддержки одинакового стиля проверок (теперь этот функционал уже доступен в Pydantic).
+Фактически это вдохновило на обновление частей Pydantic, чтобы поддерживать такой же стиль объявления валидации (вся эта функциональность теперь уже есть в Pydantic).
///
-### Hug
+### Hug { #hug }
-Hug был одним из первых фреймворков, реализовавших объявление параметров API с использованием подсказок типов Python.
-Эта отличная идея была использована и другими инструментами.
+Hug был одним из первых фреймворков, реализовавших объявление типов параметров API с использованием аннотаций типов Python. Это была отличная идея, которая вдохновила и другие инструменты.
-При объявлении параметров вместо стандартных типов Python использовались собственные типы, но всё же это был огромный шаг вперед.
+Он использовал собственные типы в объявлениях вместо стандартных типов Python, но это всё равно был огромный шаг вперёд.
-Это также был один из первых фреймворков, генерировавших полную API-схему в формате JSON.
+Он также был одним из первых фреймворков, генерировавших собственную схему, описывающую весь API в JSON.
-Данная схема не придерживалась стандартов вроде OpenAPI и JSON Schema.
-Поэтому было бы непросто совместить её с другими инструментами, такими как Swagger UI.
-Но опять же, это была очень инновационная идея.
+Он не был основан на стандартах вроде OpenAPI и JSON Schema. Поэтому интегрировать его с другими инструментами, такими как Swagger UI, было бы непросто. Но, опять же, это была очень инновационная идея.
-Ещё у него есть интересная и необычная функция: используя один и тот же фреймворк можно создавать и API, и CLI.
+У него есть интересная и необычная особенность: с помощью одного и того же фреймворка можно создавать и API, и CLI.
-Поскольку он основан на WSGI, старом стандарте для синхронных веб-фреймворков, он не может работать с веб-сокетами и другими модными штуками, но всё равно обладает высокой производительностью.
+Так как он основан на предыдущем стандарте для синхронных веб-фреймворков Python (WSGI), он не может работать с WebSocket и прочим, хотя также демонстрирует высокую производительность.
-/// info | "Информация"
+/// info | Информация
-Hug создан Timothy Crosley, автором `isort`, отличного инструмента для автоматической сортировки импортов в Python-файлах.
+Hug был создан Тимоти Кросли, тем же автором `isort`, отличного инструмента для автоматической сортировки импортов в файлах Python.
///
-/// check | "Идеи для **FastAPI**"
+/// check | Идеи, вдохновившие **FastAPI**
-Hug повлиял на создание некоторых частей APIStar и был одним из инструментов, которые я счел наиболее многообещающими, наряду с APIStar.
+Hug вдохновил части APIStar и был одним из наиболее многообещающих инструментов, которые я нашёл, наряду с APIStar.
-Hug натолкнул на мысли использовать в **FastAPI** подсказки типов Python для автоматического создания схемы, определяющей API и его параметры.
+Hug помог вдохновить **FastAPI** использовать аннотации типов Python для объявления параметров и автоматически генерировать схему, определяющую API.
-Hug вдохновил **FastAPI** объявить параметр `ответа` в функциях для установки заголовков и куки.
+Hug вдохновил **FastAPI** объявлять параметр `response` в функциях для установки HTTP-заголовков и cookie.
///
-### APIStar (<= 0.5)
+### APIStar (<= 0.5) { #apistar-0-5 }
-Непосредственно перед тем, как принять решение о создании **FastAPI**, я обнаружил **APIStar**.
-В нем было почти все, что я искал и у него был отличный дизайн.
+Прямо перед решением строить **FastAPI** я нашёл сервер **APIStar**. В нём было почти всё, что я искал, и отличный дизайн.
-Это была одна из первых реализаций фреймворка, использующего подсказки типов для объявления параметров и запросов, которые я когда-либо видел (до NestJS и Molten).
-Я нашёл его примерно в то же время, что и Hug, но APIStar использовал стандарт OpenAPI.
+Это была одна из первых реализаций фреймворка, использующего аннотации типов Python для объявления параметров и запросов (до NestJS и Molten), которые я видел. Я обнаружил его примерно в то же время, что и Hug. Но APIStar использовал стандарт OpenAPI.
-В нём были автоматические проверка и сериализация данных и генерация схемы OpenAPI основанные на подсказках типов в нескольких местах.
+В нём были автоматические валидация данных, сериализация данных и генерация схемы OpenAPI на основе тех же аннотаций типов в нескольких местах.
-При определении схемы тела сообщения не использовались подсказки типов, как в Pydantic, это больше похоже на Marshmallow, поэтому помощь редактора была недостаточно хорошей, но всё же APIStar был лучшим доступным вариантом.
+Определение схемы тела запроса не использовало те же аннотации типов Python, как в Pydantic, — это было ближе к Marshmallow, поэтому поддержка редактора была бы хуже, но всё равно APIStar оставался лучшим доступным вариантом.
-На тот момент у него были лучшие показатели производительности (проигрывающие только Starlette).
+На тот момент у него были лучшие показатели в бенчмарках (его превосходил только Starlette).
-Изначально у него не было автоматической документации API для веб-интерфейса, но я знал, что могу добавить к нему Swagger UI.
+Сначала у него не было веб‑UI для автоматической документации API, но я знал, что могу добавить к нему Swagger UI.
-В APIStar была система внедрения зависимостей, которая тоже требовала предварительную регистрацию компонентов, как и ранее описанные инструменты.
-Но, тем не менее, это была отличная штука.
+У него была система внедрения зависимостей. Она требовала предварительной регистрации компонентов, как и другие инструменты, обсуждавшиеся выше. Но всё же это была отличная возможность.
-Я не смог использовать его в полноценном проекте, так как были проблемы со встраиванием функций безопасности в схему OpenAPI, из-за которых невозможно было встроить все функции, применяемые в генераторах проектов на основе Flask-apispec.
-Я добавил в свой список задач создание пул-реквеста, добавляющего эту функциональность.
+Мне так и не удалось использовать его в полном проекте, поскольку не было интеграции с системой безопасности, поэтому я не мог заменить все возможности, которые имел с full-stack генераторами на основе Flask-apispec. В моём бэклоге было создать пулл-реквест (запрос на изменение), добавляющий эту функциональность.
-В дальнейшем фокус проекта сместился.
+Затем фокус проекта сместился.
-Это больше не был API-фреймворк, так как автор сосредоточился на Starlette.
+Это перестал быть веб-фреймворк для API, так как автору нужно было сосредоточиться на Starlette.
-Ныне APIStar - это набор инструментов для проверки спецификаций OpenAPI.
+Сейчас APIStar — это набор инструментов для валидации спецификаций OpenAPI, а не веб-фреймворк.
-/// info | "Информация"
+/// info | Информация
-APIStar был создан Tom Christie. Тот самый парень, который создал:
+APIStar был создан Томом Кристи. Тем самым человеком, который создал:
* Django REST Framework
* Starlette (на котором основан **FastAPI**)
-* Uvicorn (используемый в Starlette и **FastAPI**)
+* Uvicorn (используется Starlette и **FastAPI**)
///
-/// check | "Идеи для **FastAPI**"
+/// check | Вдохновило **FastAPI** на
-Воплощение.
+Существование.
-Мне казалось блестящей идеей объявлять множество функций (проверка данных, сериализация, документация) с помощью одних и тех же типов Python, которые при этом обеспечивают ещё и помощь редактора кода.
+Идея объявлять сразу несколько вещей (валидацию данных, сериализацию и документацию) с помощью одних и тех же типов Python, которые одновременно обеспечивают отличную поддержку в редакторе кода, показалась мне блестящей.
-После долгих поисков среди похожих друг на друга фреймворков и сравнения их различий, APIStar стал самым лучшим выбором.
+После долгих поисков похожего фреймворка и тестирования множества альтернатив APIStar был лучшим доступным вариантом.
-Но APIStar перестал быть фреймворком для создания веб-сервера, зато появился Starlette, новая и лучшая основа для построения подобных систем.
-Это была последняя капля, сподвигнувшая на создание **FastAPI**.
+Затем APIStar перестал существовать как сервер, а был создан Starlette — новая и лучшая основа для такой системы. Это стало окончательным вдохновением для создания **FastAPI**.
-Я считаю **FastAPI** "духовным преемником" APIStar, улучившим его возможности благодаря урокам, извлечённым из всех упомянутых выше инструментов.
+Я считаю **FastAPI** «духовным преемником» APIStar, который улучшает и расширяет возможности, систему типов и другие части, опираясь на уроки от всех этих предыдущих инструментов.
///
-## Что используется в **FastAPI**
+## Что используется в **FastAPI** { #used-by-fastapi }
-### Pydantic
+### Pydantic { #pydantic }
-Pydantic - это библиотека для валидации данных, сериализации и документирования (используя JSON Schema), основываясь на подсказках типов Python, что делает его чрезвычайно интуитивным.
+Pydantic — это библиотека для определения валидации данных, сериализации и документации (с использованием JSON Schema) на основе аннотаций типов Python.
-Его можно сравнить с Marshmallow, хотя в бенчмарках Pydantic быстрее, чем Marshmallow.
-И он основан на тех же подсказках типов, которые отлично поддерживаются редакторами кода.
+Благодаря этому он чрезвычайно интуитивен.
-/// check | "**FastAPI** использует Pydantic"
+Его можно сравнить с Marshmallow. Хотя в бенчмарках он быстрее Marshmallow. И поскольку он основан на тех же аннотациях типов Python, поддержка в редакторе кода отличная.
-Для проверки данных, сериализации данных и автоматической документации моделей (на основе JSON Schema).
+/// check | **FastAPI** использует его для
-Затем **FastAPI** берёт эти схемы JSON и помещает их в схему OpenAPI, не касаясь других вещей, которые он делает.
+Обработки всей валидации данных, сериализации данных и автоматической документации моделей (на основе JSON Schema).
+
+Затем **FastAPI** берёт эти данные JSON Schema и помещает их в OpenAPI, помимо всех прочих функций.
///
-### Starlette
+### Starlette { #starlette }
-Starlette - это легковесный ASGI фреймворк/набор инструментов, который идеален для построения высокопроизводительных асинхронных сервисов.
+Starlette — это лёгкий ASGI фреймворк/набор инструментов, идеально подходящий для создания высокопроизводительных asyncio‑сервисов.
-Starlette очень простой и интуитивный.
-Он разработан таким образом, чтобы быть легко расширяемым и иметь модульные компоненты.
+Он очень простой и интуитивный. Спроектирован так, чтобы его было легко расширять, и чтобы компоненты были модульными.
В нём есть:
* Впечатляющая производительность.
-* Поддержка веб-сокетов.
-* Фоновые задачи.
-* Обработка событий при старте и финише приложения.
-* Тестовый клиент на основе HTTPX.
-* Поддержка CORS, сжатие GZip, статические файлы, потоковая передача данных.
-* Поддержка сессий и куки.
+* Поддержка WebSocket.
+* Фоновые задачи, выполняемые в том же процессе.
+* События запуска и завершения.
+* Тестовый клиент на базе HTTPX.
+* CORS, GZip, статические файлы, потоковые ответы.
+* Поддержка сессий и cookie.
* 100% покрытие тестами.
-* 100% аннотированный код.
-* Несколько жёстких зависимостей.
+* 100% кодовой базы с аннотациями типов.
+* Мало жёстких зависимостей.
-В настоящее время Starlette показывает самую высокую скорость среди Python-фреймворков в тестовых замерах.
-Быстрее только Uvicorn, который является сервером, а не фреймворком.
+В настоящее время Starlette — самый быстрый из протестированных Python-фреймворков. Его превосходит только Uvicorn, который не фреймворк, а сервер.
-Starlette обеспечивает весь функционал микрофреймворка, но не предоставляет автоматическую валидацию данных, сериализацию и документацию.
+Starlette предоставляет весь базовый функционал веб-микрофреймворка.
-**FastAPI** добавляет эти функции используя подсказки типов Python и Pydantic.
-Ещё **FastAPI** добавляет систему внедрения зависимостей, утилиты безопасности, генерацию схемы OpenAPI и т.д.
+Но он не предоставляет автоматическую валидацию данных, сериализацию или документацию.
-/// note | "Технические детали"
+Это одна из главных вещей, которые **FastAPI** добавляет поверх, всё на основе аннотаций типов Python (с использованием Pydantic). Плюс система внедрения зависимостей, утилиты безопасности, генерация схемы OpenAPI и т. д.
-ASGI - это новый "стандарт" разработанный участниками команды Django.
-Он пока что не является "стандартом в Python" (то есть принятым PEP), но процесс принятия запущен.
+/// note | Технические детали
-Тем не менее он уже используется в качестве "стандарта" несколькими инструментами.
-Это значительно улучшает совместимость, поскольку Вы можете переключиться с Uvicorn на любой другой ASGI-сервер (например, Daphne или Hypercorn) или Вы можете добавить ASGI-совместимые инструменты, такие как `python-socketio`.
+ASGI — это новый «стандарт», разрабатываемый участниками core-команды Django. Он всё ещё не является «стандартом Python» (PEP), хотя процесс идёт.
+
+Тем не менее его уже используют как «стандарт» несколько инструментов. Это сильно улучшает совместимость: вы можете заменить Uvicorn на любой другой ASGI-сервер (например, Daphne или Hypercorn) или добавить совместимые с ASGI инструменты, такие как `python-socketio`.
///
-/// check | "**FastAPI** использует Starlette"
+/// check | **FastAPI** использует его для
-В качестве ядра веб-сервиса для обработки запросов, добавив некоторые функции сверху.
+Обработки всех основных веб-частей. Добавляя возможности поверх.
-Класс `FastAPI` наследуется напрямую от класса `Starlette`.
+Класс `FastAPI` напрямую наследуется от класса `Starlette`.
-Таким образом, всё что Вы могли делать со Starlette, Вы можете делать с **FastAPI**, по сути это прокачанный Starlette.
+Так что всё, что вы можете сделать со Starlette, вы можете сделать напрямую с **FastAPI**, по сути это «Starlette на стероидах».
///
-### Uvicorn
+### Uvicorn { #uvicorn }
-Uvicorn - это молниеносный ASGI-сервер, построенный на uvloop и httptools.
+Uvicorn — молниеносный ASGI-сервер, построенный на uvloop и httptools.
-Uvicorn является сервером, а не фреймворком.
-Например, он не предоставляет инструментов для маршрутизации запросов по ресурсам.
-Для этого нужна надстройка, такая как Starlette (или **FastAPI**).
+Это не веб-фреймворк, а сервер. Например, он не предоставляет инструменты для маршрутизации по путям. Это предоставляет сверху фреймворк, такой как Starlette (или **FastAPI**).
-Он рекомендуется в качестве сервера для Starlette и **FastAPI**.
+Это рекомендуемый сервер для Starlette и **FastAPI**.
-/// check | "**FastAPI** рекомендует его"
+/// check | **FastAPI** рекомендует его как
-Как основной сервер для запуска приложения **FastAPI**.
+Основной веб-сервер для запуска приложений **FastAPI**.
-Вы можете объединить его с Gunicorn, чтобы иметь асинхронный многопроцессный сервер.
+Также вы можете использовать опцию командной строки `--workers`, чтобы получить асинхронный многопроцессный сервер.
-Узнать больше деталей можно в разделе [Развёртывание](deployment/index.md){.internal-link target=_blank}.
+Подробнее см. раздел [Развёртывание](deployment/index.md){.internal-link target=_blank}.
///
-## Тестовые замеры и скорость
+## Бенчмарки и скорость { #benchmarks-and-speed }
-Чтобы понять, сравнить и увидеть разницу между Uvicorn, Starlette и FastAPI, ознакомьтесь с разделом [Тестовые замеры](benchmarks.md){.internal-link target=_blank}.
+Чтобы понять, сравнить и увидеть разницу между Uvicorn, Starlette и FastAPI, см. раздел о [Бенчмарках](benchmarks.md){.internal-link target=_blank}.
diff --git a/docs/ru/docs/async.md b/docs/ru/docs/async.md
index 6c5d982df3..15d4e108ab 100644
--- a/docs/ru/docs/async.md
+++ b/docs/ru/docs/async.md
@@ -1,18 +1,18 @@
-# Конкурентность и async / await
+# Конкурентность и async / await { #concurrency-and-async-await }
-Здесь приведена подробная информация об использовании синтаксиса `async def` при написании *функций обработки пути*, а также рассмотрены основы асинхронного программирования, конкурентности и параллелизма.
+Подробности о синтаксисе `async def` для *функций-обработчиков пути* и немного фона об асинхронном коде, конкурентности и параллелизме.
-## Нет времени?
+## Нет времени? { #in-a-hurry }
-TL;DR:
+TL;DR:
-Допустим, вы используете сторонюю библиотеку, которая требует вызова с ключевым словом `await`:
+Если вы используете сторонние библиотеки, которые нужно вызывать с `await`, например:
```Python
results = await some_library()
```
-В этом случае *функции обработки пути* необходимо объявлять с использованием синтаксиса `async def`:
+Тогда объявляйте *функции-обработчики пути* с `async def`, например:
```Python hl_lines="2"
@app.get('/')
@@ -21,18 +21,15 @@ async def read_results():
return results
```
-/// note
+/// note | Примечание
-`await` можно использовать только внутри функций, объявленных с использованием `async def`.
+`await` можно использовать только внутри функций, объявленных с `async def`.
///
---
-Если вы обращаетесь к сторонней библиотеке, которая с чем-то взаимодействует
-(с базой данных, API, файловой системой и т. д.), и не имеет поддержки синтаксиса `await`
-(что относится сейчас к большинству библиотек для работы с базами данных), то
-объявляйте *функции обработки пути* обычным образом с помощью `def`, например:
+Если вы используете стороннюю библиотеку, которая взаимодействует с чем-то (база данных, API, файловая система и т.д.) и не поддерживает использование `await` (сейчас это относится к большинству библиотек для БД), тогда объявляйте *функции-обработчики пути* как обычно, просто с `def`, например:
```Python hl_lines="2"
@app.get('/')
@@ -43,310 +40,283 @@ def results():
---
-Если вашему приложению (странным образом) не нужно ни с чем взаимодействовать и, соответственно,
-ожидать ответа, используйте `async def`.
+Если вашему приложению (по какой-то причине) не нужно ни с чем взаимодействовать и ждать ответа, используйте `async def`, даже если внутри не нужен `await`.
---
-Если вы не уверены, используйте обычный синтаксис `def`.
+Если вы просто не уверены, используйте обычный `def`.
---
-**Примечание**: при необходимости можно смешивать `def` и `async def` в *функциях обработки пути*
-и использовать в каждом случае наиболее подходящий синтаксис. А FastAPI сделает с этим всё, что нужно.
+**Примечание**: вы можете смешивать `def` и `async def` в *функциях-обработчиках пути* столько, сколько нужно, и объявлять каждую так, как лучше для вашего случая. FastAPI сделает с ними всё как надо.
-В любом из описанных случаев FastAPI работает асинхронно и очень быстро.
+В любом из случаев выше FastAPI всё равно работает асинхронно и очень быстро.
-Однако придерживаясь указанных советов, можно получить дополнительную оптимизацию производительности.
+Но следуя этим шагам, он сможет выполнить некоторые оптимизации производительности.
-## Технические подробности
+## Технические подробности { #technical-details }
-Современные версии Python поддерживают разработку так называемого **"асинхронного кода"** посредством написания **"сопрограмм"** с использованием синтаксиса **`async` и `await`**.
+Современные версии Python поддерживают **«асинхронный код»** с помощью **«сопрограмм»** (coroutines) и синтаксиса **`async` и `await`**.
-Ниже разберём эту фразу по частям:
+Разберём эту фразу по частям в разделах ниже:
* **Асинхронный код**
* **`async` и `await`**
* **Сопрограммы**
-## Асинхронный код
+## Асинхронный код { #asynchronous-code }
-Асинхронный код означает, что в языке 💬 есть возможность сообщить машине / программе 🤖,
-что в определённой точке кода ей 🤖 нужно будет ожидать завершения выполнения *чего-то ещё* в другом месте. Допустим это *что-то ещё* называется "медленный файл" 📝.
+Асинхронный код значит, что в языке 💬 есть способ сказать компьютеру/программе 🤖, что в некоторый момент кода ему 🤖 придётся подождать, пока *что-то ещё* где-то в другом месте завершится. Назовём это *что-то ещё* «медленный файл» 📝.
-И пока мы ждём завершения работы с "медленным файлом" 📝, компьютер может переключиться для выполнения других задач.
+И пока мы ждём завершения работы с «медленныи файлом» 📝, компьютер может заняться другой работой.
-Но при каждой возможности компьютер / программа 🤖 будет возвращаться обратно. Например, если он 🤖 опять окажется в режиме ожидания, или когда закончит всю работу. В этом случае компьютер 🤖 проверяет, не завершена ли какая-нибудь из текущих задач.
+Затем компьютер/программа 🤖 будет возвращаться каждый раз, когда появится возможность (пока снова где-то идёт ожидание), или когда 🤖 завершит всю текущую работу. И он 🤖 проверит, не завершилась ли какая-либо из задач, которых он ждал, и сделает то, что нужно.
-Потом он 🤖 берёт первую выполненную задачу (допустим, наш "медленный файл" 📝) и продолжает работу, производя с ней необходимые действия.
+Далее он 🤖 возьмёт первую завершившуюся задачу (скажем, наш «медленный файл» 📝) и продолжит делать с ней то, что требуется.
-Вышеупомянутое "что-то ещё", завершения которого приходится ожидать, обычно относится к достаточно "медленным" операциям I/O (по сравнению со скоростью работы процессора и оперативной памяти), например:
+Это «ожидание чего-то ещё» обычно относится к операциям I/O, которые относительно «медленные» (по сравнению со скоростью процессора и оперативной памяти), например ожидание:
-* отправка данных от клиента по сети
-* получение клиентом данных, отправленных вашей программой по сети
-* чтение системой содержимого файла с диска и передача этих данных программе
-* запись на диск данных, которые программа передала системе
-* обращение к удалённому API
-* ожидание завершения операции с базой данных
-* получение результатов запроса к базе данных
-* и т. д.
+* отправки данных клиентом по сети
+* получения клиентом данных, отправленных вашей программой по сети
+* чтения системой содержимого файла на диске и передачи этих данных вашей программе
+* записи на диск содержимого, которое ваша программа передала системе
+* операции удалённого API
+* завершения операции базы данных
+* возврата результатов запроса к базе данных
+* и т.д.
-Поскольку в основном время тратится на ожидание выполнения операций I/O,
-их обычно называют операциями, ограниченными скоростью ввода-вывода.
+Поскольку основное время выполнения уходит на ожидание операций I/O, их называют операциями, «ограниченными вводом-выводом» (I/O bound).
-Код называют "асинхронным", потому что компьютеру / программе не требуется "синхронизироваться" с медленной задачей и,
-будучи в простое, ожидать момента её завершения, с тем чтобы забрать результат и продолжить работу.
+Это называется «асинхронным», потому что компьютеру/программе не нужно «синхронизироваться» с медленной задачей, простаивая и выжидая точный момент её завершения, чтобы забрать результат и продолжить работу.
-Вместо этого в "асинхронной" системе завершённая задача может немного подождать (буквально несколько микросекунд),
-пока компьютер / программа занимается другими важными вещами, с тем чтобы потом вернуться,
-забрать результаты выполнения и начать их обрабатывать.
+Вместо этого, в «асинхронной» системе, уже завершившаяся задача может немного подождать (несколько микросекунд) в очереди, пока компьютер/программа завершит то, чем занимался, и затем вернётся, чтобы забрать результаты и продолжить работу с ними.
-"Синхронное" исполнение (в противовес "асинхронному") также называют "последовательным",
-потому что компьютер / программа последовательно выполняет все требуемые шаги перед тем, как перейти к следующей задаче,
-даже если в процессе приходится ждать.
+Для «синхронного» (в противоположность «асинхронному») исполнения часто используют термин «последовательный», потому что компьютер/программа выполняет все шаги по порядку, прежде чем переключиться на другую задачу, даже если эти шаги включают ожидание.
-### Конкурентность и бургеры
+### Конкурентность и бургеры { #concurrency-and-burgers }
-Тот **асинхронный** код, о котором идёт речь выше, иногда называют **"конкурентностью"**. Она отличается от **"параллелизма"**.
+Та идея **асинхронного** кода, описанная выше, иногда также называется **«конкурентностью»**. Она отличается от **«параллелизма»**.
-Да, **конкурентность** и **параллелизм** подразумевают, что разные вещи происходят примерно в одно время.
+И **конкурентность**, и **параллелизм** относятся к «разным вещам, происходящим примерно одновременно».
-Но внутреннее устройство **конкурентности** и **параллелизма** довольно разное.
+Но различия между *конкурентностью* и *параллелизмом* довольно существенные.
-Чтобы это понять, представьте такую картину:
+Чтобы их увидеть, представьте следующую историю про бургеры:
-### Конкурентные бургеры
+### Конкурентные бургеры { #concurrent-burgers }
-
+Вы идёте со своей возлюбленной за фастфудом, вы стоите в очереди, пока кассир принимает заказы у людей перед вами. 😍
-Вы идёте со своей возлюбленной 😍 в фастфуд 🍔 и становитесь в очередь, в это время кассир 💁 принимает заказы у посетителей перед вами.
+
-Когда наконец подходит очередь, вы заказываете парочку самых вкусных и навороченных бургеров 🍔, один для своей возлюбленной 😍, а другой себе.
+Наконец ваша очередь: вы заказываете 2 очень «навороченных» бургера — для вашей возлюбленной и для себя. 🍔🍔
-Отдаёте деньги 💸.
+
-Кассир 💁 что-то говорит поварам на кухне 👨🍳, теперь они знают, какие бургеры нужно будет приготовить 🍔
-(но пока они заняты бургерами предыдущих клиентов).
+Кассир говорит что-то повару на кухне, чтобы они знали, что нужно приготовить ваши бургеры (хотя сейчас они готовят бургеры для предыдущих клиентов).
-Кассир 💁 отдаёт вам чек с номером заказа.
+
-В ожидании еды вы идёте со своей возлюбленной 😍 выбрать столик, садитесь и довольно продолжительное время общаетесь 😍
-(поскольку ваши бургеры самые навороченные, готовятся они не так быстро ✨🍔✨).
+Вы платите. 💸
-Сидя за столиком с возлюбленной 😍 в ожидании бургеров 🍔, вы отлично проводите время,
-восхищаясь её великолепием, красотой и умом ✨😍✨.
+Кассир выдаёт вам номер вашей очереди.
-Всё ещё ожидая заказ и болтая со своей возлюбленной 😍, время от времени вы проверяете,
-какой номер горит над прилавком, и не подошла ли уже ваша очередь.
+
-И вот наконец настаёт этот момент, и вы идёте к стойке, чтобы забрать бургеры 🍔 и вернуться за столик.
+Пока вы ждёте, вы вместе со своей возлюбленной идёте и выбираете столик, садитесь и долго болтаете (ваши бургеры очень «навороченные», поэтому им нужно время на приготовление).
-Вы со своей возлюбленной 😍 едите бургеры 🍔 и отлично проводите время ✨.
+Сидя за столиком со своей возлюбленной в ожидании бургеров, вы можете провести это время, восхищаясь тем, какая она классная, милая и умная ✨😍✨.
+
+
+
+Пока вы ждёте и разговариваете, время от времени вы поглядываете на номер на табло, чтобы понять, не подошла ли уже ваша очередь.
+
+И вот в какой-то момент ваша очередь наступает. Вы подходите к стойке, забираете свои бургеры и возвращаетесь к столику.
+
+
+
+Вы со своей возлюбленной едите бургеры и отлично проводите время. ✨
+
+
+
+/// info | Информация
+
+Прекрасные иллюстрации от Ketrina Thompson. 🎨
+
+///
---
-А теперь представьте, что в этой небольшой истории вы компьютер / программа 🤖.
+Представьте, что в этой истории вы — компьютер/программа 🤖.
-В очереди вы просто глазеете по сторонам 😴, ждёте и ничего особо "продуктивного" не делаете.
-Но очередь движется довольно быстро, поскольку кассир 💁 только принимает заказы (а не занимается приготовлением еды), так что ничего страшного.
+Пока вы стоите в очереди, вы просто бездействуете 😴, ждёте своей очереди и не делаете ничего особо «продуктивного». Но очередь движется быстро, потому что кассир только принимает заказы (а не готовит их), так что это нормально.
-Когда подходит очередь вы наконец предпринимаете "продуктивные" действия 🤓: просматриваете меню, выбираете в нём что-то, узнаёте, что хочет ваша возлюбленная 😍, собираетесь оплатить 💸, смотрите, какую достали карту, проверяете, чтобы с вас списали верную сумму, и что в заказе всё верно и т. д.
+Когда приходит ваша очередь, вы выполняете действительно «продуктивную» работу: просматриваете меню, решаете, чего хотите, учитываете выбор своей возлюбленной, платите, проверяете, что дали правильную купюру/карту, что сумма списана корректно, что в заказе верные позиции и т.д.
-И хотя вы всё ещё не получили бургеры 🍔, ваша работа с кассиром 💁 ставится "на паузу" ⏸,
-поскольку теперь нужно ждать 🕙, когда заказ приготовят.
+Но затем, хотя у вас ещё нет бургеров, ваша «работа» с кассиром поставлена «на паузу» ⏸, потому что нужно подождать 🕙, пока бургеры будут готовы.
-Но отойдя с номерком от прилавка, вы садитесь за столик и можете переключить 🔀 внимание
-на свою возлюбленную 😍 и "работать" ⏯ 🤓 уже над этим. И вот вы снова очень
-"продуктивны" 🤓, мило болтаете вдвоём и всё такое 😍.
+Но, отойдя от стойки и сев за столик с номерком, вы можете переключить 🔀 внимание на свою возлюбленную и «поработать» ⏯ 🤓 над этим. Снова очень «продуктивно» — флирт с вашей возлюбленной 😍.
-В какой-то момент кассир 💁 поместит на табло ваш номер, подразумевая, что бургеры готовы 🍔, но вы не станете подскакивать как умалишённый, лишь только увидев на экране свою очередь. Вы уверены, что ваши бургеры 🍔 никто не утащит, ведь у вас свой номерок, а у других свой.
+Потом кассир 💁 «говорит»: «Я закончил делать бургеры», — выводя ваш номер на табло, но вы не подпрыгиваете как сумасшедший в ту же секунду, как только номер сменился на ваш. Вы знаете, что ваши бургеры никто не украдёт, потому что у вас есть номер вашей очереди, а у других — их.
-Поэтому вы подождёте, пока возлюбленная 😍 закончит рассказывать историю (закончите текущую работу ⏯ / задачу в обработке 🤓),
-и мило улыбнувшись, скажете, что идёте забирать заказ ⏸.
+Поэтому вы дожидаетесь, пока ваша возлюбленная закончит историю (завершится текущая работа ⏯ / выполняемая задача 🤓), мягко улыбаетесь и говорите, что идёте за бургерами ⏸.
-И вот вы подходите к стойке 🔀, к первоначальной задаче, которая уже завершена ⏯, берёте бургеры 🍔, говорите спасибо и относите заказ за столик. На этом заканчивается этап / задача взаимодействия с кассой ⏹.
-В свою очередь порождается задача "поедание бургеров" 🔀 ⏯, но предыдущая ("получение бургеров") завершена ⏹.
+Затем вы идёте к стойке 🔀, к исходной задаче, которая теперь завершена ⏯, забираете бургеры, благодарите и несёте их к столику. На этом шаг/задача взаимодействия со стойкой завершён ⏹. Это, в свою очередь, создаёт новую задачу — «есть бургеры» 🔀 ⏯, но предыдущая «получить бургеры» — завершена ⏹.
-### Параллельные бургеры
+### Параллельные бургеры { #parallel-burgers }
-Теперь представим, что вместо бургерной "Конкурентные бургеры" вы решили сходить в "Параллельные бургеры".
+Теперь представим, что это не «Конкурентные бургеры», а «Параллельные бургеры».
-И вот вы идёте со своей возлюбленной 😍 отведать параллельного фастфуда 🍔.
+Вы идёте со своей возлюбленной за параллельным фастфудом.
-Вы становитесь в очередь пока несколько (пусть будет 8) кассиров, которые по совместительству ещё и повары 👩🍳👨🍳👩🍳👨🍳👩🍳👨🍳👩🍳👨🍳, принимают заказы у посетителей перед вами.
+Вы стоите в очереди, пока несколько (скажем, 8) кассиров, которые одновременно являются поварами, принимают заказы у людей перед вами.
-При этом клиенты не отходят от стойки и ждут 🕙 получения еды, поскольку каждый
-из 8 кассиров идёт на кухню готовить бургеры 🍔, а только потом принимает следующий заказ.
+Все перед вами ждут, пока их бургеры будут готовы, не отходя от стойки, потому что каждый из 8 кассиров сразу идёт готовить бургер перед тем, как принять следующий заказ.
-Наконец настаёт ваша очередь, и вы просите два самых навороченных бургера 🍔, один для дамы сердца 😍, а другой себе.
+
-Ни о чём не жалея, расплачиваетесь 💸.
+Наконец ваша очередь: вы заказываете 2 очень «навороченных» бургера — для вашей возлюбленной и для себя.
-И кассир уходит на кухню 👨🍳.
+Вы платите 💸.
-Вам приходится ждать перед стойкой 🕙, чтобы никто по случайности не забрал ваши бургеры 🍔, ведь никаких номерков у вас нет.
+
-Поскольку вы с возлюбленной 😍 хотите получить заказ вовремя 🕙, и следите за тем, чтобы никто не вклинился в очередь,
-у вас не получается уделять должного внимание своей даме сердца 😞.
+Кассир уходит на кухню.
-Это "синхронная" работа, вы "синхронизированы" с кассиром/поваром 👨🍳. Приходится ждать 🕙 у стойки,
-когда кассир/повар 👨🍳 закончит делать бургеры 🍔 и вручит вам заказ, иначе его случайно может забрать кто-то другой.
+Вы ждёте, стоя у стойки 🕙, чтобы никто не забрал ваши бургеры раньше вас, так как никаких номерков нет.
-Наконец кассир/повар 👨🍳 возвращается с бургерами 🍔 после невыносимо долгого ожидания 🕙 за стойкой.
+
-Вы скорее забираете заказ 🍔 и идёте с возлюбленной 😍 за столик.
+Так как вы со своей возлюбленной заняты тем, чтобы никто не встал перед вами и не забрал ваши бургеры, как только они появятся, вы не можете уделить внимание своей возлюбленной. 😞
-Там вы просто едите эти бургеры, и на этом всё 🍔 ⏹.
+Это «синхронная» работа, вы «синхронизированы» с кассиром/поваром 👨🍳. Вам нужно ждать 🕙 и находиться там в точный момент, когда кассир/повар 👨🍳 закончит бургеры и вручит их вам, иначе их может забрать кто-то другой.
-Вам не особо удалось пообщаться, потому что большую часть времени 🕙 пришлось провести у кассы 😞.
+
+
+Затем ваш кассир/повар 👨🍳 наконец возвращается с вашими бургерами, после долгого ожидания 🕙 у стойки.
+
+
+
+Вы берёте бургеры и идёте со своей возлюбленной к столику.
+
+Вы просто их съедаете — и всё. ⏹
+
+
+
+Разговоров и флирта было немного, потому что большую часть времени вы ждали 🕙 у стойки. 😞
+
+/// info | Информация
+
+Прекрасные иллюстрации от Ketrina Thompson. 🎨
+
+///
---
-В описанном сценарии вы компьютер / программа 🤖 с двумя исполнителями (вы и ваша возлюбленная 😍),
-на протяжении долгого времени 🕙 вы оба уделяете всё внимание ⏯ задаче "ждать на кассе".
+В этом сценарии «параллельных бургеров» вы — компьютер/программа 🤖 с двумя процессорами (вы и ваша возлюбленная), оба ждут 🕙 и уделяют внимание ⏯ тому, чтобы «ждать у стойки» 🕙 долгое время.
-В этом ресторане быстрого питания 8 исполнителей (кассиров/поваров) 👩🍳👨🍳👩🍳👨🍳👩🍳👨🍳👩🍳👨🍳.
-Хотя в бургерной конкурентного типа было всего два (один кассир и один повар) 💁 👨🍳.
+В ресторане 8 процессоров (кассиров/поваров). Тогда как в «конкурентных бургерах» могло быть только 2 (один кассир и один повар).
-Несмотря на обилие работников, опыт в итоге получился не из лучших 😞.
+И всё же финальный опыт — не самый лучший. 😞
---
-Так бы выглядел аналог истории про бургерную 🍔 в "параллельном" мире.
+Это была параллельная версия истории про бургеры. 🍔
-Вот более реалистичный пример. Представьте себе банк.
+Для более «жизненного» примера представьте банк.
-До недавних пор в большинстве банков было несколько кассиров 👨💼👨💼👨💼👨💼 и длинные очереди 🕙🕙🕙🕙🕙🕙🕙🕙.
+До недавнего времени в большинстве банков было несколько кассиров 👨💼👨💼👨💼👨💼 и длинная очередь 🕙🕙🕙🕙🕙🕙🕙🕙.
-Каждый кассир обслуживал одного клиента, потом следующего 👨💼⏯.
+Все кассиры делают всю работу с одним клиентом за другим 👨💼⏯.
-Нужно было долгое время 🕙 стоять перед окошком вместе со всеми, иначе пропустишь свою очередь.
+И вам приходится долго 🕙 стоять в очереди, иначе вы потеряете свою очередь.
-Сомневаюсь, что у вас бы возникло желание прийти с возлюбленной 😍 в банк 🏦 оплачивать налоги.
+Вы вряд ли захотите взять свою возлюбленную 😍 с собой, чтобы заняться делами в банке 🏦.
-### Выводы о бургерах
+### Вывод про бургеры { #burger-conclusion }
-В нашей истории про поход в фастфуд за бургерами приходится много ждать 🕙,
-поэтому имеет смысл организовать конкурентную систему ⏸🔀⏯.
+В этом сценарии «фастфуда с вашей возлюбленной», так как много ожидания 🕙, гораздо логичнее иметь конкурентную систему ⏸🔀⏯.
-И то же самое с большинством веб-приложений.
+Так обстоит дело и с большинством веб-приложений.
-Пользователей очень много, но ваш сервер всё равно вынужден ждать 🕙 запросы по их слабому интернет-соединению.
+Очень много пользователей, но ваш сервер ждёт 🕙, пока их не самое хорошее соединение отправит их запросы.
-Потом снова ждать 🕙, пока вернётся ответ.
+А затем снова ждёт 🕙, пока отправятся ответы.
-
-Это ожидание 🕙 измеряется микросекундами, но если всё сложить, то набегает довольно много времени.
+Это «ожидание» 🕙 измеряется микросекундами, но если всё сложить, то в сумме получается много ожидания.
-Вот почему есть смысл использовать асинхронное ⏸🔀⏯ программирование при построении веб-API.
+Вот почему асинхронный ⏸🔀⏯ код очень уместен для веб-API.
-Большинство популярных фреймворков (включая Flask и Django) создавались
-до появления в Python новых возможностей асинхронного программирования. Поэтому
-их можно разворачивать с поддержкой параллельного исполнения или асинхронного
-программирования старого типа, которое не настолько эффективно.
+Именно такая асинхронность сделала NodeJS популярным (хотя NodeJS — не параллельный), и это сильная сторона Go как языка программирования.
-При том, что основная спецификация асинхронного взаимодействия Python с веб-сервером
-(ASGI)
-была разработана командой Django для внедрения поддержки веб-сокетов.
+Того же уровня производительности вы получаете с **FastAPI**.
-Именно асинхронность сделала NodeJS таким популярным (несмотря на то, что он не параллельный),
-и в этом преимущество Go как языка программирования.
+А так как можно одновременно использовать параллелизм и асинхронность, вы получаете производительность выше, чем у большинства протестированных фреймворков на NodeJS и на уровне Go, который — компилируемый язык, ближе к C (всё благодаря Starlette).
-И тот же уровень производительности даёт **FastAPI**.
+### Конкурентность лучше параллелизма? { #is-concurrency-better-than-parallelism }
-Поскольку можно использовать преимущества параллелизма и асинхронности вместе,
-вы получаете производительность лучше, чем у большинства протестированных NodeJS фреймворков
-и на уровне с Go, который является компилируемым языком близким к C (всё благодаря Starlette).
+Нет! Мораль истории не в этом.
-### Получается, конкурентность лучше параллелизма?
+Конкурентность отличается от параллелизма. И она лучше в **конкретных** сценариях, где много ожидания. Поэтому при разработке веб-приложений она обычно намного лучше параллелизма. Но не во всём.
-Нет! Мораль истории совсем не в этом.
+Чтобы уравновесить это, представьте такую короткую историю:
-Конкурентность отличается от параллелизма. Она лучше в **конкретных** случаях, где много времени приходится на ожидание.
-Вот почему она зачастую лучше параллелизма при разработке веб-приложений. Но это не значит, что конкурентность лучше в любых сценариях.
-
-Давайте посмотрим с другой стороны, представьте такую картину:
-
-> Вам нужно убраться в большом грязном доме.
+> Вам нужно убрать большой грязный дом.
*Да, это вся история*.
---
-Тут не нужно нигде ждать 🕙, просто есть куча работы в разных частях дома.
+Здесь нигде нет ожидания 🕙, просто очень много работы в разных местах дома.
-Можно организовать очередь как в примере с бургерами, сначала гостиная, потом кухня,
-но это ни на что не повлияет, поскольку вы нигде не ждёте 🕙, а просто трёте да моете.
+Можно организовать «очереди» как в примере с бургерами — сначала гостиная, потом кухня, — но так как вы ничего не ждёте 🕙, а просто убираете и убираете, очереди ни на что не повлияют.
-И понадобится одинаковое количество времени с очередью (конкурентностью) и без неё,
-и работы будет сделано тоже одинаковое количество.
+На завершение уйдёт одинаковое время — с очередями (конкурентностью) и без них — и объём выполненной работы будет одинаковым.
-Однако в случае, если бы вы могли привести 8 бывших кассиров/поваров, а ныне уборщиков 👩🍳👨🍳👩🍳👨🍳👩🍳👨🍳👩🍳👨🍳,
-и каждый из них (вместе с вами) взялся бы за свой участок дома,
-с такой помощью вы бы закончили намного быстрее, делая всю работу **параллельно**.
+Но в этом случае, если бы вы могли привести 8 бывших кассиров/поваров, а теперь — уборщиков, и каждый из них (плюс вы) взял бы свою зону дома для уборки, вы могли бы сделать всю работу **параллельно**, с дополнительной помощью, и завершить гораздо быстрее.
-В описанном сценарии каждый уборщик (включая вас) был бы исполнителем, занятым на своём участке работы.
+В этом сценарии каждый уборщик (включая вас) был бы процессором, выполняющим свою часть работы.
-И поскольку большую часть времени выполнения занимает реальная работа (а не ожидание),
-а работу в компьютере делает ЦП,
-такие задачи называют ограниченными производительностью процессора.
+И так как основное время выполнения уходит на реальную работу (а не ожидание), а работу в компьютере выполняет CPU, такие задачи называют «ограниченными процессором» (CPU bound).
---
-Ограничение по процессору проявляется в операциях, где требуется выполнять сложные математические вычисления.
+Типичные примеры CPU-bound операций — те, которые требуют сложной математической обработки.
Например:
-* Обработка **звука** или **изображений**.
-* **Компьютерное зрение**: изображение состоит из миллионов пикселей, в каждом пикселе 3 составляющих цвета,
-обработка обычно требует проведения расчётов по всем пикселям сразу.
-* **Машинное обучение**: здесь обычно требуется умножение "матриц" и "векторов".
-Представьте гигантскую таблицу с числами в Экселе, и все их надо одновременно перемножить.
-* **Глубокое обучение**: это область *машинного обучения*, поэтому сюда подходит то же описание.
-Просто у вас будет не одна таблица в Экселе, а множество. В ряде случаев используется
-специальный процессор для создания и / или использования построенных таким образом моделей.
+* Обработка **аудио** или **изображений**.
+* **Компьютерное зрение**: изображение состоит из миллионов пикселей, каждый пиксель имеет 3 значения/цвета; обычно требуется вычислить что-то для всех этих пикселей одновременно.
+* **Машинное обучение**: обычно требует множества умножений «матриц» и «векторов». Представьте огромную таблицу с числами и умножение всех этих чисел «одновременно».
+* **Глубокое обучение**: это подполе Машинного обучения, так что всё вышесказанное применимо. Просто это не одна таблица чисел, а их огромный набор, и во многих случаях вы используете специальный процессор, чтобы строить и/или использовать такие модели.
-### Конкурентность + параллелизм: Веб + машинное обучение
+### Конкурентность + параллелизм: Веб + Машинное обучение { #concurrency-parallelism-web-machine-learning }
-**FastAPI** предоставляет возможности конкуретного программирования,
-которое очень распространено в веб-разработке (именно этим славится NodeJS).
+С **FastAPI** вы можете использовать преимущества конкурентности, что очень распространено в веб-разработке (это та же основная «фишка» NodeJS).
-Кроме того вы сможете использовать все преимущества параллелизма и
-многопроцессорности (когда несколько процессов работают параллельно),
-если рабочая нагрузка предполагает **ограничение по процессору**,
-как, например, в системах машинного обучения.
+Но вы также можете использовать выгоды параллелизма и многопроцессности (когда несколько процессов работают параллельно) для рабочих нагрузок, **ограниченных процессором** (CPU bound), как в системах Машинного обучения.
-Необходимо также отметить, что Python является главным языком в области
-**дата-сайенс**,
-машинного обучения и, особенно, глубокого обучения. Всё это делает FastAPI
-отличным вариантом (среди многих других) для разработки веб-API и приложений
-в области дата-сайенс / машинного обучения.
+Плюс к этому простой факт, что Python — основной язык для **Data Science**, Машинного обучения и особенно Глубокого обучения, делает FastAPI очень хорошим выбором для веб-API и приложений в области Data Science / Машинного обучения (среди многих других).
-Как добиться такого параллелизма в эксплуатации описано в разделе [Развёртывание](deployment/index.md){.internal-link target=_blank}.
+Как добиться такого параллелизма в продакшн, см. раздел [Развёртывание](deployment/index.md){.internal-link target=_blank}.
-## `async` и `await`
+## `async` и `await` { #async-and-await }
-В современных версиях Python разработка асинхронного кода реализована очень интуитивно.
-Он выглядит как обычный "последовательный" код и самостоятельно выполняет "ожидание", когда это необходимо.
+В современных версиях Python есть очень интуитивный способ определять асинхронный код. Это делает его похожим на обычный «последовательный» код, а «ожидание» выполняется за вас в нужные моменты.
-Если некая операция требует ожидания перед тем, как вернуть результат, и
-поддерживает современные возможности Python, код можно написать следующим образом:
+Когда есть операция, которой нужно подождать перед тем, как вернуть результат, и она поддерживает эти новые возможности Python, вы можете написать так:
```Python
burgers = await get_burgers(2)
```
-Главное здесь слово `await`. Оно сообщает интерпретатору, что необходимо дождаться ⏸
-пока `get_burgers(2)` закончит свои дела 🕙, и только после этого сохранить результат в `burgers`.
-Зная это, Python может пока переключиться на выполнение других задач 🔀 ⏯
-(например получение следующего запроса).
+Ключ здесь — `await`. Он говорит Python, что нужно подождать ⏸, пока `get_burgers(2)` закончит своё дело 🕙, прежде чем сохранять результат в `burgers`. Благодаря этому Python будет знать, что за это время можно заняться чем-то ещё 🔀 ⏯ (например, принять другой запрос).
-Чтобы ключевое слово `await` сработало, оно должно находиться внутри функции,
-которая поддерживает асинхронность. Для этого вам просто нужно объявить её как `async def`:
+Чтобы `await` работал, он должен находиться внутри функции, которая поддерживает такую асинхронность. Для этого просто объявите её с `async def`:
```Python hl_lines="1"
async def get_burgers(number: int):
- # Готовим бургеры по специальному асинхронному рецепту
+ # Сделать что-то асинхронное, чтобы приготовить бургеры
return burgers
```
@@ -355,26 +325,22 @@ async def get_burgers(number: int):
```Python hl_lines="2"
# Это не асинхронный код
def get_sequential_burgers(number: int):
- # Готовим бургеры последовательно по шагам
+ # Сделать что-то последовательное, чтобы приготовить бургеры
return burgers
```
-Объявление `async def` указывает интерпретатору, что внутри этой функции
-следует ожидать выражений `await`, и что можно поставить выполнение такой функции на "паузу" ⏸ и
-переключиться на другие задачи 🔀, с тем чтобы вернуться сюда позже.
+С `async def` Python знает, что внутри этой функции нужно учитывать выражения `await` и что выполнение такой функции можно «приостанавливать» ⏸ и идти делать что-то ещё 🔀, чтобы потом вернуться.
-Если вы хотите вызвать функцию с `async def`, вам нужно "ожидать" её.
-Поэтому такое не сработает:
+Когда вы хотите вызвать функцию, объявленную с `async def`, нужно её «ожидать». Поэтому вот так не сработает:
```Python
-# Это не заработает, поскольку get_burgers объявлена с использованием async def
+# Это не сработает, потому что get_burgers определена с: async def
burgers = get_burgers(2)
```
---
-Если сторонняя библиотека требует вызывать её с ключевым словом `await`,
-необходимо писать *функции обработки пути* с использованием `async def`, например:
+Итак, если вы используете библиотеку, которую можно вызывать с `await`, вам нужно создать *функцию-обработчик пути*, которая её использует, с `async def`, например:
```Python hl_lines="2-3"
@app.get('/burgers')
@@ -383,129 +349,96 @@ async def read_burgers():
return burgers
```
-### Технические подробности
+### Более технические подробности { #more-technical-details }
-Как вы могли заметить, `await` может применяться только в функциях, объявленных с использованием `async def`.
+Вы могли заметить, что `await` можно использовать только внутри функций, определённых с `async def`.
-
-Но выполнение такой функции необходимо "ожидать" с помощью `await`.
-Это означает, что её можно вызвать только из другой функции, которая тоже объявлена с `async def`.
+Но при этом функции, определённые с `async def`, нужно «ожидать». Значит, функции с `async def` тоже можно вызывать только из функций, определённых с `async def`.
-Но как же тогда появилась первая курица? В смысле... как нам вызвать первую асинхронную функцию?
+Так что же с «яйцом и курицей» — как вызвать первую `async` функцию?
-При работе с **FastAPI** просто не думайте об этом, потому что "первой" функцией является ваша *функция обработки пути*,
-и дальше с этим разберётся FastAPI.
+Если вы работаете с **FastAPI**, вам не о чем беспокоиться, потому что этой «первой» функцией будет ваша *функция-обработчик пути*, а FastAPI знает, как сделать всё правильно.
-Кроме того, если хотите, вы можете использовать синтаксис `async` / `await` и без FastAPI.
+Но если вы хотите использовать `async` / `await` без FastAPI, вы тоже можете это сделать.
-### Пишите свой асинхронный код
+### Пишите свой асинхронный код { #write-your-own-async-code }
-Starlette (и **FastAPI**) основаны на AnyIO, что делает их совместимыми как со стандартной библиотекой asyncio в Python, так и с Trio.
+Starlette (и **FastAPI**) основаны на AnyIO, что делает их совместимыми и со стандартной библиотекой Python asyncio, и с Trio.
-В частности, вы можете напрямую использовать AnyIO в тех проектах, где требуется более сложная логика работы с конкурентностью.
+В частности, вы можете напрямую использовать AnyIO для продвинутых сценариев конкурентности, где в вашем коде нужны более сложные паттерны.
-Даже если вы не используете FastAPI, вы можете писать асинхронные приложения с помощью AnyIO, чтобы они были максимально совместимыми и получали его преимущества (например *структурную конкурентность*).
+И даже если вы не используете FastAPI, вы можете писать свои асинхронные приложения с AnyIO, чтобы они были максимально совместимыми и получали его преимущества (например, *структурную конкурентность*).
-### Другие виды асинхронного программирования
+Я создал ещё одну библиотеку поверх AnyIO, тонкий слой, чтобы немного улучшить аннотации типов и получить более качественное **автозавершение**, **ошибки прямо в редакторе** и т.д. Там также есть дружелюбное введение и руководство, чтобы помочь вам **понять** и писать **свой собственный асинхронный код**: Asyncer. Она особенно полезна, если вам нужно **комбинировать асинхронный код с обычным** (блокирующим/синхронным) кодом.
-Стиль написания кода с `async` и `await` появился в языке Python относительно недавно.
+### Другие формы асинхронного кода { #other-forms-of-asynchronous-code }
-Но он сильно облегчает работу с асинхронным кодом.
+Такой стиль использования `async` и `await` относительно новый в языке.
-Ровно такой же синтаксис (ну или почти такой же) недавно был включён в современные версии JavaScript (в браузере и NodeJS).
+Но он сильно упрощает работу с асинхронным кодом.
-До этого поддержка асинхронного кода была реализована намного сложнее, и его было труднее воспринимать.
+Такой же (или почти такой же) синтаксис недавно появился в современных версиях JavaScript (в браузере и NodeJS).
-В предыдущих версиях Python для этого использовались потоки или Gevent. Но такой код намного сложнее понимать, отлаживать и мысленно представлять.
+До этого работа с асинхронным кодом была заметно сложнее и труднее для понимания.
-Что касается JavaScript (в браузере и NodeJS), раньше там использовали для этой цели
-"обратные вызовы". Что выливалось в
-ад обратных вызовов.
+В предыдущих версиях Python можно было использовать потоки или Gevent. Но такой код гораздо сложнее понимать, отлаживать и держать в голове.
-## Сопрограммы
+В прежних версиях NodeJS/браузерного JavaScript вы бы использовали «callbacks» (обратные вызовы), что приводит к «callback hell» (ад обратных вызовов).
-**Корути́на** (или же сопрограмма) — это крутое словечко для именования той сущности,
-которую возвращает функция `async def`. Python знает, что её можно запустить, как и обычную функцию,
-но кроме того сопрограмму можно поставить на паузу ⏸ в том месте, где встретится слово `await`.
+## Сопрограммы { #coroutines }
-Всю функциональность асинхронного программирования с использованием `async` и `await`
-часто обобщают словом "корутины". Они аналогичны "горутинам", ключевой особенности
-языка Go.
+**Сопрограмма** (coroutine) — это просто «навороченное» слово для того, что возвращает функция `async def`. Python знает, что это похоже на функцию: её можно запустить, она когда-нибудь завершится, но её выполнение может приостанавливаться ⏸ внутри, когда встречается `await`.
-## Заключение
+Часто всю функциональность использования асинхронного кода с `async` и `await` кратко называют «сопрограммами». Это сопоставимо с ключевой особенностью Go — «goroutines».
-В самом начале была такая фраза:
+## Заключение { #conclusion }
-> Современные версии Python поддерживают разработку так называемого
-**"асинхронного кода"** посредством написания **"сопрограмм"** с использованием
-синтаксиса **`async` и `await`**.
+Вернёмся к той же фразе:
-Теперь всё должно звучать понятнее. ✨
+> Современные версии Python поддерживают **«асинхронный код»** с помощью **«сопрограмм»** (coroutines) и синтаксиса **`async` и `await`**.
-На этом основана работа FastAPI (посредством Starlette), и именно это
-обеспечивает его высокую производительность.
+Теперь это должно звучать понятнее. ✨
-## Очень технические подробности
+Именно это «движет» FastAPI (через Starlette) и обеспечивает столь впечатляющую производительность.
-/// warning
+## Очень технические подробности { #very-technical-details }
-Этот раздел читать не обязательно.
+/// warning | Предупреждение
-Здесь приводятся подробности внутреннего устройства **FastAPI**.
+Скорее всего, этот раздел можно пропустить.
-Но если вы обладаете техническими знаниями (корутины, потоки, блокировка и т. д.)
-и вам интересно, как FastAPI обрабатывает `async def` в отличие от обычных `def`,
-читайте дальше.
+Здесь — очень технические подробности о том, как **FastAPI** работает «под капотом».
+
+Если у вас есть достаточно технических знаний (сопрограммы, потоки, блокировки и т.д.) и вам интересно, как FastAPI обрабатывает `async def` по сравнению с обычным `def`, — вперёд.
///
-### Функции обработки пути
+### Функции-обработчики пути { #path-operation-functions }
-Когда вы объявляете *функцию обработки пути* обычным образом с ключевым словом `def`
-вместо `async def`, FastAPI ожидает её выполнения, запустив функцию во внешнем
-пуле потоков, а не напрямую (это бы заблокировало сервер).
+Когда вы объявляете *функцию-обработчик пути* обычным `def` вместо `async def`, она запускается во внешнем пуле потоков, который затем «ожидается», вместо прямого вызова (прямой вызов заблокировал бы сервер).
-Если ранее вы использовали другой асинхронный фреймворк, который работает иначе,
-и привыкли объявлять простые вычислительные *функции* через `def` ради
-незначительного прироста скорости (порядка 100 наносекунд), обратите внимание,
-что с **FastAPI** вы получите противоположный эффект. В таком случае больше подходит
-`async def`, если только *функция обработки пути* не использует код, приводящий
-к блокировке I/O.
-
+Если вы пришли из другого async-фреймворка, который работает иначе, и привыкли объявлять тривиальные *функции-обработчики пути*, выполняющие только вычисления, через простой `def` ради крошечной выгоды в производительности (около 100 наносекунд), обратите внимание: в **FastAPI** эффект будет противоположным. В таких случаях лучше использовать `async def`, если только ваши *функции-обработчики пути* не используют код, выполняющий блокирующий I/O.
-
-Но в любом случае велика вероятность, что **FastAPI** [окажется быстрее](index.md#_11){.internal-link target=_blank}
-другого фреймворка (или хотя бы на уровне с ним).
+Тем не менее, в обоих случаях велика вероятность, что **FastAPI** [всё равно будет быстрее](index.md#performance){.internal-link target=_blank} (или как минимум сопоставим) с вашим предыдущим фреймворком.
-### Зависимости
+### Зависимости { #dependencies }
-То же относится к зависимостям. Если это обычная функция `def`, а не `async def`,
-она запускается во внешнем пуле потоков.
+То же относится к [зависимостям](tutorial/dependencies/index.md){.internal-link target=_blank}. Если зависимость — это обычная функция `def`, а не `async def`, она запускается во внешнем пуле потоков.
-### Подзависимости
+### Подзависимости { #sub-dependencies }
-Вы можете объявить множество ссылающихся друг на друга зависимостей и подзависимостей
-(в виде параметров при определении функции). Какие-то будут созданы с помощью `async def`,
-другие обычным образом через `def`, и такая схема вполне работоспособна. Функции,
-объявленные с помощью `def` будут запускаться на внешнем потоке (из пула),
-а не с помощью `await`.
+У вас может быть несколько зависимостей и [подзависимостей](tutorial/dependencies/sub-dependencies.md){.internal-link target=_blank}, которые требуют друг друга (в виде параметров определений функций): часть из них может быть объявлена с `async def`, а часть — обычным `def`. Всё будет работать, а те, что объявлены обычным `def`, будут вызываться во внешнем потоке (из пула), а не «ожидаться».
-### Другие служебные функции
+### Другие служебные функции { #other-utility-functions }
-Любые другие служебные функции, которые вы вызываете напрямую, можно объявлять
-с использованием `def` или `async def`. FastAPI не будет влиять на то, как вы
-их запускаете.
+Любые другие служебные функции, которые вы вызываете напрямую, можно объявлять обычным `def` или `async def`, и FastAPI не будет влиять на то, как вы их вызываете.
-Этим они отличаются от функций, которые FastAPI вызывает самостоятельно:
-*функции обработки пути* и зависимости.
+В отличие от функций, которые FastAPI вызывает за вас: *функции-обработчики пути* и зависимости.
-Если служебная функция объявлена с помощью `def`, она будет вызвана напрямую
-(как вы и написали в коде), а не в отдельном потоке. Если же она объявлена с
-помощью `async def`, её вызов должен осуществляться с ожиданием через `await`.
+Если служебная функция — обычная функция с `def`, она будет вызвана напрямую (как вы и пишете в коде), не в пуле потоков; если функция объявлена с `async def`, тогда при её вызове в вашем коде вы должны использовать `await`.
---
-
-Ещё раз повторим, что все эти технические подробности полезны, только если вы специально их искали.
+Снова: это очень технические подробности, полезные, вероятно, только если вы целенаправленно их ищете.
-В противном случае просто ознакомьтесь с основными принципами в разделе выше: Нет времени?.
+Иначе вам достаточно руководствоваться рекомендациями из раздела выше: Нет времени?.
diff --git a/docs/ru/docs/benchmarks.md b/docs/ru/docs/benchmarks.md
index 259dca8e67..612b39f708 100644
--- a/docs/ru/docs/benchmarks.md
+++ b/docs/ru/docs/benchmarks.md
@@ -1,37 +1,34 @@
-# Замеры производительности
+# Бенчмарки (тесты производительности) { #benchmarks }
-Независимые тесты производительности приложений от TechEmpower показывают, что **FastAPI** под управлением Uvicorn один из самых быстрых Python-фреймворков и уступает только Starlette и Uvicorn (которые используются в FastAPI). (*)
+Независимые бенчмарки TechEmpower показывают, что приложения **FastAPI** под управлением Uvicorn — одни из самых быстрых Python‑фреймворков, уступающие только Starlette и самому Uvicorn (используются внутри FastAPI).
-Но при просмотре и сравнении замеров производительности следует иметь в виду нижеописанное.
+Но при просмотре бенчмарков и сравнений следует иметь в виду следующее.
-## Замеры производительности и скорости
+## Бенчмарки и скорость { #benchmarks-and-speed }
-В подобных тестах часто можно увидеть, что инструменты разного типа сравнивают друг с другом, как аналогичные.
+При проверке бенчмарков часто можно увидеть, что инструменты разных типов сравнивают как эквивалентные.
-В частности, сравнивают вместе Uvicorn, Starlette и FastAPI (среди многих других инструментов).
+В частности, часто сравнивают вместе Uvicorn, Starlette и FastAPI (среди многих других инструментов).
-Чем проще проблема, которую решает инструмент, тем выше его производительность. И большинство тестов не проверяют дополнительные функции, предоставляемые инструментом.
+Чем проще задача, которую решает инструмент, тем выше его производительность. И большинство бенчмарков не тестируют дополнительные возможности, предоставляемые инструментом.
-Иерархия инструментов имеет следующий вид:
+Иерархия выглядит так:
* **Uvicorn**: ASGI-сервер
- * **Starlette** (использует Uvicorn): веб-микрофреймворк
- * **FastAPI** (использует Starlette): API-микрофреймворк с дополнительными функциями для создания API, с валидацией данных и т.д.
+ * **Starlette**: (использует Uvicorn) веб-микрофреймворк
+ * **FastAPI**: (использует Starlette) API-микрофреймворк с рядом дополнительных возможностей для создания API, включая валидацию данных и т. п.
* **Uvicorn**:
- * Будет иметь наилучшую производительность, так как не имеет большого количества дополнительного кода, кроме самого сервера.
- * Вы не будете писать приложение на Uvicorn напрямую. Это означало бы, что Ваш код должен включать как минимум весь
- код, предоставляемый Starlette (или **FastAPI**). И если Вы так сделаете, то в конечном итоге Ваше приложение будет иметь те же накладные расходы, что и при использовании фреймворка, минимизирующего код Вашего приложения и Ваши ошибки.
- * Uvicorn подлежит сравнению с Daphne, Hypercorn, uWSGI и другими веб-серверами.
-
+ * Будет иметь наилучшую производительность, так как помимо самого сервера у него немного дополнительного кода.
+ * Вы не будете писать приложение непосредственно на Uvicorn. Это означало бы, что Ваш код должен включать как минимум весь код, предоставляемый Starlette (или **FastAPI**). И если Вы так сделаете, то в конечном итоге Ваше приложение будет иметь те же накладные расходы, что и при использовании фреймворка, минимизирующего код Вашего приложения и Ваши ошибки.
+ * Если Вы сравниваете Uvicorn, сравнивайте его с Daphne, Hypercorn, uWSGI и т. д. — серверами приложений.
* **Starlette**:
- * Будет уступать Uvicorn по производительности. Фактически Starlette управляется Uvicorn и из-за выполнения большего количества кода он не может быть быстрее, чем Uvicorn.
- * Зато он предоставляет Вам инструменты для создания простых веб-приложений с обработкой маршрутов URL и т.д.
- * Starlette следует сравнивать с Sanic, Flask, Django и другими веб-фреймворками (или микрофреймворками).
-
+ * Будет на следующем месте по производительности после Uvicorn. Фактически Starlette запускается под управлением Uvicorn, поэтому он может быть только «медленнее» Uvicorn из‑за выполнения большего объёма кода.
+ * Зато он предоставляет Вам инструменты для создания простых веб‑приложений с маршрутизацией по путям и т. п.
+ * Если Вы сравниваете Starlette, сравнивайте его с Sanic, Flask, Django и т. д. — веб‑фреймворками (или микрофреймворками).
* **FastAPI**:
- * Так же как Starlette использует Uvicorn и не может быть быстрее него, **FastAPI** использует Starlette, то есть он не может быть быстрее Starlette.
- * FastAPI предоставляет больше возможностей поверх Starlette, которые наверняка Вам понадобятся при создании API, такие как проверка данных и сериализация. В довесок Вы ещё и получаете автоматическую документацию (автоматическая документация даже не увеличивает накладные расходы при работе приложения, так как она создается при запуске).
- * Если Вы не используете FastAPI, а используете Starlette напрямую (или другой инструмент вроде Sanic, Flask, Responder и т.д.), Вам пришлось бы самостоятельно реализовать валидацию и сериализацию данных. То есть, в итоге, Ваше приложение имело бы такие же накладные расходы, как если бы оно было создано с использованием FastAPI. И во многих случаях валидация и сериализация данных представляют собой самый большой объём кода, написанного в приложениях.
- * Таким образом, используя FastAPI Вы потратите меньше времени на разработку, уменьшите количество ошибок, строк кода и, вероятно, получите ту же производительность (или лучше), как и если бы не использовали его (поскольку Вам пришлось бы реализовать все его возможности в своем коде).
- * FastAPI должно сравнивать с фреймворками веб-приложений (или наборами инструментов), которые обеспечивают валидацию и сериализацию данных, а также предоставляют автоматическую документацию, такими как Flask-apispec, NestJS, Molten и им подобные.
+ * Точно так же, как Starlette использует Uvicorn и не может быть быстрее него, **FastAPI** использует Starlette, поэтому не может быть быстрее его.
+ * FastAPI предоставляет больше возможностей поверх Starlette — те, которые почти всегда нужны при создании API, такие как валидация и сериализация данных. В довесок Вы ещё и получаете автоматическую документацию (автоматическая документация даже не увеличивает накладные расходы при работе приложения, так как она создаётся при запуске).
+ * Если бы Вы не использовали FastAPI, а использовали Starlette напрямую (или другой инструмент вроде Sanic, Flask, Responder и т. д.), Вам пришлось бы самостоятельно реализовать валидацию и сериализацию данных. То есть, в итоге, Ваше приложение имело бы такие же накладные расходы, как если бы оно было создано с использованием FastAPI. И во многих случаях валидация и сериализация данных представляют собой самый большой объём кода, написанного в приложениях.
+ * Таким образом, используя FastAPI, Вы экономите время разработки, уменьшаете количество ошибок, строк кода и, вероятно, получите ту же производительность (или лучше), как и если бы не использовали его (поскольку Вам пришлось бы реализовать все его возможности в своём коде).
+ * Если Вы сравниваете FastAPI, сравнивайте его с фреймворком веб‑приложений (или набором инструментов), который обеспечивает валидацию данных, сериализацию и документацию, такими как Flask-apispec, NestJS, Molten и им подобные. Фреймворки с интегрированной автоматической валидацией данных, сериализацией и документацией.
diff --git a/docs/ru/docs/contributing.md b/docs/ru/docs/contributing.md
deleted file mode 100644
index c4370f9bbf..0000000000
--- a/docs/ru/docs/contributing.md
+++ /dev/null
@@ -1,496 +0,0 @@
-# Участие в разработке фреймворка
-
-Возможно, для начала Вам стоит ознакомиться с основными способами [помочь FastAPI или получить помощь](help-fastapi.md){.internal-link target=_blank}.
-
-## Разработка
-
-Если Вы уже склонировали репозиторий и знаете, что Вам нужно более глубокое погружение в код фреймворка, то здесь представлены некоторые инструкции по настройке виртуального окружения.
-
-### Виртуальное окружение с помощью `venv`
-
-Находясь в нужной директории, Вы можете создать виртуальное окружение при помощи Python модуля `venv`.
-
-
---
-Теперь, когда нам известна разница между **процессом** и **программой**, давайте продолжим обсуждение развёртывания.
+Теперь, когда мы понимаем разницу между **процессом** и **программой**, продолжим разговор о развёртываниях.
-## Настройки запуска приложения
+## Запуск при старте { #running-on-startup }
-В большинстве случаев когда вы создаёте веб-приложение, то желаете, чтоб оно **работало постоянно** и непрерывно, предоставляя клиентам доступ в любое время. Хотя иногда у вас могут быть причины, чтоб оно запускалось только при определённых условиях.
+В большинстве случаев, создавая веб‑API, вы хотите, чтобы он **работал постоянно**, без перерывов, чтобы клиенты всегда могли к нему обратиться. Разве что у вас есть особые причины запускать его только при определённых условиях, но обычно вы хотите, чтобы он был постоянно запущен и **доступен**.
-### Удалённый сервер
+### На удалённом сервере { #in-a-remote-server }
-Когда вы настраиваете удалённый сервер (облачный сервер, виртуальную машину и т.п.), самое простое, что можно сделать, запустить Uvicorn (или его аналог) вручную, как вы делаете при локальной разработке.
+Когда вы настраиваете удалённый сервер (облачный сервер, виртуальную машину и т.п.), самый простой вариант — вручную использовать `fastapi run` (он использует Uvicorn) или что‑то похожее, как вы делаете при локальной разработке.
-Это рабочий способ и он полезен **во время разработки**.
+Это будет работать и полезно **во время разработки**.
-Но если вы потеряете соединение с сервером, то не сможете отслеживать - работает ли всё ещё **запущенный Вами процесс**.
+Но если соединение с сервером прервётся, **запущенный процесс**, скорее всего, завершится.
-И если сервер перезагрузится (например, после обновления или каких-то действий облачного провайдера), вы скорее всего **этого не заметите**, чтобы снова запустить процесс вручную. Вследствие этого Ваш API останется мёртвым. 😱
+А если сервер перезагрузится (например, после обновлений или миграций у облачного провайдера), вы, вероятно, **даже не заметите этого**. Из‑за этого вы не узнаете, что нужно вручную перезапустить процесс — и ваш API просто будет «мёртв». 😱
-### Автоматический запуск программ
+### Автоматический запуск при старте { #run-automatically-on-startup }
-Вероятно вы захотите, чтоб Ваша серверная программа (такая, как Uvicorn) стартовала автоматически при включении сервера, без **человеческого вмешательства** и всегда могла управлять Вашим API (так как Uvicorn запускает приложение FastAPI).
+Как правило, вы захотите, чтобы серверная программа (например, Uvicorn) запускалась автоматически при старте сервера и без **участия человека**, чтобы всегда был процесс, запущенный с вашим API (например, Uvicorn, запускающий ваше приложение FastAPI).
-### Отдельная программа
+### Отдельная программа { #separate-program }
-Для этого у обычно используют отдельную программу, которая следит за тем, чтобы Ваши приложения запускались при включении сервера. Такой подход гарантирует, что другие компоненты или приложения также будут запущены, например, база данных
+Чтобы этого добиться, обычно используют **отдельную программу**, которая гарантирует запуск вашего приложения при старте. Во многих случаях она также запускает и другие компоненты/приложения, например базу данных.
-### Примеры инструментов, управляющих запуском программ
+### Примеры инструментов для запуска при старте { #example-tools-to-run-at-startup }
-Вот несколько примеров, которые могут справиться с такой задачей:
+Примеры инструментов, которые могут с этим справиться:
* Docker
* Kubernetes
* Docker Compose
-* Docker в режиме Swarm
+* Docker в режиме Swarm (Swarm Mode)
* Systemd
* Supervisor
-* Использование услуг облачного провайдера
+* Обработка внутри облачного провайдера как часть его услуг
* Прочие...
-Я покажу Вам некоторые примеры их использования в следующих главах.
+Более конкретные примеры будут в следующих главах.
-## Перезапуск
+## Перезапуски { #restarts }
-Вы, вероятно, также захотите, чтоб ваше приложение **перезапускалось**, если в нём произошёл сбой.
+Подобно тому как вы обеспечиваете запуск приложения при старте, вы, вероятно, захотите обеспечить его **перезапуск** после сбоев.
-### Мы ошибаемся
+### Мы ошибаемся { #we-make-mistakes }
-Все люди совершают **ошибки**. Программное обеспечение почти *всегда* содержит **баги** спрятавшиеся в разных местах. 🐛
+Мы, люди, постоянно совершаем **ошибки**. В программном обеспечении почти всегда есть **баги**, скрытые в разных местах. 🐛
-И мы, будучи разработчиками, продолжаем улучшать код, когда обнаруживаем в нём баги или добавляем новый функционал (возможно, добавляя при этом баги 😅).
+И мы, как разработчики, продолжаем улучшать код — находим баги и добавляем новые возможности (иногда добавляя новые баги 😅).
-### Небольшие ошибки обрабатываются автоматически
+### Небольшие ошибки обрабатываются автоматически { #small-errors-automatically-handled }
-Когда вы создаёте свои API на основе FastAPI и допускаете в коде ошибку, то FastAPI обычно остановит её распространение внутри одного запроса, при обработке которого она возникла. 🛡
+Создавая веб‑API с FastAPI, если в нашем коде возникает ошибка, FastAPI обычно «локализует» её в пределах одного запроса, который эту ошибку вызвал. 🛡
-Клиент получит ошибку **500 Internal Server Error** в ответ на свой запрос, но приложение не сломается и будет продолжать работать с последующими запросами.
+Клиент получит **500 Internal Server Error** для этого запроса, но приложение продолжит работать для последующих запросов, а не «упадёт» целиком.
-### Большие ошибки - Падение приложений
+### Большие ошибки — падения { #bigger-errors-crashes }
-Тем не менее, может случиться так, что ошибка вызовет **сбой всего приложения** или даже сбой в Uvicorn, а то и в самом Python. 💥
+Тем не менее возможны случаи, когда код **роняет всё приложение**, приводя к сбою Uvicorn и Python. 💥
-Но мы всё ещё хотим, чтобы приложение **продолжало работать** несмотря на эту единственную ошибку, обрабатывая, как минимум, запросы к *операциям пути* не имеющим ошибок.
+И вы, скорее всего, не захотите, чтобы приложение оставалось «мёртвым» из‑за ошибки в одном месте — вы захотите, чтобы оно **продолжало работать** хотя бы для *операций пути*, которые не сломаны.
-### Перезапуск после падения
+### Перезапуск после падения { #restart-after-crash }
-Для случаев, когда ошибки приводят к сбою в запущенном **процессе**, Вам понадобится добавить компонент, который **перезапустит** процесс хотя бы пару раз...
+В случаях действительно серьёзных ошибок, которые роняют работающий **процесс**, вам понадобится внешний компонент, отвечающий за **перезапуск** процесса, как минимум пару раз...
-/// tip | "Заметка"
+/// tip | Совет
-... Если приложение падает сразу же после запуска, вероятно бесполезно его бесконечно перезапускать. Но полагаю, вы заметите такое поведение во время разработки или, по крайней мере, сразу после развёртывания.
+...Хотя если приложение **падает сразу же**, вероятно, нет смысла перезапускать его бесконечно. Но такие случаи вы, скорее всего, заметите во время разработки или как минимум сразу после развёртывания.
-Так что давайте сосредоточимся на конкретных случаях, когда приложение может полностью выйти из строя, но всё ещё есть смысл его запустить заново.
+Давайте сосредоточимся на основных сценариях, когда в каких‑то конкретных ситуациях **в будущем** приложение может падать целиком, и при этом имеет смысл его перезапускать.
///
-Возможно вы захотите, чтоб был некий **внешний компонент**, ответственный за перезапуск вашего приложения даже если уже не работает Uvicorn или Python. То есть ничего из того, что написано в вашем коде внутри приложения, не может быть выполнено в принципе.
+Скорее всего, вы захотите, чтобы перезапуском вашего приложения занимался **внешний компонент**, потому что к тому моменту Uvicorn и Python уже упали, и внутри того же кода вашего приложения сделать уже ничего нельзя.
-### Примеры инструментов для автоматического перезапуска
+### Примеры инструментов для автоматического перезапуска { #example-tools-to-restart-automatically }
-В большинстве случаев инструменты **запускающие программы при старте сервера** умеют **перезапускать** эти программы.
+В большинстве случаев тот же инструмент, который **запускает программу при старте**, умеет обрабатывать и автоматические **перезапуски**.
-В качестве примера можно взять те же:
+Например, это может быть:
* Docker
* Kubernetes
* Docker Compose
-* Docker в режиме Swarm
+* Docker в режиме Swarm (Swarm Mode)
* Systemd
* Supervisor
-* Использование услуг облачного провайдера
+* Обработка внутри облачного провайдера как часть его услуг
* Прочие...
-## Запуск нескольких экземпляров приложения (Репликация) - Процессы и память
+## Репликация — процессы и память { #replication-processes-and-memory }
-Приложение FastAPI, управляемое серверной программой (такой как Uvicorn), запускается как **один процесс** и может обслуживать множество клиентов одновременно.
+В приложении FastAPI, используя серверную программу (например, команду `fastapi`, которая запускает Uvicorn), запуск в **одном процессе** уже позволяет обслуживать нескольких клиентов одновременно.
-Но часто Вам может понадобиться несколько одновременно работающих одинаковых процессов.
+Но во многих случаях вы захотите одновременно запустить несколько процессов‑воркеров.
-### Множество процессов - Воркеры (Workers)
+### Несколько процессов — Воркеры { #multiple-processes-workers }
-Если количество Ваших клиентов больше, чем может обслужить один процесс (допустим, что виртуальная машина не слишком мощная), но при этом Вам доступно **несколько ядер процессора**, то вы можете запустить **несколько процессов** одного и того же приложения параллельно и распределить запросы между этими процессами.
+Если клиентов больше, чем способен обслужить один процесс (например, если виртуальная машина не слишком мощная), и на сервере есть **несколько ядер CPU**, вы можете запустить **несколько процессов** одного и того же приложения параллельно и распределять запросы между ними.
-**Несколько запущенных процессов** одной и той же API-программы часто называют **воркерами**.
+Когда вы запускаете **несколько процессов** одной и той же программы API, их обычно называют **воркерами**.
-### Процессы и порты́
+### Процессы‑воркеры и порты { #worker-processes-and-ports }
-Помните ли Вы, как на странице [Об HTTPS](https.md){.internal-link target=_blank} мы обсуждали, что на сервере только один процесс может слушать одну комбинацию IP-адреса и порта?
+Помните из раздела [Об HTTPS](https.md){.internal-link target=_blank}, что на сервере только один процесс может слушать конкретную комбинацию порта и IP‑адреса?
-С тех пор ничего не изменилось.
+Это по‑прежнему так.
-Соответственно, чтобы иметь возможность работать с **несколькими процессами** одновременно, должен быть **один процесс, прослушивающий порт** и затем каким-либо образом передающий данные каждому рабочему процессу.
+Поэтому, чтобы одновременно работало **несколько процессов**, должен быть **один процесс, слушающий порт**, который затем каким‑то образом передаёт коммуникацию каждому воркер‑процессу.
-### У каждого процесса своя память
+### Память на процесс { #memory-per-process }
-Работающая программа загружает в память данные, необходимые для её работы, например, переменные содержащие модели машинного обучения или большие файлы. Каждая переменная **потребляет некоторое количество оперативной памяти (RAM)** сервера.
+Когда программа загружает что‑то в память (например, модель машинного обучения в переменную или содержимое большого файла в переменную), всё это **потребляет часть памяти (RAM)** сервера.
-Обычно процессы **не делятся памятью друг с другом**. Сие означает, что каждый работающий процесс имеет свои данные, переменные и свой кусок памяти. И если для выполнения вашего кода процессу нужно много памяти, то **каждый такой же процесс** запущенный дополнительно, потребует такого же количества памяти.
+И разные процессы обычно **не делят память**. Это значит, что у каждого процесса свои переменные и своя память. Если ваш код потребляет много памяти, то **каждый процесс** будет потреблять сопоставимый объём памяти.
-### Память сервера
+### Память сервера { #server-memory }
-Допустим, что Ваш код загружает модель машинного обучения **размером 1 ГБ**. Когда вы запустите своё API как один процесс, он займёт в оперативной памяти не менее 1 ГБ. А если вы запустите **4 таких же процесса** (4 воркера), то каждый из них займёт 1 ГБ оперативной памяти. В результате вашему API потребуется **4 ГБ оперативной памяти (RAM)**.
+Например, если ваш код загружает модель Машинного обучения размером **1 ГБ**, то при запуске одного процесса с вашим API он будет использовать как минимум 1 ГБ RAM. А если вы запустите **4 процесса** (4 воркера), каждый процесс будет использовать 1 ГБ RAM. Всего ваш API будет потреблять **4 ГБ RAM**.
-И если Ваш удалённый сервер или виртуальная машина располагает только 3 ГБ памяти, то попытка загрузить в неё 4 ГБ данных вызовет проблемы. 🚨
+И если у вашего удалённого сервера или виртуальной машины только 3 ГБ RAM, попытка загрузить более 4 ГБ вызовет проблемы. 🚨
-### Множество процессов - Пример
+### Несколько процессов — пример { #multiple-processes-an-example }
-В этом примере **менеджер процессов** запустит и будет управлять двумя **воркерами**.
+В этом примере есть **процесс‑менеджер**, который запускает и контролирует два **процесса‑воркера**.
-Менеджер процессов будет слушать определённый **сокет** (IP:порт) и передавать данные работающим процессам.
+Процесс‑менеджер, вероятно, будет тем, кто слушает **порт** на IP. И он будет передавать всю коммуникацию воркер‑процессам.
-Каждый из этих процессов будет запускать ваше приложение для обработки полученного **запроса** и возвращения вычисленного **ответа** и они будут использовать оперативную память.
+Эти воркеры будут запускать ваше приложение, выполнять основные вычисления для получения **запроса** и возврата **ответа**, и загружать всё, что вы кладёте в переменные, в RAM.
-
+
+Но вы можете отключить её, установив `syntaxHighlight` в `False`:
+
+{* ../../docs_src/configure_swagger_ui/tutorial001.py hl[3] *}
+
+…и после этого Swagger UI больше не будет показывать подсветку синтаксиса:
+
+
+
+## Изменить тему { #change-the-theme }
+
+Аналогично вы можете задать тему подсветки синтаксиса с ключом "syntaxHighlight.theme" (обратите внимание, что посередине стоит точка):
+
+{* ../../docs_src/configure_swagger_ui/tutorial002.py hl[3] *}
+
+Эта настройка изменит цветовую тему подсветки синтаксиса:
+
+
+
+## Изменить параметры Swagger UI по умолчанию { #change-default-swagger-ui-parameters }
+
+FastAPI включает некоторые параметры конфигурации по умолчанию, подходящие для большинства случаев.
+
+Это включает следующие настройки по умолчанию:
+
+{* ../../fastapi/openapi/docs.py ln[8:23] hl[17:23] *}
+
+Вы можете переопределить любую из них, указав другое значение в аргументе `swagger_ui_parameters`.
+
+Например, чтобы отключить `deepLinking`, можно передать такие настройки в `swagger_ui_parameters`:
+
+{* ../../docs_src/configure_swagger_ui/tutorial003.py hl[3] *}
+
+## Другие параметры Swagger UI { #other-swagger-ui-parameters }
+
+Чтобы увидеть все остальные возможные настройки, прочитайте официальную документацию по параметрам Swagger UI.
+
+## Настройки только для JavaScript { #javascript-only-settings }
+
+Swagger UI также допускает другие настройки, которые являются **чисто JavaScript-объектами** (например, JavaScript-функциями).
+
+FastAPI также включает следующие настройки `presets` (только для JavaScript):
+
+```JavaScript
+presets: [
+ SwaggerUIBundle.presets.apis,
+ SwaggerUIBundle.SwaggerUIStandalonePreset
+]
+```
+
+Это объекты **JavaScript**, а не строки, поэтому напрямую передать их из Python-кода нельзя.
+
+Если вам нужны такие настройки только для JavaScript, используйте один из методов выше. Переопределите *операцию пути* Swagger UI и вручную напишите любой необходимый JavaScript.
diff --git a/docs/ru/docs/how-to/custom-docs-ui-assets.md b/docs/ru/docs/how-to/custom-docs-ui-assets.md
new file mode 100644
index 0000000000..c07a9695b9
--- /dev/null
+++ b/docs/ru/docs/how-to/custom-docs-ui-assets.md
@@ -0,0 +1,185 @@
+# Свои статические ресурсы UI документации (самостоятельный хостинг) { #custom-docs-ui-static-assets-self-hosting }
+
+Документация API использует **Swagger UI** и **ReDoc**, и для каждого из них нужны некоторые файлы JavaScript и CSS.
+
+По умолчанию эти файлы отдаются с CDN.
+
+Но это можно настроить: вы можете указать конкретный CDN или отдавать файлы самостоятельно.
+
+## Пользовательский CDN для JavaScript и CSS { #custom-cdn-for-javascript-and-css }
+
+Допустим, вы хотите использовать другой CDN, например `https://unpkg.com/`.
+
+Это может быть полезно, если, например, вы живёте в стране, где некоторые URL ограничены.
+
+### Отключить автоматическую документацию { #disable-the-automatic-docs }
+
+Первый шаг — отключить автоматическую документацию, так как по умолчанию она использует стандартный CDN.
+
+Чтобы отключить её, установите их URL в значение `None` при создании вашего приложения `FastAPI`:
+
+{* ../../docs_src/custom_docs_ui/tutorial001.py hl[8] *}
+
+### Подключить пользовательскую документацию { #include-the-custom-docs }
+
+Теперь вы можете создать *операции пути* для пользовательской документации.
+
+Вы можете переиспользовать внутренние функции FastAPI для создания HTML-страниц документации и передать им необходимые аргументы:
+
+* `openapi_url`: URL, по которому HTML-страница документации сможет получить схему OpenAPI для вашего API. Здесь можно использовать атрибут `app.openapi_url`.
+* `title`: заголовок вашего API.
+* `oauth2_redirect_url`: здесь можно использовать `app.swagger_ui_oauth2_redirect_url`, чтобы оставить значение по умолчанию.
+* `swagger_js_url`: URL, по которому HTML для документации Swagger UI сможет получить файл **JavaScript**. Это URL вашего пользовательского CDN.
+* `swagger_css_url`: URL, по которому HTML для документации Swagger UI сможет получить файл **CSS**. Это URL вашего пользовательского CDN.
+
+Аналогично и для ReDoc...
+
+{* ../../docs_src/custom_docs_ui/tutorial001.py hl[2:6,11:19,22:24,27:33] *}
+
+/// tip | Совет
+
+*Операция пути* для `swagger_ui_redirect` — это вспомогательный эндпоинт на случай, когда вы используете OAuth2.
+
+Если вы интегрируете свой API с провайдером OAuth2, вы сможете аутентифицироваться и вернуться к документации API с полученными учётными данными, а затем взаимодействовать с ним, используя реальную аутентификацию OAuth2.
+
+Swagger UI сделает это за вас «за кулисами», но для этого ему нужен этот вспомогательный «redirect» эндпоинт.
+
+///
+
+### Создайте *операцию пути*, чтобы проверить { #create-a-path-operation-to-test-it }
+
+Чтобы убедиться, что всё работает, создайте *операцию пути*:
+
+{* ../../docs_src/custom_docs_ui/tutorial001.py hl[36:38] *}
+
+### Тестирование { #test-it }
+
+Теперь вы должны иметь возможность открыть свою документацию по адресу http://127.0.0.1:8000/docs и перезагрузить страницу — «ассеты» (статические файлы) будут загружаться с нового CDN.
+
+## Самостоятельный хостинг JavaScript и CSS для документации { #self-hosting-javascript-and-css-for-docs }
+
+Самостоятельный хостинг JavaScript и CSS может быть полезен, если, например, вам нужно, чтобы приложение продолжало работать в офлайне, без доступа к открытому Интернету, или в локальной сети.
+
+Здесь вы увидите, как отдавать эти файлы самостоятельно, в том же приложении FastAPI, и настроить документацию на их использование.
+
+### Структура файлов проекта { #project-file-structure }
+
+Допустим, структура файлов вашего проекта выглядит так:
+
+```
+.
+├── app
+│ ├── __init__.py
+│ ├── main.py
+```
+
+Теперь создайте директорию для хранения этих статических файлов.
+
+Новая структура файлов может выглядеть так:
+
+```
+.
+├── app
+│ ├── __init__.py
+│ ├── main.py
+└── static/
+```
+
+### Скачайте файлы { #download-the-files }
+
+Скачайте статические файлы, необходимые для документации, и поместите их в директорию `static/`.
+
+Скорее всего, вы можете кликнуть правой кнопкой на каждой ссылке и выбрать что-то вроде «Сохранить ссылку как...».
+
+**Swagger UI** использует файлы:
+
+* `swagger-ui-bundle.js`
+* `swagger-ui.css`
+
+А **ReDoc** использует файл:
+
+* `redoc.standalone.js`
+
+После этого структура файлов может выглядеть так:
+
+```
+.
+├── app
+│ ├── __init__.py
+│ ├── main.py
+└── static
+ ├── redoc.standalone.js
+ ├── swagger-ui-bundle.js
+ └── swagger-ui.css
+```
+
+### Предоставьте доступ к статическим файлам { #serve-the-static-files }
+
+* Импортируйте `StaticFiles`.
+* Смонтируйте экземпляр `StaticFiles()` в определённый путь.
+
+{* ../../docs_src/custom_docs_ui/tutorial002.py hl[7,11] *}
+
+### Протестируйте статические файлы { #test-the-static-files }
+
+Запустите своё приложение и откройте http://127.0.0.1:8000/static/redoc.standalone.js.
+
+Вы должны увидеть очень длинный JavaScript-файл для **ReDoc**.
+
+Он может начинаться примерно так:
+
+```JavaScript
+/*! For license information please see redoc.standalone.js.LICENSE.txt */
+!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("null")):
+...
+```
+
+Это подтверждает, что ваше приложение умеет отдавать статические файлы и что вы поместили файлы документации в нужное место.
+
+Теперь можно настроить приложение так, чтобы документация использовала эти статические файлы.
+
+### Отключить автоматическую документацию для статических файлов { #disable-the-automatic-docs-for-static-files }
+
+Так же, как и при использовании пользовательского CDN, первым шагом будет отключение автоматической документации, так как по умолчанию она использует CDN.
+
+Чтобы отключить её, установите их URL в значение `None` при создании вашего приложения `FastAPI`:
+
+{* ../../docs_src/custom_docs_ui/tutorial002.py hl[9] *}
+
+### Подключить пользовательскую документацию со статическими файлами { #include-the-custom-docs-for-static-files }
+
+Аналогично пользовательскому CDN, теперь вы можете создать *операции пути* для собственной документации.
+
+Снова можно переиспользовать внутренние функции FastAPI для создания HTML-страниц документации и передать им необходимые аргументы:
+
+* `openapi_url`: URL, по которому HTML-страница документации сможет получить схему OpenAPI для вашего API. Здесь можно использовать атрибут `app.openapi_url`.
+* `title`: заголовок вашего API.
+* `oauth2_redirect_url`: здесь можно использовать `app.swagger_ui_oauth2_redirect_url`, чтобы оставить значение по умолчанию.
+* `swagger_js_url`: URL, по которому HTML для документации Swagger UI сможет получить файл **JavaScript**. **Это тот файл, который теперь отдаёт ваше собственное приложение**.
+* `swagger_css_url`: URL, по которому HTML для документации Swagger UI сможет получить файл **CSS**. **Это тот файл, который теперь отдаёт ваше собственное приложение**.
+
+Аналогично и для ReDoc...
+
+{* ../../docs_src/custom_docs_ui/tutorial002.py hl[2:6,14:22,25:27,30:36] *}
+
+/// tip | Совет
+
+*Операция пути* для `swagger_ui_redirect` — это вспомогательный эндпоинт на случай, когда вы используете OAuth2.
+
+Если вы интегрируете свой API с провайдером OAuth2, вы сможете аутентифицироваться и вернуться к документации API с полученными учётными данными, а затем взаимодействовать с ним, используя реальную аутентификацию OAuth2.
+
+Swagger UI сделает это за вас «за кулисами», но для этого ему нужен этот вспомогательный «redirect» эндпоинт.
+
+///
+
+### Создайте *операцию пути* для теста статических файлов { #create-a-path-operation-to-test-static-files }
+
+Чтобы убедиться, что всё работает, создайте *операцию пути*:
+
+{* ../../docs_src/custom_docs_ui/tutorial002.py hl[39:41] *}
+
+### Тестирование UI со статическими файлами { #test-static-files-ui }
+
+Теперь вы можете отключить Wi‑Fi, открыть свою документацию по адресу http://127.0.0.1:8000/docs и перезагрузить страницу.
+
+Даже без Интернета вы сможете видеть документацию к своему API и взаимодействовать с ним.
diff --git a/docs/ru/docs/how-to/custom-request-and-route.md b/docs/ru/docs/how-to/custom-request-and-route.md
new file mode 100644
index 0000000000..1b8d7f7ed3
--- /dev/null
+++ b/docs/ru/docs/how-to/custom-request-and-route.md
@@ -0,0 +1,109 @@
+# Пользовательские классы Request и APIRoute { #custom-request-and-apiroute-class }
+
+В некоторых случаях может понадобиться переопределить логику, используемую классами `Request` и `APIRoute`.
+
+В частности, это может быть хорошей альтернативой логике в middleware.
+
+Например, если вы хотите прочитать или изменить тело запроса до того, как оно будет обработано вашим приложением.
+
+/// danger | Опасность
+
+Это «продвинутая» возможность.
+
+Если вы только начинаете работать с **FastAPI**, возможно, стоит пропустить этот раздел.
+
+///
+
+## Сценарии использования { #use-cases }
+
+Некоторые сценарии:
+
+* Преобразование тел запросов, не в формате JSON, в JSON (например, `msgpack`).
+* Распаковка тел запросов, сжатых с помощью gzip.
+* Автоматическое логирование всех тел запросов.
+
+## Обработка пользовательского кодирования тела запроса { #handling-custom-request-body-encodings }
+
+Посмотрим как использовать пользовательский подкласс `Request` для распаковки gzip-запросов.
+
+И подкласс `APIRoute`, чтобы использовать этот пользовательский класс запроса.
+
+### Создать пользовательский класс `GzipRequest` { #create-a-custom-gziprequest-class }
+
+/// tip | Совет
+
+Это учебный пример, демонстрирующий принцип работы. Если вам нужна поддержка Gzip, вы можете использовать готовый [`GzipMiddleware`](../advanced/middleware.md#gzipmiddleware){.internal-link target=_blank}.
+
+///
+
+Сначала создадим класс `GzipRequest`, который переопределит метод `Request.body()` и распакует тело запроса при наличии соответствующего HTTP-заголовка.
+
+Если в заголовке нет `gzip`, он не будет пытаться распаковывать тело.
+
+Таким образом, один и тот же класс маршрута сможет обрабатывать как gzip-сжатые, так и несжатые запросы.
+
+{* ../../docs_src/custom_request_and_route/tutorial001.py hl[8:15] *}
+
+### Создать пользовательский класс `GzipRoute` { #create-a-custom-gziproute-class }
+
+Далее создадим пользовательский подкласс `fastapi.routing.APIRoute`, который будет использовать `GzipRequest`.
+
+На этот раз он переопределит метод `APIRoute.get_route_handler()`.
+
+Этот метод возвращает функцию. Именно эта функция получает HTTP-запрос и возвращает HTTP-ответ.
+
+Здесь мы используем её, чтобы создать `GzipRequest` из исходного HTTP-запроса.
+
+{* ../../docs_src/custom_request_and_route/tutorial001.py hl[18:26] *}
+
+/// note | Технические детали
+
+У `Request` есть атрибут `request.scope` — это просто Python-`dict`, содержащий метаданные, связанные с HTTP-запросом.
+
+У `Request` также есть `request.receive` — функция для «получения» тела запроса.
+
+И `dict` `scope`, и функция `receive` являются частью спецификации ASGI.
+
+Именно этих двух компонентов — `scope` и `receive` — достаточно, чтобы создать новый экземпляр `Request`.
+
+Чтобы узнать больше о `Request`, см. документацию Starlette о запросах.
+
+///
+
+Единственное, что делает по-другому функция, возвращённая `GzipRequest.get_route_handler`, — преобразует `Request` в `GzipRequest`.
+
+Благодаря этому наш `GzipRequest` позаботится о распаковке данных (при необходимости) до передачи их в наши *операции пути*.
+
+Дальше вся логика обработки остаётся прежней.
+
+Но благодаря изменениям в `GzipRequest.body` тело запроса будет автоматически распаковано при необходимости, когда оно будет загружено **FastAPI**.
+
+## Доступ к телу запроса в обработчике исключений { #accessing-the-request-body-in-an-exception-handler }
+
+/// tip | Совет
+
+Для решения этой задачи, вероятно, намного проще использовать `body` в пользовательском обработчике `RequestValidationError` ([Обработка ошибок](../tutorial/handling-errors.md#use-the-requestvalidationerror-body){.internal-link target=_blank}).
+
+Но этот пример всё равно актуален и показывает, как взаимодействовать с внутренними компонентами.
+
+///
+
+Тем же подходом можно воспользоваться, чтобы получить доступ к телу запроса в обработчике исключений.
+
+Нужно лишь обработать запрос внутри блока `try`/`except`:
+
+{* ../../docs_src/custom_request_and_route/tutorial002.py hl[13,15] *}
+
+Если произойдёт исключение, экземпляр `Request` всё ещё будет в области видимости, поэтому мы сможем прочитать тело запроса и использовать его при обработке ошибки:
+
+{* ../../docs_src/custom_request_and_route/tutorial002.py hl[16:18] *}
+
+## Пользовательский класс `APIRoute` в роутере { #custom-apiroute-class-in-a-router }
+
+Вы также можете задать параметр `route_class` у `APIRouter`:
+
+{* ../../docs_src/custom_request_and_route/tutorial003.py hl[26] *}
+
+В этом примере *операции пути*, объявленные в `router`, будут использовать пользовательский класс `TimedRoute` и получат дополнительный HTTP-заголовок `X-Response-Time` в ответе с временем, затраченным на формирование ответа:
+
+{* ../../docs_src/custom_request_and_route/tutorial003.py hl[13:20] *}
diff --git a/docs/ru/docs/how-to/extending-openapi.md b/docs/ru/docs/how-to/extending-openapi.md
new file mode 100644
index 0000000000..2897fb89ba
--- /dev/null
+++ b/docs/ru/docs/how-to/extending-openapi.md
@@ -0,0 +1,80 @@
+# Расширение OpenAPI { #extending-openapi }
+
+Иногда может понадобиться изменить сгенерированную схему OpenAPI.
+
+В этом разделе показано, как это сделать.
+
+## Обычный процесс { #the-normal-process }
+
+Обычный (по умолчанию) процесс выглядит так.
+
+Приложение `FastAPI` (экземпляр) имеет метод `.openapi()`, который должен возвращать схему OpenAPI.
+
+В процессе создания объекта приложения регистрируется *операция пути* (обработчик пути) для `/openapi.json` (или для того, что указано в вашем `openapi_url`).
+
+Она просто возвращает JSON-ответ с результатом вызова метода приложения `.openapi()`.
+
+По умолчанию метод `.openapi()` проверяет свойство `.openapi_schema`: если в нём уже есть данные, возвращает их.
+
+Если нет — генерирует схему с помощью вспомогательной функции `fastapi.openapi.utils.get_openapi`.
+
+Функция `get_openapi()` принимает параметры:
+
+* `title`: Заголовок OpenAPI, отображается в документации.
+* `version`: Версия вашего API, например `2.5.0`.
+* `openapi_version`: Версия используемой спецификации OpenAPI. По умолчанию — последняя: `3.1.0`.
+* `summary`: Краткое описание API.
+* `description`: Описание вашего API; может включать Markdown и будет отображается в документации.
+* `routes`: Список маршрутов — это каждая зарегистрированная *операция пути*. Берутся из `app.routes`.
+
+/// info | Информация
+
+Параметр `summary` доступен в OpenAPI 3.1.0 и выше, поддерживается FastAPI версии 0.99.0 и выше.
+
+///
+
+## Переопределение значений по умолчанию { #overriding-the-defaults }
+
+Используя информацию выше, вы можете той же вспомогательной функцией сгенерировать схему OpenAPI и переопределить любые нужные части.
+
+Например, добавим расширение OpenAPI ReDoc для включения собственного логотипа.
+
+### Обычный **FastAPI** { #normal-fastapi }
+
+Сначала напишите приложение **FastAPI** как обычно:
+
+{* ../../docs_src/extending_openapi/tutorial001.py hl[1,4,7:9] *}
+
+### Сгенерируйте схему OpenAPI { #generate-the-openapi-schema }
+
+Затем используйте ту же вспомогательную функцию для генерации схемы OpenAPI внутри функции `custom_openapi()`:
+
+{* ../../docs_src/extending_openapi/tutorial001.py hl[2,15:21] *}
+
+### Измените схему OpenAPI { #modify-the-openapi-schema }
+
+Теперь можно добавить расширение ReDoc, добавив кастомный `x-logo` в «объект» `info` в схеме OpenAPI:
+
+{* ../../docs_src/extending_openapi/tutorial001.py hl[22:24] *}
+
+### Кэшируйте схему OpenAPI { #cache-the-openapi-schema }
+
+Вы можете использовать свойство `.openapi_schema` как «кэш» для хранения сгенерированной схемы.
+
+Так приложению не придётся генерировать схему каждый раз, когда пользователь открывает документацию API.
+
+Она будет создана один раз, а затем тот же кэшированный вариант будет использоваться для последующих запросов.
+
+{* ../../docs_src/extending_openapi/tutorial001.py hl[13:14,25:26] *}
+
+### Переопределите метод { #override-the-method }
+
+Теперь вы можете заменить метод `.openapi()` на вашу новую функцию.
+
+{* ../../docs_src/extending_openapi/tutorial001.py hl[29] *}
+
+### Проверьте { #check-it }
+
+Перейдите на http://127.0.0.1:8000/redoc — вы увидите, что используется ваш кастомный логотип (в этом примере — логотип **FastAPI**):
+
+
diff --git a/docs/ru/docs/how-to/general.md b/docs/ru/docs/how-to/general.md
new file mode 100644
index 0000000000..029ea1d274
--- /dev/null
+++ b/docs/ru/docs/how-to/general.md
@@ -0,0 +1,39 @@
+# Общее — Как сделать — Рецепты { #general-how-to-recipes }
+
+Здесь несколько указателей на другие места в документации для общих или частых вопросов.
+
+## Фильтрация данных — Безопасность { #filter-data-security }
+
+Чтобы убедиться, что вы не возвращаете больше данных, чем следует, прочитайте документацию: [Руководство — Модель ответа — Возвращаемый тип](../tutorial/response-model.md){.internal-link target=_blank}.
+
+## Теги в документации — OpenAPI { #documentation-tags-openapi }
+
+Чтобы добавить теги к вашим *операциям пути* и группировать их в интерфейсе документации, прочитайте документацию: [Руководство — Конфигурации операций пути — Теги](../tutorial/path-operation-configuration.md#tags){.internal-link target=_blank}.
+
+## Краткое описание и описание в документации — OpenAPI { #documentation-summary-and-description-openapi }
+
+Чтобы добавить краткое описание и описание к вашим *операциям пути* и отобразить их в интерфейсе документации, прочитайте документацию: [Руководство — Конфигурации операций пути — Краткое описание и описание](../tutorial/path-operation-configuration.md#summary-and-description){.internal-link target=_blank}.
+
+## Описание ответа в документации — OpenAPI { #documentation-response-description-openapi }
+
+Чтобы задать описание ответа, отображаемое в интерфейсе документации, прочитайте документацию: [Руководство — Конфигурации операций пути — Описание ответа](../tutorial/path-operation-configuration.md#response-description){.internal-link target=_blank}.
+
+## Документация — пометить операцию пути устаревшей — OpenAPI { #documentation-deprecate-a-path-operation-openapi }
+
+Чтобы пометить *операцию пути* как устаревшую и показать это в интерфейсе документации, прочитайте документацию: [Руководство — Конфигурации операций пути — Пометить операцию пути устаревшей](../tutorial/path-operation-configuration.md#deprecate-a-path-operation){.internal-link target=_blank}.
+
+## Преобразование любых данных к формату, совместимому с JSON { #convert-any-data-to-json-compatible }
+
+Чтобы преобразовать любые данные к формату, совместимому с JSON, прочитайте документацию: [Руководство — JSON-совместимый кодировщик](../tutorial/encoder.md){.internal-link target=_blank}.
+
+## Метаданные OpenAPI — Документация { #openapi-metadata-docs }
+
+Чтобы добавить метаданные в вашу схему OpenAPI, включая лицензию, версию, контакты и т.д., прочитайте документацию: [Руководство — Метаданные и URL документации](../tutorial/metadata.md){.internal-link target=_blank}.
+
+## Пользовательский URL OpenAPI { #openapi-custom-url }
+
+Чтобы настроить URL OpenAPI (или удалить его), прочитайте документацию: [Руководство — Метаданные и URL документации](../tutorial/metadata.md#openapi-url){.internal-link target=_blank}.
+
+## URL документации OpenAPI { #openapi-docs-urls }
+
+Чтобы изменить URL, используемые для автоматически сгенерированных пользовательских интерфейсов документации, прочитайте документацию: [Руководство — Метаданные и URL документации](../tutorial/metadata.md#docs-urls){.internal-link target=_blank}.
diff --git a/docs/ru/docs/how-to/graphql.md b/docs/ru/docs/how-to/graphql.md
new file mode 100644
index 0000000000..9ed6d95ca1
--- /dev/null
+++ b/docs/ru/docs/how-to/graphql.md
@@ -0,0 +1,60 @@
+# GraphQL { #graphql }
+
+Так как **FastAPI** основан на стандарте **ASGI**, очень легко интегрировать любую библиотеку **GraphQL**, также совместимую с ASGI.
+
+Вы можете комбинировать обычные *операции пути* FastAPI с GraphQL в одном приложении.
+
+/// tip | Совет
+
+**GraphQL** решает некоторые очень специфические задачи.
+
+У него есть как **преимущества**, так и **недостатки** по сравнению с обычными **веб-API**.
+
+Убедитесь, что **выгоды** для вашего случая использования перевешивают **недостатки**. 🤓
+
+///
+
+## Библиотеки GraphQL { #graphql-libraries }
+
+Ниже приведены некоторые библиотеки **GraphQL** с поддержкой **ASGI**. Их можно использовать с **FastAPI**:
+
+* Strawberry 🍓
+ * С документацией для FastAPI
+* Ariadne
+ * С документацией для FastAPI
+* Tartiflette
+ * С Tartiflette ASGI для интеграции с ASGI
+* Graphene
+ * С starlette-graphene3
+
+## GraphQL со Strawberry { #graphql-with-strawberry }
+
+Если вам нужно или хочется работать с **GraphQL**, **Strawberry** — **рекомендуемая** библиотека, так как её дизайн ближе всего к дизайну **FastAPI**, всё основано на **аннотациях типов**.
+
+В зависимости от вашего сценария использования вы можете предпочесть другую библиотеку, но если бы вы спросили меня, я, скорее всего, предложил бы попробовать **Strawberry**.
+
+Вот небольшой пример того, как можно интегрировать Strawberry с FastAPI:
+
+{* ../../docs_src/graphql/tutorial001.py hl[3,22,25] *}
+
+Подробнее о Strawberry можно узнать в документации Strawberry.
+
+А также в документации по интеграции Strawberry с FastAPI.
+
+## Устаревший `GraphQLApp` из Starlette { #older-graphqlapp-from-starlette }
+
+В предыдущих версиях Starlette был класс `GraphQLApp` для интеграции с Graphene.
+
+Он был объявлен устаревшим в Starlette, но если у вас есть код, который его использовал, вы можете легко **мигрировать** на starlette-graphene3, который решает ту же задачу и имеет **почти идентичный интерфейс**.
+
+/// tip | Совет
+
+Если вам нужен GraphQL, я всё же рекомендую посмотреть Strawberry, так как он основан на аннотациях типов, а не на пользовательских классах и типах.
+
+///
+
+## Подробнее { #learn-more }
+
+Подробнее о **GraphQL** вы можете узнать в официальной документации GraphQL.
+
+Также можно почитать больше о каждой из указанных выше библиотек по приведённым ссылкам.
diff --git a/docs/ru/docs/how-to/index.md b/docs/ru/docs/how-to/index.md
new file mode 100644
index 0000000000..228c125dde
--- /dev/null
+++ b/docs/ru/docs/how-to/index.md
@@ -0,0 +1,13 @@
+# Как сделать — Рецепты { #how-to-recipes }
+
+Здесь вы найдете разные рецепты и руководства «как сделать» по различным темам.
+
+Большинство из этих идей более-менее независимы, и в большинстве случаев вам стоит изучать их только если они напрямую относятся к вашему проекту.
+
+Если что-то кажется интересным и полезным для вашего проекта, смело изучайте; в противном случае, вероятно, можно просто пропустить.
+
+/// tip | Совет
+
+Если вы хотите изучить FastAPI структурированно (рекомендуется), вместо этого читайте [Учебник — Руководство пользователя](../tutorial/index.md){.internal-link target=_blank} по главам.
+
+///
diff --git a/docs/ru/docs/how-to/separate-openapi-schemas.md b/docs/ru/docs/how-to/separate-openapi-schemas.md
new file mode 100644
index 0000000000..5b12140167
--- /dev/null
+++ b/docs/ru/docs/how-to/separate-openapi-schemas.md
@@ -0,0 +1,104 @@
+# Разделять схемы OpenAPI для входа и выхода или нет { #separate-openapi-schemas-for-input-and-output-or-not }
+
+При использовании **Pydantic v2** сгенерированный OpenAPI становится чуть более точным и **корректным**, чем раньше. 😎
+
+На самом деле, в некоторых случаях в OpenAPI будет даже **две JSON схемы** для одной и той же Pydantic‑модели: для входа и для выхода — в зависимости от наличия **значений по умолчанию**.
+
+Посмотрим, как это работает, и как это изменить при необходимости.
+
+## Pydantic‑модели для входа и выхода { #pydantic-models-for-input-and-output }
+
+Предположим, у вас есть Pydantic‑модель со значениями по умолчанию, как здесь:
+
+{* ../../docs_src/separate_openapi_schemas/tutorial001_py310.py ln[1:7] hl[7] *}
+
+### Модель для входа { #model-for-input }
+
+Если использовать эту модель как входную, как здесь:
+
+{* ../../docs_src/separate_openapi_schemas/tutorial001_py310.py ln[1:15] hl[14] *}
+
+…то поле `description` **не будет обязательным**, потому что у него значение по умолчанию `None`.
+
+### Входная модель в документации { #input-model-in-docs }
+
+В документации это видно: у поля `description` нет **красной звёздочки** — оно не отмечено как обязательное:
+
+
+
+
+
+
+- Ìlànà wẹ́ẹ́bù FastAPI, iṣẹ́ gíga, ó rọrùn láti kọ̀, o yára láti kóòdù, ó sì ṣetán fún iṣelọpọ ní lílo -
- - ---- - -**Àkọsílẹ̀**: https://fastapi.tiangolo.com - -**Orisun Kóòdù**: https://github.com/fastapi/fastapi - ---- - -FastAPI jẹ́ ìgbàlódé, tí ó yára (iṣẹ-giga), ìlànà wẹ́ẹ́bù fún kikọ àwọn API pẹ̀lú Python èyí tí ó da lori àwọn ìtọ́kasí àmì irúfẹ́ Python. - -Àwọn ẹya pàtàkì ni: - -* **Ó yára**: Iṣẹ tí ó ga púpọ̀, tí ó wa ni ibamu pẹ̀lú **NodeJS** àti **Go** (ọpẹ si Starlette àti Pydantic). [Ọkan nínú àwọn ìlànà Python ti o yára jùlọ ti o wa](#isesi). -* **Ó yára láti kóòdù**: O mu iyara pọ si láti kọ àwọn ẹya tuntun kóòdù nipasẹ "Igba ìdá ọgọ́rùn-ún" (i.e. 200%) si "ọ̀ọ́dúrún ìdá ọgọ́rùn-ún" (i.e. 300%). -* **Àìtọ́ kékeré**: O n din aṣiṣe ku bi ọgbon ìdá ọgọ́rùn-ún (i.e. 40%) ti eda eniyan (oṣiṣẹ kóòdù) fa. * -* **Ọgbọ́n àti ìmọ̀**: Atilẹyin olootu nla. Ìparí nibi gbogbo. Àkókò díẹ̀ nipa wíwá ibi tí ìṣòro kóòdù wà. -* **Irọrun**: A kọ kí ó le rọrun láti lo àti láti kọ ẹkọ nínú rè. Ó máa fún ọ ní àkókò díẹ̀ látı ka àkọsílẹ. -* **Ó kúkurú ní kikọ**: Ó dín àtúnkọ àti àtúntò kóòdù kù. Ìkéde àṣàyàn kọ̀ọ̀kan nínú rẹ̀ ní ọ̀pọ̀lọpọ̀ àwọn ìlò. O ṣe iranlọwọ láti má ṣe ní ọ̀pọ̀lọpọ̀ àṣìṣe. -* **Ó lágbára**: Ó ń ṣe àgbéjáde kóòdù tí ó ṣetán fún ìṣelọ́pọ̀. Pẹ̀lú àkọsílẹ̀ tí ó máa ṣàlàyé ara rẹ̀ fún ẹ ní ìbáṣepọ̀ aládàáṣiṣẹ́ pẹ̀lú rè. -* **Ajohunše/Ìtọ́kasí**: Ó da lori (àti ibamu ni kikun pẹ̀lú) àwọn ìmọ ajohunše/ìtọ́kasí fún àwọn API: OpenAPI (èyí tí a mọ tẹlẹ si Swagger) àti JSON Schema. - -* iṣiro yi da lori àwọn idanwo tí ẹgbẹ ìdàgbàsókè FastAPI ṣe, nígbàtí wọn kọ àwọn ohun elo iṣelọpọ kóòdù pẹ̀lú rẹ. - -## Àwọn onígbọ̀wọ́ - - - -{% if sponsors %} -{% for sponsor in sponsors.gold -%} -async def...uvicorn main:app --reload...email-validator - fún ifọwọsi ímeèlì.
-* pydantic-settings - fún ètò ìsàkóso.
-* pydantic-extra-types - fún àfikún oríṣi láti lọ pẹ̀lú Pydantic.
-
-Èyí tí Starlette ń lò:
-
-* httpx - Nílò tí ó bá fẹ́ láti lọ `TestClient`.
-* jinja2 - Nílò tí ó bá fẹ́ láti lọ iṣeto awoṣe aiyipada.
-* python-multipart - Nílò tí ó bá fẹ́ láti ṣe àtìlẹ́yìn fún "àyẹ̀wò" fọọmu, pẹ̀lú `request.form()`.
-* itsdangerous - Nílò fún àtìlẹ́yìn `SessionMiddleware`.
-* pyyaml - Nílò fún àtìlẹ́yìn Starlette's `SchemaGenerator` (ó ṣe ṣe kí ó má nílò rẹ̀ fún FastAPI).
-
-Èyí tí FastAPI / Starlette ń lò:
-
-* uvicorn - Fún olupin tí yóò sẹ́ àmúyẹ àti tí yóò ṣe ìpèsè fún iṣẹ́ rẹ tàbí ohun èlò rẹ.
-* orjson - Nílò tí ó bá fẹ́ láti lọ `ORJSONResponse`.
-* ujson - Nílò tí ó bá fẹ́ láti lọ `UJSONResponse`.
-
-Ó lè fi gbogbo àwọn wọ̀nyí sórí ẹrọ pẹ̀lú `pip install "fastapi[all]"`.
-
-## Iwe-aṣẹ
-
-Iṣẹ́ yìí ni iwe-aṣẹ lábẹ́ àwọn òfin tí iwe-aṣẹ MIT.
diff --git a/docs/yo/mkdocs.yml b/docs/yo/mkdocs.yml
deleted file mode 100644
index de18856f44..0000000000
--- a/docs/yo/mkdocs.yml
+++ /dev/null
@@ -1 +0,0 @@
-INHERIT: ../en/mkdocs.yml
diff --git a/docs/zh-hant/docs/about/index.md b/docs/zh-hant/docs/about/index.md
new file mode 100644
index 0000000000..5dcee68f2d
--- /dev/null
+++ b/docs/zh-hant/docs/about/index.md
@@ -0,0 +1,3 @@
+# 關於 FastAPI
+
+關於 FastAPI、其設計、靈感來源等更多資訊。 🤓
diff --git a/docs/zh-hant/docs/async.md b/docs/zh-hant/docs/async.md
new file mode 100644
index 0000000000..09e2bf9949
--- /dev/null
+++ b/docs/zh-hant/docs/async.md
@@ -0,0 +1,442 @@
+# 並行與 async / await
+
+有關*路徑操作函式*的 `async def` 語法的細節與非同步 (asynchronous) 程式碼、並行 (concurrency) 與平行 (parallelism) 的一些背景知識。
+
+## 趕時間嗎?
+
+TL;DR:
+
+如果你正在使用要求你以 `await` 語法呼叫的第三方函式庫,例如:
+
+```Python
+results = await some_library()
+```
+
+然後,使用 `async def` 宣告你的*路徑操作函式*:
+
+
+```Python hl_lines="2"
+@app.get('/')
+async def read_results():
+ results = await some_library()
+ return results
+```
+
+/// note | 注意
+
+你只能在 `async def` 建立的函式內使用 `await`。
+
+///
+
+---
+
+如果你使用的是第三方函式庫並且它需要與某些外部資源(例如資料庫、API、檔案系統等)進行通訊,但不支援 `await`(目前大多數資料庫函式庫都是這樣),在這種情況下,你可以像平常一樣使用 `def` 宣告*路徑操作函式*,如下所示:
+
+```Python hl_lines="2"
+@app.get('/')
+def results():
+ results = some_library()
+ return results
+```
+
+---
+
+如果你的應用程式不需要與外部資源進行任何通訊並等待其回應,請使用 `async def`。
+
+---
+
+如果你不確定該用哪個,直接用 `def` 就好。
+
+---
+
+**注意**:你可以在*路徑操作函式*中混合使用 `def` 和 `async def` ,並使用最適合你需求的方式來定義每個函式。FastAPI 會幫你做正確的處理。
+
+無論如何,在上述哪種情況下,FastAPI 仍將以非同步方式運行,並且速度非常快。
+
+但透過遵循上述步驟,它將能進行一些效能最佳化。
+
+## 技術細節
+
+現代版本的 Python 支援使用 **「協程」** 的 **`async` 和 `await`** 語法來寫 **「非同步程式碼」**。
+
+接下來我們逐一介紹:
+
+* **非同步程式碼**
+* **`async` 和 `await`**
+* **協程**
+
+## 非同步程式碼
+
+非同步程式碼僅意味著程式語言 💬 有辦法告訴電腦/程式 🤖 在程式碼中的某個點,它 🤖 需要等待某些事情完成。讓我們假設這些事情被稱為「慢速檔案」📝。
+
+因此,在等待「慢速檔案」📝 完成的這段時間,電腦可以去處理一些其他工作。
+
+接著程式 🤖 會在有空檔時回來查看是否有等待的工作已經完成,並執行必要的後續操作。
+
+接下來,它 🤖 完成第一個工作(例如我們的「慢速檔案」📝)並繼續執行相關的所有操作。
+這個「等待其他事情」通常指的是一些相對較慢的(與處理器和 RAM 記憶體的速度相比)的 I/O 操作,比如說:
+
+* 透過網路傳送來自用戶端的資料
+* 從網路接收來自用戶端的資料
+* 從磁碟讀取檔案內容
+* 將內容寫入磁碟
+* 遠端 API 操作
+* 資料庫操作
+* 資料庫查詢
+* 等等
+
+由於大部分的執行時間都消耗在等待 I/O 操作上,因此這些操作被稱為 "I/O 密集型" 操作。
+
+之所以稱為「非同步」,是因為電腦/程式不需要與那些耗時的任務「同步」,等待任務完成的精確時間,然後才能取得結果並繼續工作。
+
+相反地,非同步系統在任務完成後,可以讓任務稍微等一下(幾微秒),等待電腦/程式完成手頭上的其他工作,然後再回來取得結果繼續進行。
+
+相對於「非同步」(asynchronous),「同步」(synchronous)也常被稱作「順序性」(sequential),因為電腦/程式會依序執行所有步驟,即便這些步驟涉及等待,才會切換到其他任務。
+
+### 並行與漢堡
+
+上述非同步程式碼的概念有時也被稱為「並行」,它不同於「平行」。
+
+並行和平行都與 "不同的事情或多或少同時發生" 有關。
+
+但並行和平行之間的細節是完全不同的。
+
+為了理解差異,請想像以下有關漢堡的故事:
+
+### 並行漢堡
+
+你和你的戀人去速食店,排隊等候時,收銀員正在幫排在你前面的人點餐。😍
+
+
+
+輪到你了,你給你與你的戀人點了兩個豪華漢堡。🍔🍔
+
+
+
+收銀員通知廚房準備你的漢堡(儘管他們還在為前面其他顧客準備食物)。
+
+
+
+之後你完成付款。💸
+
+收銀員給你一個號碼牌。
+
+
+
+在等待漢堡的同時,你可以與戀人選一張桌子,然後坐下來聊很長一段時間(因為漢堡十分豪華,準備特別費工。)
+
+這段時間,你還能欣賞你的戀人有多麼的可愛、聰明與迷人。✨😍✨
+
+
+
+當你和戀人邊聊天邊等待時,你會不時地查看櫃檯上的顯示的號碼,確認是否已經輪到你了。
+
+然後在某個時刻,終於輪到你了。你走到櫃檯,拿了漢堡,然後回到桌子上。
+
+
+
+你和戀人享用這頓大餐,整個過程十分開心✨
+
+
+
+/// info
+
+漂亮的插畫來自 Ketrina Thompson. 🎨
+
+///
+
+---
+
+想像你是故事中的電腦或程式 🤖。
+
+當你排隊時,你在放空😴,等待輪到你,沒有做任何「生產性」的事情。但這沒關係,因為收銀員只是接單(而不是準備食物),所以排隊速度很快。
+
+然後,當輪到你時,你開始做真正「有生產力」的工作,處理菜單,決定你想要什麼,替戀人選擇餐點,付款,確認你給了正確的帳單或信用卡,檢查你是否被正確收費,確認訂單中的項目是否正確等等。
+
+但是,即使你還沒有拿到漢堡,你與收銀員的工作已經「暫停」了 ⏸,因為你必須等待 🕙 漢堡準備好。
+
+但當你離開櫃檯,坐到桌子旁,拿著屬於你的號碼等待時,你可以把注意力 🔀 轉移到戀人身上,並開始「工作」⏯ 🤓——也就是和戀人調情 😍。這時你又開始做一些非常「有生產力」的事情。
+
+接著,收銀員 💁 將你的號碼顯示在櫃檯螢幕上,並告訴你「漢堡已經做好了」。但你不會瘋狂地立刻跳起來,因為顯示的號碼變成了你的。你知道沒有人會搶走你的漢堡,因為你有自己的號碼,他們也有他們的號碼。
+
+所以你會等戀人講完故事(完成當前的工作 ⏯/正在進行的任務 🤓),然後微笑著溫柔地說你要去拿漢堡了 ⏸。
+
+然後你走向櫃檯 🔀,回到已經完成的最初任務 ⏯,拿起漢堡,說聲謝謝,並帶回桌上。這就結束了與櫃檯的互動步驟/任務 ⏹,接下來會產生一個新的任務,「吃漢堡」 🔀 ⏯,而先前的「拿漢堡」任務已經完成了 ⏹。
+
+### 平行漢堡
+
+現在,讓我們來想像這裡不是「並行漢堡」,而是「平行漢堡」。
+
+你和戀人一起去吃平行的速食餐。
+
+你們站在隊伍中,前面有幾位(假設有 8 位)既是收銀員又是廚師的員工,他們同時接單並準備餐點。
+
+所有排在你前面的人都在等著他們的漢堡準備好後才會離開櫃檯,因為每位收銀員在接完單後,馬上會去準備漢堡,然後才回來處理下一個訂單。
+
+
+
+終於輪到你了,你為你和你的戀人點了兩個非常豪華的漢堡。
+
+你付款了 💸。
+
+
+
+收銀員走進廚房準備食物。
+
+你站在櫃檯前等待 🕙,以免其他人先拿走你的漢堡,因為這裡沒有號碼牌系統。
+
+
+
+由於你和戀人都忙著不讓別人搶走你的漢堡,等漢堡準備好時,你根本無法專心和戀人互動。😞
+
+這是「同步」(synchronous)工作,你和收銀員/廚師 👨🍳 是「同步化」的。你必須等到 🕙 收銀員/廚師 👨🍳 完成漢堡並交給你的那一刻,否則別人可能會拿走你的餐點。
+
+
+
+最終,經過長時間的等待 🕙,收銀員/廚師 👨🍳 拿著漢堡回來了。
+
+
+
+你拿著漢堡,和你的戀人回到餐桌。
+
+你們僅僅是吃完漢堡,然後就結束了。⏹
+
+
+
+整個過程中沒有太多的談情說愛,因為大部分時間 🕙 都花在櫃檯前等待。😞
+
+/// info
+
+漂亮的插畫來自 Ketrina Thompson. 🎨
+
+///
+
+---
+
+在這個平行漢堡的情境下,你是一個程式 🤖 且有兩個處理器(你和戀人),兩者都在等待 🕙 並專注於等待櫃檯上的餐點 🕙,等待的時間非常長。
+
+這家速食店有 8 個處理器(收銀員/廚師)。而並行漢堡店可能只有 2 個處理器(一位收銀員和一位廚師)。
+
+儘管如此,最終的體驗並不是最理想的。😞
+
+---
+
+這是與漢堡類似的故事。🍔
+
+一個更「現實」的例子,想像一間銀行。
+
+直到最近,大多數銀行都有多位出納員 👨💼👨💼👨💼👨💼,以及一條長長的隊伍 🕙🕙🕙🕙🕙🕙🕙🕙。
+
+所有的出納員都在一個接一個地滿足每位客戶的所有需求 👨💼⏯。
+
+你必須長時間排隊 🕙,不然就會失去機會。
+
+所以,你不會想帶你的戀人 😍 一起去銀行辦事 🏦。
+
+### 漢堡結論
+
+在「和戀人一起吃速食漢堡」的這個場景中,由於有大量的等待 🕙,使用並行系統 ⏸🔀⏯ 更有意義。
+
+這也是大多數 Web 應用的情況。
+
+許多用戶正在使用你的應用程式,而你的伺服器則在等待 🕙 這些用戶不那麼穩定的網路來傳送請求。
+
+接著,再次等待 🕙 回應。
+
+這種「等待」 🕙 通常以微秒來衡量,但累加起來,最終還是花費了很多等待時間。
+
+這就是為什麼對於 Web API 來說,使用非同步程式碼 ⏸🔀⏯ 是非常有意義的。
+
+這種類型的非同步性正是 NodeJS 成功的原因(儘管 NodeJS 不是平行的),這也是 Go 語言作為程式語言的一個強大優勢。
+
+這與 **FastAPI** 所能提供的性能水平相同。
+
+你可以同時利用並行性和平行性,進一步提升效能,這比大多數已測試的 NodeJS 框架都更快,並且與 Go 語言相當,而 Go 是一種更接近 C 的編譯語言(感謝 Starlette)。
+
+### 並行比平行更好嗎?
+
+不是的!這不是故事的本意。
+
+並行與平行不同。並行在某些 **特定** 的需要大量等待的情境下表現更好。正因如此,並行在 Web 應用程式開發中通常比平行更有優勢。但並不是所有情境都如此。
+
+因此,為了平衡報導,想像下面這個短故事
+
+> 你需要打掃一間又大又髒的房子。
+
+*是的,這就是全部的故事。*
+
+---
+
+這裡沒有任何需要等待 🕙 的地方,只需要在房子的多個地方進行大量的工作。
+
+你可以像漢堡的例子那樣輪流進行,先打掃客廳,再打掃廚房,但由於你不需要等待 🕙 任何事情,只需要持續地打掃,輪流並不會影響任何結果。
+
+無論輪流執行與否(並行),你都需要相同的工時完成任務,同時需要執行相同工作量。
+
+但是,在這種情境下,如果你可以邀請8位前收銀員/廚師(現在是清潔工)來幫忙,每個人(加上你)負責房子的某個區域,這樣你就可以 **平行** 地更快完成工作。
+
+在這個場景中,每個清潔工(包括你)都是一個處理器,完成工作的一部分。
+
+由於大多數的執行時間都花在實際的工作上(而不是等待),而電腦中的工作由 CPU 完成,因此這些問題被稱為「CPU 密集型」。
+
+---
+
+常見的 CPU 密集型操作範例包括那些需要進行複雜數學計算的任務。
+
+例如:
+
+* **音訊**或**圖像處理**;
+* **電腦視覺**:一張圖片由數百萬個像素組成,每個像素有 3 個值/顏色,處理這些像素通常需要同時進行大量計算;
+* **機器學習**: 通常需要大量的「矩陣」和「向量」運算。想像一個包含數字的巨大電子表格,並所有的數字同時相乘;
+* **深度學習**: 這是機器學習的子領域,同樣適用。只不過這不僅僅是一張數字表格,而是大量的數據集合,並且在很多情況下,你會使用特殊的處理器來構建或使用這些模型。
+
+### 並行 + 平行: Web + 機器學習
+
+使用 **FastAPI**,你可以利用並行的優勢,這在 Web 開發中非常常見(這也是 NodeJS 的最大吸引力)。
+
+但你也可以利用平行與多行程 (multiprocessing)(讓多個行程同時運行) 的優勢來處理機器學習系統中的 **CPU 密集型**工作。
+
+這一點,再加上 Python 是 **資料科學**、機器學習,尤其是深度學習的主要語言,讓 **FastAPI** 成為資料科學/機器學習 Web API 和應用程式(以及許多其他應用程式)的絕佳選擇。
+
+想了解如何在生產環境中實現這種平行性,請參見 [部屬](deployment/index.md){.internal-link target=_blank}。
+
+## `async` 和 `await`
+
+現代 Python 版本提供一種非常直觀的方式定義非同步程式碼。這使得它看起來就像正常的「順序」程式碼,並在適當的時機「等待」。
+
+當某個操作需要等待才能回傳結果,並且支援這些新的 Python 特性時,你可以像這樣編寫程式碼:
+
+```Python
+burgers = await get_burgers(2)
+```
+
+這裡的關鍵是 `await`。它告訴 Python 必須等待 ⏸ `get_burgers(2)` 完成它的工作 🕙, 然後將結果儲存在 `burgers` 中。如此,Python 就可以在此期間去處理其他事情 🔀 ⏯ (例如接收另一個請求)。
+
+要讓 `await` 運作,它必須位於支持非同步功能的函式內。為此,只需使用 `async def` 宣告函式:
+
+```Python hl_lines="1"
+async def get_burgers(number: int):
+ # Do some asynchronous stuff to create the burgers
+ return burgers
+```
+
+...而不是 `def`:
+
+```Python hl_lines="2"
+# This is not asynchronous
+def get_sequential_burgers(number: int):
+ # Do some sequential stuff to create the burgers
+ return burgers
+```
+
+使用 `async def`,Python Python 知道在該函式內需要注意 `await`,並且它可以「暫停」 ⏸ 執行該函式,然後執行其他任務 🔀 後回來。
+
+當你想要呼叫 `async def` 函式時,必須使用「await」。因此,這樣寫將無法運行:
+
+```Python
+# This won't work, because get_burgers was defined with: async def
+burgers = get_burgers(2)
+```
+
+---
+
+如果你正在使用某個函式庫,它告訴你可以使用 `await` 呼叫它,那麼你需要用 `async def` 定義*路徑操作函式*,如:
+
+```Python hl_lines="2-3"
+@app.get('/burgers')
+async def read_burgers():
+ burgers = await get_burgers(2)
+ return burgers
+```
+
+### 更多技術細節
+
+你可能已經注意到,`await` 只能在 `async def` 定義的函式內使用。
+
+但同時,使用 `async def` 定義的函式本身也必須被「等待」。所以,帶有 `async def` 函式只能在其他使用 `async def` 定義的函式內呼叫。
+
+那麼,這就像「先有雞還是先有蛋」的問題,要如何呼叫第一個 `async` 函式呢?
+
+如果你使用 FastAPI,無需擔心這個問題,因為「第一個」函式將是你的*路徑操作函式*,FastAPI 會知道如何正確處理這個問題。
+
+但如果你想在沒有 FastAPI 的情況下使用 `async` / `await`,你也可以這樣做。
+
+### 編寫自己的非同步程式碼
+
+Starlette (和 **FastAPI**) 是基於 AnyIO 實作的,這使得它們與 Python 標準函式庫相容 asyncio 和 Trio。
+
+特別是,你可以直接使用 AnyIO 來處理更複雜的並行使用案例,這些案例需要你在自己的程式碼中使用更高階的模式。
+
+即使你不使用 **FastAPI**,你也可以使用 AnyIO 來撰寫自己的非同步應用程式,並獲得高相容性及一些好處(例如結構化並行)。
+
+### 其他形式的非同步程式碼
+
+使用 `async` 和 `await` 的風格在語言中相對較新。
+
+但它使處理異步程式碼變得更加容易。
+
+相同的語法(或幾乎相同的語法)最近也被包含在現代 JavaScript(無論是瀏覽器還是 NodeJS)中。
+
+但在此之前,處理異步程式碼要更加複雜和困難。
+
+在較舊的 Python 版本中,你可能會使用多執行緒或 Gevent。但這些程式碼要更難以理解、調試和思考。
+
+在較舊的 NodeJS / 瀏覽器 JavaScript 中,你會使用「回呼」,這可能會導致“回呼地獄”。
+
+## 協程
+
+**協程** 只是 `async def` 函式所回傳的非常特殊的事物名稱。Python 知道它是一個類似函式的東西,可以啟動它,並且在某個時刻它會結束,但它也可能在內部暫停 ⏸,只要遇到 `await`。
+
+這種使用 `async` 和 `await` 的非同步程式碼功能通常被概括為「協程」。這與 Go 語言的主要特性「Goroutines」相似。
+
+## 結論
+
+讓我們再次回顧之前的句子:
+
+> 現代版本的 Python 支持使用 **"協程"** 的 **`async` 和 `await`** 語法來寫 **"非同步程式碼"**。
+
+現在應該能明白其含意了。✨
+
+這些就是驅動 FastAPI(通過 Starlette)運作的原理,也讓它擁有如此驚人的效能。
+
+## 非常技術性的細節
+
+/// warning
+
+你大概可以跳過這段。
+
+這裡是有關 FastAPI 內部技術細節。
+
+如果你有相當多的技術背景(例如協程、執行緒、阻塞等),並且對 FastAPI 如何處理 `async def` 與常規 `def` 感到好奇,請繼續閱讀。
+
+///
+
+### 路徑操作函数
+
+當你使用 `def` 而不是 `async def` 宣告*路徑操作函式*時,該函式會在外部的執行緒池(threadpool)中執行,然後等待結果,而不是直接呼叫(因為這樣會阻塞伺服器)。
+
+如果你來自於其他不以這種方式運作的非同步框架,而且你習慣於使用普通的 `def` 定義僅進行簡單計算的*路徑操作函式*,目的是獲得微小的性能增益(大約 100 奈秒),請注意,在 FastAPI 中,效果會完全相反。在這些情況下,最好使用 `async def`除非你的*路徑操作函式*執行阻塞的 I/O 的程式碼。
+
+不過,在這兩種情況下,**FastAPI** [仍然很快](index.md#_11){.internal-link target=_blank}至少與你之前的框架相當(或者更快)。
+
+### 依賴項(Dependencies)
+
+同樣適用於[依賴項](tutorial/dependencies/index.md){.internal-link target=_blank}。如果依賴項是一個標準的 `def` 函式,而不是 `async def`,那麼它在外部的執行緒池被運行。
+
+### 子依賴項
+
+你可以擁有多個相互依賴的依賴項和[子依賴項](tutorial/dependencies/sub-dependencies.md){.internal-link target=_blank} (作為函式定義的參數),其中一些可能是用 `async def` 宣告,也可能是用 `def` 宣告。它們仍然可以正常運作,用 `def` 定義的那些將會在外部的執行緒中呼叫(來自執行緒池),而不是被「等待」。
+
+### 其他輔助函式
+
+你可以直接呼叫任何使用 `def` 或 `async def` 建立的其他輔助函式,FastAPI 不會影響你呼叫它們的方式。
+
+這與 FastAPI 為你呼叫*路徑操作函式*和依賴項的邏輯有所不同。
+
+如果你的輔助函式是用 `def` 宣告的,它將會被直接呼叫(按照你在程式碼中撰寫的方式),而不是在執行緒池中。如果該函式是用 `async def` 宣告,那麼你在呼叫時應該使用 `await` 等待其結果。
+
+---
+
+再一次強調,這些都是非常技術性的細節,如果你特地在尋找這些資訊,這些內容可能會對你有幫助。
+
+否則,只需遵循上面提到的指引即可:趕時間嗎?.
diff --git a/docs/zh-hant/docs/deployment/cloud.md b/docs/zh-hant/docs/deployment/cloud.md
new file mode 100644
index 0000000000..426937d3e4
--- /dev/null
+++ b/docs/zh-hant/docs/deployment/cloud.md
@@ -0,0 +1,13 @@
+# 在雲端部署 FastAPI
+
+你幾乎可以使用**任何雲端供應商**來部署你的 FastAPI 應用程式。
+
+在大多數情況下,主要的雲端供應商都有部署 FastAPI 的指南。
+
+## 雲端供應商 - 贊助商
+
+一些雲端供應商 ✨ [**贊助 FastAPI**](../help-fastapi.md#sponsor-the-author){.internal-link target=_blank} ✨,這確保了 FastAPI 及其**生態系統**持續健康地**發展**。
+
+這也展現了他們對 FastAPI 和其**社群**(包括你)的真正承諾,他們不僅希望為你提供**優質的服務**,還希望確保你擁有一個**良好且健康的框架**:FastAPI。🙇
+
+你可能會想嘗試他們的服務,以下有他們的指南.
diff --git a/docs/zh-hant/docs/deployment/index.md b/docs/zh-hant/docs/deployment/index.md
new file mode 100644
index 0000000000..1726562b40
--- /dev/null
+++ b/docs/zh-hant/docs/deployment/index.md
@@ -0,0 +1,21 @@
+# 部署
+
+部署 **FastAPI** 應用程式相對容易。
+
+## 部署是什麼意思
+
+**部署**應用程式指的是執行一系列必要的步驟,使其能夠**讓使用者存取和使用**。
+
+對於一個 **Web API**,部署通常涉及將其放置在**遠端伺服器**上,並使用性能優良且穩定的**伺服器程式**,確保使用者能夠高效、無中斷地存取應用程式,且不會遇到問題。
+
+這與**開發**階段形成鮮明對比,在**開發**階段,你會不斷更改程式碼、破壞程式碼、修復程式碼,然後停止和重新啟動伺服器等。
+
+## 部署策略
+
+根據你的使用場景和使用工具,有多種方法可以實現此目的。
+
+你可以使用一些工具自行**部署伺服器**,你也可以使用能為你完成部分工作的**雲端服務**,或其他可能的選項。
+
+我將向你展示在部署 **FastAPI** 應用程式時你可能應該記住的一些主要概念(儘管其中大部分適用於任何其他類型的 Web 應用程式)。
+
+在接下來的部分中,你將看到更多需要記住的細節以及一些技巧。 ✨
diff --git a/docs/zh-hant/docs/environment-variables.md b/docs/zh-hant/docs/environment-variables.md
new file mode 100644
index 0000000000..a1598fc018
--- /dev/null
+++ b/docs/zh-hant/docs/environment-variables.md
@@ -0,0 +1,298 @@
+# 環境變數
+
+/// tip
+
+如果你已經知道什麼是「環境變數」並且知道如何使用它們,你可以放心跳過這一部分。
+
+///
+
+環境變數(也稱為「**env var**」)是一個獨立於 Python 程式碼**之外**的變數,它存在於**作業系統**中,可以被你的 Python 程式碼(或其他程式)讀取。
+
+環境變數對於處理應用程式**設定**(作為 Python **安裝**的一部分等方面)非常有用。
+
+## 建立和使用環境變數
+
+你在 **shell(終端機)**中就可以**建立**和使用環境變數,並不需要用到 Python:
+
+//// tab | Linux, macOS, Windows Bash
+
+
-
+
@@ -81,13 +81,13 @@ FastAPI 是一個現代、快速(高效能)的 web 框架,用於 Python
"_我對 **FastAPI** 興奮得不得了。它太有趣了!_"
-
uvicorn - 用於加載和運行應用程式的服務器。
+- uvicorn - 用於加載和運行應用程式的服務器。
- orjson - 使用 `ORJSONResponse`時必須安裝。
- ujson - 使用 `UJSONResponse` 時必須安裝。
diff --git a/docs/zh-hant/docs/resources/index.md b/docs/zh-hant/docs/resources/index.md
new file mode 100644
index 0000000000..f4c70a3a04
--- /dev/null
+++ b/docs/zh-hant/docs/resources/index.md
@@ -0,0 +1,3 @@
+# 資源
+
+額外的資源、外部連結、文章等。 ✈️
diff --git a/docs/zh-hant/docs/tutorial/first-steps.md b/docs/zh-hant/docs/tutorial/first-steps.md
new file mode 100644
index 0000000000..d6684bc513
--- /dev/null
+++ b/docs/zh-hant/docs/tutorial/first-steps.md
@@ -0,0 +1,331 @@
+# 第一步
+
+最簡單的 FastAPI 檔案可能看起來像這樣:
+
+{* ../../docs_src/first_steps/tutorial001.py *}
+
+將其複製到一個名為 `main.py` 的文件中。
+
+執行即時重新載入伺服器(live server):
+
+get操作
+
+/// info | `@decorator` Info
+
+Python 中的 `@something` 語法被稱為「裝飾器」。
+
+你把它放在一個函式上面。像一個漂亮的裝飾帽子(我猜這是術語的來源)。
+
+一個「裝飾器」會對下面的函式做一些事情。
+
+在這種情況下,這個裝飾器告訴 **FastAPI** 那個函式對應於 **路徑** `/` 和 **操作** `get`.
+
+這就是「**路徑操作裝飾器**」。
+
+///
+
+你也可以使用其他的操作:
+
+* `@app.post()`
+* `@app.put()`
+* `@app.delete()`
+
+以及更少見的:
+
+* `@app.options()`
+* `@app.head()`
+* `@app.patch()`
+* `@app.trace()`
+
+/// tip
+
+你可以自由地使用每個操作(HTTP 方法)。
+
+**FastAPI** 不強制任何特定的意義。
+
+這裡的資訊作為一個指南,而不是要求。
+
+例如,當使用 GraphQL 時,你通常只使用 `POST` 操作。
+
+///
+
+### 第四步:定義 **路徑操作函式**
+
+這是我們的「**路徑操作函式**」:
+
+* **path**: 是 `/`.
+* **operation**: 是 `get`.
+* **function**: 是裝飾器下面的函式(在 `@app.get("/")` 下面)。
+
+{* ../../docs_src/first_steps/tutorial001.py h1[7] *}
+
+這就是一個 Python 函式。
+
+它將會在 **FastAPI** 收到一個請求時被呼叫,使用 `GET` 操作。
+
+在這種情況下,它是一個 `async` 函式。
+
+---
+
+你可以將它定義為一個正常的函式,而不是 `async def`:
+
+{* ../../docs_src/first_steps/tutorial003.py h1[7] *}
+
+/// note
+
+如果你不知道差別,請查看 [Async: *"In a hurry?"*](../async.md#in-a-hurry){.internal-link target=_blank}.
+
+///
+
+### 第五步:回傳內容
+
+{* ../../docs_src/first_steps/tutorial001.py h1[8] *}
+
+你可以返回一個 `dict`、`list`、單個值作為 `str`、`int` 等。
+
+你也可以返回 Pydantic 模型(稍後你會看到更多關於這方面的內容)。
+
+有很多其他物件和模型會自動轉換為 JSON(包括 ORMs,等等)。試用你最喜歡的,很有可能它們已經有支援。
+
+## 回顧
+
+* 引入 `FastAPI`.
+* 建立一個 `app` 實例。
+* 寫一個 **路徑操作裝飾器** 使用裝飾器像 `@app.get("/")`。
+* 定義一個 **路徑操作函式**;例如,`def root(): ...`。
+* 使用命令 `fastapi dev` 執行開發伺服器。
diff --git a/docs/zh-hant/docs/tutorial/index.md b/docs/zh-hant/docs/tutorial/index.md
new file mode 100644
index 0000000000..ae0056f528
--- /dev/null
+++ b/docs/zh-hant/docs/tutorial/index.md
@@ -0,0 +1,102 @@
+# 教學 - 使用者指南
+
+本教學將一步一步展示如何使用 **FastAPI** 及其大多數功能。
+
+每個部分都是在前一部分的基礎上逐步建置的,但內容結構是按主題分開的,因此你可以直接跳到任何特定的部分,解決你具體的 API 需求。
+
+它也被設計成可作為未來的參考,讓你隨時回來查看所需的內容。
+
+## 運行程式碼
+
+所有程式碼區塊都可以直接複製和使用(它們實際上是經過測試的 Python 檔案)。
+
+要運行任何範例,請將程式碼複製到 `main.py` 檔案,並使用以下命令啟動 `fastapi dev`:
+
+
+
-/// tip | "提示"
+/// tip | 提示
API 文档与所选的服务器进行交互。
@@ -352,9 +346,7 @@ API 文档与所选的服务器进行交互。
如果不想让 **FastAPI** 包含使用 `root_path` 的自动服务器,则要使用参数 `root_path_in_servers=False`:
-```Python hl_lines="9"
-{!../../../docs_src/behind_a_proxy/tutorial004.py!}
-```
+{* ../../docs_src/behind_a_proxy/tutorial004.py hl[9] *}
这样,就不会在 OpenAPI 概图中包含服务器了。
diff --git a/docs/zh/docs/advanced/custom-response.md b/docs/zh/docs/advanced/custom-response.md
index 9594c72ac4..22a9b4b510 100644
--- a/docs/zh/docs/advanced/custom-response.md
+++ b/docs/zh/docs/advanced/custom-response.md
@@ -12,7 +12,7 @@
并且如果该 `Response` 有一个 JSON 媒体类型(`application/json`),比如使用 `JSONResponse` 或者 `UJSONResponse` 的时候,返回的数据将使用你在路径操作装饰器中声明的任何 Pydantic 的 `response_model` 自动转换(和过滤)。
-/// note | "说明"
+/// note | 说明
如果你使用不带有任何媒体类型的响应类,FastAPI 认为你的响应没有任何内容,所以不会在生成的OpenAPI文档中记录响应格式。
@@ -24,11 +24,9 @@
导入你想要使用的 `Response` 类(子类)然后在 *路径操作装饰器* 中声明它。
-```Python hl_lines="2 7"
-{!../../../docs_src/custom_response/tutorial001b.py!}
-```
+{* ../../docs_src/custom_response/tutorial001b.py hl[2,7] *}
-/// info | "提示"
+/// info | 提示
参数 `response_class` 也会用来定义响应的「媒体类型」。
@@ -38,7 +36,7 @@
///
-/// tip | "小贴士"
+/// tip | 小贴士
`ORJSONResponse` 目前只在 FastAPI 中可用,而在 Starlette 中不可用。
@@ -51,11 +49,9 @@
* 导入 `HTMLResponse`。
* 将 `HTMLResponse` 作为你的 *路径操作* 的 `response_class` 参数传入。
-```Python hl_lines="2 7"
-{!../../../docs_src/custom_response/tutorial002.py!}
-```
+{* ../../docs_src/custom_response/tutorial002.py hl[2,7] *}
-/// info | "提示"
+/// info | 提示
参数 `response_class` 也会用来定义响应的「媒体类型」。
@@ -71,17 +67,15 @@
和上面一样的例子,返回一个 `HTMLResponse` 看起来可能是这样:
-```Python hl_lines="2 7 19"
-{!../../../docs_src/custom_response/tutorial003.py!}
-```
+{* ../../docs_src/custom_response/tutorial003.py hl[2,7,19] *}
-/// warning | "警告"
+/// warning | 警告
*路径操作函数* 直接返回的 `Response` 不会被 OpenAPI 的文档记录(比如,`Content-Type` 不会被文档记录),并且在自动化交互文档中也是不可见的。
///
-/// info | "提示"
+/// info | 提示
当然,实际的 `Content-Type` 头,状态码等等,将来自于你返回的 `Response` 对象。
@@ -97,9 +91,7 @@
比如像这样:
-```Python hl_lines="7 23 21"
-{!../../../docs_src/custom_response/tutorial004.py!}
-```
+{* ../../docs_src/custom_response/tutorial004.py hl[7,23,21] *}
在这个例子中,函数 `generate_html_response()` 已经生成并返回 `Response` 对象而不是在 `str` 中返回 HTML。
@@ -115,7 +107,7 @@
要记得你可以使用 `Response` 来返回任何其他东西,甚至创建一个自定义的子类。
-/// note | "技术细节"
+/// note | 技术细节
你也可以使用 `from starlette.responses import HTMLResponse`。
@@ -139,9 +131,7 @@
FastAPI(实际上是 Starlette)将自动包含 Content-Length 的头。它还将包含一个基于 media_type 的 Content-Type 头,并为文本类型附加一个字符集。
-```Python hl_lines="1 18"
-{!../../../docs_src/response_directly/tutorial002.py!}
-```
+{* ../../docs_src/response_directly/tutorial002.py hl[1,18] *}
### `HTMLResponse`
@@ -151,9 +141,7 @@ FastAPI(实际上是 Starlette)将自动包含 Content-Length 的头。它
接受文本或字节并返回纯文本响应。
-```Python hl_lines="2 7 9"
-{!../../../docs_src/custom_response/tutorial005.py!}
-```
+{* ../../docs_src/custom_response/tutorial005.py hl[2,7,9] *}
### `JSONResponse`
@@ -170,17 +158,15 @@ FastAPI(实际上是 Starlette)将自动包含 Content-Length 的头。它
`UJSONResponse` 是一个使用 `ujson` 的可选 JSON 响应。
-/// warning | "警告"
+/// warning | 警告
在处理某些边缘情况时,`ujson` 不如 Python 的内置实现那么谨慎。
///
-```Python hl_lines="2 7"
-{!../../../docs_src/custom_response/tutorial001.py!}
-```
+{* ../../docs_src/custom_response/tutorial001.py hl[2,7] *}
-/// tip | "小贴士"
+/// tip | 小贴士
`ORJSONResponse` 可能是一个更快的选择。
@@ -190,17 +176,13 @@ FastAPI(实际上是 Starlette)将自动包含 Content-Length 的头。它
返回 HTTP 重定向。默认情况下使用 307 状态代码(临时重定向)。
-```Python hl_lines="2 9"
-{!../../../docs_src/custom_response/tutorial006.py!}
-```
+{* ../../docs_src/custom_response/tutorial006.py hl[2,9] *}
### `StreamingResponse`
采用异步生成器或普通生成器/迭代器,然后流式传输响应主体。
-```Python hl_lines="2 14"
-{!../../../docs_src/custom_response/tutorial007.py!}
-```
+{* ../../docs_src/custom_response/tutorial007.py hl[2,14] *}
#### 对类似文件的对象使用 `StreamingResponse`
@@ -208,11 +190,9 @@ FastAPI(实际上是 Starlette)将自动包含 Content-Length 的头。它
包括许多与云存储,视频处理等交互的库。
-```Python hl_lines="2 10-12 14"
-{!../../../docs_src/custom_response/tutorial008.py!}
-```
+{* ../../docs_src/custom_response/tutorial008.py hl[2,10:12,14] *}
-/// tip | "小贴士"
+/// tip | 小贴士
注意在这里,因为我们使用的是不支持 `async` 和 `await` 的标准 `open()`,我们使用普通的 `def` 声明了路径操作。
@@ -231,9 +211,7 @@ FastAPI(实际上是 Starlette)将自动包含 Content-Length 的头。它
文件响应将包含适当的 `Content-Length`,`Last-Modified` 和 `ETag` 的响应头。
-```Python hl_lines="2 10"
-{!../../../docs_src/custom_response/tutorial009.py!}
-```
+{* ../../docs_src/custom_response/tutorial009.py hl[2,10] *}
## 额外文档
diff --git a/docs/zh/docs/advanced/dataclasses.md b/docs/zh/docs/advanced/dataclasses.md
index 72567e2458..c74ce65c3e 100644
--- a/docs/zh/docs/advanced/dataclasses.md
+++ b/docs/zh/docs/advanced/dataclasses.md
@@ -4,9 +4,7 @@ FastAPI 基于 **Pydantic** 构建,前文已经介绍过如何使用 Pydantic
但 FastAPI 还可以使用数据类(`dataclasses`):
-```Python hl_lines="1 7-12 19-20"
-{!../../../docs_src/dataclasses/tutorial001.py!}
-```
+{* ../../docs_src/dataclasses/tutorial001.py hl[1,7:12,19:20] *}
这还是借助于 **Pydantic** 及其内置的 `dataclasses`。
@@ -20,7 +18,7 @@ FastAPI 基于 **Pydantic** 构建,前文已经介绍过如何使用 Pydantic
数据类的和运作方式与 Pydantic 模型相同。实际上,它的底层使用的也是 Pydantic。
-/// info | "说明"
+/// info | 说明
注意,数据类不支持 Pydantic 模型的所有功能。
@@ -34,9 +32,7 @@ FastAPI 基于 **Pydantic** 构建,前文已经介绍过如何使用 Pydantic
在 `response_model` 参数中使用 `dataclasses`:
-```Python hl_lines="1 7-13 19"
-{!../../../docs_src/dataclasses/tutorial002.py!}
-```
+{* ../../docs_src/dataclasses/tutorial002.py hl[1,7:13,19] *}
本例把数据类自动转换为 Pydantic 数据类。
@@ -53,7 +49,7 @@ API 文档中也会显示相关概图:
本例把标准的 `dataclasses` 直接替换为 `pydantic.dataclasses`:
```{ .python .annotate hl_lines="1 5 8-11 14-17 23-25 28" }
-{!../../../docs_src/dataclasses/tutorial003.py!}
+{!../../docs_src/dataclasses/tutorial003.py!}
```
1. 本例依然要从标准的 `dataclasses` 中导入 `field`;
diff --git a/docs/zh/docs/advanced/events.md b/docs/zh/docs/advanced/events.md
index c9389f533c..1ef6cdd3ce 100644
--- a/docs/zh/docs/advanced/events.md
+++ b/docs/zh/docs/advanced/events.md
@@ -1,22 +1,118 @@
-# 事件:启动 - 关闭
+# 生命周期事件
-**FastAPI** 支持定义在应用启动前,或应用关闭后执行的事件处理器(函数)。
+你可以定义在应用**启动**前执行的逻辑(代码)。这意味着在应用**开始接收请求**之前,这些代码只会被执行**一次**。
-事件函数既可以声明为异步函数(`async def`),也可以声明为普通函数(`def`)。
+同样地,你可以定义在应用**关闭**时应执行的逻辑。在这种情况下,这段代码将在**处理可能的多次请求后**执行**一次**。
-/// warning | "警告"
+因为这段代码在应用开始接收请求**之前**执行,也会在处理可能的若干请求**之后**执行,它覆盖了整个应用程序的**生命周期**("生命周期"这个词很重要😉)。
-**FastAPI** 只执行主应用中的事件处理器,不执行[子应用 - 挂载](sub-applications.md){.internal-link target=_blank}中的事件处理器。
+这对于设置你需要在整个应用中使用的**资源**非常有用,这些资源在请求之间**共享**,你可能需要在之后进行**释放**。例如,数据库连接池,或加载一个共享的机器学习模型。
+
+## 用例
+
+让我们从一个示例用例开始,看看如何解决它。
+
+假设你有几个**机器学习的模型**,你想要用它们来处理请求。
+
+相同的模型在请求之间是共享的,因此并非每个请求或每个用户各自拥有一个模型。
+
+假设加载模型可能**需要相当长的时间**,因为它必须从**磁盘**读取大量数据。因此你不希望每个请求都加载它。
+
+你可以在模块/文件的顶部加载它,但这也意味着即使你只是在运行一个简单的自动化测试,它也会**加载模型**,这样测试将**变慢**,因为它必须在能够独立运行代码的其他部分之前等待模型加载完成。
+
+这就是我们要解决的问题——在处理请求前加载模型,但只是在应用开始接收请求前,而不是代码执行时。
+
+## 生命周期 lifespan
+
+你可以使用`FastAPI()`应用的`lifespan`参数和一个上下文管理器(稍后我将为你展示)来定义**启动**和**关闭**的逻辑。
+
+让我们从一个例子开始,然后详细介绍。
+
+我们使用`yield`创建了一个异步函数`lifespan()`像这样:
+
+```Python hl_lines="16 19"
+{!../../docs_src/events/tutorial003.py!}
+```
+
+在这里,我们在 `yield` 之前将(虚拟的)模型函数放入机器学习模型的字典中,以此模拟加载模型的耗时**启动**操作。这段代码将在应用程序**开始处理请求之前**执行,即**启动**期间。
+
+然后,在 `yield` 之后,我们卸载模型。这段代码将会在应用程序**完成处理请求后**执行,即在**关闭**之前。这可以释放诸如内存或 GPU 之类的资源。
+
+/// tip | 提示
+
+**关闭**事件只会在你停止应用时触发。
+
+可能你需要启动一个新版本,或者你只是你厌倦了运行它。 🤷
///
-## `startup` 事件
+## 生命周期函数
+
+首先要注意的是,我们定义了一个带有 `yield` 的异步函数。这与带有 `yield` 的依赖项非常相似。
+
+```Python hl_lines="14-19"
+{!../../docs_src/events/tutorial003.py!}
+```
+
+这个函数在 `yield`之前的部分,会在应用启动前执行。
+
+剩下的部分在 `yield` 之后,会在应用完成后执行。
+
+## 异步上下文管理器
+
+如你所见,这个函数有一个装饰器 `@asynccontextmanager` 。
+
+它将函数转化为所谓的“**异步上下文管理器**”。
+
+```Python hl_lines="1 13"
+{!../../docs_src/events/tutorial003.py!}
+```
+
+在 Python 中, **上下文管理器**是一个你可以在 `with` 语句中使用的东西,例如,`open()` 可以作为上下文管理器使用。
+
+```Python
+with open("file.txt") as file:
+ file.read()
+```
+
+Python 的最近几个版本也有了一个**异步上下文管理器**,你可以通过 `async with` 来使用:
+
+```Python
+async with lifespan(app):
+ await do_stuff()
+```
+
+你可以像上面一样创建了一个上下文管理器或者异步上下文管理器,它的作用是在进入 `with` 块时,执行 `yield` 之前的代码,并且在离开 `with` 块时,执行 `yield` 后面的代码。
+
+但在我们上面的例子里,我们并不是直接使用,而是传递给 FastAPI 来供其使用。
+
+`FastAPI()` 的 `lifespan` 参数接受一个**异步上下文管理器**,所以我们可以把我们新定义的上下文管理器 `lifespan` 传给它。
+
+```Python hl_lines="22"
+{!../../docs_src/events/tutorial003.py!}
+```
+
+## 替代事件(弃用)
+
+/// warning | 警告
+
+配置**启动**和**关闭**事件的推荐方法是使用 `FastAPI()` 应用的 `lifespan` 参数,如前所示。如果你提供了一个 `lifespan` 参数,启动(`startup`)和关闭(`shutdown`)事件处理器将不再生效。要么使用 `lifespan`,要么配置所有事件,两者不能共用。
+
+你可以跳过这一部分。
+
+///
+
+有一种替代方法可以定义在**启动**和**关闭**期间执行的逻辑。
+
+**FastAPI** 支持定义在应用启动前,或应用关闭时执行的事件处理器(函数)。
+
+事件函数既可以声明为异步函数(`async def`),也可以声明为普通函数(`def`)。
+
+### `startup` 事件
使用 `startup` 事件声明 `app` 启动前运行的函数:
-```Python hl_lines="8"
-{!../../../docs_src/events/tutorial001.py!}
-```
+{* ../../docs_src/events/tutorial001.py hl[8] *}
本例中,`startup` 事件处理器函数为项目数据库(只是**字典**)提供了一些初始值。
@@ -24,23 +120,21 @@
只有所有 `startup` 事件处理器运行完毕,**FastAPI** 应用才开始接收请求。
-## `shutdown` 事件
+### `shutdown` 事件
使用 `shutdown` 事件声明 `app` 关闭时运行的函数:
-```Python hl_lines="6"
-{!../../../docs_src/events/tutorial002.py!}
-```
+{* ../../docs_src/events/tutorial002.py hl[6] *}
此处,`shutdown` 事件处理器函数在 `log.txt` 中写入一行文本 `Application shutdown`。
-/// info | "说明"
+/// info | 说明
`open()` 函数中,`mode="a"` 指的是**追加**。因此这行文本会添加在文件已有内容之后,不会覆盖之前的内容。
///
-/// tip | "提示"
+/// tip | 提示
注意,本例使用 Python `open()` 标准函数与文件交互。
@@ -52,8 +146,28 @@
///
-/// info | "说明"
+### `startup` 和 `shutdown` 一起使用
-有关事件处理器的详情,请参阅 Starlette 官档 - 事件。
+启动和关闭的逻辑很可能是连接在一起的,你可能希望启动某个东西然后结束它,获取一个资源然后释放它等等。
+
+在不共享逻辑或变量的不同函数中处理这些逻辑比较困难,因为你需要在全局变量中存储值或使用类似的方式。
+
+因此,推荐使用 `lifespan` 。
+
+## 技术细节
+
+只是为好奇者提供的技术细节。🤓
+
+在底层,这部分是生命周期协议的一部分,参见 ASGI 技术规范,定义了称为启动(`startup`)和关闭(`shutdown`)的事件。
+
+/// info | 说明
+
+有关事件处理器的详情,请参阅 Starlette 官档 - 事件。
+
+包括如何处理生命周期状态,这可以用于程序的其他部分。
///
+
+## 子应用
+
+🚨 **FastAPI** 只会触发主应用中的生命周期事件,不包括[子应用 - 挂载](sub-applications.md){.internal-link target=_blank}中的。
diff --git a/docs/zh/docs/advanced/generate-clients.md b/docs/zh/docs/advanced/generate-clients.md
index 56aad3bd26..bcb9ba2bf7 100644
--- a/docs/zh/docs/advanced/generate-clients.md
+++ b/docs/zh/docs/advanced/generate-clients.md
@@ -16,21 +16,7 @@
让我们从一个简单的 FastAPI 应用开始:
-//// tab | Python 3.9+
-
-```Python hl_lines="7-9 12-13 16-17 21"
-{!> ../../../docs_src/generate_clients/tutorial001_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="9-11 14-15 18 19 23"
-{!> ../../../docs_src/generate_clients/tutorial001.py!}
-```
-
-////
+{* ../../docs_src/generate_clients/tutorial001_py39.py hl[7:9,12:13,16:17,21] *}
请注意,*路径操作* 定义了他们所用于请求数据和回应数据的模型,所使用的模型是`Item` 和 `ResponseMessage`。
@@ -135,21 +121,7 @@ frontend-app@1.0.0 generate-client /home/user/code/frontend-app
例如,您可以有一个用 `items` 的部分和另一个用于 `users` 的部分,它们可以用标签来分隔:
-//// tab | Python 3.9+
-
-```Python hl_lines="21 26 34"
-{!> ../../../docs_src/generate_clients/tutorial002_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="23 28 36"
-{!> ../../../docs_src/generate_clients/tutorial002.py!}
-```
-
-////
+{* ../../docs_src/generate_clients/tutorial002_py39.py hl[21,26,34] *}
### 生成带有标签的 TypeScript 客户端
@@ -196,21 +168,7 @@ FastAPI为每个*路径操作*使用一个**唯一ID**,它用于**操作ID**
然后,你可以将这个自定义函数作为 `generate_unique_id_function` 参数传递给 **FastAPI**:
-//// tab | Python 3.9+
-
-```Python hl_lines="6-7 10"
-{!> ../../../docs_src/generate_clients/tutorial003_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="8-9 12"
-{!> ../../../docs_src/generate_clients/tutorial003.py!}
-```
-
-////
+{* ../../docs_src/generate_clients/tutorial003_py39.py hl[6:7,10] *}
### 使用自定义操作ID生成TypeScript客户端
@@ -232,9 +190,7 @@ FastAPI为每个*路径操作*使用一个**唯一ID**,它用于**操作ID**
我们可以将 OpenAPI JSON 下载到一个名为`openapi.json`的文件中,然后使用以下脚本**删除此前缀的标签**:
-```Python
-{!../../../docs_src/generate_clients/tutorial004.py!}
-```
+{* ../../docs_src/generate_clients/tutorial004.py *}
通过这样做,操作ID将从类似于 `items-get_items` 的名称重命名为 `get_items` ,这样客户端生成器就可以生成更简洁的方法名称。
diff --git a/docs/zh/docs/advanced/middleware.md b/docs/zh/docs/advanced/middleware.md
index 926082b94f..65e8c183f2 100644
--- a/docs/zh/docs/advanced/middleware.md
+++ b/docs/zh/docs/advanced/middleware.md
@@ -43,7 +43,7 @@ app.add_middleware(UnicornMiddleware, some_config="rainbow")
**FastAPI** 为常见用例提供了一些中间件,下面介绍怎么使用这些中间件。
-/// note | "技术细节"
+/// note | 技术细节
以下几个示例中也可以使用 `from starlette.middleware.something import SomethingMiddleware`。
@@ -57,17 +57,13 @@ app.add_middleware(UnicornMiddleware, some_config="rainbow")
任何传向 `http` 或 `ws` 的请求都会被重定向至安全方案。
-```Python hl_lines="2 6"
-{!../../../docs_src/advanced_middleware/tutorial001.py!}
-```
+{* ../../docs_src/advanced_middleware/tutorial001.py hl[2,6] *}
## `TrustedHostMiddleware`
强制所有传入请求都必须正确设置 `Host` 请求头,以防 HTTP 主机头攻击。
-```Python hl_lines="2 6-8"
-{!../../../docs_src/advanced_middleware/tutorial002.py!}
-```
+{* ../../docs_src/advanced_middleware/tutorial002.py hl[2,6:8] *}
支持以下参数:
@@ -81,9 +77,7 @@ app.add_middleware(UnicornMiddleware, some_config="rainbow")
中间件会处理标准响应与流响应。
-```Python hl_lines="2 6"
-{!../../../docs_src/advanced_middleware/tutorial003.py!}
-```
+{* ../../docs_src/advanced_middleware/tutorial003.py hl[2,6] *}
支持以下参数:
@@ -98,4 +92,4 @@ app.add_middleware(UnicornMiddleware, some_config="rainbow")
* Uvicorn 的 `ProxyHeadersMiddleware`
* MessagePack
-其它可用中间件详见 Starlette 官档 - 中间件 及 ASGI Awesome 列表。
+其它可用中间件详见 Starlette 官档 - 中间件 及 ASGI Awesome 列表。
diff --git a/docs/zh/docs/advanced/openapi-callbacks.md b/docs/zh/docs/advanced/openapi-callbacks.md
index 7c7323cb5e..f021eb10ae 100644
--- a/docs/zh/docs/advanced/openapi-callbacks.md
+++ b/docs/zh/docs/advanced/openapi-callbacks.md
@@ -31,11 +31,9 @@ API 的用户 (外部开发者)要在您的 API 内使用 POST 请求创建
这部分代码很常规,您对绝大多数代码应该都比较熟悉了:
-```Python hl_lines="10-14 37-54"
-{!../../../docs_src/openapi_callbacks/tutorial001.py!}
-```
+{* ../../docs_src/openapi_callbacks/tutorial001.py hl[10:14,37:54] *}
-/// tip | "提示"
+/// tip | 提示
`callback_url` 查询参数使用 Pydantic 的 URL 类型。
@@ -64,7 +62,7 @@ requests.post(callback_url, json={"description": "Invoice paid", "paid": True})
本例没有实现回调本身(只是一行代码),只有文档部分。
-/// tip | "提示"
+/// tip | 提示
实际的回调只是 HTTP 请求。
@@ -80,7 +78,7 @@ requests.post(callback_url, json={"description": "Invoice paid", "paid": True})
我们要使用与存档*外部 API* 相同的知识……通过创建外部 API 要实现的*路径操作*(您的 API 要调用的)。
-/// tip | "提示"
+/// tip | 提示
编写存档回调的代码时,假设您是*外部开发者*可能会用的上。并且您当前正在实现的是*外部 API*,不是*您自己的 API*。
@@ -92,9 +90,7 @@ requests.post(callback_url, json={"description": "Invoice paid", "paid": True})
首先,新建包含一些用于回调的 `APIRouter`。
-```Python hl_lines="5 26"
-{!../../../docs_src/openapi_callbacks/tutorial001.py!}
-```
+{* ../../docs_src/openapi_callbacks/tutorial001.py hl[5,26] *}
### 创建回调*路径操作*
@@ -105,9 +101,7 @@ requests.post(callback_url, json={"description": "Invoice paid", "paid": True})
* 声明要接收的请求体,例如,`body: InvoiceEvent`
* 还要声明要返回的响应,例如,`response_model=InvoiceEventReceived`
-```Python hl_lines="17-19 22-23 29-33"
-{!../../../docs_src/openapi_callbacks/tutorial001.py!}
-```
+{* ../../docs_src/openapi_callbacks/tutorial001.py hl[17:19,22:23,29:33] *}
回调*路径操作*与常规*路径操作*有两点主要区别:
@@ -163,7 +157,7 @@ JSON 请求体包含如下内容:
}
```
-/// tip | "提示"
+/// tip | 提示
注意,回调 URL包含 `callback_url` (`https://www.external.org/events`)中的查询参数,还有 JSON 请求体内部的发票 ID(`2expen51ve`)。
@@ -175,11 +169,9 @@ JSON 请求体包含如下内容:
现在使用 API *路径操作装饰器*的参数 `callbacks`,从回调路由传递属性 `.routes`(实际上只是路由/路径操作的**列表**):
-```Python hl_lines="36"
-{!../../../docs_src/openapi_callbacks/tutorial001.py!}
-```
+{* ../../docs_src/openapi_callbacks/tutorial001.py hl[36] *}
-/// tip | "提示"
+/// tip | 提示
注意,不能把路由本身(`invoices_callback_router`)传递给 `callback=`,要传递 `invoices_callback_router.routes` 中的 `.routes` 属性。
diff --git a/docs/zh/docs/advanced/openapi-webhooks.md b/docs/zh/docs/advanced/openapi-webhooks.md
new file mode 100644
index 0000000000..92ae8db15f
--- /dev/null
+++ b/docs/zh/docs/advanced/openapi-webhooks.md
@@ -0,0 +1,55 @@
+# OpenAPI 网络钩子
+
+有些情况下,您可能想告诉您的 API **用户**,您的应用程序可以携带一些数据调用*他们的*应用程序(给它们发送请求),通常是为了**通知**某种**事件**。
+
+这意味着,除了您的用户向您的 API 发送请求的一般情况,**您的 API**(或您的应用)也可以向**他们的系统**(他们的 API、他们的应用)**发送请求**。
+
+这通常被称为**网络钩子**(Webhook)。
+
+## 使用网络钩子的步骤
+
+通常的过程是**您**在代码中**定义**要发送的消息,即**请求的主体**。
+
+您还需要以某种方式定义您的应用程序将在**何时**发送这些请求或事件。
+
+**用户**会以某种方式(例如在某个网页仪表板上)定义您的应用程序发送这些请求应该使用的 **URL**。
+
+所有关于注册网络钩子的 URL 的**逻辑**以及发送这些请求的实际代码都由您决定。您可以在**自己的代码**中以任何想要的方式来编写它。
+
+## 使用 `FastAPI` 和 OpenAPI 文档化网络钩子
+
+使用 **FastAPI**,您可以利用 OpenAPI 来自定义这些网络钩子的名称、您的应用可以发送的 HTTP 操作类型(例如 `POST`、`PUT` 等)以及您的应用将发送的**请求体**。
+
+这能让您的用户更轻松地**实现他们的 API** 来接收您的**网络钩子**请求,他们甚至可能能够自动生成一些自己的 API 代码。
+
+/// info
+
+网络钩子在 OpenAPI 3.1.0 及以上版本中可用,FastAPI `0.99.0` 及以上版本支持。
+
+///
+
+## 带有网络钩子的应用程序
+
+当您创建一个 **FastAPI** 应用程序时,有一个 `webhooks` 属性可以用来定义网络钩子,方式与您定义*路径操作*的时候相同,例如使用 `@app.webhooks.post()` 。
+
+{* ../../docs_src/openapi_webhooks/tutorial001.py hl[9:13,36:53] *}
+
+您定义的网络钩子将被包含在 `OpenAPI` 的架构中,并出现在自动生成的**文档 UI** 中。
+
+/// info
+
+`app.webhooks` 对象实际上只是一个 `APIRouter` ,与您在使用多个文件来构建应用程序时所使用的类型相同。
+
+///
+
+请注意,使用网络钩子时,您实际上并没有声明一个*路径*(比如 `/items/` ),您传递的文本只是这个网络钩子的**标识符**(事件的名称)。例如在 `@app.webhooks.post("new-subscription")` 中,网络钩子的名称是 `new-subscription` 。
+
+这是因为我们预计**您的用户**会以其他方式(例如通过网页仪表板)来定义他们希望接收网络钩子的请求的实际 **URL 路径**。
+
+### 查看文档
+
+现在您可以启动您的应用程序并访问 http://127.0.0.1:8000/docs.
+
+您会看到您的文档不仅有正常的*路径操作*显示,现在还多了一些**网络钩子**:
+
+
diff --git a/docs/zh/docs/advanced/path-operation-advanced-configuration.md b/docs/zh/docs/advanced/path-operation-advanced-configuration.md
index c378469162..12600eddb1 100644
--- a/docs/zh/docs/advanced/path-operation-advanced-configuration.md
+++ b/docs/zh/docs/advanced/path-operation-advanced-configuration.md
@@ -12,9 +12,7 @@
务必确保每个操作路径的 `operation_id` 都是唯一的。
-```Python hl_lines="6"
-{!../../../docs_src/path_operation_advanced_configuration/tutorial001.py!}
-```
+{* ../../docs_src/path_operation_advanced_configuration/tutorial001.py hl[6] *}
### 使用 *路径操作函数* 的函数名作为 operationId
@@ -22,9 +20,7 @@
你应该在添加了所有 *路径操作* 之后执行此操作。
-```Python hl_lines="2 12 13 14 15 16 17 18 19 20 21 24"
-{!../../../docs_src/path_operation_advanced_configuration/tutorial002.py!}
-```
+{* ../../docs_src/path_operation_advanced_configuration/tutorial002.py hl[2,12,13,14,15,16,17,18,19,20,21,24] *}
/// tip
@@ -44,9 +40,7 @@
使用参数 `include_in_schema` 并将其设置为 `False` ,来从生成的 OpenAPI 方案中排除一个 *路径操作*(这样一来,就从自动化文档系统中排除掉了)。
-```Python hl_lines="6"
-{!../../../docs_src/path_operation_advanced_configuration/tutorial003.py!}
-```
+{* ../../docs_src/path_operation_advanced_configuration/tutorial003.py hl[6] *}
## docstring 的高级描述
@@ -57,6 +51,4 @@
剩余部分不会出现在文档中,但是其他工具(比如 Sphinx)可以使用剩余部分。
-```Python hl_lines="19 20 21 22 23 24 25 26 27 28 29"
-{!../../../docs_src/path_operation_advanced_configuration/tutorial004.py!}
-```
+{* ../../docs_src/path_operation_advanced_configuration/tutorial004.py hl[19,20,21,22,23,24,25,26,27,28,29] *}
diff --git a/docs/zh/docs/advanced/response-change-status-code.md b/docs/zh/docs/advanced/response-change-status-code.md
index a289cf2017..cc1f2a73eb 100644
--- a/docs/zh/docs/advanced/response-change-status-code.md
+++ b/docs/zh/docs/advanced/response-change-status-code.md
@@ -20,9 +20,7 @@
然后你可以在这个*临时*响应对象中设置`status_code`。
-```Python hl_lines="1 9 12"
-{!../../../docs_src/response_change_status_code/tutorial001.py!}
-```
+{* ../../docs_src/response_change_status_code/tutorial001.py hl[1,9,12] *}
然后你可以像平常一样返回任何你需要的对象(例如一个`dict`或者一个数据库模型)。如果你声明了一个`response_model`,它仍然会被用来过滤和转换你返回的对象。
diff --git a/docs/zh/docs/advanced/response-cookies.md b/docs/zh/docs/advanced/response-cookies.md
index dd942a981b..d5f2fe6fc5 100644
--- a/docs/zh/docs/advanced/response-cookies.md
+++ b/docs/zh/docs/advanced/response-cookies.md
@@ -4,9 +4,7 @@
你可以在 *路径函数* 中定义一个类型为 `Response`的参数,这样你就可以在这个临时响应对象中设置cookie了。
-```Python hl_lines="1 8-9"
-{!../../../docs_src/response_cookies/tutorial002.py!}
-```
+{* ../../docs_src/response_cookies/tutorial002.py hl[1,8:9] *}
而且你还可以根据你的需要响应不同的对象,比如常用的 `dict`,数据库model等。
@@ -24,9 +22,7 @@
然后设置Cookies,并返回:
-```Python hl_lines="10-12"
-{!../../../docs_src/response_cookies/tutorial001.py!}
-```
+{* ../../docs_src/response_cookies/tutorial001.py hl[10:12] *}
/// tip
@@ -40,7 +36,7 @@
### 更多信息
-/// note | "技术细节"
+/// note | 技术细节
你也可以使用`from starlette.responses import Response` 或者 `from starlette.responses import JSONResponse`。
@@ -50,4 +46,4 @@
///
-如果你想查看所有可用的参数和选项,可以参考 Starlette帮助文档
+如果你想查看所有可用的参数和选项,可以参考 Starlette帮助文档
diff --git a/docs/zh/docs/advanced/response-directly.md b/docs/zh/docs/advanced/response-directly.md
index b2c7de8fd3..4d9cd53f2f 100644
--- a/docs/zh/docs/advanced/response-directly.md
+++ b/docs/zh/docs/advanced/response-directly.md
@@ -14,7 +14,7 @@
事实上,你可以返回任意 `Response` 或者任意 `Response` 的子类。
-/// tip | "小贴士"
+/// tip | 小贴士
`JSONResponse` 本身是一个 `Response` 的子类。
@@ -35,11 +35,9 @@
对于这些情况,在将数据传递给响应之前,你可以使用 `jsonable_encoder` 来转换你的数据。
-```Python hl_lines="4 6 20 21"
-{!../../../docs_src/response_directly/tutorial001.py!}
-```
+{* ../../docs_src/response_directly/tutorial001.py hl[4,6,20,21] *}
-/// note | "技术细节"
+/// note | 技术细节
你也可以使用 `from starlette.responses import JSONResponse`。
@@ -57,9 +55,7 @@
你可以把你的 XML 内容放到一个字符串中,放到一个 `Response` 中,然后返回。
-```Python hl_lines="1 18"
-{!../../../docs_src/response_directly/tutorial002.py!}
-```
+{* ../../docs_src/response_directly/tutorial002.py hl[1,18] *}
## 说明
diff --git a/docs/zh/docs/advanced/response-headers.md b/docs/zh/docs/advanced/response-headers.md
index e18d1620da..5c6a62e93c 100644
--- a/docs/zh/docs/advanced/response-headers.md
+++ b/docs/zh/docs/advanced/response-headers.md
@@ -5,9 +5,7 @@
你可以在你的*路径操作函数*中声明一个`Response`类型的参数(就像你可以为cookies做的那样)。
然后你可以在这个*临时*响应对象中设置头部。
-```Python hl_lines="1 7-8"
-{!../../../docs_src/response_headers/tutorial002.py!}
-```
+{* ../../docs_src/response_headers/tutorial002.py hl[1,7:8] *}
然后你可以像平常一样返回任何你需要的对象(例如一个`dict`或者一个数据库模型)。如果你声明了一个`response_model`,它仍然会被用来过滤和转换你返回的对象。
@@ -20,12 +18,11 @@
你也可以在直接返回`Response`时添加头部。
按照[直接返回响应](response-directly.md){.internal-link target=_blank}中所述创建响应,并将头部作为附加参数传递:
-```Python hl_lines="10-12"
-{!../../../docs_src/response_headers/tutorial001.py!}
-```
+
+{* ../../docs_src/response_headers/tutorial001.py hl[10:12] *}
-/// note | "技术细节"
+/// note | 技术细节
你也可以使用`from starlette.responses import Response`或`from starlette.responses import JSONResponse`。
@@ -39,4 +36,4 @@
请注意,可以使用'X-'前缀添加自定义专有头部。
-但是,如果你有自定义头部,你希望浏览器中的客户端能够看到它们,你需要将它们添加到你的CORS配置中(在[CORS(跨源资源共享)](../tutorial/cors.md){.internal-link target=_blank}中阅读更多),使用在Starlette的CORS文档中记录的`expose_headers`参数。
+但是,如果你有自定义头部,你希望浏览器中的客户端能够看到它们,你需要将它们添加到你的CORS配置中(在[CORS(跨源资源共享)](../tutorial/cors.md){.internal-link target=_blank}中阅读更多),使用在Starlette的CORS文档中记录的`expose_headers`参数。
diff --git a/docs/zh/docs/advanced/security/http-basic-auth.md b/docs/zh/docs/advanced/security/http-basic-auth.md
index a76353186d..599429f9d2 100644
--- a/docs/zh/docs/advanced/security/http-basic-auth.md
+++ b/docs/zh/docs/advanced/security/http-basic-auth.md
@@ -20,35 +20,7 @@ HTTP 基础授权让浏览器显示内置的用户名与密码提示。
* 返回类型为 `HTTPBasicCredentials` 的对象:
* 包含发送的 `username` 与 `password`
-//// tab | Python 3.9+
-
-```Python hl_lines="4 8 12"
-{!> ../../../docs_src/security/tutorial006_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="2 7 11"
-{!> ../../../docs_src/security/tutorial006_an.py!}
-```
-
-////
-
-//// tab | Python 3.8+ non-Annotated
-
-/// tip
-
-尽可能选择使用 `Annotated` 的版本。
-
-///
-
-```Python hl_lines="2 6 10"
-{!> ../../../docs_src/security/tutorial006.py!}
-```
-
-////
+{* ../../docs_src/security/tutorial006_an_py39.py hl[4,8,12] *}
第一次打开 URL(或在 API 文档中点击 **Execute** 按钮)时,浏览器要求输入用户名与密码:
@@ -68,35 +40,7 @@ HTTP 基础授权让浏览器显示内置的用户名与密码提示。
然后我们可以使用 `secrets.compare_digest()` 来确保 `credentials.username` 是 `"stanleyjobson"`,且 `credentials.password` 是`"swordfish"`。
-//// tab | Python 3.9+
-
-```Python hl_lines="1 12-24"
-{!> ../../../docs_src/security/tutorial007_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="1 12-24"
-{!> ../../../docs_src/security/tutorial007_an.py!}
-```
-
-////
-
-//// tab | Python 3.8+ non-Annotated
-
-/// tip
-
-尽可能选择使用 `Annotated` 的版本。
-
-///
-
-```Python hl_lines="1 11-21"
-{!> ../../../docs_src/security/tutorial007.py!}
-```
-
-////
+{* ../../docs_src/security/tutorial007_an_py39.py hl[1,12:24] *}
这类似于:
@@ -160,32 +104,4 @@ if "stanleyjobsox" == "stanleyjobson" and "love123" == "swordfish":
检测到凭证不正确后,返回 `HTTPException` 及状态码 401(与无凭证时返回的内容一样),并添加请求头 `WWW-Authenticate`,让浏览器再次显示登录提示:
-//// tab | Python 3.9+
-
-```Python hl_lines="26-30"
-{!> ../../../docs_src/security/tutorial007_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="26-30"
-{!> ../../../docs_src/security/tutorial007_an.py!}
-```
-
-////
-
-//// tab | Python 3.8+ non-Annotated
-
-/// tip
-
-尽可能选择使用 `Annotated` 的版本。
-
-///
-
-```Python hl_lines="23-27"
-{!> ../../../docs_src/security/tutorial007.py!}
-```
-
-////
+{* ../../docs_src/security/tutorial007_an_py39.py hl[26:30] *}
diff --git a/docs/zh/docs/advanced/security/index.md b/docs/zh/docs/advanced/security/index.md
index 836086ae27..267e7ced70 100644
--- a/docs/zh/docs/advanced/security/index.md
+++ b/docs/zh/docs/advanced/security/index.md
@@ -4,7 +4,7 @@
除 [教程 - 用户指南: 安全性](../../tutorial/security/index.md){.internal-link target=_blank} 中涵盖的功能之外,还有一些额外的功能来处理安全性.
-/// tip | "小贴士"
+/// tip | 小贴士
接下来的章节 **并不一定是 "高级的"**.
diff --git a/docs/zh/docs/advanced/security/oauth2-scopes.md b/docs/zh/docs/advanced/security/oauth2-scopes.md
index b75ae11a4b..784c384901 100644
--- a/docs/zh/docs/advanced/security/oauth2-scopes.md
+++ b/docs/zh/docs/advanced/security/oauth2-scopes.md
@@ -10,7 +10,7 @@ OAuth2 也是脸书、谷歌、GitHub、微软、推特等第三方身份验证
本章介绍如何在 **FastAPI** 应用中使用 OAuth2 作用域管理验证与授权。
-/// warning | "警告"
+/// warning | 警告
本章内容较难,刚接触 FastAPI 的新手可以跳过。
@@ -46,7 +46,7 @@ OpenAPI 中(例如 API 文档)可以定义**安全方案**。
* 脸书和 Instagram 使用 `instagram_basic`
* 谷歌使用 `https://www.googleapis.com/auth/drive`
-/// info | "说明"
+/// info | 说明
OAuth2 中,**作用域**只是声明特定权限的字符串。
@@ -62,9 +62,7 @@ OAuth2 中,**作用域**只是声明特定权限的字符串。
首先,快速浏览一下以下代码与**用户指南**中 [OAuth2 实现密码哈希与 Bearer JWT 令牌验证](../../tutorial/security/oauth2-jwt.md){.internal-link target=_blank}一章中代码的区别。以下代码使用 OAuth2 作用域:
-```Python hl_lines="2 4 8 12 46 64 105 107-115 121-124 128-134 139 153"
-{!../../../docs_src/security/tutorial005.py!}
-```
+{* ../../docs_src/security/tutorial005.py hl[2,4,8,12,46,64,105,107:115,121:124,128:134,139,153] *}
下面,我们逐步说明修改的代码内容。
@@ -74,9 +72,7 @@ OAuth2 中,**作用域**只是声明特定权限的字符串。
`scopes` 参数接收**字典**,键是作用域、值是作用域的描述:
-```Python hl_lines="62-65"
-{!../../../docs_src/security/tutorial005.py!}
-```
+{* ../../docs_src/security/tutorial005.py hl[62:65] *}
因为声明了作用域,所以登录或授权时会在 API 文档中显示。
@@ -94,7 +90,7 @@ OAuth2 中,**作用域**只是声明特定权限的字符串。
这样,返回的 JWT 令牌中就包含了作用域。
-/// danger | "危险"
+/// danger | 危险
为了简明起见,本例把接收的作用域直接添加到了令牌里。
@@ -102,9 +98,7 @@ OAuth2 中,**作用域**只是声明特定权限的字符串。
///
-```Python hl_lines="153"
-{!../../../docs_src/security/tutorial005.py!}
-```
+{* ../../docs_src/security/tutorial005.py hl[153] *}
## 在*路径操作*与依赖项中声明作用域
@@ -122,7 +116,7 @@ OAuth2 中,**作用域**只是声明特定权限的字符串。
本例要求使用作用域 `me`(还可以使用更多作用域)。
-/// note | "笔记"
+/// note | 笔记
不必在不同位置添加不同的作用域。
@@ -130,11 +124,9 @@ OAuth2 中,**作用域**只是声明特定权限的字符串。
///
-```Python hl_lines="4 139 166"
-{!../../../docs_src/security/tutorial005.py!}
-```
+{* ../../docs_src/security/tutorial005.py hl[4,139,166] *}
-/// info | "技术细节"
+/// info | 技术细节
`Security` 实际上是 `Depends` 的子类,而且只比 `Depends` 多一个参数。
@@ -154,13 +146,11 @@ OAuth2 中,**作用域**只是声明特定权限的字符串。
该依赖项函数本身不需要作用域,因此,可以使用 `Depends` 和 `oauth2_scheme`。不需要指定安全作用域时,不必使用 `Security`。
-此处还声明了从 `fastapin.security` 导入的 `SecurityScopes` 类型的特殊参数。
+此处还声明了从 `fastapi.security` 导入的 `SecurityScopes` 类型的特殊参数。
`SecuriScopes` 类与 `Request` 类似(`Request` 用于直接提取请求对象)。
-```Python hl_lines="8 105"
-{!../../../docs_src/security/tutorial005.py!}
-```
+{* ../../docs_src/security/tutorial005.py hl[8,105] *}
## 使用 `scopes`
@@ -174,9 +164,7 @@ OAuth2 中,**作用域**只是声明特定权限的字符串。
该异常包含了作用域所需的(如有),以空格分割的字符串(使用 `scope_str`)。该字符串要放到包含作用域的 `WWW-Authenticate` 请求头中(这也是规范的要求)。
-```Python hl_lines="105 107-115"
-{!../../../docs_src/security/tutorial005.py!}
-```
+{* ../../docs_src/security/tutorial005.py hl[105,107:115] *}
## 校验 `username` 与数据形状
@@ -192,9 +180,7 @@ OAuth2 中,**作用域**只是声明特定权限的字符串。
还可以使用用户名验证用户,如果没有用户,也会触发之前创建的异常。
-```Python hl_lines="46 116-127"
-{!../../../docs_src/security/tutorial005.py!}
-```
+{* ../../docs_src/security/tutorial005.py hl[46,116:127] *}
## 校验 `scopes`
@@ -202,9 +188,7 @@ OAuth2 中,**作用域**只是声明特定权限的字符串。
为此,要使用包含所有作用域**字符串列表**的 `security_scopes.scopes`, 。
-```Python hl_lines="128-134"
-{!../../../docs_src/security/tutorial005.py!}
-```
+{* ../../docs_src/security/tutorial005.py hl[128:134] *}
## 依赖项树与作用域
@@ -231,7 +215,7 @@ OAuth2 中,**作用域**只是声明特定权限的字符串。
* `security_scopes.scopes` 包含*路径操作* `read_users_me` 的 `["me"]`,因为它在依赖项里被声明
* `security_scopes.scopes` 包含用于*路径操作* `read_system_status` 的 `[]`(空列表),并且它的依赖项 `get_current_user` 也没有声明任何 `scope`
-/// tip | "提示"
+/// tip | 提示
此处重要且**神奇**的事情是,`get_current_user` 检查每个*路径操作*时可以使用不同的 `scopes` 列表。
@@ -275,7 +259,7 @@ OAuth2 中,**作用域**只是声明特定权限的字符串。
最安全的是代码流,但实现起来更复杂,而且需要更多步骤。因为它更复杂,很多第三方身份验证应用最终建议使用隐式流。
-/// note | "笔记"
+/// note | 笔记
每个身份验证应用都会采用不同方式会命名流,以便融合入自己的品牌。
diff --git a/docs/zh/docs/advanced/settings.md b/docs/zh/docs/advanced/settings.md
index 37a2d98d31..e33da136fa 100644
--- a/docs/zh/docs/advanced/settings.md
+++ b/docs/zh/docs/advanced/settings.md
@@ -150,9 +150,7 @@ Hello World from Python
您可以使用与 Pydantic 模型相同的验证功能和工具,比如不同的数据类型和使用 `Field()` 进行附加验证。
-```Python hl_lines="2 5-8 11"
-{!../../../docs_src/settings/tutorial001.py!}
-```
+{* ../../docs_src/settings/tutorial001.py hl[2,5:8,11] *}
/// tip
@@ -168,9 +166,7 @@ Hello World from Python
然后,您可以在应用程序中使用新的 `settings` 对象:
-```Python hl_lines="18-20"
-{!../../../docs_src/settings/tutorial001.py!}
-```
+{* ../../docs_src/settings/tutorial001.py hl[18:20] *}
### 运行服务器
@@ -204,15 +200,11 @@ $ ADMIN_EMAIL="deadpool@example.com" APP_NAME="ChimichangApp"uvicorn main:app
例如,您可以创建一个名为 `config.py` 的文件,其中包含以下内容:
-```Python
-{!../../../docs_src/settings/app01/config.py!}
-```
+{* ../../docs_src/settings/app01/config.py *}
然后在一个名为 `main.py` 的文件中使用它:
-```Python hl_lines="3 11-13"
-{!../../../docs_src/settings/app01/main.py!}
-```
+{* ../../docs_src/settings/app01/main.py hl[3,11:13] *}
/// tip
@@ -230,9 +222,7 @@ $ ADMIN_EMAIL="deadpool@example.com" APP_NAME="ChimichangApp"uvicorn main:app
根据前面的示例,您的 `config.py` 文件可能如下所示:
-```Python hl_lines="10"
-{!../../../docs_src/settings/app02/config.py!}
-```
+{* ../../docs_src/settings/app02/config.py hl[10] *}
请注意,现在我们不创建默认实例 `settings = Settings()`。
@@ -240,35 +230,7 @@ $ ADMIN_EMAIL="deadpool@example.com" APP_NAME="ChimichangApp"uvicorn main:app
现在我们创建一个依赖项,返回一个新的 `config.Settings()`。
-//// tab | Python 3.9+
-
-```Python hl_lines="6 12-13"
-{!> ../../../docs_src/settings/app02_an_py39/main.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="6 12-13"
-{!> ../../../docs_src/settings/app02_an/main.py!}
-```
-
-////
-
-//// tab | Python 3.8+ 非注解版本
-
-/// tip
-
-如果可能,请尽量使用 `Annotated` 版本。
-
-///
-
-```Python hl_lines="5 11-12"
-{!> ../../../docs_src/settings/app02/main.py!}
-```
-
-////
+{* ../../docs_src/settings/app02_an_py39/main.py hl[6,12:13] *}
/// tip
@@ -280,43 +242,13 @@ $ ADMIN_EMAIL="deadpool@example.com" APP_NAME="ChimichangApp"uvicorn main:app
然后,我们可以将其作为依赖项从“路径操作函数”中引入,并在需要时使用它。
-//// tab | Python 3.9+
-
-```Python hl_lines="17 19-21"
-{!> ../../../docs_src/settings/app02_an_py39/main.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="17 19-21"
-{!> ../../../docs_src/settings/app02_an/main.py!}
-```
-
-////
-
-//// tab | Python 3.8+ 非注解版本
-
-/// tip
-
-如果可能,请尽量使用 `Annotated` 版本。
-
-///
-
-```Python hl_lines="16 18-20"
-{!> ../../../docs_src/settings/app02/main.py!}
-```
-
-////
+{* ../../docs_src/settings/app02_an_py39/main.py hl[17,19:21] *}
### 设置和测试
然后,在测试期间,通过创建 `get_settings` 的依赖项覆盖,很容易提供一个不同的设置对象:
-```Python hl_lines="9-10 13 21"
-{!../../../docs_src/settings/app02/test_main.py!}
-```
+{* ../../docs_src/settings/app02/test_main.py hl[9:10,13,21] *}
在依赖项覆盖中,我们在创建新的 `Settings` 对象时为 `admin_email` 设置了一个新值,然后返回该新对象。
@@ -357,9 +289,7 @@ APP_NAME="ChimichangApp"
然后,您可以使用以下方式更新您的 `config.py`:
-```Python hl_lines="9-10"
-{!../../../docs_src/settings/app03/config.py!}
-```
+{* ../../docs_src/settings/app03/config.py hl[9:10] *}
在这里,我们在 Pydantic 的 `Settings` 类中创建了一个名为 `Config` 的类,并将 `env_file` 设置为我们想要使用的 dotenv 文件的文件名。
@@ -392,35 +322,7 @@ def get_settings():
但是,由于我们在顶部使用了 `@lru_cache` 装饰器,因此只有在第一次调用它时,才会创建 `Settings` 对象一次。 ✔️
-//// tab | Python 3.9+
-
-```Python hl_lines="1 11"
-{!> ../../../docs_src/settings/app03_an_py39/main.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="1 11"
-{!> ../../../docs_src/settings/app03_an/main.py!}
-```
-
-////
-
-//// tab | Python 3.8+ 非注解版本
-
-/// tip
-
-如果可能,请尽量使用 `Annotated` 版本。
-
-///
-
-```Python hl_lines="1 10"
-{!> ../../../docs_src/settings/app03/main.py!}
-```
-
-////
+{* ../../docs_src/settings/app03_an_py39/main.py hl[1,11] *}
然后,在下一次请求的依赖项中对 `get_settings()` 进行任何后续调用时,它不会执行 `get_settings()` 的内部代码并创建新的 `Settings` 对象,而是返回在第一次调用时返回的相同对象,一次又一次。
diff --git a/docs/zh/docs/advanced/sub-applications.md b/docs/zh/docs/advanced/sub-applications.md
index a26301b50a..c42be2849a 100644
--- a/docs/zh/docs/advanced/sub-applications.md
+++ b/docs/zh/docs/advanced/sub-applications.md
@@ -10,9 +10,7 @@
首先,创建主(顶层)**FastAPI** 应用及其*路径操作*:
-```Python hl_lines="3 6-8"
-{!../../../docs_src/sub_applications/tutorial001.py!}
-```
+{* ../../docs_src/sub_applications/tutorial001.py hl[3,6:8] *}
### 子应用
@@ -20,9 +18,7 @@
子应用只是另一个标准 FastAPI 应用,但这个应用是被**挂载**的应用:
-```Python hl_lines="11 14-16"
-{!../../../docs_src/sub_applications/tutorial001.py!}
-```
+{* ../../docs_src/sub_applications/tutorial001.py hl[11,14:16] *}
### 挂载子应用
@@ -30,9 +26,7 @@
本例的子应用挂载在 `/subapi` 路径下:
-```Python hl_lines="11 19"
-{!../../../docs_src/sub_applications/tutorial001.py!}
-```
+{* ../../docs_src/sub_applications/tutorial001.py hl[11,19] *}
### 查看文档
diff --git a/docs/zh/docs/advanced/templates.md b/docs/zh/docs/advanced/templates.md
index b09644e393..e627eed980 100644
--- a/docs/zh/docs/advanced/templates.md
+++ b/docs/zh/docs/advanced/templates.md
@@ -27,24 +27,22 @@ $ pip install jinja2
* 在返回模板的*路径操作*中声明 `Request` 参数
* 使用 `templates` 渲染并返回 `TemplateResponse`, 传递模板的名称、request对象以及一个包含多个键值对(用于Jinja2模板)的"context"字典,
-```Python hl_lines="4 11 15-16"
-{!../../../docs_src/templates/tutorial001.py!}
-```
+{* ../../docs_src/templates/tutorial001.py hl[4,11,15:16] *}
-/// note | "笔记"
+/// note | 笔记
在FastAPI 0.108.0,Starlette 0.29.0之前,`name`是第一个参数。
并且,在此之前,`request`对象是作为context的一部分以键值对的形式传递的。
///
-/// tip | "提示"
+/// tip | 提示
通过声明 `response_class=HTMLResponse`,API 文档就能识别响应的对象是 HTML。
///
-/// note | "技术细节"
+/// note | 技术细节
您还可以使用 `from starlette.templating import Jinja2Templates`。
@@ -57,7 +55,7 @@ $ pip install jinja2
编写模板 `templates/item.html`,代码如下:
```jinja hl_lines="7"
-{!../../../docs_src/templates/templates/item.html!}
+{!../../docs_src/templates/templates/item.html!}
```
### 模板上下文
@@ -111,17 +109,17 @@ Item ID: 42
你还可以在模板内部将 `url_for()`用于静态文件,例如你挂载的 `name="static"`的 `StaticFiles`。
```jinja hl_lines="4"
-{!../../../docs_src/templates/templates/item.html!}
+{!../../docs_src/templates/templates/item.html!}
```
本例中,它将链接到 `static/styles.css`中的CSS文件:
```CSS hl_lines="4"
-{!../../../docs_src/templates/static/styles.css!}
+{!../../docs_src/templates/static/styles.css!}
```
因为使用了 `StaticFiles`, **FastAPI** 应用会自动提供位于 URL `/static/styles.css`的 CSS 文件。
## 更多说明
-包括测试模板等更多详情,请参阅 Starlette 官方文档 - 模板。
+包括测试模板等更多详情,请参阅 Starlette 官方文档 - 模板。
diff --git a/docs/zh/docs/advanced/testing-database.md b/docs/zh/docs/advanced/testing-database.md
deleted file mode 100644
index 58bf9af8c5..0000000000
--- a/docs/zh/docs/advanced/testing-database.md
+++ /dev/null
@@ -1,101 +0,0 @@
-# 测试数据库
-
-您还可以使用[测试依赖项](testing-dependencies.md){.internal-link target=_blank}中的覆盖依赖项方法变更测试的数据库。
-
-实现设置其它测试数据库、在测试后回滚数据、或预填测试数据等操作。
-
-本章的主要思路与上一章完全相同。
-
-## 为 SQL 应用添加测试
-
-为了使用测试数据库,我们要升级 [SQL 关系型数据库](../tutorial/sql-databases.md){.internal-link target=_blank} 一章中的示例。
-
-应用的所有代码都一样,直接查看那一章的示例代码即可。
-
-本章只是新添加了测试文件。
-
-正常的依赖项 `get_db()` 返回数据库会话。
-
-测试时使用覆盖依赖项返回自定义数据库会话代替正常的依赖项。
-
-本例中,要创建仅用于测试的临时数据库。
-
-## 文件架构
-
-创建新文件 `sql_app/tests/test_sql_app.py`。
-
-因此,新的文件架构如下:
-
-``` hl_lines="9-11"
-.
-└── sql_app
- ├── __init__.py
- ├── crud.py
- ├── database.py
- ├── main.py
- ├── models.py
- ├── schemas.py
- └── tests
- ├── __init__.py
- └── test_sql_app.py
-```
-
-## 创建新的数据库会话
-
-首先,为新建数据库创建新的数据库会话。
-
-测试时,使用 `test.db` 替代 `sql_app.db`。
-
-但其余的会话代码基本上都是一样的,只要复制就可以了。
-
-```Python hl_lines="8-13"
-{!../../../docs_src/sql_databases/sql_app/tests/test_sql_app.py!}
-```
-
-/// tip | "提示"
-
-为减少代码重复,最好把这段代码写成函数,在 `database.py` 与 `tests/test_sql_app.py`中使用。
-
-为了把注意力集中在测试代码上,本例只是复制了这段代码。
-
-///
-
-## 创建数据库
-
-因为现在是想在新文件中使用新数据库,所以要使用以下代码创建数据库:
-
-```Python
-Base.metadata.create_all(bind=engine)
-```
-
-一般是在 `main.py` 中调用这行代码,但在 `main.py` 里,这行代码用于创建 `sql_app.db`,但是现在要为测试创建 `test.db`。
-
-因此,要在测试代码中添加这行代码创建新的数据库文件。
-
-```Python hl_lines="16"
-{!../../../docs_src/sql_databases/sql_app/tests/test_sql_app.py!}
-```
-
-## 覆盖依赖项
-
-接下来,创建覆盖依赖项,并为应用添加覆盖内容。
-
-```Python hl_lines="19-24 27"
-{!../../../docs_src/sql_databases/sql_app/tests/test_sql_app.py!}
-```
-
-/// tip | "提示"
-
-`overrider_get_db()` 与 `get_db` 的代码几乎完全一样,只是 `overrider_get_db` 中使用测试数据库的 `TestingSessionLocal`。
-
-///
-
-## 测试应用
-
-然后,就可以正常测试了。
-
-```Python hl_lines="32-47"
-{!../../../docs_src/sql_databases/sql_app/tests/test_sql_app.py!}
-```
-
-测试期间,所有在数据库中所做的修改都在 `test.db` 里,不会影响主应用的 `sql_app.db`。
diff --git a/docs/zh/docs/advanced/testing-dependencies.md b/docs/zh/docs/advanced/testing-dependencies.md
index cc9a38200e..8d53a6d496 100644
--- a/docs/zh/docs/advanced/testing-dependencies.md
+++ b/docs/zh/docs/advanced/testing-dependencies.md
@@ -28,11 +28,9 @@
这样一来,**FastAPI** 就会调用覆盖依赖项,不再调用原依赖项。
-```Python hl_lines="26-27 30"
-{!../../../docs_src/dependency_testing/tutorial001.py!}
-```
+{* ../../docs_src/dependency_testing/tutorial001_an_py310.py hl[26:27,30] *}
-/// tip | "提示"
+/// tip | 提示
**FastAPI** 应用中的任何位置都可以实现覆盖依赖项。
@@ -48,7 +46,7 @@ FastAPI 可以覆盖这些位置的依赖项。
app.dependency_overrides = {}
```
-/// tip | "提示"
+/// tip | 提示
如果只在某些测试时覆盖依赖项,您可以在测试开始时(在测试函数内)设置覆盖依赖项,并在结束时(在测试函数结尾)重置覆盖依赖项。
diff --git a/docs/zh/docs/advanced/testing-events.md b/docs/zh/docs/advanced/testing-events.md
index 222a67c8cf..71b3739c3d 100644
--- a/docs/zh/docs/advanced/testing-events.md
+++ b/docs/zh/docs/advanced/testing-events.md
@@ -2,6 +2,4 @@
使用 `TestClient` 和 `with` 语句,在测试中运行事件处理器(`startup` 与 `shutdown`)。
-```Python hl_lines="9-12 20-24"
-{!../../../docs_src/app_testing/tutorial003.py!}
-```
+{* ../../docs_src/app_testing/tutorial003.py hl[9:12,20:24] *}
diff --git a/docs/zh/docs/advanced/testing-websockets.md b/docs/zh/docs/advanced/testing-websockets.md
index 795b739458..b84647a3ed 100644
--- a/docs/zh/docs/advanced/testing-websockets.md
+++ b/docs/zh/docs/advanced/testing-websockets.md
@@ -4,12 +4,10 @@
为此,要在 `with` 语句中使用 `TestClient` 连接 WebSocket。
-```Python hl_lines="27-31"
-{!../../../docs_src/app_testing/tutorial002.py!}
-```
+{* ../../docs_src/app_testing/tutorial002.py hl[27:31] *}
-/// note | "笔记"
+/// note | 笔记
-更多细节详见 Starlette 官档 - 测试 WebSockets。
+更多细节详见 Starlette 官档 - 测试 WebSockets。
///
diff --git a/docs/zh/docs/advanced/using-request-directly.md b/docs/zh/docs/advanced/using-request-directly.md
index bc9e898d97..a9658c0348 100644
--- a/docs/zh/docs/advanced/using-request-directly.md
+++ b/docs/zh/docs/advanced/using-request-directly.md
@@ -15,7 +15,7 @@
## `Request` 对象的细节
-实际上,**FastAPI** 的底层是 **Starlette**,**FastAPI** 只不过是在 **Starlette** 顶层提供了一些工具,所以能直接使用 Starlette 的 `Request` 对象。
+实际上,**FastAPI** 的底层是 **Starlette**,**FastAPI** 只不过是在 **Starlette** 顶层提供了一些工具,所以能直接使用 Starlette 的 `Request` 对象。
但直接从 `Request` 对象提取数据时(例如,读取请求体),**FastAPI** 不会验证、转换和存档数据(为 API 文档使用 OpenAPI)。
@@ -29,13 +29,11 @@
此时,需要直接访问请求。
-```Python hl_lines="1 7-8"
-{!../../../docs_src/using_request_directly/tutorial001.py!}
-```
+{* ../../docs_src/using_request_directly/tutorial001.py hl[1,7:8] *}
把*路径操作函数*的参数类型声明为 `Request`,**FastAPI** 就能把 `Request` 传递到参数里。
-/// tip | "提示"
+/// tip | 提示
注意,本例除了声明请求参数之外,还声明了路径参数。
@@ -47,9 +45,9 @@
## `Request` 文档
-更多细节详见 Starlette 官档 - `Request` 对象。
+更多细节详见 Starlette 官档 - `Request` 对象。
-/// note | "技术细节"
+/// note | 技术细节
您也可以使用 `from starlette.requests import Request`。
diff --git a/docs/zh/docs/advanced/websockets.md b/docs/zh/docs/advanced/websockets.md
index 3fcc36dfee..005ed9242c 100644
--- a/docs/zh/docs/advanced/websockets.md
+++ b/docs/zh/docs/advanced/websockets.md
@@ -34,19 +34,15 @@ $ pip install websockets
但这是一种专注于 WebSockets 的服务器端并提供一个工作示例的最简单方式:
-```Python hl_lines="2 6-38 41-43"
-{!../../../docs_src/websockets/tutorial001.py!}
-```
+{* ../../docs_src/websockets/tutorial001.py hl[2,6:38,41:43] *}
## 创建 `websocket`
在您的 **FastAPI** 应用程序中,创建一个 `websocket`:
-```Python hl_lines="1 46-47"
-{!../../../docs_src/websockets/tutorial001.py!}
-```
+{* ../../docs_src/websockets/tutorial001.py hl[1,46:47] *}
-/// note | "技术细节"
+/// note | 技术细节
您也可以使用 `from starlette.websockets import WebSocket`。
@@ -58,9 +54,7 @@ $ pip install websockets
在您的 WebSocket 路由中,您可以使用 `await` 等待消息并发送消息。
-```Python hl_lines="48-52"
-{!../../../docs_src/websockets/tutorial001.py!}
-```
+{* ../../docs_src/websockets/tutorial001.py hl[48:52] *}
您可以接收和发送二进制、文本和 JSON 数据。
@@ -109,57 +103,7 @@ $ uvicorn main:app --reload
它们的工作方式与其他 FastAPI 端点/ *路径操作* 相同:
-//// tab | Python 3.10+
-
-```Python hl_lines="68-69 82"
-{!> ../../../docs_src/websockets/tutorial002_an_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="68-69 82"
-{!> ../../../docs_src/websockets/tutorial002_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="69-70 83"
-{!> ../../../docs_src/websockets/tutorial002_an.py!}
-```
-
-////
-
-//// tab | Python 3.10+ 非带注解版本
-
-/// tip
-
-如果可能,请尽量使用 `Annotated` 版本。
-
-///
-
-```Python hl_lines="66-67 79"
-{!> ../../../docs_src/websockets/tutorial002_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+ 非带注解版本
-
-/// tip
-
-如果可能,请尽量使用 `Annotated` 版本。
-
-///
-
-```Python hl_lines="68-69 81"
-{!> ../../../docs_src/websockets/tutorial002.py!}
-```
-
-////
+{* ../../docs_src/websockets/tutorial002_an_py310.py hl[68:69,82] *}
/// info
@@ -200,21 +144,7 @@ $ uvicorn main:app --reload
当 WebSocket 连接关闭时,`await websocket.receive_text()` 将引发 `WebSocketDisconnect` 异常,您可以捕获并处理该异常,就像本示例中的示例一样。
-//// tab | Python 3.9+
-
-```Python hl_lines="79-81"
-{!> ../../../docs_src/websockets/tutorial003_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="81-83"
-{!> ../../../docs_src/websockets/tutorial003.py!}
-```
-
-////
+{* ../../docs_src/websockets/tutorial003_py39.py hl[79:81] *}
尝试以下操作:
@@ -242,5 +172,5 @@ Client #1596980209979 left the chat
要了解更多选项,请查看 Starlette 的文档:
-* [WebSocket 类](https://www.starlette.io/websockets/)
-* [基于类的 WebSocket 处理](https://www.starlette.io/endpoints/#websocketendpoint)。
+* [WebSocket 类](https://www.starlette.dev/websockets/)
+* [基于类的 WebSocket 处理](https://www.starlette.dev/endpoints/#websocketendpoint)。
diff --git a/docs/zh/docs/advanced/wsgi.md b/docs/zh/docs/advanced/wsgi.md
index 179ec88aab..363025a343 100644
--- a/docs/zh/docs/advanced/wsgi.md
+++ b/docs/zh/docs/advanced/wsgi.md
@@ -12,9 +12,7 @@
之后将其挂载到某一个路径下。
-```Python hl_lines="2-3 22"
-{!../../../docs_src/wsgi/tutorial001.py!}
-```
+{* ../../docs_src/wsgi/tutorial001.py hl[2:3,22] *}
## 检查
diff --git a/docs/zh/docs/async.md b/docs/zh/docs/async.md
index 822ceeee49..4028ed51aa 100644
--- a/docs/zh/docs/async.md
+++ b/docs/zh/docs/async.md
@@ -251,7 +251,7 @@ Python 的现代版本支持通过一种叫**"协程"**——使用 `async` 和
这与 **FastAPI** 的性能水平相同。
-您可以同时拥有并行性和异步性,您可以获得比大多数经过测试的 NodeJS 框架更高的性能,并且与 Go 不相上下, Go 是一种更接近于 C 的编译语言(全部归功于 Starlette)。
+你可以同时拥有并行性和异步性,你可以获得比大多数经过测试的 NodeJS 框架更高的性能,并且与 Go 不相上下, Go 是一种更接近于 C 的编译语言(全部归功于 Starlette)。
### 并发比并行好吗?
@@ -275,7 +275,7 @@ Python 的现代版本支持通过一种叫**"协程"**——使用 `async` 和
但在这种情况下,如果你能带上 8 名前收银员/厨师,现在是清洁工一起清扫,他们中的每一个人(加上你)都能占据房子的一个区域来清扫,你就可以在额外的帮助下并行的更快地完成所有工作。
-在这个场景中,每个清洁工(包括您)都将是一个处理器,完成这个工作的一部分。
+在这个场景中,每个清洁工(包括你)都将是一个处理器,完成这个工作的一部分。
由于大多数执行时间是由实际工作(而不是等待)占用的,并且计算机中的工作是由 CPU 完成的,所以他们称这些问题为"CPU 密集型"。
@@ -292,9 +292,9 @@ CPU 密集型操作的常见示例是需要复杂的数学处理。
### 并发 + 并行: Web + 机器学习
-使用 **FastAPI**,您可以利用 Web 开发中常见的并发机制的优势(NodeJS 的主要吸引力)。
+使用 **FastAPI**,你可以利用 Web 开发中常见的并发机制的优势(NodeJS 的主要吸引力)。
-并且,您也可以利用并行和多进程(让多个进程并行运行)的优点来处理与机器学习系统中类似的 **CPU 密集型** 工作。
+并且,你也可以利用并行和多进程(让多个进程并行运行)的优点来处理与机器学习系统中类似的 **CPU 密集型** 工作。
这一点,再加上 Python 是**数据科学**、机器学习(尤其是深度学习)的主要语言这一简单事实,使得 **FastAPI** 与数据科学/机器学习 Web API 和应用程序(以及其他许多应用程序)非常匹配。
@@ -304,7 +304,7 @@ CPU 密集型操作的常见示例是需要复杂的数学处理。
现代版本的 Python 有一种非常直观的方式来定义异步代码。这使它看起来就像正常的"顺序"代码,并在适当的时候"等待"。
-当有一个操作需要等待才能给出结果,且支持这个新的 Python 特性时,您可以编写如下代码:
+当有一个操作需要等待才能给出结果,且支持这个新的 Python 特性时,你可以编写如下代码:
```Python
burgers = await get_burgers(2)
@@ -340,7 +340,7 @@ burgers = get_burgers(2)
---
-因此,如果您使用的库告诉您可以使用 `await` 调用它,则需要使用 `async def` 创建路径操作函数 ,如:
+因此,如果你使用的库告诉你可以使用 `await` 调用它,则需要使用 `async def` 创建路径操作函数 ,如:
```Python hl_lines="2-3"
@app.get('/burgers')
@@ -351,15 +351,15 @@ async def read_burgers():
### 更多技术细节
-您可能已经注意到,`await` 只能在 `async def` 定义的函数内部使用。
+你可能已经注意到,`await` 只能在 `async def` 定义的函数内部使用。
但与此同时,必须"等待"通过 `async def` 定义的函数。因此,带 `async def` 的函数也只能在 `async def` 定义的函数内部调用。
那么,这关于先有鸡还是先有蛋的问题,如何调用第一个 `async` 函数?
-如果您使用 **FastAPI**,你不必担心这一点,因为"第一个"函数将是你的路径操作函数,FastAPI 将知道如何做正确的事情。
+如果你使用 **FastAPI**,你不必担心这一点,因为"第一个"函数将是你的路径操作函数,FastAPI 将知道如何做正确的事情。
-但如果您想在没有 FastAPI 的情况下使用 `async` / `await`,则可以这样做。
+但如果你想在没有 FastAPI 的情况下使用 `async` / `await`,则可以这样做。
### 编写自己的异步代码
@@ -367,7 +367,9 @@ Starlette (和 **FastAPI**) 是基于 AnyIO 来处理高级的并发用例,这些用例需要在自己的代码中使用更高级的模式。
-即使您没有使用 **FastAPI**,您也可以使用 AnyIO 编写自己的异步程序,使其拥有较高的兼容性并获得一些好处(例如, 结构化并发)。
+即使你没有使用 **FastAPI**,你也可以使用 AnyIO 编写自己的异步程序,使其拥有较高的兼容性并获得一些好处(例如, 结构化并发)。
+
+我(指原作者 —— 译者注)基于 AnyIO 新建了一个库,作为一个轻量级的封装层,用来优化类型注解,同时提供了更好的**自动补全**、**内联错误提示**等功能。这个库还附带了一个友好的入门指南和教程,能帮助你**理解**并编写**自己的异步代码**:Asyncer。如果你有**结合使用异步代码和常规**(阻塞/同步)代码的需求,这个库会特别有用。
### 其他形式的异步代码
@@ -381,7 +383,7 @@ Starlette (和 **FastAPI**) 是基于 Gevent。但代码的理解、调试和思考都要复杂许多。
-在以前版本的 NodeJS / 浏览器 JavaScript 中,你会使用"回调",因此也可能导致回调地狱。
+在以前版本的 NodeJS / 浏览器 JavaScript 中,你会使用"回调",因此也可能导致“回调地狱”。
## 协程
@@ -407,7 +409,7 @@ Starlette (和 **FastAPI**) 是基于 I/O 的代码。
+如果你使用过另一个不以上述方式工作的异步框架,并且你习惯于用普通的 `def` 定义普通的仅计算路径操作函数,以获得微小的性能增益(大约100纳秒),请注意,在 FastAPI 中,效果将完全相反。在这些情况下,最好使用 `async def`,除非路径操作函数内使用执行阻塞 I/O 的代码。
-在这两种情况下,与您之前的框架相比,**FastAPI** 可能[仍然很快](index.md#_11){.internal-link target=_blank}。
+在这两种情况下,与你之前的框架相比,**FastAPI** 可能[仍然很快](index.md#_11){.internal-link target=_blank}。
### 依赖
@@ -429,9 +431,9 @@ Starlette (和 **FastAPI**) 是基于 赶时间吗?.
+否则,你最好应该遵守的指导原则赶时间吗?.
diff --git a/docs/zh/docs/contributing.md b/docs/zh/docs/contributing.md
deleted file mode 100644
index 85b341a8d6..0000000000
--- a/docs/zh/docs/contributing.md
+++ /dev/null
@@ -1,472 +0,0 @@
-# 开发 - 贡献
-
-首先,你可能想了解 [帮助 FastAPI 及获取帮助](help-fastapi.md){.internal-link target=_blank}的基本方式。
-
-## 开发
-
-如果你已经克隆了源码仓库,并且需要深入研究代码,下面是设置开发环境的指南。
-
-### 通过 `venv` 管理虚拟环境
-
-你可以使用 Python 的 `venv` 模块在一个目录中创建虚拟环境:
-
-
-
-
+
+
-
-
+
+
@@ -88,13 +88,13 @@ FastAPI 是一个用于构建 API 的现代、快速(高性能)的 web 框
「_**FastAPI** 让我兴奋的欣喜若狂。它太棒了!_」
-
uvicorn - 用于加载和运行你的应用程序的服务器。
+* uvicorn - 用于加载和运行你的应用程序的服务器。
* orjson - 使用 `ORJSONResponse` 时安装。
* ujson - 使用 `UJSONResponse` 时安装。
diff --git a/docs/zh/docs/project-generation.md b/docs/zh/docs/project-generation.md
index 0655cb0a9c..48eb990df3 100644
--- a/docs/zh/docs/project-generation.md
+++ b/docs/zh/docs/project-generation.md
@@ -1,84 +1,28 @@
-# 项目生成 - 模板
+# FastAPI全栈模板
-项目生成器一般都会提供很多初始设置、安全措施、数据库,甚至还准备好了第一个 API 端点,能帮助您快速上手。
+模板通常带有特定的设置,而且被设计为灵活和可定制的。这允许您根据项目的需求修改和调整它们,使它们成为一个很好的起点。🏁
-项目生成器的设置通常都很主观,您可以按需更新或修改,但对于您的项目来说,它是非常好的起点。
+您可以使用此模板开始,因为它包含了许多已经为您完成的初始设置、安全性、数据库和一些API端点。
-## 全栈 FastAPI + PostgreSQL
+代码仓: Full Stack FastAPI Template
-GitHub:https://github.com/tiangolo/full-stack-fastapi-postgresql
+## FastAPI全栈模板 - 技术栈和特性
-### 全栈 FastAPI + PostgreSQL - 功能
-
-* 完整的 **Docker** 集成(基于 Docker)
-* Docker Swarm 开发模式
-* **Docker Compose** 本地开发集成与优化
-* **生产可用**的 Python 网络服务器,使用 Uvicorn 或 Gunicorn
-* Python **FastAPI** 后端:
-* * **速度快**:可与 **NodeJS** 和 **Go** 比肩的极高性能(归功于 Starlette 和 Pydantic)
- * **直观**:强大的编辑器支持,处处皆可自动补全,减少调试时间
- * **简单**:易学、易用,阅读文档所需时间更短
- * **简短**:代码重复最小化,每次参数声明都可以实现多个功能
- * **健壮**: 生产级别的代码,还有自动交互文档
- * **基于标准**:完全兼容并基于 API 开放标准:OpenAPI 和 JSON Schema
- * **更多功能**包括自动验证、序列化、交互文档、OAuth2 JWT 令牌身份验证等
-* **安全密码**,默认使用密码哈希
-* **JWT 令牌**身份验证
-* **SQLAlchemy** 模型(独立于 Flask 扩展,可直接用于 Celery Worker)
-* 基础的用户模型(可按需修改或删除)
-* **Alembic** 迁移
-* **CORS**(跨域资源共享)
-* **Celery** Worker 可从后端其它部分有选择地导入并使用模型和代码
-* REST 后端测试基于 Pytest,并与 Docker 集成,可独立于数据库实现完整的 API 交互测试。因为是在 Docker 中运行,每次都可从头构建新的数据存储(使用 ElasticSearch、MongoDB、CouchDB 等数据库,仅测试 API 运行)
-* Python 与 **Jupyter Kernels** 集成,用于远程或 Docker 容器内部开发,使用 Atom Hydrogen 或 Visual Studio Code 的 Jupyter 插件
-* **Vue** 前端:
- * 由 Vue CLI 生成
- * **JWT 身份验证**处理
- * 登录视图
- * 登录后显示主仪表盘视图
- * 主仪表盘支持用户创建与编辑
- * 用户信息编辑
- * **Vuex**
- * **Vue-router**
- * **Vuetify** 美化组件
- * **TypeScript**
- * 基于 **Nginx** 的 Docker 服务器(优化了 Vue-router 配置)
- * Docker 多阶段构建,无需保存或提交编译的代码
- * 在构建时运行前端测试(可禁用)
- * 尽量模块化,开箱即用,但仍可使用 Vue CLI 重新生成或创建所需项目,或复用所需内容
-* 使用 **PGAdmin** 管理 PostgreSQL 数据库,可轻松替换为 PHPMyAdmin 或 MySQL
-* 使用 **Flower** 监控 Celery 任务
-* 使用 **Traefik** 处理前后端负载平衡,可把前后端放在同一个域下,按路径分隔,但在不同容器中提供服务
-* Traefik 集成,包括自动生成 Let's Encrypt **HTTPS** 凭证
-* GitLab **CI**(持续集成),包括前后端测试
-
-## 全栈 FastAPI + Couchbase
-
-GitHub:https://github.com/tiangolo/full-stack-fastapi-couchbase
-
-⚠️ **警告** ⚠️
-
-如果您想从头开始创建新项目,建议使用以下备选方案。
-
-例如,项目生成器全栈 FastAPI + PostgreSQL 会更适用,这个项目的维护积极,用的人也多,还包括了所有新功能和改进内容。
-
-当然,您也可以放心使用这个基于 Couchbase 的生成器,它也能正常使用。就算用它生成项目也没有任何问题(为了更好地满足需求,您可以自行更新这个项目)。
-
-详见资源仓库中的文档。
-
-## 全栈 FastAPI + MongoDB
-
-……敬请期待,得看我有没有时间做这个项目。😅 🎉
-
-## FastAPI + spaCy 机器学习模型
-
-GitHub:https://github.com/microsoft/cookiecutter-spacy-fastapi
-
-### FastAPI + spaCy 机器学习模型 - 功能
-
-* 集成 **spaCy** NER 模型
-* 内置 **Azure 认知搜索**请求格式
-* **生产可用**的 Python 网络服务器,使用 Uvicorn 与 Gunicorn
-* 内置 **Azure DevOps** Kubernetes (AKS) CI/CD 开发
-* **多语**支持,可在项目设置时选择 spaCy 内置的语言
-* 不仅局限于 spaCy,可**轻松扩展**至其它模型框架(Pytorch、TensorFlow)
+- ⚡ [**FastAPI**](https://fastapi.tiangolo.com) 用于Python后端API.
+ - 🧰 [SQLModel](https://sqlmodel.tiangolo.com) 用于Python和SQL数据库的集成(ORM)。
+ - 🔍 [Pydantic](https://docs.pydantic.dev) FastAPI的依赖项之一,用于数据验证和配置管理。
+ - 💾 [PostgreSQL](https://www.postgresql.org) 作为SQL数据库。
+- 🚀 [React](https://react.dev) 用于前端。
+ - 💃 使用了TypeScript、hooks、[Vite](https://vitejs.dev)和其他一些现代化的前端技术栈。
+ - 🎨 [Chakra UI](https://chakra-ui.com) 用于前端组件。
+ - 🤖 一个自动化生成的前端客户端。
+ - 🧪 [Playwright](https://playwright.dev)用于端到端测试。
+ - 🦇 支持暗黑主题(Dark mode)。
+- 🐋 [Docker Compose](https://www.docker.com) 用于开发环境和生产环境。
+- 🔒 默认使用密码哈希来保证安全。
+- 🔑 JWT令牌用于权限验证。
+- 📫 使用邮箱来进行密码恢复。
+- ✅ 单元测试用了[Pytest](https://pytest.org).
+- 📞 [Traefik](https://traefik.io) 用于反向代理和负载均衡。
+- 🚢 部署指南(Docker Compose)包含了如何起一个Traefik前端代理来自动化HTTPS认证。
+- 🏭 CI(持续集成)和 CD(持续部署)基于GitHub Actions。
diff --git a/docs/zh/docs/python-types.md b/docs/zh/docs/python-types.md
index c788525391..a7f76d97fa 100644
--- a/docs/zh/docs/python-types.md
+++ b/docs/zh/docs/python-types.md
@@ -22,9 +22,8 @@
让我们从一个简单的例子开始:
-```Python
-{!../../../docs_src/python_types/tutorial001.py!}
-```
+{* ../../docs_src/python_types/tutorial001.py *}
+
运行这段程序将输出:
@@ -38,9 +37,8 @@ John Doe
* 通过 `title()` 将每个参数的第一个字母转换为大写形式。
* 中间用一个空格来拼接它们。
-```Python hl_lines="2"
-{!../../../docs_src/python_types/tutorial001.py!}
-```
+{* ../../docs_src/python_types/tutorial001.py hl[2] *}
+
### 修改示例
@@ -82,9 +80,8 @@ John Doe
这些就是"类型提示":
-```Python hl_lines="1"
-{!../../../docs_src/python_types/tutorial002.py!}
-```
+{* ../../docs_src/python_types/tutorial002.py hl[1] *}
+
这和声明默认值是不同的,例如:
@@ -112,9 +109,8 @@ John Doe
下面是一个已经有类型提示的函数:
-```Python hl_lines="1"
-{!../../../docs_src/python_types/tutorial003.py!}
-```
+{* ../../docs_src/python_types/tutorial003.py hl[1] *}
+
因为编辑器已经知道了这些变量的类型,所以不仅能对代码进行补全,还能检查其中的错误:
@@ -122,9 +118,8 @@ John Doe
现在你知道了必须先修复这个问题,通过 `str(age)` 把 `age` 转换成字符串:
-```Python hl_lines="2"
-{!../../../docs_src/python_types/tutorial004.py!}
-```
+{* ../../docs_src/python_types/tutorial004.py hl[2] *}
+
## 声明类型
@@ -143,9 +138,8 @@ John Doe
* `bool`
* `bytes`
-```Python hl_lines="1"
-{!../../../docs_src/python_types/tutorial005.py!}
-```
+{* ../../docs_src/python_types/tutorial005.py hl[1] *}
+
### 嵌套类型
@@ -161,9 +155,8 @@ John Doe
从 `typing` 模块导入 `List`(注意是大写的 `L`):
-```Python hl_lines="1"
-{!../../../docs_src/python_types/tutorial006.py!}
-```
+{* ../../docs_src/python_types/tutorial006.py hl[1] *}
+
同样以冒号(`:`)来声明这个变量。
@@ -171,9 +164,8 @@ John Doe
由于列表是带有"子类型"的类型,所以我们把子类型放在方括号中:
-```Python hl_lines="4"
-{!../../../docs_src/python_types/tutorial006.py!}
-```
+{* ../../docs_src/python_types/tutorial006.py hl[4] *}
+
这表示:"变量 `items` 是一个 `list`,并且这个列表里的每一个元素都是 `str`"。
@@ -191,9 +183,8 @@ John Doe
声明 `tuple` 和 `set` 的方法也是一样的:
-```Python hl_lines="1 4"
-{!../../../docs_src/python_types/tutorial007.py!}
-```
+{* ../../docs_src/python_types/tutorial007.py hl[1,4] *}
+
这表示:
@@ -208,9 +199,8 @@ John Doe
第二个子类型声明 `dict` 的所有值:
-```Python hl_lines="1 4"
-{!../../../docs_src/python_types/tutorial008.py!}
-```
+{* ../../docs_src/python_types/tutorial008.py hl[1,4] *}
+
这表示:
@@ -224,15 +214,13 @@ John Doe
假设你有一个名为 `Person` 的类,拥有 name 属性:
-```Python hl_lines="1-3"
-{!../../../docs_src/python_types/tutorial010.py!}
-```
+{* ../../docs_src/python_types/tutorial010.py hl[1:3] *}
+
接下来,你可以将一个变量声明为 `Person` 类型:
-```Python hl_lines="6"
-{!../../../docs_src/python_types/tutorial010.py!}
-```
+{* ../../docs_src/python_types/tutorial010.py hl[6] *}
+
然后,你将再次获得所有的编辑器支持:
@@ -240,7 +228,7 @@ John Doe
## Pydantic 模型
-Pydantic 是一个用来用来执行数据校验的 Python 库。
+Pydantic 是一个用来执行数据校验的 Python 库。
你可以将数据的"结构"声明为具有属性的类。
@@ -252,10 +240,31 @@ John Doe
下面的例子来自 Pydantic 官方文档:
+//// tab | Python 3.10+
+
```Python
-{!../../../docs_src/python_types/tutorial010.py!}
+{!> ../../docs_src/python_types/tutorial011_py310.py!}
```
+////
+
+//// tab | Python 3.9+
+
+```Python
+{!> ../../docs_src/python_types/tutorial011_py39.py!}
+```
+
+////
+
+//// tab | Python 3.8+
+
+```Python
+{!> ../../docs_src/python_types/tutorial011.py!}
+```
+
+////
+
+
/// info
想进一步了解 Pydantic,请阅读其文档.
diff --git a/docs/zh/docs/tutorial/background-tasks.md b/docs/zh/docs/tutorial/background-tasks.md
index 95fd7b6b57..b9becd8bff 100644
--- a/docs/zh/docs/tutorial/background-tasks.md
+++ b/docs/zh/docs/tutorial/background-tasks.md
@@ -15,9 +15,7 @@
首先导入 `BackgroundTasks` 并在 *路径操作函数* 中使用类型声明 `BackgroundTasks` 定义一个参数:
-```Python hl_lines="1 13"
-{!../../../docs_src/background_tasks/tutorial001.py!}
-```
+{* ../../docs_src/background_tasks/tutorial001.py hl[1, 13] *}
**FastAPI** 会创建一个 `BackgroundTasks` 类型的对象并作为该参数传入。
@@ -33,17 +31,13 @@
由于写操作不使用 `async` 和 `await`,我们用普通的 `def` 定义函数:
-```Python hl_lines="6-9"
-{!../../../docs_src/background_tasks/tutorial001.py!}
-```
+{* ../../docs_src/background_tasks/tutorial001.py hl[6:9] *}
## 添加后台任务
在你的 *路径操作函数* 里,用 `.add_task()` 方法将任务函数传到 *后台任务* 对象中:
-```Python hl_lines="14"
-{!../../../docs_src/background_tasks/tutorial001.py!}
-```
+{* ../../docs_src/background_tasks/tutorial001.py hl[14] *}
`.add_task()` 接收以下参数:
@@ -59,25 +53,19 @@
//// tab | Python 3.10+
-```Python hl_lines="13 15 22 25"
-{!> ../../../docs_src/background_tasks/tutorial002_an_py310.py!}
-```
+{* ../../docs_src/background_tasks/tutorial002_an_py310.py hl[13, 15, 22, 25] *}
////
//// tab | Python 3.9+
-```Python hl_lines="13 15 22 25"
-{!> ../../../docs_src/background_tasks/tutorial002_an_py39.py!}
-```
+{* ../../docs_src/background_tasks/tutorial002_an_py39.py hl[13, 15, 22, 25] *}
////
//// tab | Python 3.8+
-```Python hl_lines="14 16 23 26"
-{!> ../../../docs_src/background_tasks/tutorial002_an.py!}
-```
+{* ../../docs_src/background_tasks/tutorial002_an.py hl[14, 16, 23, 26] *}
////
@@ -89,9 +77,7 @@
///
-```Python hl_lines="11 13 20 23"
-{!> ../../../docs_src/background_tasks/tutorial002_py310.py!}
-```
+{* ../../docs_src/background_tasks/tutorial002_py310.py hl[11, 13, 20, 23] *}
////
@@ -103,9 +89,7 @@
///
-```Python hl_lines="13 15 22 25"
-{!> ../../../docs_src/background_tasks/tutorial002.py!}
-```
+{* ../../docs_src/background_tasks/tutorial002.py hl[13, 15, 22, 25] *}
////
@@ -117,7 +101,7 @@
## 技术细节
-`BackgroundTasks` 类直接来自 `starlette.background`。
+`BackgroundTasks` 类直接来自 `starlette.background`。
它被直接导入/包含到FastAPI以便你可以从 `fastapi` 导入,并避免意外从 `starlette.background` 导入备用的 `BackgroundTask` (后面没有 `s`)。
@@ -125,7 +109,7 @@
在FastAPI中仍然可以单独使用 `BackgroundTask`,但您必须在代码中创建对象,并返回包含它的Starlette `Response`。
-更多细节查看 Starlette's official docs for Background Tasks.
+更多细节查看 Starlette's official docs for Background Tasks.
## 告诫
@@ -133,8 +117,6 @@
它们往往需要更复杂的配置,即消息/作业队列管理器,如RabbitMQ或Redis,但它们允许您在多个进程中运行后台任务,甚至是在多个服务器中。
-要查看示例,查阅 [Project Generators](../project-generation.md){.internal-link target=_blank},它们都包括已经配置的Celery。
-
但是,如果您需要从同一个**FastAPI**应用程序访问变量和对象,或者您需要执行小型后台任务(如发送电子邮件通知),您只需使用 `BackgroundTasks` 即可。
## 回顾
diff --git a/docs/zh/docs/tutorial/bigger-applications.md b/docs/zh/docs/tutorial/bigger-applications.md
index a0c7095e93..554bc654f0 100644
--- a/docs/zh/docs/tutorial/bigger-applications.md
+++ b/docs/zh/docs/tutorial/bigger-applications.md
@@ -52,7 +52,7 @@ from app.routers import items
* 还有一个子目录 `app/internal/` 包含另一个 `__init__.py` 文件,因此它是又一个「Python 子包」:`app.internal`。
* `app/internal/admin.py` 是另一个子模块:`app.internal.admin`。
-
-/// tip | "提示"
+/// tip | 提示
使用 PyCharm 编辑器时,推荐安装 Pydantic PyCharm 插件。
@@ -167,21 +125,7 @@ Pydantic 模型的 JSON 概图是 OpenAPI 生成的概图部件,可在 API 文
在*路径操作*函数内部直接访问模型对象的属性:
-//// tab | Python 3.10+
-
-```Python hl_lines="19"
-{!> ../../../docs_src/body/tutorial002_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="21"
-{!> ../../../docs_src/body/tutorial002.py!}
-```
-
-////
+{* ../../docs_src/body/tutorial002_py310.py hl[19] *}
## 请求体 + 路径参数
@@ -189,21 +133,7 @@ Pydantic 模型的 JSON 概图是 OpenAPI 生成的概图部件,可在 API 文
**FastAPI** 能识别与**路径参数**匹配的函数参数,还能识别从**请求体**中获取的类型为 Pydantic 模型的函数参数。
-//// tab | Python 3.10+
-
-```Python hl_lines="15-16"
-{!> ../../../docs_src/body/tutorial003_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="17-18"
-{!> ../../../docs_src/body/tutorial003.py!}
-```
-
-////
+{* ../../docs_src/body/tutorial003_py310.py hl[15:16] *}
## 请求体 + 路径参数 + 查询参数
@@ -211,21 +141,7 @@ Pydantic 模型的 JSON 概图是 OpenAPI 生成的概图部件,可在 API 文
**FastAPI** 能够正确识别这三种参数,并从正确的位置获取数据。
-//// tab | Python 3.10+
-
-```Python hl_lines="16"
-{!> ../../../docs_src/body/tutorial004_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="18"
-{!> ../../../docs_src/body/tutorial004.py!}
-```
-
-////
+{* ../../docs_src/body/tutorial004_py310.py hl[16] *}
函数参数按如下规则进行识别:
@@ -233,7 +149,7 @@ Pydantic 模型的 JSON 概图是 OpenAPI 生成的概图部件,可在 API 文
- 类型是(`int`、`float`、`str`、`bool` 等)**单类型**的参数,是**查询**参数
- 类型是 **Pydantic 模型**的参数,是**请求体**
-/// note | "笔记"
+/// note | 笔记
因为默认值是 `None`, FastAPI 会把 `q` 当作可选参数。
diff --git a/docs/zh/docs/tutorial/cookie-param-models.md b/docs/zh/docs/tutorial/cookie-param-models.md
new file mode 100644
index 0000000000..6a7b09e257
--- /dev/null
+++ b/docs/zh/docs/tutorial/cookie-param-models.md
@@ -0,0 +1,76 @@
+# Cookie 参数模型
+
+如果您有一组相关的 **cookie**,您可以创建一个 **Pydantic 模型**来声明它们。🍪
+
+这将允许您在**多个地方**能够**重用模型**,并且可以一次性声明所有参数的验证方式和元数据。😎
+
+/// note
+
+自 FastAPI 版本 `0.115.0` 起支持此功能。🤓
+
+///
+
+/// tip
+
+此技术同样适用于 `Query` 、 `Cookie` 和 `Header` 。😎
+
+///
+
+## 带有 Pydantic 模型的 Cookie
+
+在 **Pydantic** 模型中声明所需的 **cookie** 参数,然后将参数声明为 `Cookie` :
+
+{* ../../docs_src/cookie_param_models/tutorial001_an_py310.py hl[9:12,16] *}
+
+**FastAPI** 将从请求中接收到的 **cookie** 中**提取**出**每个字段**的数据,并提供您定义的 Pydantic 模型。
+
+## 查看文档
+
+您可以在文档 UI 的 `/docs` 中查看定义的 cookie:
+
+
+kwargs,来调用。即使它们没有默认值。
-```Python hl_lines="7"
-{!../../../docs_src/path_params_numeric_validations/tutorial003.py!}
-```
+{* ../../docs_src/path_params_numeric_validations/tutorial003.py hl[7] *}
## 数值校验:大于等于
@@ -174,9 +60,7 @@ Python 不会对该 `*` 做任何事情,但是它将知道之后的所有参
像下面这样,添加 `ge=1` 后,`item_id` 将必须是一个大于(`g`reater than)或等于(`e`qual)`1` 的整数。
-```Python hl_lines="8"
-{!../../../docs_src/path_params_numeric_validations/tutorial004.py!}
-```
+{* ../../docs_src/path_params_numeric_validations/tutorial004.py hl[8] *}
## 数值校验:大于和小于等于
@@ -185,9 +69,7 @@ Python 不会对该 `*` 做任何事情,但是它将知道之后的所有参
* `gt`:大于(`g`reater `t`han)
* `le`:小于等于(`l`ess than or `e`qual)
-```Python hl_lines="9"
-{!../../../docs_src/path_params_numeric_validations/tutorial005.py!}
-```
+{* ../../docs_src/path_params_numeric_validations/tutorial005.py hl[9] *}
## 数值校验:浮点数、大于和小于
@@ -199,9 +81,7 @@ Python 不会对该 `*` 做任何事情,但是它将知道之后的所有参
对于 lt 也是一样的。
-```Python hl_lines="11"
-{!../../../docs_src/path_params_numeric_validations/tutorial006.py!}
-```
+{* ../../docs_src/path_params_numeric_validations/tutorial006.py hl[11] *}
## 总结
@@ -222,7 +102,7 @@ Python 不会对该 `*` 做任何事情,但是它将知道之后的所有参
///
-/// note | "技术细节"
+/// note | 技术细节
当你从 `fastapi` 导入 `Query`、`Path` 和其他同类对象时,它们实际上是函数。
diff --git a/docs/zh/docs/tutorial/path-params.md b/docs/zh/docs/tutorial/path-params.md
index 091dcbeb0d..ac9df08317 100644
--- a/docs/zh/docs/tutorial/path-params.md
+++ b/docs/zh/docs/tutorial/path-params.md
@@ -2,9 +2,7 @@
FastAPI 支持使用 Python 字符串格式化语法声明**路径参数**(**变量**):
-```Python hl_lines="6-7"
-{!../../../docs_src/path_params/tutorial001.py!}
-```
+{* ../../docs_src/path_params/tutorial001.py hl[6:7] *}
这段代码把路径参数 `item_id` 的值传递给路径函数的参数 `item_id`。
@@ -18,13 +16,11 @@ FastAPI 支持使用 Python 字符串格式化语法声明**路径参数**(**
使用 Python 标准类型注解,声明路径操作函数中路径参数的类型。
-```Python hl_lines="7"
-{!../../../docs_src/path_params/tutorial002.py!}
-```
+{* ../../docs_src/path_params/tutorial002.py hl[7] *}
本例把 `item_id` 的类型声明为 `int`。
-/// check | "检查"
+/// check | 检查
类型声明将为函数提供错误检查、代码补全等编辑器支持。
@@ -38,7 +34,7 @@ FastAPI 支持使用 Python 字符串格式化语法声明**路径参数**(**
{"item_id":3}
```
-/// check | "检查"
+/// check | 检查
注意,函数接收并返回的值是 `3`( `int`),不是 `"3"`(`str`)。
@@ -69,7 +65,7 @@ FastAPI 支持使用 Python 字符串格式化语法声明**路径参数**(**
值的类型不是 `int ` 而是浮点数(`float`)时也会显示同样的错误,比如: http://127.0.0.1:8000/items/4.2。
-/// check | "检查"
+/// check | 检查
**FastAPI** 使用 Python 类型声明实现了数据校验。
@@ -85,7 +81,7 @@ FastAPI 支持使用 Python 字符串格式化语法声明**路径参数**(**
-/// check | "检查"
+/// check | 检查
还是使用 Python 类型声明,**FastAPI** 提供了(集成 Swagger UI 的)API 文档。
@@ -121,9 +117,7 @@ FastAPI 充分地利用了 枚举(即 enums)。
+Python 3.4 及之后版本支持枚举(即 enums)。
///
-/// tip | "提示"
+/// tip | 提示
**AlexNet**、**ResNet**、**LeNet** 是机器学习模型。
@@ -159,9 +151,7 @@ Python 3.4 及之后版本支持
+
+
+
-/// note | "笔记"
+/// note | 笔记
某些响应状态码表示响应没有响应体(参阅下一章)。
@@ -43,7 +41,7 @@ FastAPI 可以进行识别,并生成表明无响应体的 OpenAPI 文档。
## 关于 HTTP 状态码
-/// note | "笔记"
+/// note | 笔记
如果已经了解 HTTP 状态码,请跳到下一章。
@@ -66,7 +64,7 @@ FastAPI 可以进行识别,并生成表明无响应体的 OpenAPI 文档。
* 对于来自客户端的一般错误,可以只使用 `400`
* `500` 及以上的状态码用于表示服务器端错误。几乎永远不会直接使用这些状态码。应用代码或服务器出现问题时,会自动返回这些状态代码
-/// tip | "提示"
+/// tip | 提示
状态码及适用场景的详情,请参阅 MDN 的 HTTP 状态码文档。
@@ -76,9 +74,7 @@ FastAPI 可以进行识别,并生成表明无响应体的 OpenAPI 文档。
再看下之前的例子:
-```Python hl_lines="6"
-{!../../../docs_src/response_status_code/tutorial001.py!}
-```
+{* ../../docs_src/response_status_code/tutorial001.py hl[6] *}
`201` 表示**已创建**的状态码。
@@ -86,15 +82,13 @@ FastAPI 可以进行识别,并生成表明无响应体的 OpenAPI 文档。
可以使用 `fastapi.status` 中的快捷变量。
-```Python hl_lines="1 6"
-{!../../../docs_src/response_status_code/tutorial002.py!}
-```
+{* ../../docs_src/response_status_code/tutorial002.py hl[1,6] *}
这只是一种快捷方式,具有相同的数字代码,但它可以使用编辑器的自动补全功能:
-/// note | "技术细节"
+/// note | 技术细节
也可以使用 `from starlette import status`。
diff --git a/docs/zh/docs/tutorial/schema-extra-example.md b/docs/zh/docs/tutorial/schema-extra-example.md
index 6063bd731e..6c132eed3f 100644
--- a/docs/zh/docs/tutorial/schema-extra-example.md
+++ b/docs/zh/docs/tutorial/schema-extra-example.md
@@ -10,21 +10,7 @@
您可以使用 `Config` 和 `schema_extra` 为Pydantic模型声明一个示例,如Pydantic 文档:定制 Schema 中所述:
-//// tab | Python 3.10+
-
-```Python hl_lines="13-21"
-{!> ../../../docs_src/schema_extra_example/tutorial001_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="15-23"
-{!> ../../../docs_src/schema_extra_example/tutorial001.py!}
-```
-
-////
+{* ../../docs_src/schema_extra_example/tutorial001_py310.py hl[13:21] *}
这些额外的信息将按原样添加到输出的JSON模式中。
@@ -32,21 +18,7 @@
在 `Field`, `Path`, `Query`, `Body` 和其他你之后将会看到的工厂函数,你可以为JSON 模式声明额外信息,你也可以通过给工厂函数传递其他的任意参数来给JSON 模式声明额外信息,比如增加 `example`:
-//// tab | Python 3.10+
-
-```Python hl_lines="2 8-11"
-{!> ../../../docs_src/schema_extra_example/tutorial002_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="4 10-13"
-{!> ../../../docs_src/schema_extra_example/tutorial002.py!}
-```
-
-////
+{* ../../docs_src/schema_extra_example/tutorial002_py310.py hl[2,8:11] *}
/// warning
@@ -60,57 +32,7 @@
比如,你可以将请求体的一个 `example` 传递给 `Body`:
-//// tab | Python 3.10+
-
-```Python hl_lines="22-27"
-{!> ../../../docs_src/schema_extra_example/tutorial003_an_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="22-27"
-{!> ../../../docs_src/schema_extra_example/tutorial003_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="23-28"
-{!> ../../../docs_src/schema_extra_example/tutorial003_an.py!}
-```
-
-////
-
-//// tab | Python 3.10+ non-Annotated
-
-/// tip
-
-尽可能选择使用 `Annotated` 的版本。
-
-///
-
-```Python hl_lines="18-23"
-{!> ../../../docs_src/schema_extra_example/tutorial003_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+ non-Annotated
-
-/// tip
-
-尽可能选择使用 `Annotated` 的版本。
-
-///
-
-```Python hl_lines="20-25"
-{!> ../../../docs_src/schema_extra_example/tutorial003.py!}
-```
-
-////
+{* ../../docs_src/schema_extra_example/tutorial003_an_py310.py hl[22:27] *}
## 文档 UI 中的例子
diff --git a/docs/zh/docs/tutorial/security/first-steps.md b/docs/zh/docs/tutorial/security/first-steps.md
index 266a5fcdf8..225eb26951 100644
--- a/docs/zh/docs/tutorial/security/first-steps.md
+++ b/docs/zh/docs/tutorial/security/first-steps.md
@@ -20,39 +20,11 @@
把下面的示例代码复制到 `main.py`:
-//// tab | Python 3.9+
-
-```Python
-{!> ../../../docs_src/security/tutorial001_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python
-{!> ../../../docs_src/security/tutorial001_an.py!}
-```
-
-////
-
-//// tab | Python 3.8+ non-Annotated
-
-/// tip
-
-尽可能选择使用 `Annotated` 的版本。
-
-///
-
-```Python
-{!> ../../../docs_src/security/tutorial001.py!}
-```
-
-////
+{* ../../docs_src/security/tutorial001_an_py39.py *}
## 运行
-/// info | "说明"
+/// info | 说明
先安装 `python-multipart`。
@@ -82,7 +54,7 @@ $ uvicorn main:app --reload
-/// check | "Authorize 按钮!"
+/// check | Authorize 按钮!
页面右上角出现了一个「**Authorize**」按钮。
@@ -94,7 +66,7 @@ $ uvicorn main:app --reload
-/// note | "笔记"
+/// note | 笔记
目前,在表单中输入内容不会有任何反应,后文会介绍相关内容。
@@ -140,7 +112,7 @@ OAuth2 的设计目标是为了让后端或 API 独立于服务器验证用户
本例使用 **OAuth2** 的 **Password** 流以及 **Bearer** 令牌(`Token`)。为此要使用 `OAuth2PasswordBearer` 类。
-/// info | "说明"
+/// info | 说明
`Bearer` 令牌不是唯一的选择。
@@ -154,11 +126,9 @@ OAuth2 的设计目标是为了让后端或 API 独立于服务器验证用户
创建 `OAuth2PasswordBearer` 的类实例时,要传递 `tokenUrl` 参数。该参数包含客户端(用户浏览器中运行的前端) 的 URL,用于发送 `username` 与 `password`,并获取令牌。
-```Python hl_lines="6"
-{!../../../docs_src/security/tutorial001.py!}
-```
+{* ../../docs_src/security/tutorial001.py hl[6] *}
-/// tip | "提示"
+/// tip | 提示
在此,`tokenUrl="token"` 指向的是暂未创建的相对 URL `token`。这个相对 URL 相当于 `./token`。
@@ -172,7 +142,7 @@ OAuth2 的设计目标是为了让后端或 API 独立于服务器验证用户
接下来,学习如何创建实际的路径操作。
-/// info | "说明"
+/// info | 说明
严苛的 **Pythonista** 可能不喜欢用 `tokenUrl` 这种命名风格代替 `token_url`。
@@ -194,15 +164,13 @@ oauth2_scheme(some, parameters)
接下来,使用 `Depends` 把 `oauth2_scheme` 传入依赖项。
-```Python hl_lines="10"
-{!../../../docs_src/security/tutorial001.py!}
-```
+{* ../../docs_src/security/tutorial001.py hl[10] *}
该依赖项使用字符串(`str`)接收*路径操作函数*的参数 `token` 。
**FastAPI** 使用依赖项在 OpenAPI 概图(及 API 文档)中定义**安全方案**。
-/// info | "技术细节"
+/// info | 技术细节
**FastAPI** 使用(在依赖项中声明的)类 `OAuth2PasswordBearer` 在 OpenAPI 中定义安全方案,这是因为它继承自 `fastapi.security.oauth2.OAuth2`,而该类又是继承自`fastapi.security.base.SecurityBase`。
diff --git a/docs/zh/docs/tutorial/security/get-current-user.md b/docs/zh/docs/tutorial/security/get-current-user.md
index f8094e86a8..1f254a1037 100644
--- a/docs/zh/docs/tutorial/security/get-current-user.md
+++ b/docs/zh/docs/tutorial/security/get-current-user.md
@@ -2,9 +2,7 @@
上一章中,(基于依赖注入系统的)安全系统向*路径操作函数*传递了 `str` 类型的 `token`:
-```Python hl_lines="10"
-{!../../../docs_src/security/tutorial001.py!}
-```
+{* ../../docs_src/security/tutorial001.py hl[10] *}
但这并不实用。
@@ -17,9 +15,7 @@
与使用 Pydantic 声明请求体相同,并且可在任何位置使用:
-```Python hl_lines="5 12-16"
-{!../../../docs_src/security/tutorial002.py!}
-```
+{* ../../docs_src/security/tutorial002.py hl[5,12:16] *}
## 创建 `get_current_user` 依赖项
@@ -31,31 +27,25 @@
与之前直接在路径操作中的做法相同,新的 `get_current_user` 依赖项从子依赖项 `oauth2_scheme` 中接收 `str` 类型的 `token`:
-```Python hl_lines="25"
-{!../../../docs_src/security/tutorial002.py!}
-```
+{* ../../docs_src/security/tutorial002.py hl[25] *}
## 获取用户
`get_current_user` 使用创建的(伪)工具函数,该函数接收 `str` 类型的令牌,并返回 Pydantic 的 `User` 模型:
-```Python hl_lines="19-22 26-27"
-{!../../../docs_src/security/tutorial002.py!}
-```
+{* ../../docs_src/security/tutorial002.py hl[19:22,26:27] *}
## 注入当前用户
在*路径操作* 的 `Depends` 中使用 `get_current_user`:
-```Python hl_lines="31"
-{!../../../docs_src/security/tutorial002.py!}
-```
+{* ../../docs_src/security/tutorial002.py hl[31] *}
注意,此处把 `current_user` 的类型声明为 Pydantic 的 `User` 模型。
这有助于在函数内部使用代码补全和类型检查。
-/// tip | "提示"
+/// tip | 提示
还记得请求体也是使用 Pydantic 模型声明的吧。
@@ -63,7 +53,7 @@
///
-/// check | "检查"
+/// check | 检查
依赖系统的这种设计方式可以支持不同的依赖项返回同一个 `User` 模型。
@@ -104,9 +94,7 @@
所有*路径操作*只需 3 行代码就可以了:
-```Python hl_lines="30-32"
-{!../../../docs_src/security/tutorial002.py!}
-```
+{* ../../docs_src/security/tutorial002.py hl[30:32] *}
## 小结
diff --git a/docs/zh/docs/tutorial/security/index.md b/docs/zh/docs/tutorial/security/index.md
index e888a4fe9b..1484b99fd8 100644
--- a/docs/zh/docs/tutorial/security/index.md
+++ b/docs/zh/docs/tutorial/security/index.md
@@ -22,7 +22,7 @@ OAuth2是一个规范,它定义了几种处理身份认证和授权的方法
它包括了使用「第三方」进行身份认证的方法。
-这就是所有带有「使用 Facebook,Google,Twitter,GitHub 登录」的系统背后所使用的机制。
+这就是所有带有「使用 Facebook,Google,X (Twitter),GitHub 登录」的系统背后所使用的机制。
### OAuth 1
@@ -79,7 +79,7 @@ OpenAPI 定义了以下安全方案:
* HTTP Basic 认证方式。
* HTTP Digest,等等。
* `oauth2`:所有的 OAuth2 处理安全性的方式(称为「流程」)。
- *以下几种流程适合构建 OAuth 2.0 身份认证的提供者(例如 Google,Facebook,Twitter,GitHub 等):
+ *以下几种流程适合构建 OAuth 2.0 身份认证的提供者(例如 Google,Facebook,X (Twitter),GitHub 等):
* `implicit`
* `clientCredentials`
* `authorizationCode`
@@ -91,7 +91,7 @@ OpenAPI 定义了以下安全方案:
/// tip
-集成其他身份认证/授权提供者(例如Google,Facebook,Twitter,GitHub等)也是可能的,而且较为容易。
+集成其他身份认证/授权提供者(例如Google,Facebook,X (Twitter),GitHub等)也是可能的,而且较为容易。
最复杂的问题是创建一个像这样的身份认证/授权提供程序,但是 **FastAPI** 为你提供了轻松完成任务的工具,同时为你解决了重活。
diff --git a/docs/zh/docs/tutorial/security/oauth2-jwt.md b/docs/zh/docs/tutorial/security/oauth2-jwt.md
index 690bd1789b..7d338419b5 100644
--- a/docs/zh/docs/tutorial/security/oauth2-jwt.md
+++ b/docs/zh/docs/tutorial/security/oauth2-jwt.md
@@ -40,7 +40,7 @@ $ pip install pyjwt
-/// info | "说明"
+/// info | 说明
如果您打算使用类似 RSA 或 ECDSA 的数字签名算法,您应该安装加密库依赖项 `pyjwt[crypto]`。
@@ -82,7 +82,7 @@ $ pip install passlib[bcrypt]
-/// tip | "提示"
+/// tip | 提示
`passlib` 甚至可以读取 Django、Flask 的安全插件等工具创建的密码。
@@ -98,7 +98,7 @@ $ pip install passlib[bcrypt]
创建用于密码哈希和身份校验的 PassLib **上下文**。
-/// tip | "提示"
+/// tip | 提示
PassLib 上下文还支持使用不同哈希算法的功能,包括只能校验的已弃用旧算法等。
@@ -114,59 +114,9 @@ PassLib 上下文还支持使用不同哈希算法的功能,包括只能校验
第三个函数用于身份验证,并返回用户。
-//// tab | Python 3.10+
+{* ../../docs_src/security/tutorial004_an_py310.py hl[8,49,56:57,60:61,70:76] *}
-```Python hl_lines="8 49 56-57 60-61 70-76"
-{!> ../../../docs_src/security/tutorial004_an_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="8 49 56-57 60-61 70-76"
-{!> ../../../docs_src/security/tutorial004_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="8 50 57-58 61-62 71-77"
-{!> ../../../docs_src/security/tutorial004_an.py!}
-```
-
-////
-
-//// tab | Python 3.10+ 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_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+ non-Annotated
-
-/// tip
-
-Prefer to use the `Annotated` version if possible.
-
-///
-
-```Python hl_lines="8 49 56-57 60-61 70-76"
-{!> ../../../docs_src/security/tutorial004.py!}
-```
-
-////
-
-/// note | "笔记"
+/// note | 笔记
查看新的(伪)数据库 `fake_users_db`,就能看到哈希后的密码:`"$2b$12$EixZaYVK1fsbw1ZfbX3OXePaWxn96p36WQoeG6Lruj3vjPGga31lW"`。
@@ -200,9 +150,7 @@ $ openssl rand -hex 32
创建生成新的访问令牌的工具函数。
-```Python hl_lines="6 12-14 28-30 78-86"
-{!../../../docs_src/security/tutorial004.py!}
-```
+{* ../../docs_src/security/tutorial004.py hl[6,12:14,28:30,78:86] *}
## 更新依赖项
@@ -212,57 +160,7 @@ $ openssl rand -hex 32
如果令牌无效,则直接返回 HTTP 错误。
-//// tab | Python 3.10+
-
-```Python hl_lines="4 7 13-15 29-31 79-87"
-{!> ../../../docs_src/security/tutorial004_an_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="4 7 13-15 29-31 79-87"
-{!> ../../../docs_src/security/tutorial004_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="4 7 14-16 30-32 80-88"
-{!> ../../../docs_src/security/tutorial004_an.py!}
-```
-
-////
-
-//// tab | Python 3.10+ non-Annotated
-
-/// tip
-
-Prefer to use the `Annotated` version if possible.
-
-///
-
-```Python hl_lines="3 6 12-14 28-30 78-86"
-{!> ../../../docs_src/security/tutorial004_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+ non-Annotated
-
-/// tip
-
-Prefer to use the `Annotated` version if possible.
-
-///
-
-```Python hl_lines="4 7 13-15 29-31 79-87"
-{!> ../../../docs_src/security/tutorial004.py!}
-```
-
-////
+{* ../../docs_src/security/tutorial004_an_py310.py hl[4,7,13:15,29:31,79:87] *}
## 更新 `/token` *路径操作*
@@ -270,57 +168,7 @@ Prefer to use the `Annotated` version if possible.
创建并返回真正的 JWT 访问令牌。
-//// tab | Python 3.10+
-
-```Python hl_lines="118-133"
-{!> ../../../docs_src/security/tutorial004_an_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python hl_lines="118-133"
-{!> ../../../docs_src/security/tutorial004_an_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="119-134"
-{!> ../../../docs_src/security/tutorial004_an.py!}
-```
-
-////
-
-//// tab | Python 3.10+ non-Annotated
-
-/// tip
-
-Prefer to use the `Annotated` version if possible.
-
-///
-
-```Python hl_lines="115-130"
-{!> ../../../docs_src/security/tutorial004_py310.py!}
-```
-
-////
-
-//// tab | Python 3.8+ non-Annotated
-
-/// tip
-
-Prefer to use the `Annotated` version if possible.
-
-///
-
-```Python hl_lines="116-131"
-{!> ../../../docs_src/security/tutorial004.py!}
-```
-
-////
+{* ../../docs_src/security/tutorial004_an_py310.py hl[118:133] *}
### JWT `sub` 的技术细节
@@ -358,7 +206,7 @@ JWT 规范还包括 `sub` 键,值是令牌的主题。
用户名: `johndoe` 密码: `secret`
-/// check | "检查"
+/// check | 检查
注意,代码中没有明文密码**`secret`**,只保存了它的哈希值。
@@ -383,7 +231,7 @@ JWT 规范还包括 `sub` 键,值是令牌的主题。
-/// note | "笔记"
+/// note | 笔记
注意,请求中 `Authorization` 响应头的值以 `Bearer` 开头。
diff --git a/docs/zh/docs/tutorial/security/simple-oauth2.md b/docs/zh/docs/tutorial/security/simple-oauth2.md
index 261421dad0..f70678df8f 100644
--- a/docs/zh/docs/tutorial/security/simple-oauth2.md
+++ b/docs/zh/docs/tutorial/security/simple-oauth2.md
@@ -32,7 +32,7 @@ OAuth2 还支持客户端发送**`scope`**表单字段。
* 脸书和 Instagram 使用 `instagram_basic`
* 谷歌使用 `https://www.googleapis.com/auth/drive`
-/// info | "说明"
+/// info | 说明
OAuth2 中,**作用域**只是声明指定权限的字符串。
@@ -52,9 +52,7 @@ OAuth2 中,**作用域**只是声明指定权限的字符串。
首先,导入 `OAuth2PasswordRequestForm`,然后,在 `/token` *路径操作* 中,用 `Depends` 把该类作为依赖项。
-```Python hl_lines="4 76"
-{!../../../docs_src/security/tutorial003.py!}
-```
+{* ../../docs_src/security/tutorial003.py hl[4,76] *}
`OAuth2PasswordRequestForm` 是用以下几项内容声明表单请求体的类依赖项:
@@ -63,7 +61,7 @@ OAuth2 中,**作用域**只是声明指定权限的字符串。
* 可选的 `scope` 字段,由多个空格分隔的字符串组成的长字符串
* 可选的 `grant_type`
-/// tip | "提示"
+/// tip | 提示
实际上,OAuth2 规范*要求* `grant_type` 字段使用固定值 `password`,但 `OAuth2PasswordRequestForm` 没有作强制约束。
@@ -74,7 +72,7 @@ OAuth2 中,**作用域**只是声明指定权限的字符串。
* 可选的 `client_id`(本例未使用)
* 可选的 `client_secret`(本例未使用)
-/// info | "说明"
+/// info | 说明
`OAuth2PasswordRequestForm` 与 `OAuth2PasswordBearer` 一样,都不是 FastAPI 的特殊类。
@@ -88,7 +86,7 @@ OAuth2 中,**作用域**只是声明指定权限的字符串。
### 使用表单数据
-/// tip | "提示"
+/// tip | 提示
`OAuth2PasswordRequestForm` 类依赖项的实例没有以空格分隔的长字符串属性 `scope`,但它支持 `scopes` 属性,由已发送的 scope 字符串列表组成。
@@ -102,9 +100,7 @@ OAuth2 中,**作用域**只是声明指定权限的字符串。
本例使用 `HTTPException` 异常显示此错误:
-```Python hl_lines="3 77-79"
-{!../../../docs_src/security/tutorial003.py!}
-```
+{* ../../docs_src/security/tutorial003.py hl[3,77:79] *}
### 校验密码
@@ -130,9 +126,7 @@ OAuth2 中,**作用域**只是声明指定权限的字符串。
这样一来,窃贼就无法在其它应用中使用窃取的密码,要知道,很多用户在所有系统中都使用相同的密码,风险超大。
-```Python hl_lines="80-83"
-{!../../../docs_src/security/tutorial003.py!}
-```
+{* ../../docs_src/security/tutorial003.py hl[80:83] *}
#### 关于 `**user_dict`
@@ -150,7 +144,7 @@ UserInDB(
)
```
-/// info | "说明"
+/// info | 说明
`user_dict` 的说明,详见[**更多模型**一章](../extra-models.md#user_indict){.internal-link target=_blank}。
@@ -166,7 +160,7 @@ UserInDB(
本例只是简单的演示,返回的 Token 就是 `username`,但这种方式极不安全。
-/// tip | "提示"
+/// tip | 提示
下一章介绍使用哈希密码和 JWT Token 的真正安全机制。
@@ -174,11 +168,9 @@ UserInDB(
///
-```Python hl_lines="85"
-{!../../../docs_src/security/tutorial003.py!}
-```
+{* ../../docs_src/security/tutorial003.py hl[85] *}
-/// tip | "提示"
+/// tip | 提示
按规范的要求,应像本示例一样,返回带有 `access_token` 和 `token_type` 的 JSON 对象。
@@ -202,11 +194,9 @@ UserInDB(
因此,在端点中,只有当用户存在、通过身份验证、且状态为激活时,才能获得该用户:
-```Python hl_lines="58-67 69-72 90"
-{!../../../docs_src/security/tutorial003.py!}
-```
+{* ../../docs_src/security/tutorial003.py hl[58:67,69:72,90] *}
-/// info | "说明"
+/// info | 说明
此处返回值为 `Bearer` 的响应头 `WWW-Authenticate` 也是规范的一部分。
diff --git a/docs/zh/docs/tutorial/sql-databases.md b/docs/zh/docs/tutorial/sql-databases.md
index 45ec71f7ca..fbdf3be6cc 100644
--- a/docs/zh/docs/tutorial/sql-databases.md
+++ b/docs/zh/docs/tutorial/sql-databases.md
@@ -1,908 +1,360 @@
-# SQL (关系型) 数据库
+# SQL(关系型)数据库
-/// info
+**FastAPI** 并不要求您使用 SQL(关系型)数据库。您可以使用**任何**想用的数据库。
-这些文档即将被更新。🎉
+这里,我们来看一个使用 SQLModel 的示例。
-当前版本假设Pydantic v1和SQLAlchemy版本小于2。
+**SQLModel** 是基于 SQLAlchemy 和 Pydantic 构建的。它由 **FastAPI** 的同一作者制作,旨在完美匹配需要使用 **SQL 数据库**的 FastAPI 应用程序。
-新的文档将包括Pydantic v2以及 SQLModel(也是基于SQLAlchemy),一旦SQLModel更新为为使用Pydantic v2。
+/// tip
+
+您可以使用任何其他您想要的 SQL 或 NoSQL 数据库(在某些情况下称为 “ORM”),FastAPI 不会强迫您使用任何东西。😎
///
-**FastAPI**不需要你使用SQL(关系型)数据库。
-
-但是您可以使用任何您想要的关系型数据库。
-
-在这里,让我们看一个使用着[SQLAlchemy](https://www.sqlalchemy.org/)的示例。
-
-您可以很容易地将其调整为任何SQLAlchemy支持的数据库,如:
+由于 SQLModel 基于 SQLAlchemy,因此您可以轻松使用任何由 SQLAlchemy **支持的数据库**(这也让它们被 SQLModel 支持),例如:
* PostgreSQL
* MySQL
* SQLite
* Oracle
-* Microsoft SQL Server,等等其它数据库
+* Microsoft SQL Server 等.
-在此示例中,我们将使用**SQLite**,因为它使用单个文件并且 在Python中具有集成支持。因此,您可以复制此示例并按原样来运行它。
+在这个例子中,我们将使用 **SQLite**,因为它使用单个文件,并且 Python 对其有集成支持。因此,您可以直接复制这个例子并运行。
-稍后,对于您的产品级别的应用程序,您可能会要使用像**PostgreSQL**这样的数据库服务器。
+之后,对于您的生产应用程序,您可能会想要使用像 PostgreSQL 这样的数据库服务器。
/// tip
-这儿有一个**FastAPI**和**PostgreSQL**的官方项目生成器,全部基于**Docker**,包括前端和更多工具:https://github.com/tiangolo/full-stack-fastapi-postgresql
+有一个使用 **FastAPI** 和 **PostgreSQL** 的官方的项目生成器,其中包括了前端和更多工具: https://github.com/fastapi/full-stack-fastapi-template
///
-/// note
+这是一个非常简单和简短的教程。如果您想了解一般的数据库、SQL 或更高级的功能,请查看 SQLModel 文档。
-请注意,大部分代码是`SQLAlchemy`的标准代码,您可以用于任何框架。FastAPI特定的代码和往常一样少。
+## 安装 `SQLModel`
-///
-
-## ORMs(对象关系映射)
-
-**FastAPI**可与任何数据库在任何样式的库中一起与 数据库进行通信。
-
-一种常见的模式是使用“ORM”:对象关系映射。
-
-ORM 具有在代码和数据库表(“*关系型”)中的**对象**之间转换(“*映射*”)的工具。
-
-使用 ORM,您通常会在 SQL 数据库中创建一个代表映射的类,该类的每个属性代表一个列,具有名称和类型。
-
-例如,一个类`Pet`可以表示一个 SQL 表`pets`。
-
-该类的每个*实例对象都代表数据库中的一行数据。*
-
-又例如,一个对象`orion_cat`(`Pet`的一个实例)可以有一个属性`orion_cat.type`, 对标数据库中的`type`列。并且该属性的值可以是其它,例如`"cat"`。
-
-这些 ORM 还具有在表或实体之间建立关系的工具(比如创建多表关系)。
-
-这样,您还可以拥有一个属性`orion_cat.owner`,它包含该宠物所有者的数据,这些数据取自另外一个表。
-
-因此,`orion_cat.owner.name`可能是该宠物主人的姓名(来自表`owners`中的列`name`)。
-
-它可能有一个像`"Arquilian"`(一种业务逻辑)。
-
-当您尝试从您的宠物对象访问它时,ORM 将完成所有工作以从相应的表*所有者那里再获取信息。*
-
-常见的 ORM 例如:Django-ORM(Django 框架的一部分)、SQLAlchemy ORM(SQLAlchemy 的一部分,独立于框架)和 Peewee(独立于框架)等。
-
-在这里,我们将看到如何使用**SQLAlchemy ORM**。
-
-以类似的方式,您也可以使用任何其他 ORM。
-
-/// tip
-
-在文档中也有一篇使用 Peewee 的等效的文章。
-
-///
-
-## 文件结构
-
-对于这些示例,假设您有一个名为的目录`my_super_project`,其中包含一个名为的子目录`sql_app`,其结构如下:
-
-```
-.
-└── sql_app
- ├── __init__.py
- ├── crud.py
- ├── database.py
- ├── main.py
- ├── models.py
- └── schemas.py
-```
-
-该文件`__init__.py`只是一个空文件,但它告诉 Python `sql_app` 是一个包。
-
-现在让我们看看每个文件/模块的作用。
-
-## 安装 SQLAlchemy
-
-首先你需要安装`SQLAlchemy`:
+首先,确保您创建并激活了[虚拟环境](../virtual-environments.md){.internal-link target=_blank},然后安装了 `sqlmodel` :
+
+我们不应该允许这样做,因为他们可能会覆盖我们在数据库中已经分配的 `id` 。决定 `id` 的行为应该由**后端**或**数据库**来完成,**而非客户端**。
-您还可以使用[SQLite Viewer](https://inloop.github.io/sqlite-viewer/)或[ExtendsClass](https://extendsclass.com/sqlite-browser.html)等在线 SQLite 浏览器。
+此外,我们为 hero 创建了一个 `secret_name` ,但到目前为止,我们在各处都返回了它,这就不太**秘密**了……😅
-## 中间件替代数据库会话
+我们将通过添加一些**额外的模型**来解决这些问题,而 SQLModel 将在这里大放异彩。✨
-如果你不能使用带有`yield`的依赖项——例如,如果你没有使用**Python 3.7**并且不能安装上面提到的**Python 3.6**的“backports” ——你可以使用类似的方法在“中间件”中设置会话。
+### 创建多个模型
-“中间件”基本上是一个对每个请求都执行的函数,其中一些代码在端点函数之前执行,另一些代码在端点函数之后执行。
+在 **SQLModel** 中,任何含有 `table=True` 属性的模型类都是一个**表模型**。
-### 创建中间件
+任何不含有 `table=True` 属性的模型类都是**数据模型**,这些实际上只是 Pydantic 模型(附带一些小的额外功能)。🤓
-我们要添加的中间件(只是一个函数)将为每个请求创建一个新的 SQLAlchemy`SessionLocal`,将其添加到请求中,然后在请求完成后关闭它。
+有了 SQLModel,我们就可以利用**继承**来在所有情况下**避免重复**所有字段。
-//// tab | Python 3.9+
+#### `HeroBase` - 基类
-```Python hl_lines="12-20"
-{!> ../../../docs_src/sql_databases/sql_app_py39/alt_main.py!}
-```
+我们从一个 `HeroBase` 模型开始,该模型具有所有模型**共享的字段**:
-////
+* `name`
+* `age`
-//// tab | Python 3.8+
+{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:9] hl[7:9] *}
-```Python hl_lines="14-22"
-{!> ../../../docs_src/sql_databases/sql_app/alt_main.py!}
-```
+#### `Hero` - *表模型*
-////
+接下来,我们创建 `Hero` ,实际的*表模型*,并添加那些不总是在其他模型中的**额外字段**:
-/// info
+* `id`
+* `secret_name`
-我们将`SessionLocal()`请求的创建和处理放在一个`try`块中。
+因为 `Hero` 继承自 HeroBase ,所以它**也**包含了在 `HeroBase` 中声明过的**字段**。因此 `Hero` 的所有字段为:
-然后我们在finally块中关闭它。
+* `id`
+* `name`
+* `age`
+* `secret_name`
-通过这种方式,我们确保数据库会话在请求后始终关闭,即使在处理请求时出现异常也会关闭。
+{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:14] hl[12:14] *}
-///
+#### `HeroPublic` - 公共*数据模型*
-### 关于`request.state`
+接下来,我们创建一个 `HeroPublic` 模型,这是将**返回**给 API 客户端的模型。
-`request.state`是每个`Request`对象的属性。它用于存储附加到请求本身的任意对象,例如本例中的数据库会话。您可以在[Starlette 的关于`Request`state](https://www.starlette.io/requests/#other-state)的文档中了解更多信息。
+它包含与 `HeroBase` 相同的字段,因此不会包括 `secret_name` 。
-对于这种情况下,它帮助我们确保在整个请求中使用单个数据库会话,然后关闭(在中间件中)。
+终于,我们英雄(hero)的身份得到了保护! 🥷
-### 使用`yield`依赖项与使用中间件的区别
-
-在此处添加**中间件**与`yield`的依赖项的作用效果类似,但也有一些区别:
-
-* 中间件需要更多的代码并且更复杂一些。
-* 中间件必须是一个`async`函数。
- * 如果其中有代码必须“等待”网络,它可能会在那里“阻止”您的应用程序并稍微降低性能。
- * 尽管这里的`SQLAlchemy`工作方式可能不是很成问题。
- * 但是,如果您向等待大量I/O的中间件添加更多代码,则可能会出现问题。
-* *每个*请求都会运行一个中间件。
- * 将为每个请求创建一个连接。
- * 即使处理该请求的*路径操作*不需要数据库。
+它还重新声明了 `id: int` 。这样我们便与 API 客户端建立了一种**约定**,使他们始终可以期待 `id` 存在并且是一个整数 `int`(永远不会是 `None` )。
/// tip
-最好使用带有yield的依赖项,如果这足够满足用例需求
+确保返回模型始终提供一个值并且始终是 `int` (而不是 `None` )对 API 客户端非常有用,他们可以在这种确定性下编写更简单的代码。
+
+此外,**自动生成的客户端**将拥有更简洁的接口,这样与您的 API 交互的开发者就能更轻松地使用您的 API。😎
///
-/// info
+`HeroPublic` 中的所有字段都与 `HeroBase` 中的相同,其中 `id` 声明为 `int` (不是 `None` ):
-带有`yield`的依赖项是最近刚加入**FastAPI**中的。
+* `id`
+* `name`
+* `age`
+* `secret_name`
-所以本教程的先前版本只有带有中间件的示例,并且可能有多个应用程序使用中间件进行数据库会话管理。
+{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:18] hl[17:18] *}
+
+#### `HeroCreate` - 用于创建 hero 的*数据模型*
+
+现在我们创建一个 `HeroCreate` 模型,这是用于**验证**客户数据的模型。
+
+它不仅拥有与 `HeroBase` 相同的字段,还有 `secret_name` 。
+
+现在,当客户端**创建一个新的 hero** 时,他们会发送 `secret_name` ,它会被存储到数据库中,但这些 `secret_name` 不会通过 API 返回给客户端。
+
+/// tip
+
+这应当是**密码**被处理的方式:接收密码,但不要通过 API 返回它们。
+
+在存储密码之前,您还应该对密码的值进行**哈希**处理,**绝不要以明文形式存储它们**。
///
+
+`HeroCreate` 的字段包括:
+
+* `name`
+* `age`
+* `secret_name`
+
+{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:22] hl[21:22] *}
+
+#### `HeroUpdate` - 用于更新 hero 的*数据模型*
+
+在之前的应用程序中,我们没有办法**更新 hero**,但现在有了**多个模型**,我们便能做到这一点了。🎉
+
+`HeroUpdate` *数据模型*有些特殊,它包含创建新 hero 所需的**所有相同字段**,但所有字段都是**可选的**(它们都有默认值)。这样,当您更新一个 hero 时,您可以只发送您想要更新的字段。
+
+因为所有**字段实际上**都发生了**变化**(类型现在包括 `None` ,并且它们现在有一个默认值 `None` ),我们需要**重新声明**它们。
+
+我们会重新声明所有字段,因此我们并不是真的需要从 `HeroBase` 继承。我会让它继承只是为了保持一致,但这并不必要。这更多是个人喜好的问题。🤷
+
+`HeroUpdate` 的字段包括:
+
+* `name`
+* `age`
+* `secret_name`
+
+{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:28] hl[25:28] *}
+
+### 使用 `HeroCreate` 创建并返回 `HeroPublic`
+
+既然我们有了**多个模型**,我们就可以对使用它们的应用程序部分进行更新。
+
+我们在请求中接收到一个 `HeroCreate` *数据模型*,然后从中创建一个 `Hero` *表模型*。
+
+这个新的*表模型* `Hero` 会包含客户端发送的字段,以及一个由数据库生成的 `id` 。
+
+然后我们将与函数中相同的*表模型* `Hero` 原样返回。但是由于我们使用 `HeroPublic` *数据模型*声明了 `response_model` ,**FastAPI** 会使用 `HeroPublic` 来验证和序列化数据。
+
+{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[56:62] hl[56:58] *}
+
+/// tip
+
+现在我们使用 `response_model=HeroPublic` 来代替**返回类型注释** `-> HeroPublic` ,因为我们返回的值实际上**并不是** `HeroPublic` 类型。
+
+如果我们声明了 `-> HeroPublic` ,您的编辑器和代码检查工具会抱怨(但也确实理所应当)您返回了一个 `Hero` 而不是一个 `HeroPublic` 。
+
+通过 `response_model` 的声明,我们让 **FastAPI** 按照它自己的方式处理,而不会干扰类型注解以及编辑器和其他工具提供的帮助。
+
+///
+
+### 用 `HeroPublic` 读取 Hero
+
+我们可以像之前一样**读取** `Hero` 。同样,使用 `response_model=list[HeroPublic]` 确保正确地验证和序列化数据。
+
+{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[65:72] hl[65] *}
+
+### 用 `HeroPublic` 读取单个 Hero
+
+我们可以**读取**单个 `hero` 。
+
+{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[75:80] hl[77] *}
+
+### 用 `HeroUpdate` 更新单个 Hero
+
+我们可以**更新**单个 `hero` 。为此,我们会使用 HTTP 的 `PATCH` 操作。
+
+在代码中,我们会得到一个 `dict` ,其中包含客户端发送的所有数据,**只有客户端发送的数据**,并排除了任何一个仅仅作为默认值存在的值。为此,我们使用 `exclude_unset=True` 。这是最主要的技巧。🪄
+
+然后我们会使用 `hero_db.sqlmodel_update(hero_data)` ,来利用 `hero_data` 的数据更新 `hero_db` 。
+
+{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[83:93] hl[83:84,88:89] *}
+
+### (又一次)删除单个 Hero
+
+**删除**一个 hero 基本保持不变。
+
+我们不会满足在这一部分中重构一切的愿望。😅
+
+{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[96:103] hl[101] *}
+
+### (又一次)运行应用程序
+
+您可以再运行一次应用程序:
+
+
+text» or «`text`» or «"text"», ignore that further markup when deciding if the text is an abbreviation), and if the description (the text inside the title attribute) contains the full phrase for this abbreviation, then append a dash («–») to the full phrase, followed by the translation of the full phrase.
+
+Conversion scheme:
+
+ Source (English):
+
+ {abbreviation}
+
+ Result:
+
+ {abbreviation}
+
+Examples:
+
+ Source (English):
+
+ «««
+ IoT
+ CPU
+ TL;DR:
+ »»»
+
+ Result (German):
+
+ «««
+ IoT
+ CPU
+ TL;DR:
+ »»»
+
+1.1) If the language to which you translate mostly uses the letters of the ASCII char set (for example Spanish, French, German, but not Russian, Chinese) and if the translation of the full phrase is identical to, or starts with the same letters as the original full phrase, then only give the translation of the full phrase.
+
+Conversion scheme:
+
+ Source (English):
+
+ {abbreviation}
+
+ Result:
+
+ {abbreviation}
+
+Examples:
+
+ Source (English):
+
+ «««
+ JWT
+ Enum
+ ASGI
+ »»»
+
+ Result (German):
+
+ «««
+ JWT
+ Enum
+ ASGI
+ »»»
+
+2) If the description is not a full phrase for an abbreviation which the abbr element surrounds, but some other information, then just translate the description.
+
+Conversion scheme:
+
+ Source (English):
+
+ {text}
+
+ Result:
+
+ {translation of text}
+
+Examples:
+
+ Source (English):
+
+ «««
+ path
+ linter
+ parsing
+ 0.95.0
+ at the time of writing this
+ »»»
+
+ Result (German):
+
+ «««
+ Pfad
+ Linter
+ Parsen
+ 0.95.0
+ zum Zeitpunkt als das hier geschrieben wurde
+ »»»
+
+
+3) If the text surrounded by the abbr element is an abbreviation and the description contains both the full phrase for that abbreviation, and other information, separated by a colon («:»), then append a dash («–») and the translation of the full phrase to the original full phrase and translate the other information.
+
+Conversion scheme:
+
+ Source (English):
+
+ {abbreviation}
+
+ Result:
+
+ {abbreviation}
+
+Examples:
+
+ Source (English):
+
+ «««
+ I/O
+ CDN
+ IDE
+ »»»
+
+ Result (German):
+
+ «««
+ I/O
+ CDN
+ IDE
+ »»»
+
+3.1) Like in rule 2.1, you can leave the original full phrase away, if the translated full phrase is identical or starts with the same letters as the original full phrase.
+
+Conversion scheme:
+
+ Source (English):
+
+ {abbreviation}
+
+ Result:
+
+ {abbreviation}
+
+Example:
+
+ Source (English):
+
+ «««
+ ORM
+ »»»
+
+ Result (German):
+
+ «««
+ ORM
+ »»»
+
+4) If there is an existing translation, and it has ADDITIONAL abbr elements in a sentence, and these additional abbr elements do not exist in the related sentence in the English text, then KEEP those additional abbr elements in the translation. Do not remove them. Except when you remove the whole sentence from the translation, because the whole sentence was removed from the English text, then also remove the abbr element. The reasoning for this rule is, that such additional abbr elements are manually added by the human editor of the translation, in order to translate or explain an English word to the human readers of the translation. These additional abbr elements would not make sense in the English text, but they do make sense in the translation. So keep them in the translation, even though they are not part of the English text. This rule only applies to abbr elements.
+
+5) Apply above rules also when there is an existing translation! Make sure that all title attributes in abbr elements get properly translated or updated, using the schemes given above. However, leave the ADDITIONAL abbr's from rule 4 alone. Do not change their formatting or content.
+
+"""
+
+app = typer.Typer()
+
+
+@lru_cache
+def get_langs() -> dict[str, str]:
+ return yaml.safe_load(Path("docs/language_names.yml").read_text(encoding="utf-8"))
+
+
+def generate_lang_path(*, lang: str, path: Path) -> Path:
+ en_docs_path = Path("docs/en/docs")
+ assert str(path).startswith(str(en_docs_path)), (
+ f"Path must be inside {en_docs_path}"
+ )
+ lang_docs_path = Path(f"docs/{lang}/docs")
+ out_path = Path(str(path).replace(str(en_docs_path), str(lang_docs_path)))
+ return out_path
+
+
+def generate_en_path(*, lang: str, path: Path) -> Path:
+ en_docs_path = Path("docs/en/docs")
+ assert not str(path).startswith(str(en_docs_path)), (
+ f"Path must not be inside {en_docs_path}"
+ )
+ lang_docs_path = Path(f"docs/{lang}/docs")
+ out_path = Path(str(path).replace(str(lang_docs_path), str(en_docs_path)))
+ return out_path
+
+
+@app.command()
+def translate_page(
+ *,
+ language: Annotated[str, typer.Option(envvar="LANGUAGE")],
+ en_path: Annotated[Path, typer.Option(envvar="EN_PATH")],
+) -> None:
+ assert language != "en", (
+ "`en` is the source language, choose another language as translation target"
+ )
+ langs = get_langs()
+ language_name = langs[language]
+ lang_path = Path(f"docs/{language}")
+ lang_path.mkdir(exist_ok=True)
+ lang_prompt_path = lang_path / "llm-prompt.md"
+ assert lang_prompt_path.exists(), f"Prompt file not found: {lang_prompt_path}"
+ lang_prompt_content = lang_prompt_path.read_text(encoding="utf-8")
+
+ en_docs_path = Path("docs/en/docs")
+ assert str(en_path).startswith(str(en_docs_path)), (
+ f"Path must be inside {en_docs_path}"
+ )
+ out_path = generate_lang_path(lang=language, path=en_path)
+ out_path.parent.mkdir(parents=True, exist_ok=True)
+ original_content = en_path.read_text(encoding="utf-8")
+ old_translation: str | None = None
+ if out_path.exists():
+ print(f"Found existing translation: {out_path}")
+ old_translation = out_path.read_text(encoding="utf-8")
+ print(f"Translating {en_path} to {language} ({language_name})")
+ agent = Agent("openai:gpt-5")
+
+ prompt_segments = [
+ general_prompt,
+ lang_prompt_content,
+ ]
+ if old_translation:
+ prompt_segments.extend(
+ [
+ "There is an existing previous translation for the original English content, that may be outdated.",
+ "Update the translation only where necessary:",
+ "- If the original English content has added parts, also add these parts to the translation.",
+ "- If the original English content has removed parts, also remove them from the translation, unless you were instructed earlier to not do that in specific cases.",
+ "- If parts of the original English content have changed, also change those parts in the translation.",
+ "- If the previous translation violates current instructions, update it.",
+ "- Otherwise, preserve the original translation LINE-BY-LINE, AS-IS.",
+ "Do not:",
+ "- rephrase or rewrite correct lines just to improve the style.",
+ "- add or remove line breaks, unless the original English content changed.",
+ "- change formatting or whitespace unless absolutely required.",
+ "Only change what must be changed. The goal is to minimize diffs for easier human review.",
+ "UNLESS you were instructed earlier to behave different, there MUST NOT be whole sentences or partial sentences in the updated translation, which are not in the original English content, and there MUST NOT be whole sentences or partial sentences in the original English content, which are not in the updated translation. Remember: the updated translation shall be IN SYNC with the original English content.",
+ "Previous translation:",
+ f"%%%\n{old_translation}%%%",
+ ]
+ )
+ prompt_segments.extend(
+ [
+ f"Translate to {language} ({language_name}).",
+ "Original content:",
+ f"%%%\n{original_content}%%%",
+ ]
+ )
+ prompt = "\n\n".join(prompt_segments)
+ print(f"Running agent for {out_path}")
+ result = agent.run_sync(prompt)
+ out_content = f"{result.output.strip()}\n"
+ print(f"Saving translation to {out_path}")
+ out_path.write_text(out_content, encoding="utf-8", newline="\n")
+
+
+def iter_all_en_paths() -> Iterable[Path]:
+ """
+ Iterate on the markdown files to translate in order of priority.
+ """
+ first_dirs = [
+ Path("docs/en/docs/learn"),
+ Path("docs/en/docs/tutorial"),
+ Path("docs/en/docs/advanced"),
+ Path("docs/en/docs/about"),
+ Path("docs/en/docs/how-to"),
+ ]
+ first_parent = Path("docs/en/docs")
+ yield from first_parent.glob("*.md")
+ for dir_path in first_dirs:
+ yield from dir_path.rglob("*.md")
+ first_dirs_str = tuple(str(d) for d in first_dirs)
+ for path in Path("docs/en/docs").rglob("*.md"):
+ if str(path).startswith(first_dirs_str):
+ continue
+ if path.parent == first_parent:
+ continue
+ yield path
+
+
+def iter_en_paths_to_translate() -> Iterable[Path]:
+ en_docs_root = Path("docs/en/docs/")
+ for path in iter_all_en_paths():
+ relpath = path.relative_to(en_docs_root)
+ if not str(relpath).startswith(non_translated_sections):
+ yield path
+
+
+@app.command()
+def translate_lang(language: Annotated[str, typer.Option(envvar="LANGUAGE")]) -> None:
+ paths_to_process = list(iter_en_paths_to_translate())
+ print("Original paths:")
+ for p in paths_to_process:
+ print(f" - {p}")
+ print(f"Total original paths: {len(paths_to_process)}")
+ missing_paths: list[Path] = []
+ skipped_paths: list[Path] = []
+ for p in paths_to_process:
+ lang_path = generate_lang_path(lang=language, path=p)
+ if lang_path.exists():
+ skipped_paths.append(p)
+ continue
+ missing_paths.append(p)
+ print("Paths to skip:")
+ for p in skipped_paths:
+ print(f" - {p}")
+ print(f"Total paths to skip: {len(skipped_paths)}")
+ print("Paths to process:")
+ for p in missing_paths:
+ print(f" - {p}")
+ print(f"Total paths to process: {len(missing_paths)}")
+ for p in missing_paths:
+ print(f"Translating: {p}")
+ translate_page(language="es", en_path=p)
+ print(f"Done translating: {p}")
+
+
+@app.command()
+def list_removable(language: str) -> list[Path]:
+ removable_paths: list[Path] = []
+ lang_paths = Path(f"docs/{language}").rglob("*.md")
+ for path in lang_paths:
+ en_path = generate_en_path(lang=language, path=path)
+ if not en_path.exists():
+ removable_paths.append(path)
+ print(removable_paths)
+ return removable_paths
+
+
+@app.command()
+def list_all_removable() -> list[Path]:
+ all_removable_paths: list[Path] = []
+ langs = get_langs()
+ for lang in langs:
+ if lang == "en":
+ continue
+ removable_paths = list_removable(lang)
+ all_removable_paths.extend(removable_paths)
+ print(all_removable_paths)
+ return all_removable_paths
+
+
+@app.command()
+def remove_removable(language: str) -> None:
+ removable_paths = list_removable(language)
+ for path in removable_paths:
+ path.unlink()
+ print(f"Removed: {path}")
+ print("Done removing all removable paths")
+
+
+@app.command()
+def remove_all_removable() -> None:
+ all_removable = list_all_removable()
+ for removable_path in all_removable:
+ removable_path.unlink()
+ print(f"Removed: {removable_path}")
+ print("Done removing all removable paths")
+
+
+@app.command()
+def list_missing(language: str) -> list[Path]:
+ missing_paths: list[Path] = []
+ en_lang_paths = list(iter_en_paths_to_translate())
+ for path in en_lang_paths:
+ lang_path = generate_lang_path(lang=language, path=path)
+ if not lang_path.exists():
+ missing_paths.append(path)
+ print(missing_paths)
+ return missing_paths
+
+
+@app.command()
+def list_outdated(language: str) -> list[Path]:
+ dir_path = Path(__file__).absolute().parent.parent
+ repo = git.Repo(dir_path)
+
+ outdated_paths: list[Path] = []
+ en_lang_paths = list(iter_en_paths_to_translate())
+ for path in en_lang_paths:
+ lang_path = generate_lang_path(lang=language, path=path)
+ if not lang_path.exists():
+ continue
+ en_commit_datetime = list(repo.iter_commits(paths=path, max_count=1))[
+ 0
+ ].committed_datetime
+ lang_commit_datetime = list(repo.iter_commits(paths=lang_path, max_count=1))[
+ 0
+ ].committed_datetime
+ if lang_commit_datetime < en_commit_datetime:
+ outdated_paths.append(path)
+ print(outdated_paths)
+ return outdated_paths
+
+
+@app.command()
+def update_outdated(language: Annotated[str, typer.Option(envvar="LANGUAGE")]) -> None:
+ outdated_paths = list_outdated(language)
+ for path in outdated_paths:
+ print(f"Updating lang: {language} path: {path}")
+ translate_page(language=language, en_path=path)
+ print(f"Done updating: {path}")
+ print("Done updating all outdated paths")
+
+
+@app.command()
+def add_missing(language: Annotated[str, typer.Option(envvar="LANGUAGE")]) -> None:
+ missing_paths = list_missing(language)
+ for path in missing_paths:
+ print(f"Adding lang: {language} path: {path}")
+ translate_page(language=language, en_path=path)
+ print(f"Done adding: {path}")
+ print("Done adding all missing paths")
+
+
+@app.command()
+def update_and_add(language: Annotated[str, typer.Option(envvar="LANGUAGE")]) -> None:
+ print(f"Updating outdated translations for {language}")
+ update_outdated(language=language)
+ print(f"Adding missing translations for {language}")
+ add_missing(language=language)
+ print(f"Done updating and adding for {language}")
+
+
+@app.command()
+def make_pr(
+ *,
+ language: Annotated[str | None, typer.Option(envvar="LANGUAGE")] = None,
+ github_token: Annotated[str, typer.Option(envvar="GITHUB_TOKEN")],
+ github_repository: Annotated[str, typer.Option(envvar="GITHUB_REPOSITORY")],
+) -> None:
+ print("Setting up GitHub Actions git user")
+ repo = git.Repo(Path(__file__).absolute().parent.parent)
+ if not repo.is_dirty(untracked_files=True):
+ print("Repository is clean, no changes to commit")
+ return
+ subprocess.run(["git", "config", "user.name", "github-actions"], check=True)
+ subprocess.run(
+ ["git", "config", "user.email", "github-actions@github.com"], check=True
+ )
+ branch_name = "translate"
+ if language:
+ branch_name += f"-{language}"
+ branch_name += f"-{secrets.token_hex(4)}"
+ print(f"Creating a new branch {branch_name}")
+ subprocess.run(["git", "checkout", "-b", branch_name], check=True)
+ print("Adding updated files")
+ git_path = Path("docs")
+ subprocess.run(["git", "add", str(git_path)], check=True)
+ print("Committing updated file")
+ message = "🌐 Update translations"
+ if language:
+ message += f" for {language}"
+ subprocess.run(["git", "commit", "-m", message], check=True)
+ print("Pushing branch")
+ subprocess.run(["git", "push", "origin", branch_name], check=True)
+ print("Creating PR")
+ g = Github(github_token)
+ gh_repo = g.get_repo(github_repository)
+ pr = gh_repo.create_pull(
+ title=message, body=message, base="master", head=branch_name
+ )
+ print(f"Created PR: {pr.number}")
+ print("Finished")
+
+
+if __name__ == "__main__":
+ app()
diff --git a/tests/main.py b/tests/main.py
index 6927eab61b..2f1d617115 100644
--- a/tests/main.py
+++ b/tests/main.py
@@ -3,7 +3,12 @@ from typing import FrozenSet, List, Optional
from fastapi import FastAPI, Path, Query
-app = FastAPI()
+external_docs = {
+ "description": "External API documentation.",
+ "url": "https://docs.example.com/api-general",
+}
+
+app = FastAPI(openapi_external_docs=external_docs)
@app.api_route("/api_route")
diff --git a/tests/test_application.py b/tests/test_application.py
index 5c62f5f6e2..8f1b0a18d3 100644
--- a/tests/test_application.py
+++ b/tests/test_application.py
@@ -43,7 +43,7 @@ def test_redoc():
response = client.get("/redoc")
assert response.status_code == 200, response.text
assert response.headers["content-type"] == "text/html; charset=utf-8"
- assert "redoc@next" in response.text
+ assert "redoc@2" in response.text
def test_enum_status_code_response():
@@ -58,6 +58,10 @@ def test_openapi_schema():
assert response.json() == {
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
+ "externalDocs": {
+ "description": "External API documentation.",
+ "url": "https://docs.example.com/api-general",
+ },
"paths": {
"/api_route": {
"get": {
diff --git a/tests/test_compat.py b/tests/test_compat.py
index bf268b860b..0184c9a2ee 100644
--- a/tests/test_compat.py
+++ b/tests/test_compat.py
@@ -1,51 +1,49 @@
-from typing import List, Union
+from typing import Any, Dict, List, Union
from fastapi import FastAPI, UploadFile
from fastapi._compat import (
- ModelField,
Undefined,
_get_model_config,
- is_bytes_sequence_annotation,
+ get_cached_model_fields,
+ is_scalar_field,
is_uploadfile_sequence_annotation,
+ may_v1,
)
+from fastapi._compat.shared import is_bytes_sequence_annotation
from fastapi.testclient import TestClient
-from pydantic import BaseConfig, BaseModel, ConfigDict
+from pydantic import BaseModel, ConfigDict
from pydantic.fields import FieldInfo
-from .utils import needs_pydanticv1, needs_pydanticv2
+from .utils import needs_py_lt_314, needs_pydanticv2
@needs_pydanticv2
def test_model_field_default_required():
+ from fastapi._compat import v2
+
# For coverage
field_info = FieldInfo(annotation=str)
- field = ModelField(name="foo", field_info=field_info)
+ field = v2.ModelField(name="foo", field_info=field_info)
assert field.default is Undefined
-@needs_pydanticv1
-def test_upload_file_dummy_with_info_plain_validator_function():
+@needs_py_lt_314
+def test_v1_plain_validator_function():
+ from fastapi._compat import v1
+
# For coverage
- assert UploadFile.__get_pydantic_core_schema__(str, lambda x: None) == {}
+ def func(v): # pragma: no cover
+ return v
+
+ result = v1.with_info_plain_validator_function(func)
+ assert result == {}
-@needs_pydanticv1
-def test_union_scalar_list():
+def test_is_model_field():
# For coverage
- # TODO: there might not be a current valid code path that uses this, it would
- # potentially enable query parameters defined as both a scalar and a list
- # but that would require more refactors, also not sure it's really useful
- from fastapi._compat import is_pv1_scalar_field
+ from fastapi._compat import _is_model_field
- field_info = FieldInfo()
- field = ModelField(
- name="foo",
- field_info=field_info,
- type_=Union[str, List[int]],
- class_validators={},
- model_config=BaseConfig,
- )
- assert not is_pv1_scalar_field(field)
+ assert not _is_model_field(str)
@needs_pydanticv2
@@ -77,6 +75,51 @@ def test_complex():
assert response2.json() == [1, 2]
+@needs_pydanticv2
+def test_propagates_pydantic2_model_config():
+ app = FastAPI()
+
+ class Missing:
+ def __bool__(self):
+ return False
+
+ class EmbeddedModel(BaseModel):
+ model_config = ConfigDict(arbitrary_types_allowed=True)
+ value: Union[str, Missing] = Missing()
+
+ class Model(BaseModel):
+ model_config = ConfigDict(
+ arbitrary_types_allowed=True,
+ )
+ value: Union[str, Missing] = Missing()
+ embedded_model: EmbeddedModel = EmbeddedModel()
+
+ @app.post("/")
+ def foo(req: Model) -> Dict[str, Union[str, None]]:
+ return {
+ "value": req.value or None,
+ "embedded_value": req.embedded_model.value or None,
+ }
+
+ client = TestClient(app)
+
+ response = client.post("/", json={})
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "value": None,
+ "embedded_value": None,
+ }
+
+ response2 = client.post(
+ "/", json={"value": "foo", "embedded_model": {"value": "bar"}}
+ )
+ assert response2.status_code == 200, response2.text
+ assert response2.json() == {
+ "value": "foo",
+ "embedded_value": "bar",
+ }
+
+
def test_is_bytes_sequence_annotation_union():
# For coverage
# TODO: in theory this would allow declaring types that could be lists of bytes
@@ -91,3 +134,33 @@ def test_is_uploadfile_sequence_annotation():
# and other types, but I'm not even sure it's a good idea to support it as a first
# class "feature"
assert is_uploadfile_sequence_annotation(Union[List[str], List[UploadFile]])
+
+
+@needs_py_lt_314
+def test_is_pv1_scalar_field():
+ from fastapi._compat import v1
+
+ # For coverage
+ class Model(v1.BaseModel):
+ foo: Union[str, Dict[str, Any]]
+
+ fields = v1.get_model_fields(Model)
+ assert not is_scalar_field(fields[0])
+
+
+@needs_py_lt_314
+def test_get_model_fields_cached():
+ from fastapi._compat import v1
+
+ class Model(may_v1.BaseModel):
+ foo: str
+
+ non_cached_fields = v1.get_model_fields(Model)
+ non_cached_fields2 = v1.get_model_fields(Model)
+ cached_fields = get_cached_model_fields(Model)
+ cached_fields2 = get_cached_model_fields(Model)
+ for f1, f2 in zip(cached_fields, cached_fields2):
+ assert f1 is f2
+
+ assert non_cached_fields is not non_cached_fields2
+ assert cached_fields is cached_fields2
diff --git a/tests/test_compat_params_v1.py b/tests/test_compat_params_v1.py
new file mode 100644
index 0000000000..7064761cb5
--- /dev/null
+++ b/tests/test_compat_params_v1.py
@@ -0,0 +1,1122 @@
+import sys
+from typing import List, Optional
+
+import pytest
+
+from tests.utils import pydantic_snapshot, skip_module_if_py_gte_314
+
+if sys.version_info >= (3, 14):
+ skip_module_if_py_gte_314()
+
+from fastapi import FastAPI
+from fastapi._compat.v1 import BaseModel
+from fastapi.temp_pydantic_v1_params import (
+ Body,
+ Cookie,
+ File,
+ Form,
+ Header,
+ Path,
+ Query,
+)
+from fastapi.testclient import TestClient
+from inline_snapshot import snapshot
+from typing_extensions import Annotated
+
+
+class Item(BaseModel):
+ name: str
+ price: float
+ description: Optional[str] = None
+
+
+app = FastAPI()
+
+
+@app.get("/items/{item_id}")
+def get_item_with_path(
+ item_id: Annotated[int, Path(title="The ID of the item", ge=1, le=1000)],
+):
+ return {"item_id": item_id}
+
+
+@app.get("/items/")
+def get_items_with_query(
+ q: Annotated[
+ Optional[str], Query(min_length=3, max_length=50, pattern="^[a-zA-Z0-9 ]+$")
+ ] = None,
+ skip: Annotated[int, Query(ge=0)] = 0,
+ limit: Annotated[int, Query(ge=1, le=100, examples=[5])] = 10,
+):
+ return {"q": q, "skip": skip, "limit": limit}
+
+
+@app.get("/users/")
+def get_user_with_header(
+ x_custom: Annotated[Optional[str], Header()] = None,
+ x_token: Annotated[Optional[str], Header(convert_underscores=True)] = None,
+):
+ return {"x_custom": x_custom, "x_token": x_token}
+
+
+@app.get("/cookies/")
+def get_cookies(
+ session_id: Annotated[Optional[str], Cookie()] = None,
+ tracking_id: Annotated[Optional[str], Cookie(min_length=10)] = None,
+):
+ return {"session_id": session_id, "tracking_id": tracking_id}
+
+
+@app.post("/items/")
+def create_item(
+ item: Annotated[
+ Item,
+ Body(examples=[{"name": "Foo", "price": 35.4, "description": "The Foo item"}]),
+ ],
+):
+ return {"item": item}
+
+
+@app.post("/items-embed/")
+def create_item_embed(
+ item: Annotated[Item, Body(embed=True)],
+):
+ return {"item": item}
+
+
+@app.put("/items/{item_id}")
+def update_item(
+ item_id: Annotated[int, Path(ge=1)],
+ item: Annotated[Item, Body()],
+ importance: Annotated[int, Body(gt=0, le=10)],
+):
+ return {"item": item, "importance": importance}
+
+
+@app.post("/form-data/")
+def submit_form(
+ username: Annotated[str, Form(min_length=3, max_length=50)],
+ password: Annotated[str, Form(min_length=8)],
+ email: Annotated[Optional[str], Form()] = None,
+):
+ return {"username": username, "password": password, "email": email}
+
+
+@app.post("/upload/")
+def upload_file(
+ file: Annotated[bytes, File()],
+ description: Annotated[Optional[str], Form()] = None,
+):
+ return {"file_size": len(file), "description": description}
+
+
+@app.post("/upload-multiple/")
+def upload_multiple_files(
+ files: Annotated[List[bytes], File()],
+ note: Annotated[str, Form()] = "",
+):
+ return {
+ "file_count": len(files),
+ "total_size": sum(len(f) for f in files),
+ "note": note,
+ }
+
+
+client = TestClient(app)
+
+
+# Path parameter tests
+def test_path_param_valid():
+ response = client.get("/items/50")
+ assert response.status_code == 200
+ assert response.json() == {"item_id": 50}
+
+
+def test_path_param_too_large():
+ response = client.get("/items/1001")
+ assert response.status_code == 422
+ error = response.json()["detail"][0]
+ assert error["loc"] == ["path", "item_id"]
+
+
+def test_path_param_too_small():
+ response = client.get("/items/0")
+ assert response.status_code == 422
+ error = response.json()["detail"][0]
+ assert error["loc"] == ["path", "item_id"]
+
+
+# Query parameter tests
+def test_query_params_valid():
+ response = client.get("/items/?q=test search&skip=5&limit=20")
+ assert response.status_code == 200
+ assert response.json() == {"q": "test search", "skip": 5, "limit": 20}
+
+
+def test_query_params_defaults():
+ response = client.get("/items/")
+ assert response.status_code == 200
+ assert response.json() == {"q": None, "skip": 0, "limit": 10}
+
+
+def test_query_param_too_short():
+ response = client.get("/items/?q=ab")
+ assert response.status_code == 422
+ error = response.json()["detail"][0]
+ assert error["loc"] == ["query", "q"]
+
+
+def test_query_param_invalid_pattern():
+ response = client.get("/items/?q=test@#$")
+ assert response.status_code == 422
+ error = response.json()["detail"][0]
+ assert error["loc"] == ["query", "q"]
+
+
+def test_query_param_limit_too_large():
+ response = client.get("/items/?limit=101")
+ assert response.status_code == 422
+ error = response.json()["detail"][0]
+ assert error["loc"] == ["query", "limit"]
+
+
+# Header parameter tests
+def test_header_params():
+ response = client.get(
+ "/users/",
+ headers={"X-Custom": "Plumbus", "X-Token": "secret-token"},
+ )
+ assert response.status_code == 200
+ assert response.json() == {
+ "x_custom": "Plumbus",
+ "x_token": "secret-token",
+ }
+
+
+def test_header_underscore_conversion():
+ response = client.get(
+ "/users/",
+ headers={"x-token": "secret-token-with-dash"},
+ )
+ assert response.status_code == 200
+ assert response.json()["x_token"] == "secret-token-with-dash"
+
+
+def test_header_params_none():
+ response = client.get("/users/")
+ assert response.status_code == 200
+ assert response.json() == {"x_custom": None, "x_token": None}
+
+
+# Cookie parameter tests
+def test_cookie_params():
+ with TestClient(app) as client:
+ client.cookies.set("session_id", "abc123")
+ client.cookies.set("tracking_id", "1234567890abcdef")
+ response = client.get("/cookies/")
+ assert response.status_code == 200
+ assert response.json() == {
+ "session_id": "abc123",
+ "tracking_id": "1234567890abcdef",
+ }
+
+
+def test_cookie_tracking_id_too_short():
+ with TestClient(app) as client:
+ client.cookies.set("tracking_id", "short")
+ response = client.get("/cookies/")
+ assert response.status_code == 422
+ assert response.json() == snapshot(
+ {
+ "detail": [
+ {
+ "loc": ["cookie", "tracking_id"],
+ "msg": "ensure this value has at least 10 characters",
+ "type": "value_error.any_str.min_length",
+ "ctx": {"limit_value": 10},
+ }
+ ]
+ }
+ )
+
+
+def test_cookie_params_none():
+ response = client.get("/cookies/")
+ assert response.status_code == 200
+ assert response.json() == {"session_id": None, "tracking_id": None}
+
+
+# Body parameter tests
+def test_body_param():
+ response = client.post(
+ "/items/",
+ json={"name": "Test Item", "price": 29.99, "description": "A test item"},
+ )
+ assert response.status_code == 200
+ assert response.json() == {
+ "item": {
+ "name": "Test Item",
+ "price": 29.99,
+ "description": "A test item",
+ }
+ }
+
+
+def test_body_param_minimal():
+ response = client.post(
+ "/items/",
+ json={"name": "Minimal", "price": 9.99},
+ )
+ assert response.status_code == 200
+ assert response.json() == {
+ "item": {"name": "Minimal", "price": 9.99, "description": None}
+ }
+
+
+def test_body_param_missing_required():
+ response = client.post(
+ "/items/",
+ json={"name": "Incomplete"},
+ )
+ assert response.status_code == 422
+ error = response.json()["detail"][0]
+ assert error["loc"] == ["body", "price"]
+
+
+def test_body_embed():
+ response = client.post(
+ "/items-embed/",
+ json={"item": {"name": "Embedded", "price": 15.0}},
+ )
+ assert response.status_code == 200
+ assert response.json() == {
+ "item": {"name": "Embedded", "price": 15.0, "description": None}
+ }
+
+
+def test_body_embed_wrong_structure():
+ response = client.post(
+ "/items-embed/",
+ json={"name": "Not Embedded", "price": 15.0},
+ )
+ assert response.status_code == 422
+
+
+# Multiple body parameters test
+def test_multiple_body_params():
+ response = client.put(
+ "/items/5",
+ json={
+ "item": {"name": "Updated Item", "price": 49.99},
+ "importance": 8,
+ },
+ )
+ assert response.status_code == 200
+ assert response.json() == snapshot(
+ {
+ "item": {"name": "Updated Item", "price": 49.99, "description": None},
+ "importance": 8,
+ }
+ )
+
+
+def test_multiple_body_params_importance_too_large():
+ response = client.put(
+ "/items/5",
+ json={
+ "item": {"name": "Item", "price": 10.0},
+ "importance": 11,
+ },
+ )
+ assert response.status_code == 422
+ assert response.json() == snapshot(
+ {
+ "detail": [
+ {
+ "loc": ["body", "importance"],
+ "msg": "ensure this value is less than or equal to 10",
+ "type": "value_error.number.not_le",
+ "ctx": {"limit_value": 10},
+ }
+ ]
+ }
+ )
+
+
+def test_multiple_body_params_importance_too_small():
+ response = client.put(
+ "/items/5",
+ json={
+ "item": {"name": "Item", "price": 10.0},
+ "importance": 0,
+ },
+ )
+ assert response.status_code == 422
+ assert response.json() == snapshot(
+ {
+ "detail": [
+ {
+ "loc": ["body", "importance"],
+ "msg": "ensure this value is greater than 0",
+ "type": "value_error.number.not_gt",
+ "ctx": {"limit_value": 0},
+ }
+ ]
+ }
+ )
+
+
+# Form parameter tests
+def test_form_data_valid():
+ response = client.post(
+ "/form-data/",
+ data={
+ "username": "testuser",
+ "password": "password123",
+ "email": "test@example.com",
+ },
+ )
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "username": "testuser",
+ "password": "password123",
+ "email": "test@example.com",
+ }
+
+
+def test_form_data_optional_field():
+ response = client.post(
+ "/form-data/",
+ data={"username": "testuser", "password": "password123"},
+ )
+ assert response.status_code == 200
+ assert response.json() == {
+ "username": "testuser",
+ "password": "password123",
+ "email": None,
+ }
+
+
+def test_form_data_username_too_short():
+ response = client.post(
+ "/form-data/",
+ data={"username": "ab", "password": "password123"},
+ )
+ assert response.status_code == 422
+ assert response.json() == snapshot(
+ {
+ "detail": [
+ {
+ "loc": ["body", "username"],
+ "msg": "ensure this value has at least 3 characters",
+ "type": "value_error.any_str.min_length",
+ "ctx": {"limit_value": 3},
+ }
+ ]
+ }
+ )
+
+
+def test_form_data_password_too_short():
+ response = client.post(
+ "/form-data/",
+ data={"username": "testuser", "password": "short"},
+ )
+ assert response.status_code == 422
+ assert response.json() == snapshot(
+ {
+ "detail": [
+ {
+ "loc": ["body", "password"],
+ "msg": "ensure this value has at least 8 characters",
+ "type": "value_error.any_str.min_length",
+ "ctx": {"limit_value": 8},
+ }
+ ]
+ }
+ )
+
+
+# File upload tests
+def test_upload_file():
+ response = client.post(
+ "/upload/",
+ files={"file": ("test.txt", b"Hello, World!", "text/plain")},
+ data={"description": "A test file"},
+ )
+ assert response.status_code == 200
+ assert response.json() == {
+ "file_size": 13,
+ "description": "A test file",
+ }
+
+
+def test_upload_file_without_description():
+ response = client.post(
+ "/upload/",
+ files={"file": ("test.txt", b"Hello!", "text/plain")},
+ )
+ assert response.status_code == 200
+ assert response.json() == {
+ "file_size": 6,
+ "description": None,
+ }
+
+
+def test_upload_multiple_files():
+ response = client.post(
+ "/upload-multiple/",
+ files=[
+ ("files", ("file1.txt", b"Content 1", "text/plain")),
+ ("files", ("file2.txt", b"Content 2", "text/plain")),
+ ("files", ("file3.txt", b"Content 3", "text/plain")),
+ ],
+ data={"note": "Multiple files uploaded"},
+ )
+ assert response.status_code == 200
+ assert response.json() == {
+ "file_count": 3,
+ "total_size": 27,
+ "note": "Multiple files uploaded",
+ }
+
+
+def test_upload_multiple_files_empty_note():
+ response = client.post(
+ "/upload-multiple/",
+ files=[
+ ("files", ("file1.txt", b"Test", "text/plain")),
+ ],
+ )
+ assert response.status_code == 200
+ assert response.json()["file_count"] == 1
+ assert response.json()["note"] == ""
+
+
+# __repr__ tests
+def test_query_repr():
+ query_param = Query(default=None, min_length=3)
+ assert repr(query_param) == "Query(None)"
+
+
+def test_body_repr():
+ body_param = Body(default=None)
+ assert repr(body_param) == "Body(None)"
+
+
+# Deprecation warning tests for regex parameter
+def test_query_regex_deprecation_warning():
+ with pytest.warns(DeprecationWarning, match="`regex` has been deprecated"):
+ Query(regex="^test$")
+
+
+def test_body_regex_deprecation_warning():
+ with pytest.warns(DeprecationWarning, match="`regex` has been deprecated"):
+ Body(regex="^test$")
+
+
+# Deprecation warning tests for example parameter
+def test_query_example_deprecation_warning():
+ with pytest.warns(DeprecationWarning, match="`example` has been deprecated"):
+ Query(example="test example")
+
+
+def test_body_example_deprecation_warning():
+ with pytest.warns(DeprecationWarning, match="`example` has been deprecated"):
+ Body(example={"test": "example"})
+
+
+def test_openapi_schema():
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == snapshot(
+ {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/items/{item_id}": {
+ "get": {
+ "summary": "Get Item With Path",
+ "operationId": "get_item_with_path_items__item_id__get",
+ "parameters": [
+ {
+ "name": "item_id",
+ "in": "path",
+ "required": True,
+ "schema": {
+ "title": "The ID of the item",
+ "minimum": 1,
+ "maximum": 1000,
+ "type": "integer",
+ },
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ },
+ "put": {
+ "summary": "Update Item",
+ "operationId": "update_item_items__item_id__put",
+ "parameters": [
+ {
+ "name": "item_id",
+ "in": "path",
+ "required": True,
+ "schema": {
+ "title": "Item Id",
+ "minimum": 1,
+ "type": "integer",
+ },
+ }
+ ],
+ "requestBody": {
+ "required": True,
+ "content": {
+ "application/json": {
+ "schema": pydantic_snapshot(
+ v1=snapshot(
+ {
+ "$ref": "#/components/schemas/Body_update_item_items__item_id__put"
+ }
+ ),
+ v2=snapshot(
+ {
+ "title": "Body",
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/Body_update_item_items__item_id__put"
+ }
+ ],
+ }
+ ),
+ ),
+ }
+ },
+ },
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ },
+ },
+ "/items/": {
+ "get": {
+ "summary": "Get Items With Query",
+ "operationId": "get_items_with_query_items__get",
+ "parameters": [
+ {
+ "name": "q",
+ "in": "query",
+ "required": False,
+ "schema": {
+ "title": "Q",
+ "maxLength": 50,
+ "minLength": 3,
+ "pattern": "^[a-zA-Z0-9 ]+$",
+ "type": "string",
+ },
+ },
+ {
+ "name": "skip",
+ "in": "query",
+ "required": False,
+ "schema": {
+ "title": "Skip",
+ "default": 0,
+ "minimum": 0,
+ "type": "integer",
+ },
+ },
+ {
+ "name": "limit",
+ "in": "query",
+ "required": False,
+ "schema": {
+ "title": "Limit",
+ "default": 10,
+ "minimum": 1,
+ "maximum": 100,
+ "examples": [5],
+ "type": "integer",
+ },
+ },
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ },
+ "post": {
+ "summary": "Create Item",
+ "operationId": "create_item_items__post",
+ "requestBody": {
+ "required": True,
+ "content": {
+ "application/json": {
+ "schema": {
+ "title": "Item",
+ "examples": [
+ {
+ "name": "Foo",
+ "price": 35.4,
+ "description": "The Foo item",
+ }
+ ],
+ "allOf": [
+ {"$ref": "#/components/schemas/Item"}
+ ],
+ }
+ }
+ },
+ },
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ },
+ },
+ "/users/": {
+ "get": {
+ "summary": "Get User With Header",
+ "operationId": "get_user_with_header_users__get",
+ "parameters": [
+ {
+ "name": "x-custom",
+ "in": "header",
+ "required": False,
+ "schema": {"title": "X-Custom", "type": "string"},
+ },
+ {
+ "name": "x-token",
+ "in": "header",
+ "required": False,
+ "schema": {"title": "X-Token", "type": "string"},
+ },
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ }
+ },
+ "/cookies/": {
+ "get": {
+ "summary": "Get Cookies",
+ "operationId": "get_cookies_cookies__get",
+ "parameters": [
+ {
+ "name": "session_id",
+ "in": "cookie",
+ "required": False,
+ "schema": {"title": "Session Id", "type": "string"},
+ },
+ {
+ "name": "tracking_id",
+ "in": "cookie",
+ "required": False,
+ "schema": {
+ "title": "Tracking Id",
+ "minLength": 10,
+ "type": "string",
+ },
+ },
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ }
+ },
+ "/items-embed/": {
+ "post": {
+ "summary": "Create Item Embed",
+ "operationId": "create_item_embed_items_embed__post",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": pydantic_snapshot(
+ v1=snapshot(
+ {
+ "$ref": "#/components/schemas/Body_create_item_embed_items_embed__post"
+ }
+ ),
+ v2=snapshot(
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/Body_create_item_embed_items_embed__post"
+ }
+ ],
+ "title": "Body",
+ }
+ ),
+ ),
+ }
+ },
+ "required": True,
+ },
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ }
+ },
+ "/form-data/": {
+ "post": {
+ "summary": "Submit Form",
+ "operationId": "submit_form_form_data__post",
+ "requestBody": {
+ "content": {
+ "application/x-www-form-urlencoded": {
+ "schema": pydantic_snapshot(
+ v1=snapshot(
+ {
+ "$ref": "#/components/schemas/Body_submit_form_form_data__post"
+ }
+ ),
+ v2=snapshot(
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/Body_submit_form_form_data__post"
+ }
+ ],
+ "title": "Body",
+ }
+ ),
+ ),
+ }
+ },
+ "required": True,
+ },
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ }
+ },
+ "/upload/": {
+ "post": {
+ "summary": "Upload File",
+ "operationId": "upload_file_upload__post",
+ "requestBody": {
+ "content": {
+ "multipart/form-data": {
+ "schema": pydantic_snapshot(
+ v1=snapshot(
+ {
+ "$ref": "#/components/schemas/Body_upload_file_upload__post"
+ }
+ ),
+ v2=snapshot(
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/Body_upload_file_upload__post"
+ }
+ ],
+ "title": "Body",
+ }
+ ),
+ ),
+ }
+ },
+ "required": True,
+ },
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ }
+ },
+ "/upload-multiple/": {
+ "post": {
+ "summary": "Upload Multiple Files",
+ "operationId": "upload_multiple_files_upload_multiple__post",
+ "requestBody": {
+ "content": {
+ "multipart/form-data": {
+ "schema": pydantic_snapshot(
+ v1=snapshot(
+ {
+ "$ref": "#/components/schemas/Body_upload_multiple_files_upload_multiple__post"
+ }
+ ),
+ v2=snapshot(
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/Body_upload_multiple_files_upload_multiple__post"
+ }
+ ],
+ "title": "Body",
+ }
+ ),
+ ),
+ }
+ },
+ "required": True,
+ },
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ }
+ },
+ },
+ "components": {
+ "schemas": {
+ "Body_create_item_embed_items_embed__post": {
+ "properties": pydantic_snapshot(
+ v1=snapshot(
+ {"item": {"$ref": "#/components/schemas/Item"}}
+ ),
+ v2=snapshot(
+ {
+ "item": {
+ "allOf": [
+ {"$ref": "#/components/schemas/Item"}
+ ],
+ "title": "Item",
+ }
+ }
+ ),
+ ),
+ "type": "object",
+ "required": ["item"],
+ "title": "Body_create_item_embed_items_embed__post",
+ },
+ "Body_submit_form_form_data__post": {
+ "properties": {
+ "username": {
+ "type": "string",
+ "maxLength": 50,
+ "minLength": 3,
+ "title": "Username",
+ },
+ "password": {
+ "type": "string",
+ "minLength": 8,
+ "title": "Password",
+ },
+ "email": {"type": "string", "title": "Email"},
+ },
+ "type": "object",
+ "required": ["username", "password"],
+ "title": "Body_submit_form_form_data__post",
+ },
+ "Body_update_item_items__item_id__put": {
+ "properties": {
+ "item": pydantic_snapshot(
+ v1=snapshot({"$ref": "#/components/schemas/Item"}),
+ v2=snapshot(
+ {
+ "allOf": [
+ {"$ref": "#/components/schemas/Item"}
+ ],
+ "title": "Item",
+ }
+ ),
+ ),
+ "importance": {
+ "type": "integer",
+ "maximum": 10.0,
+ "exclusiveMinimum": 0.0,
+ "title": "Importance",
+ },
+ },
+ "type": "object",
+ "required": ["item", "importance"],
+ "title": "Body_update_item_items__item_id__put",
+ },
+ "Body_upload_file_upload__post": {
+ "properties": {
+ "file": {
+ "type": "string",
+ "format": "binary",
+ "title": "File",
+ },
+ "description": {"type": "string", "title": "Description"},
+ },
+ "type": "object",
+ "required": ["file"],
+ "title": "Body_upload_file_upload__post",
+ },
+ "Body_upload_multiple_files_upload_multiple__post": {
+ "properties": {
+ "files": {
+ "items": {"type": "string", "format": "binary"},
+ "type": "array",
+ "title": "Files",
+ },
+ "note": {"type": "string", "title": "Note", "default": ""},
+ },
+ "type": "object",
+ "required": ["files"],
+ "title": "Body_upload_multiple_files_upload_multiple__post",
+ },
+ "HTTPValidationError": {
+ "properties": {
+ "detail": {
+ "items": {
+ "$ref": "#/components/schemas/ValidationError"
+ },
+ "type": "array",
+ "title": "Detail",
+ }
+ },
+ "type": "object",
+ "title": "HTTPValidationError",
+ },
+ "Item": {
+ "properties": {
+ "name": {"type": "string", "title": "Name"},
+ "price": {"type": "number", "title": "Price"},
+ "description": {"type": "string", "title": "Description"},
+ },
+ "type": "object",
+ "required": ["name", "price"],
+ "title": "Item",
+ },
+ "ValidationError": {
+ "properties": {
+ "loc": {
+ "items": {
+ "anyOf": [{"type": "string"}, {"type": "integer"}]
+ },
+ "type": "array",
+ "title": "Location",
+ },
+ "msg": {"type": "string", "title": "Message"},
+ "type": {"type": "string", "title": "Error Type"},
+ },
+ "type": "object",
+ "required": ["loc", "msg", "type"],
+ "title": "ValidationError",
+ },
+ }
+ },
+ }
+ )
diff --git a/tests/test_computed_fields.py b/tests/test_computed_fields.py
index 5286507b2d..a1b4121688 100644
--- a/tests/test_computed_fields.py
+++ b/tests/test_computed_fields.py
@@ -24,13 +24,18 @@ def get_client():
def read_root() -> Rectangle:
return Rectangle(width=3, length=4)
+ @app.get("/responses", responses={200: {"model": Rectangle}})
+ def read_responses() -> Rectangle:
+ return Rectangle(width=3, length=4)
+
client = TestClient(app)
return client
+@pytest.mark.parametrize("path", ["/", "/responses"])
@needs_pydanticv2
-def test_get(client: TestClient):
- response = client.get("/")
+def test_get(client: TestClient, path: str):
+ response = client.get(path)
assert response.status_code == 200, response.text
assert response.json() == {"width": 3, "length": 4, "area": 12}
@@ -58,7 +63,23 @@ def test_openapi_schema(client: TestClient):
}
},
}
- }
+ },
+ "/responses": {
+ "get": {
+ "summary": "Read Responses",
+ "operationId": "read_responses_responses_get",
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {"$ref": "#/components/schemas/Rectangle"}
+ }
+ },
+ }
+ },
+ }
+ },
},
"components": {
"schemas": {
diff --git a/tests/test_custom_schema_fields.py b/tests/test_custom_schema_fields.py
index ee51fc7ff5..d890291b19 100644
--- a/tests/test_custom_schema_fields.py
+++ b/tests/test_custom_schema_fields.py
@@ -1,7 +1,13 @@
+from typing import Optional
+
from fastapi import FastAPI
from fastapi._compat import PYDANTIC_V2
from fastapi.testclient import TestClient
from pydantic import BaseModel
+from typing_extensions import Annotated
+
+if PYDANTIC_V2:
+ from pydantic import WithJsonSchema
app = FastAPI()
@@ -10,12 +16,17 @@ class Item(BaseModel):
name: str
if PYDANTIC_V2:
+ description: Annotated[
+ Optional[str], WithJsonSchema({"type": ["string", "null"]})
+ ] = None
+
model_config = {
"json_schema_extra": {
"x-something-internal": {"level": 4},
}
}
else:
+ description: Optional[str] = None # type: ignore[no-redef]
class Config:
schema_extra = {
@@ -42,7 +53,11 @@ item_schema = {
"name": {
"title": "Name",
"type": "string",
- }
+ },
+ "description": {
+ "title": "Description",
+ "type": ["string", "null"] if PYDANTIC_V2 else "string",
+ },
},
}
@@ -57,4 +72,4 @@ def test_response():
# For coverage
response = client.get("/foo")
assert response.status_code == 200, response.text
- assert response.json() == {"name": "Foo item"}
+ assert response.json() == {"name": "Foo item", "description": None}
diff --git a/tests/test_dependency_after_yield_raise.py b/tests/test_dependency_after_yield_raise.py
new file mode 100644
index 0000000000..b560dc36f9
--- /dev/null
+++ b/tests/test_dependency_after_yield_raise.py
@@ -0,0 +1,69 @@
+from typing import Any
+
+import pytest
+from fastapi import Depends, FastAPI, HTTPException
+from fastapi.testclient import TestClient
+from typing_extensions import Annotated
+
+
+class CustomError(Exception):
+ pass
+
+
+def catching_dep() -> Any:
+ try:
+ yield "s"
+ except CustomError as err:
+ raise HTTPException(status_code=418, detail="Session error") from err
+
+
+def broken_dep() -> Any:
+ yield "s"
+ raise ValueError("Broken after yield")
+
+
+app = FastAPI()
+
+
+@app.get("/catching")
+def catching(d: Annotated[str, Depends(catching_dep)]) -> Any:
+ raise CustomError("Simulated error during streaming")
+
+
+@app.get("/broken")
+def broken(d: Annotated[str, Depends(broken_dep)]) -> Any:
+ return {"message": "all good?"}
+
+
+client = TestClient(app)
+
+
+def test_catching():
+ response = client.get("/catching")
+ assert response.status_code == 418
+ assert response.json() == {"detail": "Session error"}
+
+
+def test_broken_raise():
+ with pytest.raises(ValueError, match="Broken after yield"):
+ client.get("/broken")
+
+
+def test_broken_no_raise():
+ """
+ When a dependency with yield raises after the yield (not in an except), the
+ response is already "successfully" sent back to the client, but there's still
+ an error in the server afterwards, an exception is raised and captured or shown
+ in the server logs.
+ """
+ with TestClient(app, raise_server_exceptions=False) as client:
+ response = client.get("/broken")
+ assert response.status_code == 200
+ assert response.json() == {"message": "all good?"}
+
+
+def test_broken_return_finishes():
+ client = TestClient(app, raise_server_exceptions=False)
+ response = client.get("/broken")
+ assert response.status_code == 200
+ assert response.json() == {"message": "all good?"}
diff --git a/tests/test_dependency_after_yield_streaming.py b/tests/test_dependency_after_yield_streaming.py
new file mode 100644
index 0000000000..7e1c8822b8
--- /dev/null
+++ b/tests/test_dependency_after_yield_streaming.py
@@ -0,0 +1,130 @@
+from contextlib import contextmanager
+from typing import Any, Generator
+
+import pytest
+from fastapi import Depends, FastAPI
+from fastapi.responses import StreamingResponse
+from fastapi.testclient import TestClient
+from typing_extensions import Annotated
+
+
+class Session:
+ def __init__(self) -> None:
+ self.data = ["foo", "bar", "baz"]
+ self.open = True
+
+ def __iter__(self) -> Generator[str, None, None]:
+ for item in self.data:
+ if self.open:
+ yield item
+ else:
+ raise ValueError("Session closed")
+
+
+@contextmanager
+def acquire_session() -> Generator[Session, None, None]:
+ session = Session()
+ try:
+ yield session
+ finally:
+ session.open = False
+
+
+def dep_session() -> Any:
+ with acquire_session() as s:
+ yield s
+
+
+def broken_dep_session() -> Any:
+ with acquire_session() as s:
+ s.open = False
+ yield s
+
+
+SessionDep = Annotated[Session, Depends(dep_session)]
+BrokenSessionDep = Annotated[Session, Depends(broken_dep_session)]
+
+app = FastAPI()
+
+
+@app.get("/data")
+def get_data(session: SessionDep) -> Any:
+ data = list(session)
+ return data
+
+
+@app.get("/stream-simple")
+def get_stream_simple(session: SessionDep) -> Any:
+ def iter_data():
+ yield from ["x", "y", "z"]
+
+ return StreamingResponse(iter_data())
+
+
+@app.get("/stream-session")
+def get_stream_session(session: SessionDep) -> Any:
+ def iter_data():
+ yield from session
+
+ return StreamingResponse(iter_data())
+
+
+@app.get("/broken-session-data")
+def get_broken_session_data(session: BrokenSessionDep) -> Any:
+ return list(session)
+
+
+@app.get("/broken-session-stream")
+def get_broken_session_stream(session: BrokenSessionDep) -> Any:
+ def iter_data():
+ yield from session
+
+ return StreamingResponse(iter_data())
+
+
+client = TestClient(app)
+
+
+def test_regular_no_stream():
+ response = client.get("/data")
+ assert response.json() == ["foo", "bar", "baz"]
+
+
+def test_stream_simple():
+ response = client.get("/stream-simple")
+ assert response.text == "xyz"
+
+
+def test_stream_session():
+ response = client.get("/stream-session")
+ assert response.text == "foobarbaz"
+
+
+def test_broken_session_data():
+ with pytest.raises(ValueError, match="Session closed"):
+ client.get("/broken-session-data")
+
+
+def test_broken_session_data_no_raise():
+ client = TestClient(app, raise_server_exceptions=False)
+ response = client.get("/broken-session-data")
+ assert response.status_code == 500
+ assert response.text == "Internal Server Error"
+
+
+def test_broken_session_stream_raise():
+ # Can raise ValueError on Pydantic v2 and ExceptionGroup on Pydantic v1
+ with pytest.raises((ValueError, Exception)):
+ client.get("/broken-session-stream")
+
+
+def test_broken_session_stream_no_raise():
+ """
+ When a dependency with yield raises after the streaming response already started
+ the 200 status code is already sent, but there's still an error in the server
+ afterwards, an exception is raised and captured or shown in the server logs.
+ """
+ with TestClient(app, raise_server_exceptions=False) as client:
+ response = client.get("/broken-session-stream")
+ assert response.status_code == 200
+ assert response.text == ""
diff --git a/tests/test_dependency_after_yield_websockets.py b/tests/test_dependency_after_yield_websockets.py
new file mode 100644
index 0000000000..7c323c338b
--- /dev/null
+++ b/tests/test_dependency_after_yield_websockets.py
@@ -0,0 +1,79 @@
+from contextlib import contextmanager
+from typing import Any, Generator
+
+import pytest
+from fastapi import Depends, FastAPI, WebSocket
+from fastapi.testclient import TestClient
+from typing_extensions import Annotated
+
+
+class Session:
+ def __init__(self) -> None:
+ self.data = ["foo", "bar", "baz"]
+ self.open = True
+
+ def __iter__(self) -> Generator[str, None, None]:
+ for item in self.data:
+ if self.open:
+ yield item
+ else:
+ raise ValueError("Session closed")
+
+
+@contextmanager
+def acquire_session() -> Generator[Session, None, None]:
+ session = Session()
+ try:
+ yield session
+ finally:
+ session.open = False
+
+
+def dep_session() -> Any:
+ with acquire_session() as s:
+ yield s
+
+
+def broken_dep_session() -> Any:
+ with acquire_session() as s:
+ s.open = False
+ yield s
+
+
+SessionDep = Annotated[Session, Depends(dep_session)]
+BrokenSessionDep = Annotated[Session, Depends(broken_dep_session)]
+
+app = FastAPI()
+
+
+@app.websocket("/ws")
+async def websocket_endpoint(websocket: WebSocket, session: SessionDep):
+ await websocket.accept()
+ for item in session:
+ await websocket.send_text(f"{item}")
+
+
+@app.websocket("/ws-broken")
+async def websocket_endpoint_broken(websocket: WebSocket, session: BrokenSessionDep):
+ await websocket.accept()
+ for item in session:
+ await websocket.send_text(f"{item}") # pragma no cover
+
+
+client = TestClient(app)
+
+
+def test_websocket_dependency_after_yield():
+ with client.websocket_connect("/ws") as websocket:
+ data = websocket.receive_text()
+ assert data == "foo"
+ data = websocket.receive_text()
+ assert data == "bar"
+ data = websocket.receive_text()
+ assert data == "baz"
+
+
+def test_websocket_dependency_after_yield_broken():
+ with pytest.raises(ValueError, match="Session closed"):
+ with client.websocket_connect("/ws-broken"):
+ pass # pragma no cover
diff --git a/tests/test_dependency_contextmanager.py b/tests/test_dependency_contextmanager.py
index 039c423b98..02c10458cb 100644
--- a/tests/test_dependency_contextmanager.py
+++ b/tests/test_dependency_contextmanager.py
@@ -286,12 +286,12 @@ def test_background_tasks():
assert data["context_a"] == "started a"
assert data["bg"] == "not set"
middleware_state = json.loads(response.headers["x-state"])
- assert middleware_state["context_b"] == "finished b with a: started a"
- assert middleware_state["context_a"] == "finished a"
+ assert middleware_state["context_b"] == "started b"
+ assert middleware_state["context_a"] == "started a"
assert middleware_state["bg"] == "not set"
assert state["context_b"] == "finished b with a: started a"
assert state["context_a"] == "finished a"
- assert state["bg"] == "bg set - b: finished b with a: started a - a: finished a"
+ assert state["bg"] == "bg set - b: started b - a: started a"
def test_sync_raise_raises():
@@ -397,7 +397,4 @@ def test_sync_background_tasks():
assert data["sync_bg"] == "not set"
assert state["context_b"] == "finished b with a: started a"
assert state["context_a"] == "finished a"
- assert (
- state["sync_bg"]
- == "sync_bg set - b: finished b with a: started a - a: finished a"
- )
+ assert state["sync_bg"] == "sync_bg set - b: started b - a: started a"
diff --git a/tests/test_dependency_normal_exceptions.py b/tests/test_dependency_yield_except_httpexception.py
similarity index 100%
rename from tests/test_dependency_normal_exceptions.py
rename to tests/test_dependency_yield_except_httpexception.py
diff --git a/tests/test_enforce_once_required_parameter.py b/tests/test_enforce_once_required_parameter.py
index b64f8341b8..2e5ac6c062 100644
--- a/tests/test_enforce_once_required_parameter.py
+++ b/tests/test_enforce_once_required_parameter.py
@@ -48,7 +48,7 @@ expected_schema = {
"type": "array",
},
"msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error " "Type", "type": "string"},
+ "type": {"title": "Error Type", "type": "string"},
},
"required": ["loc", "msg", "type"],
"title": "ValidationError",
@@ -73,7 +73,7 @@ expected_schema = {
"responses": {
"200": {
"content": {"application/json": {"schema": {}}},
- "description": "Successful " "Response",
+ "description": "Successful Response",
},
"422": {
"content": {
@@ -83,7 +83,7 @@ expected_schema = {
}
}
},
- "description": "Validation " "Error",
+ "description": "Validation Error",
},
},
"summary": "Foo Handler",
@@ -102,7 +102,7 @@ def test_schema():
def test_get_invalid():
response = client.get("/foo")
- assert response.status_code == status.HTTP_422_UNPROCESSABLE_ENTITY
+ assert response.status_code == 422
def test_get_valid():
diff --git a/tests/test_exception_handlers.py b/tests/test_exception_handlers.py
index 67a4becec7..6a3cbd830d 100644
--- a/tests/test_exception_handlers.py
+++ b/tests/test_exception_handlers.py
@@ -1,5 +1,5 @@
import pytest
-from fastapi import FastAPI, HTTPException
+from fastapi import Depends, FastAPI, HTTPException
from fastapi.exceptions import RequestValidationError
from fastapi.testclient import TestClient
from starlette.responses import JSONResponse
@@ -28,6 +28,18 @@ app = FastAPI(
client = TestClient(app)
+def raise_value_error():
+ raise ValueError()
+
+
+def dependency_with_yield():
+ yield raise_value_error()
+
+
+@app.get("/dependency-with-yield", dependencies=[Depends(dependency_with_yield)])
+def with_yield(): ...
+
+
@app.get("/http-exception")
def route_with_http_exception():
raise HTTPException(status_code=400)
@@ -65,3 +77,12 @@ def test_override_server_error_exception_response():
response = client.get("/server-error")
assert response.status_code == 500
assert response.json() == {"exception": "server-error"}
+
+
+def test_traceback_for_dependency_with_yield():
+ client = TestClient(app, raise_server_exceptions=True)
+ with pytest.raises(ValueError) as exc_info:
+ client.get("/dependency-with-yield")
+ last_frame = exc_info.traceback[-1]
+ assert str(last_frame.path) == __file__
+ assert last_frame.lineno == raise_value_error.__code__.co_firstlineno
diff --git a/tests/test_fastapi_cli.py b/tests/test_fastapi_cli.py
index 20c9281570..a5c10778ad 100644
--- a/tests/test_fastapi_cli.py
+++ b/tests/test_fastapi_cli.py
@@ -22,7 +22,7 @@ def test_fastapi_cli():
encoding="utf-8",
)
assert result.returncode == 1, result.stdout
- assert "Using path non_existent_file.py" in result.stdout
+ assert "Path does not exist non_existent_file.py" in result.stdout
def test_fastapi_cli_not_installed():
diff --git a/tests/test_file_and_form_order_issue_9116.py b/tests/test_file_and_form_order_issue_9116.py
new file mode 100644
index 0000000000..cb9a31d314
--- /dev/null
+++ b/tests/test_file_and_form_order_issue_9116.py
@@ -0,0 +1,90 @@
+"""
+Regression test, Error 422 if Form is declared before File
+See https://github.com/tiangolo/fastapi/discussions/9116
+"""
+
+from pathlib import Path
+from typing import List
+
+import pytest
+from fastapi import FastAPI, File, Form
+from fastapi.testclient import TestClient
+from typing_extensions import Annotated
+
+app = FastAPI()
+
+
+@app.post("/file_before_form")
+def file_before_form(
+ file: bytes = File(),
+ city: str = Form(),
+):
+ return {"file_content": file, "city": city}
+
+
+@app.post("/file_after_form")
+def file_after_form(
+ city: str = Form(),
+ file: bytes = File(),
+):
+ return {"file_content": file, "city": city}
+
+
+@app.post("/file_list_before_form")
+def file_list_before_form(
+ files: Annotated[List[bytes], File()],
+ city: Annotated[str, Form()],
+):
+ return {"file_contents": files, "city": city}
+
+
+@app.post("/file_list_after_form")
+def file_list_after_form(
+ city: Annotated[str, Form()],
+ files: Annotated[List[bytes], File()],
+):
+ return {"file_contents": files, "city": city}
+
+
+client = TestClient(app)
+
+
+@pytest.fixture
+def tmp_file_1(tmp_path: Path) -> Path:
+ f = tmp_path / "example1.txt"
+ f.write_text("foo")
+ return f
+
+
+@pytest.fixture
+def tmp_file_2(tmp_path: Path) -> Path:
+ f = tmp_path / "example2.txt"
+ f.write_text("bar")
+ return f
+
+
+@pytest.mark.parametrize("endpoint_path", ("/file_before_form", "/file_after_form"))
+def test_file_form_order(endpoint_path: str, tmp_file_1: Path):
+ response = client.post(
+ url=endpoint_path,
+ data={"city": "Thimphou"},
+ files={"file": (tmp_file_1.name, tmp_file_1.read_bytes())},
+ )
+ assert response.status_code == 200, response.text
+ assert response.json() == {"file_content": "foo", "city": "Thimphou"}
+
+
+@pytest.mark.parametrize(
+ "endpoint_path", ("/file_list_before_form", "/file_list_after_form")
+)
+def test_file_list_form_order(endpoint_path: str, tmp_file_1: Path, tmp_file_2: Path):
+ response = client.post(
+ url=endpoint_path,
+ data={"city": "Thimphou"},
+ files=(
+ ("files", (tmp_file_1.name, tmp_file_1.read_bytes())),
+ ("files", (tmp_file_2.name, tmp_file_2.read_bytes())),
+ ),
+ )
+ assert response.status_code == 200, response.text
+ assert response.json() == {"file_contents": ["foo", "bar"], "city": "Thimphou"}
diff --git a/tests/test_forms_single_model.py b/tests/test_forms_single_model.py
new file mode 100644
index 0000000000..880ab38200
--- /dev/null
+++ b/tests/test_forms_single_model.py
@@ -0,0 +1,133 @@
+from typing import List, Optional
+
+from dirty_equals import IsDict
+from fastapi import FastAPI, Form
+from fastapi.testclient import TestClient
+from pydantic import BaseModel, Field
+from typing_extensions import Annotated
+
+app = FastAPI()
+
+
+class FormModel(BaseModel):
+ username: str
+ lastname: str
+ age: Optional[int] = None
+ tags: List[str] = ["foo", "bar"]
+ alias_with: str = Field(alias="with", default="nothing")
+
+
+@app.post("/form/")
+def post_form(user: Annotated[FormModel, Form()]):
+ return user
+
+
+client = TestClient(app)
+
+
+def test_send_all_data():
+ response = client.post(
+ "/form/",
+ data={
+ "username": "Rick",
+ "lastname": "Sanchez",
+ "age": "70",
+ "tags": ["plumbus", "citadel"],
+ "with": "something",
+ },
+ )
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "username": "Rick",
+ "lastname": "Sanchez",
+ "age": 70,
+ "tags": ["plumbus", "citadel"],
+ "with": "something",
+ }
+
+
+def test_defaults():
+ response = client.post("/form/", data={"username": "Rick", "lastname": "Sanchez"})
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "username": "Rick",
+ "lastname": "Sanchez",
+ "age": None,
+ "tags": ["foo", "bar"],
+ "with": "nothing",
+ }
+
+
+def test_invalid_data():
+ response = client.post(
+ "/form/",
+ data={
+ "username": "Rick",
+ "lastname": "Sanchez",
+ "age": "seventy",
+ "tags": ["plumbus", "citadel"],
+ },
+ )
+ assert response.status_code == 422, response.text
+ assert response.json() == IsDict(
+ {
+ "detail": [
+ {
+ "type": "int_parsing",
+ "loc": ["body", "age"],
+ "msg": "Input should be a valid integer, unable to parse string as an integer",
+ "input": "seventy",
+ }
+ ]
+ }
+ ) | IsDict(
+ # TODO: remove when deprecating Pydantic v1
+ {
+ "detail": [
+ {
+ "loc": ["body", "age"],
+ "msg": "value is not a valid integer",
+ "type": "type_error.integer",
+ }
+ ]
+ }
+ )
+
+
+def test_no_data():
+ response = client.post("/form/")
+ assert response.status_code == 422, response.text
+ assert response.json() == IsDict(
+ {
+ "detail": [
+ {
+ "type": "missing",
+ "loc": ["body", "username"],
+ "msg": "Field required",
+ "input": {"tags": ["foo", "bar"], "with": "nothing"},
+ },
+ {
+ "type": "missing",
+ "loc": ["body", "lastname"],
+ "msg": "Field required",
+ "input": {"tags": ["foo", "bar"], "with": "nothing"},
+ },
+ ]
+ }
+ ) | IsDict(
+ # TODO: remove when deprecating Pydantic v1
+ {
+ "detail": [
+ {
+ "loc": ["body", "username"],
+ "msg": "field required",
+ "type": "value_error.missing",
+ },
+ {
+ "loc": ["body", "lastname"],
+ "msg": "field required",
+ "type": "value_error.missing",
+ },
+ ]
+ }
+ )
diff --git a/tests/test_tutorial/test_query_params_str_validations/test_tutorial014_an.py b/tests/test_forms_single_param.py
similarity index 62%
rename from tests/test_tutorial/test_query_params_str_validations/test_tutorial014_an.py
rename to tests/test_forms_single_param.py
index 2182e87b75..3bb951441f 100644
--- a/tests/test_tutorial/test_query_params_str_validations/test_tutorial014_an.py
+++ b/tests/test_forms_single_param.py
@@ -1,20 +1,22 @@
+from fastapi import FastAPI, Form
from fastapi.testclient import TestClient
+from typing_extensions import Annotated
+
+app = FastAPI()
+
+
+@app.post("/form/")
+def post_form(username: Annotated[str, Form()]):
+ return username
-from docs_src.query_params_str_validations.tutorial014_an import app
client = TestClient(app)
-def test_hidden_query():
- response = client.get("/items?hidden_query=somevalue")
+def test_single_form_field():
+ response = client.post("/form/", data={"username": "Rick"})
assert response.status_code == 200, response.text
- assert response.json() == {"hidden_query": "somevalue"}
-
-
-def test_no_hidden_query():
- response = client.get("/items")
- assert response.status_code == 200, response.text
- assert response.json() == {"hidden_query": "Not found"}
+ assert response.json() == "Rick"
def test_openapi_schema():
@@ -24,10 +26,20 @@ def test_openapi_schema():
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
- "/items/": {
- "get": {
- "summary": "Read Items",
- "operationId": "read_items_items__get",
+ "/form/": {
+ "post": {
+ "summary": "Post Form",
+ "operationId": "post_form_form__post",
+ "requestBody": {
+ "content": {
+ "application/x-www-form-urlencoded": {
+ "schema": {
+ "$ref": "#/components/schemas/Body_post_form_form__post"
+ }
+ }
+ },
+ "required": True,
+ },
"responses": {
"200": {
"description": "Successful Response",
@@ -49,32 +61,38 @@ def test_openapi_schema():
},
"components": {
"schemas": {
- "HTTPValidationError": {
- "title": "HTTPValidationError",
+ "Body_post_form_form__post": {
+ "properties": {"username": {"type": "string", "title": "Username"}},
"type": "object",
+ "required": ["username"],
+ "title": "Body_post_form_form__post",
+ },
+ "HTTPValidationError": {
"properties": {
"detail": {
- "title": "Detail",
- "type": "array",
"items": {"$ref": "#/components/schemas/ValidationError"},
+ "type": "array",
+ "title": "Detail",
}
},
+ "type": "object",
+ "title": "HTTPValidationError",
},
"ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
"properties": {
"loc": {
- "title": "Location",
- "type": "array",
"items": {
"anyOf": [{"type": "string"}, {"type": "integer"}]
},
+ "type": "array",
+ "title": "Location",
},
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
+ "msg": {"type": "string", "title": "Message"},
+ "type": {"type": "string", "title": "Error Type"},
},
+ "type": "object",
+ "required": ["loc", "msg", "type"],
+ "title": "ValidationError",
},
}
},
diff --git a/tests/test_generic_parameterless_depends.py b/tests/test_generic_parameterless_depends.py
index fe13ff89b8..5aa35320c9 100644
--- a/tests/test_generic_parameterless_depends.py
+++ b/tests/test_generic_parameterless_depends.py
@@ -55,7 +55,7 @@ def test_openapi_schema():
"responses": {
"200": {
"content": {"application/json": {"schema": {}}},
- "description": "Successful " "Response",
+ "description": "Successful Response",
}
},
"summary": "A",
@@ -67,7 +67,7 @@ def test_openapi_schema():
"responses": {
"200": {
"content": {"application/json": {"schema": {}}},
- "description": "Successful " "Response",
+ "description": "Successful Response",
}
},
"summary": "B",
diff --git a/tests/test_get_model_definitions_formfeed_escape.py b/tests/test_get_model_definitions_formfeed_escape.py
new file mode 100644
index 0000000000..6601585ef0
--- /dev/null
+++ b/tests/test_get_model_definitions_formfeed_escape.py
@@ -0,0 +1,180 @@
+from typing import Any, Iterator, Set, Type
+
+import fastapi._compat
+import fastapi.openapi.utils
+import pydantic.schema
+import pytest
+from fastapi import FastAPI
+from pydantic import BaseModel
+from starlette.testclient import TestClient
+
+from .utils import needs_pydanticv1
+
+
+class Address(BaseModel):
+ """
+ This is a public description of an Address
+ \f
+ You can't see this part of the docstring, it's private!
+ """
+
+ line_1: str
+ city: str
+ state_province: str
+
+
+class Facility(BaseModel):
+ id: str
+ address: Address
+
+
+app = FastAPI()
+
+client = TestClient(app)
+
+
+@app.get("/facilities/{facility_id}")
+def get_facility(facility_id: str) -> Facility: ...
+
+
+openapi_schema = {
+ "components": {
+ "schemas": {
+ "Address": {
+ # NOTE: the description of this model shows only the public-facing text, before the `\f` in docstring
+ "description": "This is a public description of an Address\n",
+ "properties": {
+ "city": {"title": "City", "type": "string"},
+ "line_1": {"title": "Line 1", "type": "string"},
+ "state_province": {"title": "State Province", "type": "string"},
+ },
+ "required": ["line_1", "city", "state_province"],
+ "title": "Address",
+ "type": "object",
+ },
+ "Facility": {
+ "properties": {
+ "address": {"$ref": "#/components/schemas/Address"},
+ "id": {"title": "Id", "type": "string"},
+ },
+ "required": ["id", "address"],
+ "title": "Facility",
+ "type": "object",
+ },
+ "HTTPValidationError": {
+ "properties": {
+ "detail": {
+ "items": {"$ref": "#/components/schemas/ValidationError"},
+ "title": "Detail",
+ "type": "array",
+ }
+ },
+ "title": "HTTPValidationError",
+ "type": "object",
+ },
+ "ValidationError": {
+ "properties": {
+ "loc": {
+ "items": {"anyOf": [{"type": "string"}, {"type": "integer"}]},
+ "title": "Location",
+ "type": "array",
+ },
+ "msg": {"title": "Message", "type": "string"},
+ "type": {"title": "Error Type", "type": "string"},
+ },
+ "required": ["loc", "msg", "type"],
+ "title": "ValidationError",
+ "type": "object",
+ },
+ }
+ },
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "openapi": "3.1.0",
+ "paths": {
+ "/facilities/{facility_id}": {
+ "get": {
+ "operationId": "get_facility_facilities__facility_id__get",
+ "parameters": [
+ {
+ "in": "path",
+ "name": "facility_id",
+ "required": True,
+ "schema": {"title": "Facility Id", "type": "string"},
+ }
+ ],
+ "responses": {
+ "200": {
+ "content": {
+ "application/json": {
+ "schema": {"$ref": "#/components/schemas/Facility"}
+ }
+ },
+ "description": "Successful Response",
+ },
+ "422": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ "description": "Validation Error",
+ },
+ },
+ "summary": "Get Facility",
+ }
+ }
+ },
+}
+
+
+def test_openapi_schema():
+ """
+ Sanity check to ensure our app's openapi schema renders as we expect
+ """
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == openapi_schema
+
+
+class SortedTypeSet(set):
+ """
+ Set of Types whose `__iter__()` method yields results sorted by the type names
+ """
+
+ def __init__(self, seq: Set[Type[Any]], *, sort_reversed: bool):
+ super().__init__(seq)
+ self.sort_reversed = sort_reversed
+
+ def __iter__(self) -> Iterator[Type[Any]]:
+ members_sorted = sorted(
+ super().__iter__(),
+ key=lambda type_: type_.__name__,
+ reverse=self.sort_reversed,
+ )
+ yield from members_sorted
+
+
+@needs_pydanticv1
+@pytest.mark.parametrize("sort_reversed", [True, False])
+def test_model_description_escaped_with_formfeed(sort_reversed: bool):
+ """
+ Regression test for bug fixed by https://github.com/fastapi/fastapi/pull/6039.
+
+ Test `get_model_definitions` with models passed in different order.
+ """
+ from fastapi._compat import v1
+
+ all_fields = fastapi.openapi.utils.get_fields_from_routes(app.routes)
+
+ flat_models = v1.get_flat_models_from_fields(all_fields, known_models=set())
+ model_name_map = pydantic.schema.get_model_name_map(flat_models)
+
+ expected_address_description = "This is a public description of an Address\n"
+
+ models = v1.get_model_definitions(
+ flat_models=SortedTypeSet(flat_models, sort_reversed=sort_reversed),
+ model_name_map=model_name_map,
+ )
+ assert models["Address"]["description"] == expected_address_description
diff --git a/tests/test_jsonable_encoder.py b/tests/test_jsonable_encoder.py
index 1906d6bf17..447c5b4d6a 100644
--- a/tests/test_jsonable_encoder.py
+++ b/tests/test_jsonable_encoder.py
@@ -216,9 +216,12 @@ def test_custom_encoders():
instance = MyModel(dt_field=safe_datetime.now())
encoded_instance = jsonable_encoder(
- instance, custom_encoder={safe_datetime: lambda o: o.isoformat()}
+ instance, custom_encoder={safe_datetime: lambda o: o.strftime("%H:%M:%S")}
)
- assert encoded_instance["dt_field"] == instance.dt_field.isoformat()
+ assert encoded_instance["dt_field"] == instance.dt_field.strftime("%H:%M:%S")
+
+ encoded_instance2 = jsonable_encoder(instance)
+ assert encoded_instance2["dt_field"] == instance.dt_field.isoformat()
def test_custom_enum_encoders():
diff --git a/tests/test_modules_same_name_body/test_main.py b/tests/test_modules_same_name_body/test_main.py
index cc165bdcaa..263d87df26 100644
--- a/tests/test_modules_same_name_body/test_main.py
+++ b/tests/test_modules_same_name_body/test_main.py
@@ -1,3 +1,4 @@
+import pytest
from fastapi.testclient import TestClient
from .app.main import app
@@ -5,29 +6,22 @@ from .app.main import app
client = TestClient(app)
-def test_post_a():
+@pytest.mark.parametrize(
+ "path", ["/a/compute", "/a/compute/", "/b/compute", "/b/compute/"]
+)
+def test_post(path):
data = {"a": 2, "b": "foo"}
- response = client.post("/a/compute", json=data)
+ response = client.post(path, json=data)
assert response.status_code == 200, response.text
- data = response.json()
+ assert data == response.json()
-def test_post_a_invalid():
+@pytest.mark.parametrize(
+ "path", ["/a/compute", "/a/compute/", "/b/compute", "/b/compute/"]
+)
+def test_post_invalid(path):
data = {"a": "bar", "b": "foo"}
- response = client.post("/a/compute", json=data)
- assert response.status_code == 422, response.text
-
-
-def test_post_b():
- data = {"a": 2, "b": "foo"}
- response = client.post("/b/compute/", json=data)
- assert response.status_code == 200, response.text
- data = response.json()
-
-
-def test_post_b_invalid():
- data = {"a": "bar", "b": "foo"}
- response = client.post("/b/compute/", json=data)
+ response = client.post(path, json=data)
assert response.status_code == 422, response.text
diff --git a/tests/test_multi_body_errors.py b/tests/test_multi_body_errors.py
index 0102f0f1a0..33304827a9 100644
--- a/tests/test_multi_body_errors.py
+++ b/tests/test_multi_body_errors.py
@@ -185,7 +185,15 @@ def test_openapi_schema():
"title": "Age",
"anyOf": [
{"exclusiveMinimum": 0.0, "type": "number"},
- {"type": "string"},
+ IsOneOf(
+ # pydantic < 2.12.0
+ {"type": "string"},
+ # pydantic >= 2.12.0
+ {
+ "type": "string",
+ "pattern": r"^(?!^[-+.]*$)[+-]?0*\d*\.?\d*$",
+ },
+ ),
],
}
)
diff --git a/tests/test_multipart_installation.py b/tests/test_multipart_installation.py
index 788d9ef5af..9c3e47c495 100644
--- a/tests/test_multipart_installation.py
+++ b/tests/test_multipart_installation.py
@@ -1,3 +1,5 @@
+import warnings
+
import pytest
from fastapi import FastAPI, File, Form, UploadFile
from fastapi.dependencies.utils import (
@@ -7,7 +9,10 @@ from fastapi.dependencies.utils import (
def test_incorrect_multipart_installed_form(monkeypatch):
- monkeypatch.delattr("multipart.multipart.parse_options_header", raising=False)
+ monkeypatch.setattr("python_multipart.__version__", "0.0.12")
+ with warnings.catch_warnings(record=True):
+ warnings.simplefilter("always")
+ monkeypatch.delattr("multipart.multipart.parse_options_header", raising=False)
with pytest.raises(RuntimeError, match=multipart_incorrect_install_error):
app = FastAPI()
@@ -17,7 +22,10 @@ def test_incorrect_multipart_installed_form(monkeypatch):
def test_incorrect_multipart_installed_file_upload(monkeypatch):
- monkeypatch.delattr("multipart.multipart.parse_options_header", raising=False)
+ monkeypatch.setattr("python_multipart.__version__", "0.0.12")
+ with warnings.catch_warnings(record=True):
+ warnings.simplefilter("always")
+ monkeypatch.delattr("multipart.multipart.parse_options_header", raising=False)
with pytest.raises(RuntimeError, match=multipart_incorrect_install_error):
app = FastAPI()
@@ -27,7 +35,10 @@ def test_incorrect_multipart_installed_file_upload(monkeypatch):
def test_incorrect_multipart_installed_file_bytes(monkeypatch):
- monkeypatch.delattr("multipart.multipart.parse_options_header", raising=False)
+ monkeypatch.setattr("python_multipart.__version__", "0.0.12")
+ with warnings.catch_warnings(record=True):
+ warnings.simplefilter("always")
+ monkeypatch.delattr("multipart.multipart.parse_options_header", raising=False)
with pytest.raises(RuntimeError, match=multipart_incorrect_install_error):
app = FastAPI()
@@ -37,7 +48,10 @@ def test_incorrect_multipart_installed_file_bytes(monkeypatch):
def test_incorrect_multipart_installed_multi_form(monkeypatch):
- monkeypatch.delattr("multipart.multipart.parse_options_header", raising=False)
+ monkeypatch.setattr("python_multipart.__version__", "0.0.12")
+ with warnings.catch_warnings(record=True):
+ warnings.simplefilter("always")
+ monkeypatch.delattr("multipart.multipart.parse_options_header", raising=False)
with pytest.raises(RuntimeError, match=multipart_incorrect_install_error):
app = FastAPI()
@@ -47,7 +61,10 @@ def test_incorrect_multipart_installed_multi_form(monkeypatch):
def test_incorrect_multipart_installed_form_file(monkeypatch):
- monkeypatch.delattr("multipart.multipart.parse_options_header", raising=False)
+ monkeypatch.setattr("python_multipart.__version__", "0.0.12")
+ with warnings.catch_warnings(record=True):
+ warnings.simplefilter("always")
+ monkeypatch.delattr("multipart.multipart.parse_options_header", raising=False)
with pytest.raises(RuntimeError, match=multipart_incorrect_install_error):
app = FastAPI()
@@ -57,50 +74,76 @@ def test_incorrect_multipart_installed_form_file(monkeypatch):
def test_no_multipart_installed(monkeypatch):
- monkeypatch.delattr("multipart.__version__", raising=False)
- with pytest.raises(RuntimeError, match=multipart_not_installed_error):
+ monkeypatch.setattr("python_multipart.__version__", "0.0.12")
+ with warnings.catch_warnings(record=True):
+ warnings.simplefilter("always")
+ monkeypatch.delattr("multipart.__version__", raising=False)
+ with pytest.raises(RuntimeError, match=multipart_not_installed_error):
+ app = FastAPI()
+
+ @app.post("/")
+ async def root(username: str = Form()):
+ return username # pragma: nocover
+
+
+def test_no_multipart_installed_file(monkeypatch):
+ monkeypatch.setattr("python_multipart.__version__", "0.0.12")
+ with warnings.catch_warnings(record=True):
+ warnings.simplefilter("always")
+ monkeypatch.delattr("multipart.__version__", raising=False)
+ with pytest.raises(RuntimeError, match=multipart_not_installed_error):
+ app = FastAPI()
+
+ @app.post("/")
+ async def root(f: UploadFile = File()):
+ return f # pragma: nocover
+
+
+def test_no_multipart_installed_file_bytes(monkeypatch):
+ monkeypatch.setattr("python_multipart.__version__", "0.0.12")
+ with warnings.catch_warnings(record=True):
+ warnings.simplefilter("always")
+ monkeypatch.delattr("multipart.__version__", raising=False)
+ with pytest.raises(RuntimeError, match=multipart_not_installed_error):
+ app = FastAPI()
+
+ @app.post("/")
+ async def root(f: bytes = File()):
+ return f # pragma: nocover
+
+
+def test_no_multipart_installed_multi_form(monkeypatch):
+ monkeypatch.setattr("python_multipart.__version__", "0.0.12")
+ with warnings.catch_warnings(record=True):
+ warnings.simplefilter("always")
+ monkeypatch.delattr("multipart.__version__", raising=False)
+ with pytest.raises(RuntimeError, match=multipart_not_installed_error):
+ app = FastAPI()
+
+ @app.post("/")
+ async def root(username: str = Form(), password: str = Form()):
+ return username # pragma: nocover
+
+
+def test_no_multipart_installed_form_file(monkeypatch):
+ monkeypatch.setattr("python_multipart.__version__", "0.0.12")
+ with warnings.catch_warnings(record=True):
+ warnings.simplefilter("always")
+ monkeypatch.delattr("multipart.__version__", raising=False)
+ with pytest.raises(RuntimeError, match=multipart_not_installed_error):
+ app = FastAPI()
+
+ @app.post("/")
+ async def root(username: str = Form(), f: UploadFile = File()):
+ return username # pragma: nocover
+
+
+def test_old_multipart_installed(monkeypatch):
+ monkeypatch.setattr("python_multipart.__version__", "0.0.12")
+ with warnings.catch_warnings(record=True):
+ warnings.simplefilter("always")
app = FastAPI()
@app.post("/")
async def root(username: str = Form()):
return username # pragma: nocover
-
-
-def test_no_multipart_installed_file(monkeypatch):
- monkeypatch.delattr("multipart.__version__", raising=False)
- with pytest.raises(RuntimeError, match=multipart_not_installed_error):
- app = FastAPI()
-
- @app.post("/")
- async def root(f: UploadFile = File()):
- return f # pragma: nocover
-
-
-def test_no_multipart_installed_file_bytes(monkeypatch):
- monkeypatch.delattr("multipart.__version__", raising=False)
- with pytest.raises(RuntimeError, match=multipart_not_installed_error):
- app = FastAPI()
-
- @app.post("/")
- async def root(f: bytes = File()):
- return f # pragma: nocover
-
-
-def test_no_multipart_installed_multi_form(monkeypatch):
- monkeypatch.delattr("multipart.__version__", raising=False)
- with pytest.raises(RuntimeError, match=multipart_not_installed_error):
- app = FastAPI()
-
- @app.post("/")
- async def root(username: str = Form(), password: str = Form()):
- return username # pragma: nocover
-
-
-def test_no_multipart_installed_form_file(monkeypatch):
- monkeypatch.delattr("multipart.__version__", raising=False)
- with pytest.raises(RuntimeError, match=multipart_not_installed_error):
- app = FastAPI()
-
- @app.post("/")
- async def root(username: str = Form(), f: UploadFile = File()):
- return username # pragma: nocover
diff --git a/tests/test_openapi_examples.py b/tests/test_openapi_examples.py
index 6597e5058b..b3f83ae237 100644
--- a/tests/test_openapi_examples.py
+++ b/tests/test_openapi_examples.py
@@ -155,13 +155,26 @@ def test_openapi_schema():
"requestBody": {
"content": {
"application/json": {
- "schema": {
- "allOf": [{"$ref": "#/components/schemas/Item"}],
- "title": "Item",
- "examples": [
- {"data": "Data in Body examples, example1"}
- ],
- },
+ "schema": IsDict(
+ {
+ "$ref": "#/components/schemas/Item",
+ "examples": [
+ {"data": "Data in Body examples, example1"}
+ ],
+ }
+ )
+ | IsDict(
+ {
+ # TODO: remove when deprecating Pydantic v1
+ "allOf": [
+ {"$ref": "#/components/schemas/Item"}
+ ],
+ "title": "Item",
+ "examples": [
+ {"data": "Data in Body examples, example1"}
+ ],
+ }
+ ),
"examples": {
"Example One": {
"summary": "Example One Summary",
diff --git a/tests/test_openapi_model_description_trim_on_formfeed.py b/tests/test_openapi_model_description_trim_on_formfeed.py
new file mode 100644
index 0000000000..e18d4f6b29
--- /dev/null
+++ b/tests/test_openapi_model_description_trim_on_formfeed.py
@@ -0,0 +1,31 @@
+from fastapi import FastAPI
+from fastapi.testclient import TestClient
+from pydantic import BaseModel
+
+app = FastAPI()
+
+
+class MyModel(BaseModel):
+ """
+ A model with a form feed character in the title.
+ \f
+ Text after form feed character.
+ """
+
+
+@app.get("/foo")
+def foo(v: MyModel): # pragma: no cover
+ pass
+
+
+client = TestClient(app)
+
+
+def test_openapi():
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ openapi_schema = response.json()
+
+ assert openapi_schema["components"]["schemas"]["MyModel"]["description"] == (
+ "A model with a form feed character in the title.\n"
+ )
diff --git a/tests/test_openapi_schema_type.py b/tests/test_openapi_schema_type.py
new file mode 100644
index 0000000000..a45ea20c8c
--- /dev/null
+++ b/tests/test_openapi_schema_type.py
@@ -0,0 +1,26 @@
+from typing import List, Optional, Union
+
+import pytest
+from fastapi.openapi.models import Schema, SchemaType
+
+
+@pytest.mark.parametrize(
+ "type_value",
+ [
+ "array",
+ ["string", "null"],
+ None,
+ ],
+)
+def test_allowed_schema_type(
+ type_value: Optional[Union[SchemaType, List[SchemaType]]],
+) -> None:
+ """Test that Schema accepts SchemaType, List[SchemaType] and None for type field."""
+ schema = Schema(type=type_value)
+ assert schema.type == type_value
+
+
+def test_invalid_type_value() -> None:
+ """Test that Schema raises ValueError for invalid type values."""
+ with pytest.raises(ValueError, match="2 validation errors for Schema"):
+ Schema(type=True) # type: ignore[arg-type]
diff --git a/tests/test_openapi_separate_input_output_schemas.py b/tests/test_openapi_separate_input_output_schemas.py
index aeb85f735b..fa73620eae 100644
--- a/tests/test_openapi_separate_input_output_schemas.py
+++ b/tests/test_openapi_separate_input_output_schemas.py
@@ -2,6 +2,7 @@ from typing import List, Optional
from fastapi import FastAPI
from fastapi.testclient import TestClient
+from inline_snapshot import snapshot
from pydantic import BaseModel
from .utils import PYDANTIC_V2, needs_pydanticv2
@@ -26,8 +27,8 @@ class Item(BaseModel):
def get_app_client(separate_input_output_schemas: bool = True) -> TestClient:
app = FastAPI(separate_input_output_schemas=separate_input_output_schemas)
- @app.post("/items/")
- def create_item(item: Item):
+ @app.post("/items/", responses={402: {"model": Item}})
+ def create_item(item: Item) -> Item:
return item
@app.post("/items-list/")
@@ -135,201 +136,223 @@ def test_openapi_schema():
client = get_app_client()
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/": {
- "get": {
- "summary": "Read Items",
- "operationId": "read_items_items__get",
- "responses": {
- "200": {
- "description": "Successful Response",
+ assert response.json() == snapshot(
+ {
+ "openapi": "3.1.0",
+ "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": {
+ "items": {
+ "$ref": "#/components/schemas/Item-Output"
+ },
+ "type": "array",
+ "title": "Response Read Items Items Get",
+ }
+ }
+ },
+ }
+ },
+ },
+ "post": {
+ "summary": "Create Item",
+ "operationId": "create_item_items__post",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Item-Input"
+ }
+ }
+ },
+ "required": True,
+ },
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Item-Output"
+ }
+ }
+ },
+ },
+ "402": {
+ "description": "Payment Required",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Item-Output"
+ }
+ }
+ },
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ },
+ },
+ "/items-list/": {
+ "post": {
+ "summary": "Create Item List",
+ "operationId": "create_item_list_items_list__post",
+ "requestBody": {
"content": {
"application/json": {
"schema": {
"items": {
- "$ref": "#/components/schemas/Item-Output"
+ "$ref": "#/components/schemas/Item-Input"
},
"type": "array",
- "title": "Response Read Items Items Get",
+ "title": "Item",
}
}
},
- }
- },
- },
- "post": {
- "summary": "Create Item",
- "operationId": "create_item_items__post",
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {"$ref": "#/components/schemas/Item-Input"}
- }
+ "required": True,
},
- "required": True,
- },
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
}
- }
+ },
},
},
- },
+ }
},
},
- "/items-list/": {
- "post": {
- "summary": "Create Item List",
- "operationId": "create_item_list_items_list__post",
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "items": {
- "$ref": "#/components/schemas/Item-Input"
- },
- "type": "array",
- "title": "Item",
- }
+ "components": {
+ "schemas": {
+ "HTTPValidationError": {
+ "properties": {
+ "detail": {
+ "items": {
+ "$ref": "#/components/schemas/ValidationError"
+ },
+ "type": "array",
+ "title": "Detail",
}
},
- "required": True,
+ "type": "object",
+ "title": "HTTPValidationError",
},
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
+ "Item-Input": {
+ "properties": {
+ "name": {"type": "string", "title": "Name"},
+ "description": {
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ "title": "Description",
+ },
+ "sub": {
+ "anyOf": [
+ {"$ref": "#/components/schemas/SubItem-Input"},
+ {"type": "null"},
+ ]
},
},
+ "type": "object",
+ "required": ["name"],
+ "title": "Item",
+ },
+ "Item-Output": {
+ "properties": {
+ "name": {"type": "string", "title": "Name"},
+ "description": {
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ "title": "Description",
+ },
+ "sub": {
+ "anyOf": [
+ {"$ref": "#/components/schemas/SubItem-Output"},
+ {"type": "null"},
+ ]
+ },
+ },
+ "type": "object",
+ "required": ["name", "description", "sub"],
+ "title": "Item",
+ },
+ "SubItem-Input": {
+ "properties": {
+ "subname": {"type": "string", "title": "Subname"},
+ "sub_description": {
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ "title": "Sub Description",
+ },
+ "tags": {
+ "items": {"type": "string"},
+ "type": "array",
+ "title": "Tags",
+ "default": [],
+ },
+ },
+ "type": "object",
+ "required": ["subname"],
+ "title": "SubItem",
+ },
+ "SubItem-Output": {
+ "properties": {
+ "subname": {"type": "string", "title": "Subname"},
+ "sub_description": {
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ "title": "Sub Description",
+ },
+ "tags": {
+ "items": {"type": "string"},
+ "type": "array",
+ "title": "Tags",
+ "default": [],
+ },
+ },
+ "type": "object",
+ "required": ["subname", "sub_description", "tags"],
+ "title": "SubItem",
+ },
+ "ValidationError": {
+ "properties": {
+ "loc": {
+ "items": {
+ "anyOf": [{"type": "string"}, {"type": "integer"}]
+ },
+ "type": "array",
+ "title": "Location",
+ },
+ "msg": {"type": "string", "title": "Message"},
+ "type": {"type": "string", "title": "Error Type"},
+ },
+ "type": "object",
+ "required": ["loc", "msg", "type"],
+ "title": "ValidationError",
},
}
},
- },
- "components": {
- "schemas": {
- "HTTPValidationError": {
- "properties": {
- "detail": {
- "items": {"$ref": "#/components/schemas/ValidationError"},
- "type": "array",
- "title": "Detail",
- }
- },
- "type": "object",
- "title": "HTTPValidationError",
- },
- "Item-Input": {
- "properties": {
- "name": {"type": "string", "title": "Name"},
- "description": {
- "anyOf": [{"type": "string"}, {"type": "null"}],
- "title": "Description",
- },
- "sub": {
- "anyOf": [
- {"$ref": "#/components/schemas/SubItem-Input"},
- {"type": "null"},
- ]
- },
- },
- "type": "object",
- "required": ["name"],
- "title": "Item",
- },
- "Item-Output": {
- "properties": {
- "name": {"type": "string", "title": "Name"},
- "description": {
- "anyOf": [{"type": "string"}, {"type": "null"}],
- "title": "Description",
- },
- "sub": {
- "anyOf": [
- {"$ref": "#/components/schemas/SubItem-Output"},
- {"type": "null"},
- ]
- },
- },
- "type": "object",
- "required": ["name", "description", "sub"],
- "title": "Item",
- },
- "SubItem-Input": {
- "properties": {
- "subname": {"type": "string", "title": "Subname"},
- "sub_description": {
- "anyOf": [{"type": "string"}, {"type": "null"}],
- "title": "Sub Description",
- },
- "tags": {
- "items": {"type": "string"},
- "type": "array",
- "title": "Tags",
- "default": [],
- },
- },
- "type": "object",
- "required": ["subname"],
- "title": "SubItem",
- },
- "SubItem-Output": {
- "properties": {
- "subname": {"type": "string", "title": "Subname"},
- "sub_description": {
- "anyOf": [{"type": "string"}, {"type": "null"}],
- "title": "Sub Description",
- },
- "tags": {
- "items": {"type": "string"},
- "type": "array",
- "title": "Tags",
- "default": [],
- },
- },
- "type": "object",
- "required": ["subname", "sub_description", "tags"],
- "title": "SubItem",
- },
- "ValidationError": {
- "properties": {
- "loc": {
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- "type": "array",
- "title": "Location",
- },
- "msg": {"type": "string", "title": "Message"},
- "type": {"type": "string", "title": "Error Type"},
- },
- "type": "object",
- "required": ["loc", "msg", "type"],
- "title": "ValidationError",
- },
- }
- },
- }
+ }
+ )
@needs_pydanticv2
@@ -374,7 +397,19 @@ def test_openapi_schema_no_separate():
"responses": {
"200": {
"description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
+ "content": {
+ "application/json": {
+ "schema": {"$ref": "#/components/schemas/Item"}
+ }
+ },
+ },
+ "402": {
+ "description": "Payment Required",
+ "content": {
+ "application/json": {
+ "schema": {"$ref": "#/components/schemas/Item"}
+ }
+ },
},
"422": {
"description": "Validation Error",
diff --git a/tests/test_pydantic_v1_v2_01.py b/tests/test_pydantic_v1_v2_01.py
new file mode 100644
index 0000000000..769e5fab62
--- /dev/null
+++ b/tests/test_pydantic_v1_v2_01.py
@@ -0,0 +1,475 @@
+import sys
+from typing import Any, List, Union
+
+from tests.utils import pydantic_snapshot, skip_module_if_py_gte_314
+
+if sys.version_info >= (3, 14):
+ skip_module_if_py_gte_314()
+
+from fastapi import FastAPI
+from fastapi._compat.v1 import BaseModel
+from fastapi.testclient import TestClient
+from inline_snapshot import snapshot
+
+
+class SubItem(BaseModel):
+ name: str
+
+
+class Item(BaseModel):
+ title: str
+ size: int
+ description: Union[str, None] = None
+ sub: SubItem
+ multi: List[SubItem] = []
+
+
+app = FastAPI()
+
+
+@app.post("/simple-model")
+def handle_simple_model(data: SubItem) -> SubItem:
+ return data
+
+
+@app.post("/simple-model-filter", response_model=SubItem)
+def handle_simple_model_filter(data: SubItem) -> Any:
+ extended_data = data.dict()
+ extended_data.update({"secret_price": 42})
+ return extended_data
+
+
+@app.post("/item")
+def handle_item(data: Item) -> Item:
+ return data
+
+
+@app.post("/item-filter", response_model=Item)
+def handle_item_filter(data: Item) -> Any:
+ extended_data = data.dict()
+ extended_data.update({"secret_data": "classified", "internal_id": 12345})
+ extended_data["sub"].update({"internal_id": 67890})
+ return extended_data
+
+
+client = TestClient(app)
+
+
+def test_old_simple_model():
+ response = client.post(
+ "/simple-model",
+ json={"name": "Foo"},
+ )
+ assert response.status_code == 200, response.text
+ assert response.json() == {"name": "Foo"}
+
+
+def test_old_simple_model_validation_error():
+ response = client.post(
+ "/simple-model",
+ json={"wrong_name": "Foo"},
+ )
+ assert response.status_code == 422, response.text
+ assert response.json() == snapshot(
+ {
+ "detail": [
+ {
+ "loc": ["body", "name"],
+ "msg": "field required",
+ "type": "value_error.missing",
+ }
+ ]
+ }
+ )
+
+
+def test_old_simple_model_filter():
+ response = client.post(
+ "/simple-model-filter",
+ json={"name": "Foo"},
+ )
+ assert response.status_code == 200, response.text
+ assert response.json() == {"name": "Foo"}
+
+
+def test_item_model():
+ response = client.post(
+ "/item",
+ json={
+ "title": "Test Item",
+ "size": 100,
+ "description": "This is a test item",
+ "sub": {"name": "SubItem1"},
+ "multi": [{"name": "Multi1"}, {"name": "Multi2"}],
+ },
+ )
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "title": "Test Item",
+ "size": 100,
+ "description": "This is a test item",
+ "sub": {"name": "SubItem1"},
+ "multi": [{"name": "Multi1"}, {"name": "Multi2"}],
+ }
+
+
+def test_item_model_minimal():
+ response = client.post(
+ "/item",
+ json={"title": "Minimal Item", "size": 50, "sub": {"name": "SubMin"}},
+ )
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "title": "Minimal Item",
+ "size": 50,
+ "description": None,
+ "sub": {"name": "SubMin"},
+ "multi": [],
+ }
+
+
+def test_item_model_validation_errors():
+ response = client.post(
+ "/item",
+ json={"title": "Missing fields"},
+ )
+ assert response.status_code == 422, response.text
+ error_detail = response.json()["detail"]
+ assert len(error_detail) == 2
+ assert {
+ "loc": ["body", "size"],
+ "msg": "field required",
+ "type": "value_error.missing",
+ } in error_detail
+ assert {
+ "loc": ["body", "sub"],
+ "msg": "field required",
+ "type": "value_error.missing",
+ } in error_detail
+
+
+def test_item_model_nested_validation_error():
+ response = client.post(
+ "/item",
+ json={"title": "Test Item", "size": 100, "sub": {"wrong_field": "test"}},
+ )
+ assert response.status_code == 422, response.text
+ assert response.json() == snapshot(
+ {
+ "detail": [
+ {
+ "loc": ["body", "sub", "name"],
+ "msg": "field required",
+ "type": "value_error.missing",
+ }
+ ]
+ }
+ )
+
+
+def test_item_model_invalid_type():
+ response = client.post(
+ "/item",
+ json={"title": "Test Item", "size": "not_a_number", "sub": {"name": "SubItem"}},
+ )
+ assert response.status_code == 422, response.text
+ assert response.json() == snapshot(
+ {
+ "detail": [
+ {
+ "loc": ["body", "size"],
+ "msg": "value is not a valid integer",
+ "type": "type_error.integer",
+ }
+ ]
+ }
+ )
+
+
+def test_item_filter():
+ response = client.post(
+ "/item-filter",
+ json={
+ "title": "Filtered Item",
+ "size": 200,
+ "description": "Test filtering",
+ "sub": {"name": "SubFiltered"},
+ "multi": [],
+ },
+ )
+ assert response.status_code == 200, response.text
+ result = response.json()
+ assert result == {
+ "title": "Filtered Item",
+ "size": 200,
+ "description": "Test filtering",
+ "sub": {"name": "SubFiltered"},
+ "multi": [],
+ }
+ assert "secret_data" not in result
+ assert "internal_id" not in result
+
+
+def test_openapi_schema():
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == snapshot(
+ {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/simple-model": {
+ "post": {
+ "summary": "Handle Simple Model",
+ "operationId": "handle_simple_model_simple_model_post",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": pydantic_snapshot(
+ v2=snapshot(
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/SubItem"
+ }
+ ],
+ "title": "Data",
+ }
+ ),
+ v1=snapshot(
+ {"$ref": "#/components/schemas/SubItem"}
+ ),
+ )
+ }
+ },
+ "required": True,
+ },
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/SubItem"
+ }
+ }
+ },
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ }
+ },
+ "/simple-model-filter": {
+ "post": {
+ "summary": "Handle Simple Model Filter",
+ "operationId": "handle_simple_model_filter_simple_model_filter_post",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": pydantic_snapshot(
+ v2=snapshot(
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/SubItem"
+ }
+ ],
+ "title": "Data",
+ }
+ ),
+ v1=snapshot(
+ {"$ref": "#/components/schemas/SubItem"}
+ ),
+ )
+ }
+ },
+ "required": True,
+ },
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/SubItem"
+ }
+ }
+ },
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ }
+ },
+ "/item": {
+ "post": {
+ "summary": "Handle Item",
+ "operationId": "handle_item_item_post",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": pydantic_snapshot(
+ v2=snapshot(
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/Item"
+ }
+ ],
+ "title": "Data",
+ }
+ ),
+ v1=snapshot(
+ {"$ref": "#/components/schemas/Item"}
+ ),
+ )
+ }
+ },
+ "required": True,
+ },
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {"$ref": "#/components/schemas/Item"}
+ }
+ },
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ }
+ },
+ "/item-filter": {
+ "post": {
+ "summary": "Handle Item Filter",
+ "operationId": "handle_item_filter_item_filter_post",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": pydantic_snapshot(
+ v2=snapshot(
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/Item"
+ }
+ ],
+ "title": "Data",
+ }
+ ),
+ v1=snapshot(
+ {"$ref": "#/components/schemas/Item"}
+ ),
+ )
+ }
+ },
+ "required": True,
+ },
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {"$ref": "#/components/schemas/Item"}
+ }
+ },
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ }
+ },
+ },
+ "components": {
+ "schemas": {
+ "HTTPValidationError": {
+ "properties": {
+ "detail": {
+ "items": {
+ "$ref": "#/components/schemas/ValidationError"
+ },
+ "type": "array",
+ "title": "Detail",
+ }
+ },
+ "type": "object",
+ "title": "HTTPValidationError",
+ },
+ "Item": {
+ "properties": {
+ "title": {"type": "string", "title": "Title"},
+ "size": {"type": "integer", "title": "Size"},
+ "description": {"type": "string", "title": "Description"},
+ "sub": {"$ref": "#/components/schemas/SubItem"},
+ "multi": {
+ "items": {"$ref": "#/components/schemas/SubItem"},
+ "type": "array",
+ "title": "Multi",
+ "default": [],
+ },
+ },
+ "type": "object",
+ "required": ["title", "size", "sub"],
+ "title": "Item",
+ },
+ "SubItem": {
+ "properties": {"name": {"type": "string", "title": "Name"}},
+ "type": "object",
+ "required": ["name"],
+ "title": "SubItem",
+ },
+ "ValidationError": {
+ "properties": {
+ "loc": {
+ "items": {
+ "anyOf": [{"type": "string"}, {"type": "integer"}]
+ },
+ "type": "array",
+ "title": "Location",
+ },
+ "msg": {"type": "string", "title": "Message"},
+ "type": {"type": "string", "title": "Error Type"},
+ },
+ "type": "object",
+ "required": ["loc", "msg", "type"],
+ "title": "ValidationError",
+ },
+ }
+ },
+ }
+ )
diff --git a/tests/test_pydantic_v1_v2_list.py b/tests/test_pydantic_v1_v2_list.py
new file mode 100644
index 0000000000..64f3dd3446
--- /dev/null
+++ b/tests/test_pydantic_v1_v2_list.py
@@ -0,0 +1,701 @@
+import sys
+from typing import Any, List, Union
+
+from tests.utils import pydantic_snapshot, skip_module_if_py_gte_314
+
+if sys.version_info >= (3, 14):
+ skip_module_if_py_gte_314()
+
+from fastapi import FastAPI
+from fastapi._compat.v1 import BaseModel
+from fastapi.testclient import TestClient
+from inline_snapshot import snapshot
+
+
+class SubItem(BaseModel):
+ name: str
+
+
+class Item(BaseModel):
+ title: str
+ size: int
+ description: Union[str, None] = None
+ sub: SubItem
+ multi: List[SubItem] = []
+
+
+app = FastAPI()
+
+
+@app.post("/item")
+def handle_item(data: Item) -> List[Item]:
+ return [data, data]
+
+
+@app.post("/item-filter", response_model=List[Item])
+def handle_item_filter(data: Item) -> Any:
+ extended_data = data.dict()
+ extended_data.update({"secret_data": "classified", "internal_id": 12345})
+ extended_data["sub"].update({"internal_id": 67890})
+ return [extended_data, extended_data]
+
+
+@app.post("/item-list")
+def handle_item_list(data: List[Item]) -> Item:
+ if data:
+ return data[0]
+ return Item(title="", size=0, sub=SubItem(name=""))
+
+
+@app.post("/item-list-filter", response_model=Item)
+def handle_item_list_filter(data: List[Item]) -> Any:
+ if data:
+ extended_data = data[0].dict()
+ extended_data.update({"secret_data": "classified", "internal_id": 12345})
+ extended_data["sub"].update({"internal_id": 67890})
+ return extended_data
+ return Item(title="", size=0, sub=SubItem(name=""))
+
+
+@app.post("/item-list-to-list")
+def handle_item_list_to_list(data: List[Item]) -> List[Item]:
+ return data
+
+
+@app.post("/item-list-to-list-filter", response_model=List[Item])
+def handle_item_list_to_list_filter(data: List[Item]) -> Any:
+ if data:
+ extended_data = data[0].dict()
+ extended_data.update({"secret_data": "classified", "internal_id": 12345})
+ extended_data["sub"].update({"internal_id": 67890})
+ return [extended_data, extended_data]
+ return []
+
+
+client = TestClient(app)
+
+
+def test_item_to_list():
+ response = client.post(
+ "/item",
+ json={
+ "title": "Test Item",
+ "size": 100,
+ "description": "This is a test item",
+ "sub": {"name": "SubItem1"},
+ "multi": [{"name": "Multi1"}, {"name": "Multi2"}],
+ },
+ )
+ assert response.status_code == 200, response.text
+ result = response.json()
+ assert isinstance(result, list)
+ assert len(result) == 2
+ for item in result:
+ assert item == {
+ "title": "Test Item",
+ "size": 100,
+ "description": "This is a test item",
+ "sub": {"name": "SubItem1"},
+ "multi": [{"name": "Multi1"}, {"name": "Multi2"}],
+ }
+
+
+def test_item_to_list_filter():
+ response = client.post(
+ "/item-filter",
+ json={
+ "title": "Filtered Item",
+ "size": 200,
+ "description": "Test filtering",
+ "sub": {"name": "SubFiltered"},
+ "multi": [],
+ },
+ )
+ assert response.status_code == 200, response.text
+ result = response.json()
+ assert isinstance(result, list)
+ assert len(result) == 2
+ for item in result:
+ assert item == {
+ "title": "Filtered Item",
+ "size": 200,
+ "description": "Test filtering",
+ "sub": {"name": "SubFiltered"},
+ "multi": [],
+ }
+ # Verify secret fields are filtered out
+ assert "secret_data" not in item
+ assert "internal_id" not in item
+ assert "internal_id" not in item["sub"]
+
+
+def test_list_to_item():
+ response = client.post(
+ "/item-list",
+ json=[
+ {"title": "First Item", "size": 50, "sub": {"name": "First Sub"}},
+ {"title": "Second Item", "size": 75, "sub": {"name": "Second Sub"}},
+ ],
+ )
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "title": "First Item",
+ "size": 50,
+ "description": None,
+ "sub": {"name": "First Sub"},
+ "multi": [],
+ }
+
+
+def test_list_to_item_empty():
+ response = client.post(
+ "/item-list",
+ json=[],
+ )
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "title": "",
+ "size": 0,
+ "description": None,
+ "sub": {"name": ""},
+ "multi": [],
+ }
+
+
+def test_list_to_item_filter():
+ response = client.post(
+ "/item-list-filter",
+ json=[
+ {
+ "title": "First Item",
+ "size": 100,
+ "sub": {"name": "First Sub"},
+ "multi": [{"name": "Multi1"}],
+ },
+ {"title": "Second Item", "size": 200, "sub": {"name": "Second Sub"}},
+ ],
+ )
+ assert response.status_code == 200, response.text
+ result = response.json()
+ assert result == {
+ "title": "First Item",
+ "size": 100,
+ "description": None,
+ "sub": {"name": "First Sub"},
+ "multi": [{"name": "Multi1"}],
+ }
+ # Verify secret fields are filtered out
+ assert "secret_data" not in result
+ assert "internal_id" not in result
+
+
+def test_list_to_item_filter_no_data():
+ response = client.post("/item-list-filter", json=[])
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "title": "",
+ "size": 0,
+ "description": None,
+ "sub": {"name": ""},
+ "multi": [],
+ }
+
+
+def test_list_to_list():
+ input_items = [
+ {"title": "Item 1", "size": 10, "sub": {"name": "Sub1"}},
+ {
+ "title": "Item 2",
+ "size": 20,
+ "description": "Second item",
+ "sub": {"name": "Sub2"},
+ "multi": [{"name": "M1"}, {"name": "M2"}],
+ },
+ {"title": "Item 3", "size": 30, "sub": {"name": "Sub3"}},
+ ]
+ response = client.post(
+ "/item-list-to-list",
+ json=input_items,
+ )
+ assert response.status_code == 200, response.text
+ result = response.json()
+ assert isinstance(result, list)
+ assert len(result) == 3
+ assert result[0] == {
+ "title": "Item 1",
+ "size": 10,
+ "description": None,
+ "sub": {"name": "Sub1"},
+ "multi": [],
+ }
+ assert result[1] == {
+ "title": "Item 2",
+ "size": 20,
+ "description": "Second item",
+ "sub": {"name": "Sub2"},
+ "multi": [{"name": "M1"}, {"name": "M2"}],
+ }
+ assert result[2] == {
+ "title": "Item 3",
+ "size": 30,
+ "description": None,
+ "sub": {"name": "Sub3"},
+ "multi": [],
+ }
+
+
+def test_list_to_list_filter():
+ response = client.post(
+ "/item-list-to-list-filter",
+ json=[{"title": "Item 1", "size": 100, "sub": {"name": "Sub1"}}],
+ )
+ assert response.status_code == 200, response.text
+ result = response.json()
+ assert isinstance(result, list)
+ assert len(result) == 2
+ for item in result:
+ assert item == {
+ "title": "Item 1",
+ "size": 100,
+ "description": None,
+ "sub": {"name": "Sub1"},
+ "multi": [],
+ }
+ # Verify secret fields are filtered out
+ assert "secret_data" not in item
+ assert "internal_id" not in item
+
+
+def test_list_to_list_filter_no_data():
+ response = client.post(
+ "/item-list-to-list-filter",
+ json=[],
+ )
+ assert response.status_code == 200, response.text
+ assert response.json() == []
+
+
+def test_list_validation_error():
+ response = client.post(
+ "/item-list",
+ json=[
+ {"title": "Valid Item", "size": 100, "sub": {"name": "Sub1"}},
+ {
+ "title": "Invalid Item"
+ # Missing required fields: size and sub
+ },
+ ],
+ )
+ assert response.status_code == 422, response.text
+ error_detail = response.json()["detail"]
+ assert len(error_detail) == 2
+ assert {
+ "loc": ["body", 1, "size"],
+ "msg": "field required",
+ "type": "value_error.missing",
+ } in error_detail
+ assert {
+ "loc": ["body", 1, "sub"],
+ "msg": "field required",
+ "type": "value_error.missing",
+ } in error_detail
+
+
+def test_list_nested_validation_error():
+ response = client.post(
+ "/item-list",
+ json=[
+ {"title": "Item with bad sub", "size": 100, "sub": {"wrong_field": "value"}}
+ ],
+ )
+ assert response.status_code == 422, response.text
+ assert response.json() == snapshot(
+ {
+ "detail": [
+ {
+ "loc": ["body", 0, "sub", "name"],
+ "msg": "field required",
+ "type": "value_error.missing",
+ }
+ ]
+ }
+ )
+
+
+def test_list_type_validation_error():
+ response = client.post(
+ "/item-list",
+ json=[{"title": "Item", "size": "not_a_number", "sub": {"name": "Sub"}}],
+ )
+ assert response.status_code == 422, response.text
+ assert response.json() == snapshot(
+ {
+ "detail": [
+ {
+ "loc": ["body", 0, "size"],
+ "msg": "value is not a valid integer",
+ "type": "type_error.integer",
+ }
+ ]
+ }
+ )
+
+
+def test_invalid_list_structure():
+ response = client.post(
+ "/item-list",
+ json={"title": "Not a list", "size": 100, "sub": {"name": "Sub"}},
+ )
+ assert response.status_code == 422, response.text
+ assert response.json() == snapshot(
+ {
+ "detail": [
+ {
+ "loc": ["body"],
+ "msg": "value is not a valid list",
+ "type": "type_error.list",
+ }
+ ]
+ }
+ )
+
+
+def test_openapi_schema():
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == snapshot(
+ {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/item": {
+ "post": {
+ "summary": "Handle Item",
+ "operationId": "handle_item_item_post",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": pydantic_snapshot(
+ v2=snapshot(
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/Item"
+ }
+ ],
+ "title": "Data",
+ }
+ ),
+ v1=snapshot(
+ {"$ref": "#/components/schemas/Item"}
+ ),
+ )
+ }
+ },
+ "required": True,
+ },
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "items": {
+ "$ref": "#/components/schemas/Item"
+ },
+ "type": "array",
+ "title": "Response Handle Item Item Post",
+ }
+ }
+ },
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ }
+ },
+ "/item-filter": {
+ "post": {
+ "summary": "Handle Item Filter",
+ "operationId": "handle_item_filter_item_filter_post",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": pydantic_snapshot(
+ v2=snapshot(
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/Item"
+ }
+ ],
+ "title": "Data",
+ }
+ ),
+ v1=snapshot(
+ {"$ref": "#/components/schemas/Item"}
+ ),
+ )
+ }
+ },
+ "required": True,
+ },
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "items": {
+ "$ref": "#/components/schemas/Item"
+ },
+ "type": "array",
+ "title": "Response Handle Item Filter Item Filter Post",
+ }
+ }
+ },
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ }
+ },
+ "/item-list": {
+ "post": {
+ "summary": "Handle Item List",
+ "operationId": "handle_item_list_item_list_post",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "items": {"$ref": "#/components/schemas/Item"},
+ "type": "array",
+ "title": "Data",
+ }
+ }
+ },
+ "required": True,
+ },
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {"$ref": "#/components/schemas/Item"}
+ }
+ },
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ }
+ },
+ "/item-list-filter": {
+ "post": {
+ "summary": "Handle Item List Filter",
+ "operationId": "handle_item_list_filter_item_list_filter_post",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "items": {"$ref": "#/components/schemas/Item"},
+ "type": "array",
+ "title": "Data",
+ }
+ }
+ },
+ "required": True,
+ },
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {"$ref": "#/components/schemas/Item"}
+ }
+ },
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ }
+ },
+ "/item-list-to-list": {
+ "post": {
+ "summary": "Handle Item List To List",
+ "operationId": "handle_item_list_to_list_item_list_to_list_post",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "items": {"$ref": "#/components/schemas/Item"},
+ "type": "array",
+ "title": "Data",
+ }
+ }
+ },
+ "required": True,
+ },
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "items": {
+ "$ref": "#/components/schemas/Item"
+ },
+ "type": "array",
+ "title": "Response Handle Item List To List Item List To List Post",
+ }
+ }
+ },
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ }
+ },
+ "/item-list-to-list-filter": {
+ "post": {
+ "summary": "Handle Item List To List Filter",
+ "operationId": "handle_item_list_to_list_filter_item_list_to_list_filter_post",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "items": {"$ref": "#/components/schemas/Item"},
+ "type": "array",
+ "title": "Data",
+ }
+ }
+ },
+ "required": True,
+ },
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "items": {
+ "$ref": "#/components/schemas/Item"
+ },
+ "type": "array",
+ "title": "Response Handle Item List To List Filter Item List To List Filter Post",
+ }
+ }
+ },
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ }
+ },
+ },
+ "components": {
+ "schemas": {
+ "HTTPValidationError": {
+ "properties": {
+ "detail": {
+ "items": {
+ "$ref": "#/components/schemas/ValidationError"
+ },
+ "type": "array",
+ "title": "Detail",
+ }
+ },
+ "type": "object",
+ "title": "HTTPValidationError",
+ },
+ "Item": {
+ "properties": {
+ "title": {"type": "string", "title": "Title"},
+ "size": {"type": "integer", "title": "Size"},
+ "description": {"type": "string", "title": "Description"},
+ "sub": {"$ref": "#/components/schemas/SubItem"},
+ "multi": {
+ "items": {"$ref": "#/components/schemas/SubItem"},
+ "type": "array",
+ "title": "Multi",
+ "default": [],
+ },
+ },
+ "type": "object",
+ "required": ["title", "size", "sub"],
+ "title": "Item",
+ },
+ "SubItem": {
+ "properties": {"name": {"type": "string", "title": "Name"}},
+ "type": "object",
+ "required": ["name"],
+ "title": "SubItem",
+ },
+ "ValidationError": {
+ "properties": {
+ "loc": {
+ "items": {
+ "anyOf": [{"type": "string"}, {"type": "integer"}]
+ },
+ "type": "array",
+ "title": "Location",
+ },
+ "msg": {"type": "string", "title": "Message"},
+ "type": {"type": "string", "title": "Error Type"},
+ },
+ "type": "object",
+ "required": ["loc", "msg", "type"],
+ "title": "ValidationError",
+ },
+ }
+ },
+ }
+ )
diff --git a/tests/test_pydantic_v1_v2_mixed.py b/tests/test_pydantic_v1_v2_mixed.py
new file mode 100644
index 0000000000..54d408827f
--- /dev/null
+++ b/tests/test_pydantic_v1_v2_mixed.py
@@ -0,0 +1,1499 @@
+import sys
+from typing import Any, List, Union
+
+from tests.utils import pydantic_snapshot, skip_module_if_py_gte_314
+
+if sys.version_info >= (3, 14):
+ skip_module_if_py_gte_314()
+
+from fastapi import FastAPI
+from fastapi._compat.v1 import BaseModel
+from fastapi.testclient import TestClient
+from inline_snapshot import snapshot
+from pydantic import BaseModel as NewBaseModel
+
+
+class SubItem(BaseModel):
+ name: str
+
+
+class Item(BaseModel):
+ title: str
+ size: int
+ description: Union[str, None] = None
+ sub: SubItem
+ multi: List[SubItem] = []
+
+
+class NewSubItem(NewBaseModel):
+ new_sub_name: str
+
+
+class NewItem(NewBaseModel):
+ new_title: str
+ new_size: int
+ new_description: Union[str, None] = None
+ new_sub: NewSubItem
+ new_multi: List[NewSubItem] = []
+
+
+app = FastAPI()
+
+
+@app.post("/v1-to-v2/item")
+def handle_v1_item_to_v2(data: Item) -> NewItem:
+ return NewItem(
+ new_title=data.title,
+ new_size=data.size,
+ new_description=data.description,
+ new_sub=NewSubItem(new_sub_name=data.sub.name),
+ new_multi=[NewSubItem(new_sub_name=s.name) for s in data.multi],
+ )
+
+
+@app.post("/v1-to-v2/item-filter", response_model=NewItem)
+def handle_v1_item_to_v2_filter(data: Item) -> Any:
+ result = {
+ "new_title": data.title,
+ "new_size": data.size,
+ "new_description": data.description,
+ "new_sub": {"new_sub_name": data.sub.name, "new_sub_secret": "sub_hidden"},
+ "new_multi": [
+ {"new_sub_name": s.name, "new_sub_secret": "sub_hidden"} for s in data.multi
+ ],
+ "secret": "hidden_v1_to_v2",
+ }
+ return result
+
+
+@app.post("/v2-to-v1/item")
+def handle_v2_item_to_v1(data: NewItem) -> Item:
+ return Item(
+ title=data.new_title,
+ size=data.new_size,
+ description=data.new_description,
+ sub=SubItem(name=data.new_sub.new_sub_name),
+ multi=[SubItem(name=s.new_sub_name) for s in data.new_multi],
+ )
+
+
+@app.post("/v2-to-v1/item-filter", response_model=Item)
+def handle_v2_item_to_v1_filter(data: NewItem) -> Any:
+ result = {
+ "title": data.new_title,
+ "size": data.new_size,
+ "description": data.new_description,
+ "sub": {"name": data.new_sub.new_sub_name, "sub_secret": "sub_hidden"},
+ "multi": [
+ {"name": s.new_sub_name, "sub_secret": "sub_hidden"} for s in data.new_multi
+ ],
+ "secret": "hidden_v2_to_v1",
+ }
+ return result
+
+
+@app.post("/v1-to-v2/item-to-list")
+def handle_v1_item_to_v2_list(data: Item) -> List[NewItem]:
+ converted = NewItem(
+ new_title=data.title,
+ new_size=data.size,
+ new_description=data.description,
+ new_sub=NewSubItem(new_sub_name=data.sub.name),
+ new_multi=[NewSubItem(new_sub_name=s.name) for s in data.multi],
+ )
+ return [converted, converted]
+
+
+@app.post("/v1-to-v2/list-to-list")
+def handle_v1_list_to_v2_list(data: List[Item]) -> List[NewItem]:
+ result = []
+ for item in data:
+ result.append(
+ NewItem(
+ new_title=item.title,
+ new_size=item.size,
+ new_description=item.description,
+ new_sub=NewSubItem(new_sub_name=item.sub.name),
+ new_multi=[NewSubItem(new_sub_name=s.name) for s in item.multi],
+ )
+ )
+ return result
+
+
+@app.post("/v1-to-v2/list-to-list-filter", response_model=List[NewItem])
+def handle_v1_list_to_v2_list_filter(data: List[Item]) -> Any:
+ result = []
+ for item in data:
+ converted = {
+ "new_title": item.title,
+ "new_size": item.size,
+ "new_description": item.description,
+ "new_sub": {"new_sub_name": item.sub.name, "new_sub_secret": "sub_hidden"},
+ "new_multi": [
+ {"new_sub_name": s.name, "new_sub_secret": "sub_hidden"}
+ for s in item.multi
+ ],
+ "secret": "hidden_v2_to_v1",
+ }
+ result.append(converted)
+ return result
+
+
+@app.post("/v1-to-v2/list-to-item")
+def handle_v1_list_to_v2_item(data: List[Item]) -> NewItem:
+ if data:
+ item = data[0]
+ return NewItem(
+ new_title=item.title,
+ new_size=item.size,
+ new_description=item.description,
+ new_sub=NewSubItem(new_sub_name=item.sub.name),
+ new_multi=[NewSubItem(new_sub_name=s.name) for s in item.multi],
+ )
+ return NewItem(new_title="", new_size=0, new_sub=NewSubItem(new_sub_name=""))
+
+
+@app.post("/v2-to-v1/item-to-list")
+def handle_v2_item_to_v1_list(data: NewItem) -> List[Item]:
+ converted = Item(
+ title=data.new_title,
+ size=data.new_size,
+ description=data.new_description,
+ sub=SubItem(name=data.new_sub.new_sub_name),
+ multi=[SubItem(name=s.new_sub_name) for s in data.new_multi],
+ )
+ return [converted, converted]
+
+
+@app.post("/v2-to-v1/list-to-list")
+def handle_v2_list_to_v1_list(data: List[NewItem]) -> List[Item]:
+ result = []
+ for item in data:
+ result.append(
+ Item(
+ title=item.new_title,
+ size=item.new_size,
+ description=item.new_description,
+ sub=SubItem(name=item.new_sub.new_sub_name),
+ multi=[SubItem(name=s.new_sub_name) for s in item.new_multi],
+ )
+ )
+ return result
+
+
+@app.post("/v2-to-v1/list-to-list-filter", response_model=List[Item])
+def handle_v2_list_to_v1_list_filter(data: List[NewItem]) -> Any:
+ result = []
+ for item in data:
+ converted = {
+ "title": item.new_title,
+ "size": item.new_size,
+ "description": item.new_description,
+ "sub": {"name": item.new_sub.new_sub_name, "sub_secret": "sub_hidden"},
+ "multi": [
+ {"name": s.new_sub_name, "sub_secret": "sub_hidden"}
+ for s in item.new_multi
+ ],
+ "secret": "hidden_v2_to_v1",
+ }
+ result.append(converted)
+ return result
+
+
+@app.post("/v2-to-v1/list-to-item")
+def handle_v2_list_to_v1_item(data: List[NewItem]) -> Item:
+ if data:
+ item = data[0]
+ return Item(
+ title=item.new_title,
+ size=item.new_size,
+ description=item.new_description,
+ sub=SubItem(name=item.new_sub.new_sub_name),
+ multi=[SubItem(name=s.new_sub_name) for s in item.new_multi],
+ )
+ return Item(title="", size=0, sub=SubItem(name=""))
+
+
+client = TestClient(app)
+
+
+def test_v1_to_v2_item():
+ response = client.post(
+ "/v1-to-v2/item",
+ json={
+ "title": "Old Item",
+ "size": 100,
+ "description": "V1 description",
+ "sub": {"name": "V1 Sub"},
+ "multi": [{"name": "M1"}, {"name": "M2"}],
+ },
+ )
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "new_title": "Old Item",
+ "new_size": 100,
+ "new_description": "V1 description",
+ "new_sub": {"new_sub_name": "V1 Sub"},
+ "new_multi": [{"new_sub_name": "M1"}, {"new_sub_name": "M2"}],
+ }
+
+
+def test_v1_to_v2_item_minimal():
+ response = client.post(
+ "/v1-to-v2/item",
+ json={"title": "Minimal", "size": 50, "sub": {"name": "MinSub"}},
+ )
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "new_title": "Minimal",
+ "new_size": 50,
+ "new_description": None,
+ "new_sub": {"new_sub_name": "MinSub"},
+ "new_multi": [],
+ }
+
+
+def test_v1_to_v2_item_filter():
+ response = client.post(
+ "/v1-to-v2/item-filter",
+ json={
+ "title": "Filtered Item",
+ "size": 50,
+ "sub": {"name": "Sub"},
+ "multi": [{"name": "Multi1"}],
+ },
+ )
+ assert response.status_code == 200, response.text
+ result = response.json()
+ assert result == snapshot(
+ {
+ "new_title": "Filtered Item",
+ "new_size": 50,
+ "new_description": None,
+ "new_sub": {"new_sub_name": "Sub"},
+ "new_multi": [{"new_sub_name": "Multi1"}],
+ }
+ )
+ # Verify secret fields are filtered out
+ assert "secret" not in result
+ assert "new_sub_secret" not in result["new_sub"]
+ assert "new_sub_secret" not in result["new_multi"][0]
+
+
+def test_v2_to_v1_item():
+ response = client.post(
+ "/v2-to-v1/item",
+ json={
+ "new_title": "New Item",
+ "new_size": 200,
+ "new_description": "V2 description",
+ "new_sub": {"new_sub_name": "V2 Sub"},
+ "new_multi": [{"new_sub_name": "N1"}, {"new_sub_name": "N2"}],
+ },
+ )
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "title": "New Item",
+ "size": 200,
+ "description": "V2 description",
+ "sub": {"name": "V2 Sub"},
+ "multi": [{"name": "N1"}, {"name": "N2"}],
+ }
+
+
+def test_v2_to_v1_item_minimal():
+ response = client.post(
+ "/v2-to-v1/item",
+ json={
+ "new_title": "MinimalNew",
+ "new_size": 75,
+ "new_sub": {"new_sub_name": "MinNewSub"},
+ },
+ )
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "title": "MinimalNew",
+ "size": 75,
+ "description": None,
+ "sub": {"name": "MinNewSub"},
+ "multi": [],
+ }
+
+
+def test_v2_to_v1_item_filter():
+ response = client.post(
+ "/v2-to-v1/item-filter",
+ json={
+ "new_title": "Filtered New",
+ "new_size": 75,
+ "new_sub": {"new_sub_name": "NewSub"},
+ "new_multi": [],
+ },
+ )
+ assert response.status_code == 200, response.text
+ result = response.json()
+ assert result == snapshot(
+ {
+ "title": "Filtered New",
+ "size": 75,
+ "description": None,
+ "sub": {"name": "NewSub"},
+ "multi": [],
+ }
+ )
+ # Verify secret fields are filtered out
+ assert "secret" not in result
+ assert "sub_secret" not in result["sub"]
+
+
+def test_v1_item_to_v2_list():
+ response = client.post(
+ "/v1-to-v2/item-to-list",
+ json={
+ "title": "Single to List",
+ "size": 150,
+ "description": "Convert to list",
+ "sub": {"name": "Sub1"},
+ "multi": [],
+ },
+ )
+ assert response.status_code == 200, response.text
+ result = response.json()
+ assert result == [
+ {
+ "new_title": "Single to List",
+ "new_size": 150,
+ "new_description": "Convert to list",
+ "new_sub": {"new_sub_name": "Sub1"},
+ "new_multi": [],
+ },
+ {
+ "new_title": "Single to List",
+ "new_size": 150,
+ "new_description": "Convert to list",
+ "new_sub": {"new_sub_name": "Sub1"},
+ "new_multi": [],
+ },
+ ]
+
+
+def test_v1_list_to_v2_list():
+ response = client.post(
+ "/v1-to-v2/list-to-list",
+ json=[
+ {"title": "Item1", "size": 10, "sub": {"name": "Sub1"}},
+ {
+ "title": "Item2",
+ "size": 20,
+ "description": "Second item",
+ "sub": {"name": "Sub2"},
+ "multi": [{"name": "M1"}, {"name": "M2"}],
+ },
+ {"title": "Item3", "size": 30, "sub": {"name": "Sub3"}},
+ ],
+ )
+ assert response.status_code == 200, response.text
+ assert response.json() == [
+ {
+ "new_title": "Item1",
+ "new_size": 10,
+ "new_description": None,
+ "new_sub": {"new_sub_name": "Sub1"},
+ "new_multi": [],
+ },
+ {
+ "new_title": "Item2",
+ "new_size": 20,
+ "new_description": "Second item",
+ "new_sub": {"new_sub_name": "Sub2"},
+ "new_multi": [{"new_sub_name": "M1"}, {"new_sub_name": "M2"}],
+ },
+ {
+ "new_title": "Item3",
+ "new_size": 30,
+ "new_description": None,
+ "new_sub": {"new_sub_name": "Sub3"},
+ "new_multi": [],
+ },
+ ]
+
+
+def test_v1_list_to_v2_list_filter():
+ response = client.post(
+ "/v1-to-v2/list-to-list-filter",
+ json=[{"title": "FilterMe", "size": 30, "sub": {"name": "SubF"}}],
+ )
+ assert response.status_code == 200, response.text
+ result = response.json()
+ assert result == snapshot(
+ [
+ {
+ "new_title": "FilterMe",
+ "new_size": 30,
+ "new_description": None,
+ "new_sub": {"new_sub_name": "SubF"},
+ "new_multi": [],
+ }
+ ]
+ )
+ # Verify secret fields are filtered out
+ assert "secret" not in result[0]
+ assert "new_sub_secret" not in result[0]["new_sub"]
+
+
+def test_v1_list_to_v2_item():
+ response = client.post(
+ "/v1-to-v2/list-to-item",
+ json=[
+ {"title": "First", "size": 100, "sub": {"name": "FirstSub"}},
+ {"title": "Second", "size": 200, "sub": {"name": "SecondSub"}},
+ ],
+ )
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "new_title": "First",
+ "new_size": 100,
+ "new_description": None,
+ "new_sub": {"new_sub_name": "FirstSub"},
+ "new_multi": [],
+ }
+
+
+def test_v1_list_to_v2_item_empty():
+ response = client.post("/v1-to-v2/list-to-item", json=[])
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "new_title": "",
+ "new_size": 0,
+ "new_description": None,
+ "new_sub": {"new_sub_name": ""},
+ "new_multi": [],
+ }
+
+
+def test_v2_item_to_v1_list():
+ response = client.post(
+ "/v2-to-v1/item-to-list",
+ json={
+ "new_title": "Single New",
+ "new_size": 250,
+ "new_description": "New to list",
+ "new_sub": {"new_sub_name": "NewSub"},
+ "new_multi": [],
+ },
+ )
+ assert response.status_code == 200, response.text
+ assert response.json() == [
+ {
+ "title": "Single New",
+ "size": 250,
+ "description": "New to list",
+ "sub": {"name": "NewSub"},
+ "multi": [],
+ },
+ {
+ "title": "Single New",
+ "size": 250,
+ "description": "New to list",
+ "sub": {"name": "NewSub"},
+ "multi": [],
+ },
+ ]
+
+
+def test_v2_list_to_v1_list():
+ response = client.post(
+ "/v2-to-v1/list-to-list",
+ json=[
+ {"new_title": "New1", "new_size": 15, "new_sub": {"new_sub_name": "NS1"}},
+ {
+ "new_title": "New2",
+ "new_size": 25,
+ "new_description": "Second new",
+ "new_sub": {"new_sub_name": "NS2"},
+ "new_multi": [{"new_sub_name": "NM1"}],
+ },
+ ],
+ )
+ assert response.status_code == 200, response.text
+ assert response.json() == [
+ {
+ "title": "New1",
+ "size": 15,
+ "description": None,
+ "sub": {"name": "NS1"},
+ "multi": [],
+ },
+ {
+ "title": "New2",
+ "size": 25,
+ "description": "Second new",
+ "sub": {"name": "NS2"},
+ "multi": [{"name": "NM1"}],
+ },
+ ]
+
+
+def test_v2_list_to_v1_list_filter():
+ response = client.post(
+ "/v2-to-v1/list-to-list-filter",
+ json=[
+ {
+ "new_title": "FilterNew",
+ "new_size": 35,
+ "new_sub": {"new_sub_name": "NSF"},
+ }
+ ],
+ )
+ assert response.status_code == 200, response.text
+ result = response.json()
+ assert result == snapshot(
+ [
+ {
+ "title": "FilterNew",
+ "size": 35,
+ "description": None,
+ "sub": {"name": "NSF"},
+ "multi": [],
+ }
+ ]
+ )
+ # Verify secret fields are filtered out
+ assert "secret" not in result[0]
+ assert "sub_secret" not in result[0]["sub"]
+
+
+def test_v2_list_to_v1_item():
+ response = client.post(
+ "/v2-to-v1/list-to-item",
+ json=[
+ {
+ "new_title": "FirstNew",
+ "new_size": 300,
+ "new_sub": {"new_sub_name": "FNS"},
+ },
+ {
+ "new_title": "SecondNew",
+ "new_size": 400,
+ "new_sub": {"new_sub_name": "SNS"},
+ },
+ ],
+ )
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "title": "FirstNew",
+ "size": 300,
+ "description": None,
+ "sub": {"name": "FNS"},
+ "multi": [],
+ }
+
+
+def test_v2_list_to_v1_item_empty():
+ response = client.post("/v2-to-v1/list-to-item", json=[])
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "title": "",
+ "size": 0,
+ "description": None,
+ "sub": {"name": ""},
+ "multi": [],
+ }
+
+
+def test_v1_to_v2_validation_error():
+ response = client.post("/v1-to-v2/item", json={"title": "Missing fields"})
+ assert response.status_code == 422, response.text
+ assert response.json() == snapshot(
+ {
+ "detail": [
+ {
+ "loc": ["body", "size"],
+ "msg": "field required",
+ "type": "value_error.missing",
+ },
+ {
+ "loc": ["body", "sub"],
+ "msg": "field required",
+ "type": "value_error.missing",
+ },
+ ]
+ }
+ )
+
+
+def test_v1_to_v2_nested_validation_error():
+ response = client.post(
+ "/v1-to-v2/item",
+ json={"title": "Bad sub", "size": 100, "sub": {"wrong_field": "value"}},
+ )
+ assert response.status_code == 422, response.text
+ assert response.json() == snapshot(
+ {
+ "detail": [
+ {
+ "loc": ["body", "sub", "name"],
+ "msg": "field required",
+ "type": "value_error.missing",
+ }
+ ]
+ }
+ )
+
+
+def test_v1_to_v2_type_validation_error():
+ response = client.post(
+ "/v1-to-v2/item",
+ json={"title": "Bad type", "size": "not_a_number", "sub": {"name": "Sub"}},
+ )
+ assert response.status_code == 422, response.text
+ assert response.json() == snapshot(
+ {
+ "detail": [
+ {
+ "loc": ["body", "size"],
+ "msg": "value is not a valid integer",
+ "type": "type_error.integer",
+ }
+ ]
+ }
+ )
+
+
+def test_v2_to_v1_validation_error():
+ response = client.post(
+ "/v2-to-v1/item",
+ json={"new_title": "Missing fields"},
+ )
+ assert response.status_code == 422, response.text
+ assert response.json() == snapshot(
+ {
+ "detail": pydantic_snapshot(
+ v2=snapshot(
+ [
+ {
+ "type": "missing",
+ "loc": ["body", "new_size"],
+ "msg": "Field required",
+ "input": {"new_title": "Missing fields"},
+ },
+ {
+ "type": "missing",
+ "loc": ["body", "new_sub"],
+ "msg": "Field required",
+ "input": {"new_title": "Missing fields"},
+ },
+ ]
+ ),
+ v1=snapshot(
+ [
+ {
+ "loc": ["body", "new_size"],
+ "msg": "field required",
+ "type": "value_error.missing",
+ },
+ {
+ "loc": ["body", "new_sub"],
+ "msg": "field required",
+ "type": "value_error.missing",
+ },
+ ]
+ ),
+ )
+ }
+ )
+
+
+def test_v2_to_v1_nested_validation_error():
+ response = client.post(
+ "/v2-to-v1/item",
+ json={
+ "new_title": "Bad sub",
+ "new_size": 200,
+ "new_sub": {"wrong_field": "value"},
+ },
+ )
+ assert response.status_code == 422, response.text
+ assert response.json() == snapshot(
+ {
+ "detail": [
+ pydantic_snapshot(
+ v2=snapshot(
+ {
+ "type": "missing",
+ "loc": ["body", "new_sub", "new_sub_name"],
+ "msg": "Field required",
+ "input": {"wrong_field": "value"},
+ }
+ ),
+ v1=snapshot(
+ {
+ "loc": ["body", "new_sub", "new_sub_name"],
+ "msg": "field required",
+ "type": "value_error.missing",
+ }
+ ),
+ )
+ ]
+ }
+ )
+
+
+def test_v1_list_validation_error():
+ response = client.post(
+ "/v1-to-v2/list-to-list",
+ json=[
+ {"title": "Valid", "size": 10, "sub": {"name": "S"}},
+ {"title": "Invalid"},
+ ],
+ )
+ assert response.status_code == 422, response.text
+ assert response.json() == snapshot(
+ {
+ "detail": [
+ {
+ "loc": ["body", 1, "size"],
+ "msg": "field required",
+ "type": "value_error.missing",
+ },
+ {
+ "loc": ["body", 1, "sub"],
+ "msg": "field required",
+ "type": "value_error.missing",
+ },
+ ]
+ }
+ )
+
+
+def test_v2_list_validation_error():
+ response = client.post(
+ "/v2-to-v1/list-to-list",
+ json=[
+ {"new_title": "Valid", "new_size": 10, "new_sub": {"new_sub_name": "NS"}},
+ {"new_title": "Invalid"},
+ ],
+ )
+ assert response.status_code == 422, response.text
+ assert response.json() == snapshot(
+ {
+ "detail": pydantic_snapshot(
+ v2=snapshot(
+ [
+ {
+ "type": "missing",
+ "loc": ["body", 1, "new_size"],
+ "msg": "Field required",
+ "input": {"new_title": "Invalid"},
+ },
+ {
+ "type": "missing",
+ "loc": ["body", 1, "new_sub"],
+ "msg": "Field required",
+ "input": {"new_title": "Invalid"},
+ },
+ ]
+ ),
+ v1=snapshot(
+ [
+ {
+ "loc": ["body", 1, "new_size"],
+ "msg": "field required",
+ "type": "value_error.missing",
+ },
+ {
+ "loc": ["body", 1, "new_sub"],
+ "msg": "field required",
+ "type": "value_error.missing",
+ },
+ ]
+ ),
+ )
+ }
+ )
+
+
+def test_invalid_list_structure_v1():
+ response = client.post(
+ "/v1-to-v2/list-to-list",
+ json={"title": "Not a list", "size": 100, "sub": {"name": "Sub"}},
+ )
+ assert response.status_code == 422, response.text
+ assert response.json() == snapshot(
+ {
+ "detail": [
+ {
+ "loc": ["body"],
+ "msg": "value is not a valid list",
+ "type": "type_error.list",
+ }
+ ]
+ }
+ )
+
+
+def test_invalid_list_structure_v2():
+ response = client.post(
+ "/v2-to-v1/list-to-list",
+ json={
+ "new_title": "Not a list",
+ "new_size": 100,
+ "new_sub": {"new_sub_name": "Sub"},
+ },
+ )
+ assert response.status_code == 422, response.text
+ assert response.json() == snapshot(
+ {
+ "detail": pydantic_snapshot(
+ v2=snapshot(
+ [
+ {
+ "type": "list_type",
+ "loc": ["body"],
+ "msg": "Input should be a valid list",
+ "input": {
+ "new_title": "Not a list",
+ "new_size": 100,
+ "new_sub": {"new_sub_name": "Sub"},
+ },
+ }
+ ]
+ ),
+ v1=snapshot(
+ [
+ {
+ "loc": ["body"],
+ "msg": "value is not a valid list",
+ "type": "type_error.list",
+ }
+ ]
+ ),
+ )
+ }
+ )
+
+
+def test_openapi_schema():
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == snapshot(
+ {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/v1-to-v2/item": {
+ "post": {
+ "summary": "Handle V1 Item To V2",
+ "operationId": "handle_v1_item_to_v2_v1_to_v2_item_post",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": pydantic_snapshot(
+ v2=snapshot(
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/Item"
+ }
+ ],
+ "title": "Data",
+ }
+ ),
+ v1=snapshot(
+ {"$ref": "#/components/schemas/Item"}
+ ),
+ )
+ }
+ },
+ "required": True,
+ },
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/NewItem"
+ }
+ }
+ },
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ }
+ },
+ "/v1-to-v2/item-filter": {
+ "post": {
+ "summary": "Handle V1 Item To V2 Filter",
+ "operationId": "handle_v1_item_to_v2_filter_v1_to_v2_item_filter_post",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": pydantic_snapshot(
+ v2=snapshot(
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/Item"
+ }
+ ],
+ "title": "Data",
+ }
+ ),
+ v1=snapshot(
+ {"$ref": "#/components/schemas/Item"}
+ ),
+ )
+ }
+ },
+ "required": True,
+ },
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/NewItem"
+ }
+ }
+ },
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ }
+ },
+ "/v2-to-v1/item": {
+ "post": {
+ "summary": "Handle V2 Item To V1",
+ "operationId": "handle_v2_item_to_v1_v2_to_v1_item_post",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {"$ref": "#/components/schemas/NewItem"}
+ }
+ },
+ "required": True,
+ },
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {"$ref": "#/components/schemas/Item"}
+ }
+ },
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ }
+ },
+ "/v2-to-v1/item-filter": {
+ "post": {
+ "summary": "Handle V2 Item To V1 Filter",
+ "operationId": "handle_v2_item_to_v1_filter_v2_to_v1_item_filter_post",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {"$ref": "#/components/schemas/NewItem"}
+ }
+ },
+ "required": True,
+ },
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {"$ref": "#/components/schemas/Item"}
+ }
+ },
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ }
+ },
+ "/v1-to-v2/item-to-list": {
+ "post": {
+ "summary": "Handle V1 Item To V2 List",
+ "operationId": "handle_v1_item_to_v2_list_v1_to_v2_item_to_list_post",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": pydantic_snapshot(
+ v2=snapshot(
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/Item"
+ }
+ ],
+ "title": "Data",
+ }
+ ),
+ v1=snapshot(
+ {"$ref": "#/components/schemas/Item"}
+ ),
+ )
+ }
+ },
+ "required": True,
+ },
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "items": {
+ "$ref": "#/components/schemas/NewItem"
+ },
+ "type": "array",
+ "title": "Response Handle V1 Item To V2 List V1 To V2 Item To List Post",
+ }
+ }
+ },
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ }
+ },
+ "/v1-to-v2/list-to-list": {
+ "post": {
+ "summary": "Handle V1 List To V2 List",
+ "operationId": "handle_v1_list_to_v2_list_v1_to_v2_list_to_list_post",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "items": {"$ref": "#/components/schemas/Item"},
+ "type": "array",
+ "title": "Data",
+ }
+ }
+ },
+ "required": True,
+ },
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "items": {
+ "$ref": "#/components/schemas/NewItem"
+ },
+ "type": "array",
+ "title": "Response Handle V1 List To V2 List V1 To V2 List To List Post",
+ }
+ }
+ },
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ }
+ },
+ "/v1-to-v2/list-to-list-filter": {
+ "post": {
+ "summary": "Handle V1 List To V2 List Filter",
+ "operationId": "handle_v1_list_to_v2_list_filter_v1_to_v2_list_to_list_filter_post",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "items": {"$ref": "#/components/schemas/Item"},
+ "type": "array",
+ "title": "Data",
+ }
+ }
+ },
+ "required": True,
+ },
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "items": {
+ "$ref": "#/components/schemas/NewItem"
+ },
+ "type": "array",
+ "title": "Response Handle V1 List To V2 List Filter V1 To V2 List To List Filter Post",
+ }
+ }
+ },
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ }
+ },
+ "/v1-to-v2/list-to-item": {
+ "post": {
+ "summary": "Handle V1 List To V2 Item",
+ "operationId": "handle_v1_list_to_v2_item_v1_to_v2_list_to_item_post",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "items": {"$ref": "#/components/schemas/Item"},
+ "type": "array",
+ "title": "Data",
+ }
+ }
+ },
+ "required": True,
+ },
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/NewItem"
+ }
+ }
+ },
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ }
+ },
+ "/v2-to-v1/item-to-list": {
+ "post": {
+ "summary": "Handle V2 Item To V1 List",
+ "operationId": "handle_v2_item_to_v1_list_v2_to_v1_item_to_list_post",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {"$ref": "#/components/schemas/NewItem"}
+ }
+ },
+ "required": True,
+ },
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "items": {
+ "$ref": "#/components/schemas/Item"
+ },
+ "type": "array",
+ "title": "Response Handle V2 Item To V1 List V2 To V1 Item To List Post",
+ }
+ }
+ },
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ }
+ },
+ "/v2-to-v1/list-to-list": {
+ "post": {
+ "summary": "Handle V2 List To V1 List",
+ "operationId": "handle_v2_list_to_v1_list_v2_to_v1_list_to_list_post",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "items": {
+ "$ref": "#/components/schemas/NewItem"
+ },
+ "type": "array",
+ "title": "Data",
+ }
+ }
+ },
+ "required": True,
+ },
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "items": {
+ "$ref": "#/components/schemas/Item"
+ },
+ "type": "array",
+ "title": "Response Handle V2 List To V1 List V2 To V1 List To List Post",
+ }
+ }
+ },
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ }
+ },
+ "/v2-to-v1/list-to-list-filter": {
+ "post": {
+ "summary": "Handle V2 List To V1 List Filter",
+ "operationId": "handle_v2_list_to_v1_list_filter_v2_to_v1_list_to_list_filter_post",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "items": {
+ "$ref": "#/components/schemas/NewItem"
+ },
+ "type": "array",
+ "title": "Data",
+ }
+ }
+ },
+ "required": True,
+ },
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "items": {
+ "$ref": "#/components/schemas/Item"
+ },
+ "type": "array",
+ "title": "Response Handle V2 List To V1 List Filter V2 To V1 List To List Filter Post",
+ }
+ }
+ },
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ }
+ },
+ "/v2-to-v1/list-to-item": {
+ "post": {
+ "summary": "Handle V2 List To V1 Item",
+ "operationId": "handle_v2_list_to_v1_item_v2_to_v1_list_to_item_post",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "items": {
+ "$ref": "#/components/schemas/NewItem"
+ },
+ "type": "array",
+ "title": "Data",
+ }
+ }
+ },
+ "required": True,
+ },
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {"$ref": "#/components/schemas/Item"}
+ }
+ },
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ }
+ },
+ },
+ "components": {
+ "schemas": {
+ "HTTPValidationError": {
+ "properties": {
+ "detail": {
+ "items": {
+ "$ref": "#/components/schemas/ValidationError"
+ },
+ "type": "array",
+ "title": "Detail",
+ }
+ },
+ "type": "object",
+ "title": "HTTPValidationError",
+ },
+ "Item": {
+ "properties": {
+ "title": {"type": "string", "title": "Title"},
+ "size": {"type": "integer", "title": "Size"},
+ "description": {"type": "string", "title": "Description"},
+ "sub": {"$ref": "#/components/schemas/SubItem"},
+ "multi": {
+ "items": {"$ref": "#/components/schemas/SubItem"},
+ "type": "array",
+ "title": "Multi",
+ "default": [],
+ },
+ },
+ "type": "object",
+ "required": ["title", "size", "sub"],
+ "title": "Item",
+ },
+ "NewItem": {
+ "properties": {
+ "new_title": {"type": "string", "title": "New Title"},
+ "new_size": {"type": "integer", "title": "New Size"},
+ "new_description": pydantic_snapshot(
+ v2=snapshot(
+ {
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ "title": "New Description",
+ }
+ ),
+ v1=snapshot(
+ {"type": "string", "title": "New Description"}
+ ),
+ ),
+ "new_sub": {"$ref": "#/components/schemas/NewSubItem"},
+ "new_multi": {
+ "items": {"$ref": "#/components/schemas/NewSubItem"},
+ "type": "array",
+ "title": "New Multi",
+ "default": [],
+ },
+ },
+ "type": "object",
+ "required": ["new_title", "new_size", "new_sub"],
+ "title": "NewItem",
+ },
+ "NewSubItem": {
+ "properties": {
+ "new_sub_name": {"type": "string", "title": "New Sub Name"}
+ },
+ "type": "object",
+ "required": ["new_sub_name"],
+ "title": "NewSubItem",
+ },
+ "SubItem": {
+ "properties": {"name": {"type": "string", "title": "Name"}},
+ "type": "object",
+ "required": ["name"],
+ "title": "SubItem",
+ },
+ "ValidationError": {
+ "properties": {
+ "loc": {
+ "items": {
+ "anyOf": [{"type": "string"}, {"type": "integer"}]
+ },
+ "type": "array",
+ "title": "Location",
+ },
+ "msg": {"type": "string", "title": "Message"},
+ "type": {"type": "string", "title": "Error Type"},
+ },
+ "type": "object",
+ "required": ["loc", "msg", "type"],
+ "title": "ValidationError",
+ },
+ }
+ },
+ }
+ )
diff --git a/docs_src/sql_databases/sql_app/__init__.py b/tests/test_pydantic_v1_v2_multifile/__init__.py
similarity index 100%
rename from docs_src/sql_databases/sql_app/__init__.py
rename to tests/test_pydantic_v1_v2_multifile/__init__.py
diff --git a/tests/test_pydantic_v1_v2_multifile/main.py b/tests/test_pydantic_v1_v2_multifile/main.py
new file mode 100644
index 0000000000..8985cb7b4c
--- /dev/null
+++ b/tests/test_pydantic_v1_v2_multifile/main.py
@@ -0,0 +1,142 @@
+from typing import List
+
+from fastapi import FastAPI
+
+from . import modelsv1, modelsv2, modelsv2b
+
+app = FastAPI()
+
+
+@app.post("/v1-to-v2/item")
+def handle_v1_item_to_v2(data: modelsv1.Item) -> modelsv2.Item:
+ return modelsv2.Item(
+ new_title=data.title,
+ new_size=data.size,
+ new_description=data.description,
+ new_sub=modelsv2.SubItem(new_sub_name=data.sub.name),
+ new_multi=[modelsv2.SubItem(new_sub_name=s.name) for s in data.multi],
+ )
+
+
+@app.post("/v2-to-v1/item")
+def handle_v2_item_to_v1(data: modelsv2.Item) -> modelsv1.Item:
+ return modelsv1.Item(
+ title=data.new_title,
+ size=data.new_size,
+ description=data.new_description,
+ sub=modelsv1.SubItem(name=data.new_sub.new_sub_name),
+ multi=[modelsv1.SubItem(name=s.new_sub_name) for s in data.new_multi],
+ )
+
+
+@app.post("/v1-to-v2/item-to-list")
+def handle_v1_item_to_v2_list(data: modelsv1.Item) -> List[modelsv2.Item]:
+ converted = modelsv2.Item(
+ new_title=data.title,
+ new_size=data.size,
+ new_description=data.description,
+ new_sub=modelsv2.SubItem(new_sub_name=data.sub.name),
+ new_multi=[modelsv2.SubItem(new_sub_name=s.name) for s in data.multi],
+ )
+ return [converted, converted]
+
+
+@app.post("/v1-to-v2/list-to-list")
+def handle_v1_list_to_v2_list(data: List[modelsv1.Item]) -> List[modelsv2.Item]:
+ result = []
+ for item in data:
+ result.append(
+ modelsv2.Item(
+ new_title=item.title,
+ new_size=item.size,
+ new_description=item.description,
+ new_sub=modelsv2.SubItem(new_sub_name=item.sub.name),
+ new_multi=[modelsv2.SubItem(new_sub_name=s.name) for s in item.multi],
+ )
+ )
+ return result
+
+
+@app.post("/v1-to-v2/list-to-item")
+def handle_v1_list_to_v2_item(data: List[modelsv1.Item]) -> modelsv2.Item:
+ if data:
+ item = data[0]
+ return modelsv2.Item(
+ new_title=item.title,
+ new_size=item.size,
+ new_description=item.description,
+ new_sub=modelsv2.SubItem(new_sub_name=item.sub.name),
+ new_multi=[modelsv2.SubItem(new_sub_name=s.name) for s in item.multi],
+ )
+ return modelsv2.Item(
+ new_title="", new_size=0, new_sub=modelsv2.SubItem(new_sub_name="")
+ )
+
+
+@app.post("/v2-to-v1/item-to-list")
+def handle_v2_item_to_v1_list(data: modelsv2.Item) -> List[modelsv1.Item]:
+ converted = modelsv1.Item(
+ title=data.new_title,
+ size=data.new_size,
+ description=data.new_description,
+ sub=modelsv1.SubItem(name=data.new_sub.new_sub_name),
+ multi=[modelsv1.SubItem(name=s.new_sub_name) for s in data.new_multi],
+ )
+ return [converted, converted]
+
+
+@app.post("/v2-to-v1/list-to-list")
+def handle_v2_list_to_v1_list(data: List[modelsv2.Item]) -> List[modelsv1.Item]:
+ result = []
+ for item in data:
+ result.append(
+ modelsv1.Item(
+ title=item.new_title,
+ size=item.new_size,
+ description=item.new_description,
+ sub=modelsv1.SubItem(name=item.new_sub.new_sub_name),
+ multi=[modelsv1.SubItem(name=s.new_sub_name) for s in item.new_multi],
+ )
+ )
+ return result
+
+
+@app.post("/v2-to-v1/list-to-item")
+def handle_v2_list_to_v1_item(data: List[modelsv2.Item]) -> modelsv1.Item:
+ if data:
+ item = data[0]
+ return modelsv1.Item(
+ title=item.new_title,
+ size=item.new_size,
+ description=item.new_description,
+ sub=modelsv1.SubItem(name=item.new_sub.new_sub_name),
+ multi=[modelsv1.SubItem(name=s.new_sub_name) for s in item.new_multi],
+ )
+ return modelsv1.Item(title="", size=0, sub=modelsv1.SubItem(name=""))
+
+
+@app.post("/v2-to-v1/same-name")
+def handle_v2_same_name_to_v1(
+ item1: modelsv2.Item, item2: modelsv2b.Item
+) -> modelsv1.Item:
+ return modelsv1.Item(
+ title=item1.new_title,
+ size=item2.dup_size,
+ description=item1.new_description,
+ sub=modelsv1.SubItem(name=item1.new_sub.new_sub_name),
+ multi=[modelsv1.SubItem(name=s.dup_sub_name) for s in item2.dup_multi],
+ )
+
+
+@app.post("/v2-to-v1/list-of-items-to-list-of-items")
+def handle_v2_items_in_list_to_v1_item_in_list(
+ data1: List[modelsv2.ItemInList], data2: List[modelsv2b.ItemInList]
+) -> List[modelsv1.ItemInList]:
+ result = []
+ item1 = data1[0]
+ item2 = data2[0]
+ result = [
+ modelsv1.ItemInList(name1=item1.name2),
+ modelsv1.ItemInList(name1=item2.dup_name2),
+ ]
+ return result
diff --git a/tests/test_pydantic_v1_v2_multifile/modelsv1.py b/tests/test_pydantic_v1_v2_multifile/modelsv1.py
new file mode 100644
index 0000000000..889291a1a7
--- /dev/null
+++ b/tests/test_pydantic_v1_v2_multifile/modelsv1.py
@@ -0,0 +1,19 @@
+from typing import List, Union
+
+from fastapi._compat.v1 import BaseModel
+
+
+class SubItem(BaseModel):
+ name: str
+
+
+class Item(BaseModel):
+ title: str
+ size: int
+ description: Union[str, None] = None
+ sub: SubItem
+ multi: List[SubItem] = []
+
+
+class ItemInList(BaseModel):
+ name1: str
diff --git a/tests/test_pydantic_v1_v2_multifile/modelsv2.py b/tests/test_pydantic_v1_v2_multifile/modelsv2.py
new file mode 100644
index 0000000000..2c8c6ea356
--- /dev/null
+++ b/tests/test_pydantic_v1_v2_multifile/modelsv2.py
@@ -0,0 +1,19 @@
+from typing import List, Union
+
+from pydantic import BaseModel
+
+
+class SubItem(BaseModel):
+ new_sub_name: str
+
+
+class Item(BaseModel):
+ new_title: str
+ new_size: int
+ new_description: Union[str, None] = None
+ new_sub: SubItem
+ new_multi: List[SubItem] = []
+
+
+class ItemInList(BaseModel):
+ name2: str
diff --git a/tests/test_pydantic_v1_v2_multifile/modelsv2b.py b/tests/test_pydantic_v1_v2_multifile/modelsv2b.py
new file mode 100644
index 0000000000..dc0c06c669
--- /dev/null
+++ b/tests/test_pydantic_v1_v2_multifile/modelsv2b.py
@@ -0,0 +1,19 @@
+from typing import List, Union
+
+from pydantic import BaseModel
+
+
+class SubItem(BaseModel):
+ dup_sub_name: str
+
+
+class Item(BaseModel):
+ dup_title: str
+ dup_size: int
+ dup_description: Union[str, None] = None
+ dup_sub: SubItem
+ dup_multi: List[SubItem] = []
+
+
+class ItemInList(BaseModel):
+ dup_name2: str
diff --git a/tests/test_pydantic_v1_v2_multifile/test_multifile.py b/tests/test_pydantic_v1_v2_multifile/test_multifile.py
new file mode 100644
index 0000000000..4472bd73e3
--- /dev/null
+++ b/tests/test_pydantic_v1_v2_multifile/test_multifile.py
@@ -0,0 +1,1237 @@
+import sys
+
+from tests.utils import pydantic_snapshot, skip_module_if_py_gte_314
+
+if sys.version_info >= (3, 14):
+ skip_module_if_py_gte_314()
+
+from fastapi.testclient import TestClient
+from inline_snapshot import snapshot
+
+from .main import app
+
+client = TestClient(app)
+
+
+def test_v1_to_v2_item():
+ response = client.post(
+ "/v1-to-v2/item",
+ json={"title": "Test", "size": 10, "sub": {"name": "SubTest"}},
+ )
+ assert response.status_code == 200
+ assert response.json() == {
+ "new_title": "Test",
+ "new_size": 10,
+ "new_description": None,
+ "new_sub": {"new_sub_name": "SubTest"},
+ "new_multi": [],
+ }
+
+
+def test_v2_to_v1_item():
+ response = client.post(
+ "/v2-to-v1/item",
+ json={
+ "new_title": "NewTest",
+ "new_size": 20,
+ "new_sub": {"new_sub_name": "NewSubTest"},
+ },
+ )
+ assert response.status_code == 200
+ assert response.json() == {
+ "title": "NewTest",
+ "size": 20,
+ "description": None,
+ "sub": {"name": "NewSubTest"},
+ "multi": [],
+ }
+
+
+def test_v1_to_v2_item_to_list():
+ response = client.post(
+ "/v1-to-v2/item-to-list",
+ json={"title": "ListTest", "size": 30, "sub": {"name": "SubListTest"}},
+ )
+ assert response.status_code == 200
+ assert response.json() == [
+ {
+ "new_title": "ListTest",
+ "new_size": 30,
+ "new_description": None,
+ "new_sub": {"new_sub_name": "SubListTest"},
+ "new_multi": [],
+ },
+ {
+ "new_title": "ListTest",
+ "new_size": 30,
+ "new_description": None,
+ "new_sub": {"new_sub_name": "SubListTest"},
+ "new_multi": [],
+ },
+ ]
+
+
+def test_v1_to_v2_list_to_list():
+ response = client.post(
+ "/v1-to-v2/list-to-list",
+ json=[
+ {"title": "Item1", "size": 40, "sub": {"name": "Sub1"}},
+ {"title": "Item2", "size": 50, "sub": {"name": "Sub2"}},
+ ],
+ )
+ assert response.status_code == 200
+ assert response.json() == [
+ {
+ "new_title": "Item1",
+ "new_size": 40,
+ "new_description": None,
+ "new_sub": {"new_sub_name": "Sub1"},
+ "new_multi": [],
+ },
+ {
+ "new_title": "Item2",
+ "new_size": 50,
+ "new_description": None,
+ "new_sub": {"new_sub_name": "Sub2"},
+ "new_multi": [],
+ },
+ ]
+
+
+def test_v1_to_v2_list_to_item():
+ response = client.post(
+ "/v1-to-v2/list-to-item",
+ json=[
+ {"title": "FirstItem", "size": 60, "sub": {"name": "FirstSub"}},
+ {"title": "SecondItem", "size": 70, "sub": {"name": "SecondSub"}},
+ ],
+ )
+ assert response.status_code == 200
+ assert response.json() == {
+ "new_title": "FirstItem",
+ "new_size": 60,
+ "new_description": None,
+ "new_sub": {"new_sub_name": "FirstSub"},
+ "new_multi": [],
+ }
+
+
+def test_v2_to_v1_item_to_list():
+ response = client.post(
+ "/v2-to-v1/item-to-list",
+ json={
+ "new_title": "ListNew",
+ "new_size": 80,
+ "new_sub": {"new_sub_name": "SubListNew"},
+ },
+ )
+ assert response.status_code == 200
+ assert response.json() == [
+ {
+ "title": "ListNew",
+ "size": 80,
+ "description": None,
+ "sub": {"name": "SubListNew"},
+ "multi": [],
+ },
+ {
+ "title": "ListNew",
+ "size": 80,
+ "description": None,
+ "sub": {"name": "SubListNew"},
+ "multi": [],
+ },
+ ]
+
+
+def test_v2_to_v1_list_to_list():
+ response = client.post(
+ "/v2-to-v1/list-to-list",
+ json=[
+ {
+ "new_title": "New1",
+ "new_size": 90,
+ "new_sub": {"new_sub_name": "NewSub1"},
+ },
+ {
+ "new_title": "New2",
+ "new_size": 100,
+ "new_sub": {"new_sub_name": "NewSub2"},
+ },
+ ],
+ )
+ assert response.status_code == 200
+ assert response.json() == [
+ {
+ "title": "New1",
+ "size": 90,
+ "description": None,
+ "sub": {"name": "NewSub1"},
+ "multi": [],
+ },
+ {
+ "title": "New2",
+ "size": 100,
+ "description": None,
+ "sub": {"name": "NewSub2"},
+ "multi": [],
+ },
+ ]
+
+
+def test_v2_to_v1_list_to_item():
+ response = client.post(
+ "/v2-to-v1/list-to-item",
+ json=[
+ {
+ "new_title": "FirstNew",
+ "new_size": 110,
+ "new_sub": {"new_sub_name": "FirstNewSub"},
+ },
+ {
+ "new_title": "SecondNew",
+ "new_size": 120,
+ "new_sub": {"new_sub_name": "SecondNewSub"},
+ },
+ ],
+ )
+ assert response.status_code == 200
+ assert response.json() == {
+ "title": "FirstNew",
+ "size": 110,
+ "description": None,
+ "sub": {"name": "FirstNewSub"},
+ "multi": [],
+ }
+
+
+def test_v1_to_v2_list_to_item_empty():
+ response = client.post("/v1-to-v2/list-to-item", json=[])
+ assert response.status_code == 200
+ assert response.json() == {
+ "new_title": "",
+ "new_size": 0,
+ "new_description": None,
+ "new_sub": {"new_sub_name": ""},
+ "new_multi": [],
+ }
+
+
+def test_v2_to_v1_list_to_item_empty():
+ response = client.post("/v2-to-v1/list-to-item", json=[])
+ assert response.status_code == 200
+ assert response.json() == {
+ "title": "",
+ "size": 0,
+ "description": None,
+ "sub": {"name": ""},
+ "multi": [],
+ }
+
+
+def test_v2_same_name_to_v1():
+ response = client.post(
+ "/v2-to-v1/same-name",
+ json={
+ "item1": {
+ "new_title": "Title1",
+ "new_size": 100,
+ "new_description": "Description1",
+ "new_sub": {"new_sub_name": "Sub1"},
+ "new_multi": [{"new_sub_name": "Multi1"}],
+ },
+ "item2": {
+ "dup_title": "Title2",
+ "dup_size": 200,
+ "dup_description": "Description2",
+ "dup_sub": {"dup_sub_name": "Sub2"},
+ "dup_multi": [
+ {"dup_sub_name": "Multi2a"},
+ {"dup_sub_name": "Multi2b"},
+ ],
+ },
+ },
+ )
+ assert response.status_code == 200
+ assert response.json() == {
+ "title": "Title1",
+ "size": 200,
+ "description": "Description1",
+ "sub": {"name": "Sub1"},
+ "multi": [{"name": "Multi2a"}, {"name": "Multi2b"}],
+ }
+
+
+def test_v2_items_in_list_to_v1_item_in_list():
+ response = client.post(
+ "/v2-to-v1/list-of-items-to-list-of-items",
+ json={
+ "data1": [{"name2": "Item1"}, {"name2": "Item2"}],
+ "data2": [{"dup_name2": "Item3"}, {"dup_name2": "Item4"}],
+ },
+ )
+ assert response.status_code == 200, response.text
+ assert response.json() == [
+ {"name1": "Item1"},
+ {"name1": "Item3"},
+ ]
+
+
+def test_openapi_schema():
+ response = client.get("/openapi.json")
+ assert response.status_code == 200
+ assert response.json() == snapshot(
+ {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/v1-to-v2/item": {
+ "post": {
+ "summary": "Handle V1 Item To V2",
+ "operationId": "handle_v1_item_to_v2_v1_to_v2_item_post",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": pydantic_snapshot(
+ v2=snapshot(
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv1__Item"
+ }
+ ],
+ "title": "Data",
+ }
+ ),
+ v1=snapshot(
+ {
+ "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv1__Item"
+ }
+ ),
+ )
+ }
+ },
+ "required": True,
+ },
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv2__Item"
+ }
+ }
+ },
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ }
+ },
+ "/v2-to-v1/item": {
+ "post": {
+ "summary": "Handle V2 Item To V1",
+ "operationId": "handle_v2_item_to_v1_v2_to_v1_item_post",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": pydantic_snapshot(
+ v2=snapshot(
+ {
+ "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv2__Item-Input"
+ }
+ ),
+ v1=snapshot(
+ {
+ "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv2__Item"
+ }
+ ),
+ ),
+ }
+ },
+ "required": True,
+ },
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv1__Item"
+ }
+ }
+ },
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ }
+ },
+ "/v1-to-v2/item-to-list": {
+ "post": {
+ "summary": "Handle V1 Item To V2 List",
+ "operationId": "handle_v1_item_to_v2_list_v1_to_v2_item_to_list_post",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": pydantic_snapshot(
+ v2=snapshot(
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv1__Item"
+ }
+ ],
+ "title": "Data",
+ }
+ ),
+ v1=snapshot(
+ {
+ "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv1__Item"
+ }
+ ),
+ )
+ }
+ },
+ "required": True,
+ },
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "items": {
+ "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv2__Item"
+ },
+ "type": "array",
+ "title": "Response Handle V1 Item To V2 List V1 To V2 Item To List Post",
+ }
+ }
+ },
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ }
+ },
+ "/v1-to-v2/list-to-list": {
+ "post": {
+ "summary": "Handle V1 List To V2 List",
+ "operationId": "handle_v1_list_to_v2_list_v1_to_v2_list_to_list_post",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "items": {
+ "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv1__Item"
+ },
+ "type": "array",
+ "title": "Data",
+ }
+ }
+ },
+ "required": True,
+ },
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "items": {
+ "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv2__Item"
+ },
+ "type": "array",
+ "title": "Response Handle V1 List To V2 List V1 To V2 List To List Post",
+ }
+ }
+ },
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ }
+ },
+ "/v1-to-v2/list-to-item": {
+ "post": {
+ "summary": "Handle V1 List To V2 Item",
+ "operationId": "handle_v1_list_to_v2_item_v1_to_v2_list_to_item_post",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "items": {
+ "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv1__Item"
+ },
+ "type": "array",
+ "title": "Data",
+ }
+ }
+ },
+ "required": True,
+ },
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv2__Item"
+ }
+ }
+ },
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ }
+ },
+ "/v2-to-v1/item-to-list": {
+ "post": {
+ "summary": "Handle V2 Item To V1 List",
+ "operationId": "handle_v2_item_to_v1_list_v2_to_v1_item_to_list_post",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": pydantic_snapshot(
+ v2=snapshot(
+ {
+ "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv2__Item-Input"
+ }
+ ),
+ v1=snapshot(
+ {
+ "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv2__Item"
+ }
+ ),
+ ),
+ }
+ },
+ "required": True,
+ },
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "items": {
+ "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv1__Item"
+ },
+ "type": "array",
+ "title": "Response Handle V2 Item To V1 List V2 To V1 Item To List Post",
+ }
+ }
+ },
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ }
+ },
+ "/v2-to-v1/list-to-list": {
+ "post": {
+ "summary": "Handle V2 List To V1 List",
+ "operationId": "handle_v2_list_to_v1_list_v2_to_v1_list_to_list_post",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "items": pydantic_snapshot(
+ v2=snapshot(
+ {
+ "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv2__Item-Input"
+ }
+ ),
+ v1=snapshot(
+ {
+ "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv2__Item"
+ }
+ ),
+ ),
+ "type": "array",
+ "title": "Data",
+ }
+ }
+ },
+ "required": True,
+ },
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "items": {
+ "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv1__Item"
+ },
+ "type": "array",
+ "title": "Response Handle V2 List To V1 List V2 To V1 List To List Post",
+ }
+ }
+ },
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ }
+ },
+ "/v2-to-v1/list-to-item": {
+ "post": {
+ "summary": "Handle V2 List To V1 Item",
+ "operationId": "handle_v2_list_to_v1_item_v2_to_v1_list_to_item_post",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "items": pydantic_snapshot(
+ v2=snapshot(
+ {
+ "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv2__Item-Input"
+ }
+ ),
+ v1=snapshot(
+ {
+ "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv2__Item"
+ }
+ ),
+ ),
+ "type": "array",
+ "title": "Data",
+ }
+ }
+ },
+ "required": True,
+ },
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv1__Item"
+ }
+ }
+ },
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ }
+ },
+ "/v2-to-v1/same-name": {
+ "post": {
+ "summary": "Handle V2 Same Name To V1",
+ "operationId": "handle_v2_same_name_to_v1_v2_to_v1_same_name_post",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Body_handle_v2_same_name_to_v1_v2_to_v1_same_name_post"
+ }
+ }
+ },
+ "required": True,
+ },
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv1__Item"
+ }
+ }
+ },
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ }
+ },
+ "/v2-to-v1/list-of-items-to-list-of-items": {
+ "post": {
+ "summary": "Handle V2 Items In List To V1 Item In List",
+ "operationId": "handle_v2_items_in_list_to_v1_item_in_list_v2_to_v1_list_of_items_to_list_of_items_post",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Body_handle_v2_items_in_list_to_v1_item_in_list_v2_to_v1_list_of_items_to_list_of_items_post"
+ }
+ }
+ },
+ "required": True,
+ },
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "items": {
+ "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv1__ItemInList"
+ },
+ "type": "array",
+ "title": "Response Handle V2 Items In List To V1 Item In List V2 To V1 List Of Items To List Of Items Post",
+ }
+ }
+ },
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ }
+ },
+ },
+ "components": {
+ "schemas": pydantic_snapshot(
+ v1=snapshot(
+ {
+ "Body_handle_v2_items_in_list_to_v1_item_in_list_v2_to_v1_list_of_items_to_list_of_items_post": {
+ "properties": {
+ "data1": {
+ "items": {
+ "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv2__ItemInList"
+ },
+ "type": "array",
+ "title": "Data1",
+ },
+ "data2": {
+ "items": {
+ "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv2b__ItemInList"
+ },
+ "type": "array",
+ "title": "Data2",
+ },
+ },
+ "type": "object",
+ "required": ["data1", "data2"],
+ "title": "Body_handle_v2_items_in_list_to_v1_item_in_list_v2_to_v1_list_of_items_to_list_of_items_post",
+ },
+ "Body_handle_v2_same_name_to_v1_v2_to_v1_same_name_post": {
+ "properties": {
+ "item1": {
+ "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv2__Item"
+ },
+ "item2": {
+ "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv2b__Item"
+ },
+ },
+ "type": "object",
+ "required": ["item1", "item2"],
+ "title": "Body_handle_v2_same_name_to_v1_v2_to_v1_same_name_post",
+ },
+ "HTTPValidationError": {
+ "properties": {
+ "detail": {
+ "items": {
+ "$ref": "#/components/schemas/ValidationError"
+ },
+ "type": "array",
+ "title": "Detail",
+ }
+ },
+ "type": "object",
+ "title": "HTTPValidationError",
+ },
+ "ValidationError": {
+ "properties": {
+ "loc": {
+ "items": {
+ "anyOf": [
+ {"type": "string"},
+ {"type": "integer"},
+ ]
+ },
+ "type": "array",
+ "title": "Location",
+ },
+ "msg": {"type": "string", "title": "Message"},
+ "type": {"type": "string", "title": "Error Type"},
+ },
+ "type": "object",
+ "required": ["loc", "msg", "type"],
+ "title": "ValidationError",
+ },
+ "tests__test_pydantic_v1_v2_multifile__modelsv1__Item": {
+ "properties": {
+ "title": {"type": "string", "title": "Title"},
+ "size": {"type": "integer", "title": "Size"},
+ "description": {
+ "type": "string",
+ "title": "Description",
+ },
+ "sub": {
+ "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv1__SubItem"
+ },
+ "multi": {
+ "items": {
+ "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv1__SubItem"
+ },
+ "type": "array",
+ "title": "Multi",
+ "default": [],
+ },
+ },
+ "type": "object",
+ "required": ["title", "size", "sub"],
+ "title": "Item",
+ },
+ "tests__test_pydantic_v1_v2_multifile__modelsv1__ItemInList": {
+ "properties": {
+ "name1": {"type": "string", "title": "Name1"}
+ },
+ "type": "object",
+ "required": ["name1"],
+ "title": "ItemInList",
+ },
+ "tests__test_pydantic_v1_v2_multifile__modelsv1__SubItem": {
+ "properties": {
+ "name": {"type": "string", "title": "Name"}
+ },
+ "type": "object",
+ "required": ["name"],
+ "title": "SubItem",
+ },
+ "tests__test_pydantic_v1_v2_multifile__modelsv2__Item": {
+ "properties": {
+ "new_title": {
+ "type": "string",
+ "title": "New Title",
+ },
+ "new_size": {
+ "type": "integer",
+ "title": "New Size",
+ },
+ "new_description": {
+ "type": "string",
+ "title": "New Description",
+ },
+ "new_sub": {
+ "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv2__SubItem"
+ },
+ "new_multi": {
+ "items": {
+ "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv2__SubItem"
+ },
+ "type": "array",
+ "title": "New Multi",
+ "default": [],
+ },
+ },
+ "type": "object",
+ "required": ["new_title", "new_size", "new_sub"],
+ "title": "Item",
+ },
+ "tests__test_pydantic_v1_v2_multifile__modelsv2__ItemInList": {
+ "properties": {
+ "name2": {"type": "string", "title": "Name2"}
+ },
+ "type": "object",
+ "required": ["name2"],
+ "title": "ItemInList",
+ },
+ "tests__test_pydantic_v1_v2_multifile__modelsv2__SubItem": {
+ "properties": {
+ "new_sub_name": {
+ "type": "string",
+ "title": "New Sub Name",
+ }
+ },
+ "type": "object",
+ "required": ["new_sub_name"],
+ "title": "SubItem",
+ },
+ "tests__test_pydantic_v1_v2_multifile__modelsv2b__Item": {
+ "properties": {
+ "dup_title": {
+ "type": "string",
+ "title": "Dup Title",
+ },
+ "dup_size": {
+ "type": "integer",
+ "title": "Dup Size",
+ },
+ "dup_description": {
+ "type": "string",
+ "title": "Dup Description",
+ },
+ "dup_sub": {
+ "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv2b__SubItem"
+ },
+ "dup_multi": {
+ "items": {
+ "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv2b__SubItem"
+ },
+ "type": "array",
+ "title": "Dup Multi",
+ "default": [],
+ },
+ },
+ "type": "object",
+ "required": ["dup_title", "dup_size", "dup_sub"],
+ "title": "Item",
+ },
+ "tests__test_pydantic_v1_v2_multifile__modelsv2b__ItemInList": {
+ "properties": {
+ "dup_name2": {
+ "type": "string",
+ "title": "Dup Name2",
+ }
+ },
+ "type": "object",
+ "required": ["dup_name2"],
+ "title": "ItemInList",
+ },
+ "tests__test_pydantic_v1_v2_multifile__modelsv2b__SubItem": {
+ "properties": {
+ "dup_sub_name": {
+ "type": "string",
+ "title": "Dup Sub Name",
+ }
+ },
+ "type": "object",
+ "required": ["dup_sub_name"],
+ "title": "SubItem",
+ },
+ }
+ ),
+ v2=snapshot(
+ {
+ "Body_handle_v2_items_in_list_to_v1_item_in_list_v2_to_v1_list_of_items_to_list_of_items_post": {
+ "properties": {
+ "data1": {
+ "items": {
+ "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv2__ItemInList"
+ },
+ "type": "array",
+ "title": "Data1",
+ },
+ "data2": {
+ "items": {
+ "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv2b__ItemInList"
+ },
+ "type": "array",
+ "title": "Data2",
+ },
+ },
+ "type": "object",
+ "required": ["data1", "data2"],
+ "title": "Body_handle_v2_items_in_list_to_v1_item_in_list_v2_to_v1_list_of_items_to_list_of_items_post",
+ },
+ "Body_handle_v2_same_name_to_v1_v2_to_v1_same_name_post": {
+ "properties": {
+ "item1": {
+ "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv2__Item-Input"
+ },
+ "item2": {
+ "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv2b__Item"
+ },
+ },
+ "type": "object",
+ "required": ["item1", "item2"],
+ "title": "Body_handle_v2_same_name_to_v1_v2_to_v1_same_name_post",
+ },
+ "HTTPValidationError": {
+ "properties": {
+ "detail": {
+ "items": {
+ "$ref": "#/components/schemas/ValidationError"
+ },
+ "type": "array",
+ "title": "Detail",
+ }
+ },
+ "type": "object",
+ "title": "HTTPValidationError",
+ },
+ "SubItem-Output": {
+ "properties": {
+ "new_sub_name": {
+ "type": "string",
+ "title": "New Sub Name",
+ }
+ },
+ "type": "object",
+ "required": ["new_sub_name"],
+ "title": "SubItem",
+ },
+ "ValidationError": {
+ "properties": {
+ "loc": {
+ "items": {
+ "anyOf": [
+ {"type": "string"},
+ {"type": "integer"},
+ ]
+ },
+ "type": "array",
+ "title": "Location",
+ },
+ "msg": {"type": "string", "title": "Message"},
+ "type": {"type": "string", "title": "Error Type"},
+ },
+ "type": "object",
+ "required": ["loc", "msg", "type"],
+ "title": "ValidationError",
+ },
+ "tests__test_pydantic_v1_v2_multifile__modelsv1__Item": {
+ "properties": {
+ "title": {"type": "string", "title": "Title"},
+ "size": {"type": "integer", "title": "Size"},
+ "description": {
+ "type": "string",
+ "title": "Description",
+ },
+ "sub": {
+ "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv1__SubItem"
+ },
+ "multi": {
+ "items": {
+ "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv1__SubItem"
+ },
+ "type": "array",
+ "title": "Multi",
+ "default": [],
+ },
+ },
+ "type": "object",
+ "required": ["title", "size", "sub"],
+ "title": "Item",
+ },
+ "tests__test_pydantic_v1_v2_multifile__modelsv1__ItemInList": {
+ "properties": {
+ "name1": {"type": "string", "title": "Name1"}
+ },
+ "type": "object",
+ "required": ["name1"],
+ "title": "ItemInList",
+ },
+ "tests__test_pydantic_v1_v2_multifile__modelsv1__SubItem": {
+ "properties": {
+ "name": {"type": "string", "title": "Name"}
+ },
+ "type": "object",
+ "required": ["name"],
+ "title": "SubItem",
+ },
+ "tests__test_pydantic_v1_v2_multifile__modelsv2__Item": {
+ "properties": {
+ "new_title": {
+ "type": "string",
+ "title": "New Title",
+ },
+ "new_size": {
+ "type": "integer",
+ "title": "New Size",
+ },
+ "new_description": {
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ "title": "New Description",
+ },
+ "new_sub": {
+ "$ref": "#/components/schemas/SubItem-Output"
+ },
+ "new_multi": {
+ "items": {
+ "$ref": "#/components/schemas/SubItem-Output"
+ },
+ "type": "array",
+ "title": "New Multi",
+ "default": [],
+ },
+ },
+ "type": "object",
+ "required": ["new_title", "new_size", "new_sub"],
+ "title": "Item",
+ },
+ "tests__test_pydantic_v1_v2_multifile__modelsv2__Item-Input": {
+ "properties": {
+ "new_title": {
+ "type": "string",
+ "title": "New Title",
+ },
+ "new_size": {
+ "type": "integer",
+ "title": "New Size",
+ },
+ "new_description": {
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ "title": "New Description",
+ },
+ "new_sub": {
+ "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv2__SubItem"
+ },
+ "new_multi": {
+ "items": {
+ "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv2__SubItem"
+ },
+ "type": "array",
+ "title": "New Multi",
+ "default": [],
+ },
+ },
+ "type": "object",
+ "required": ["new_title", "new_size", "new_sub"],
+ "title": "Item",
+ },
+ "tests__test_pydantic_v1_v2_multifile__modelsv2__ItemInList": {
+ "properties": {
+ "name2": {"type": "string", "title": "Name2"}
+ },
+ "type": "object",
+ "required": ["name2"],
+ "title": "ItemInList",
+ },
+ "tests__test_pydantic_v1_v2_multifile__modelsv2__SubItem": {
+ "properties": {
+ "new_sub_name": {
+ "type": "string",
+ "title": "New Sub Name",
+ }
+ },
+ "type": "object",
+ "required": ["new_sub_name"],
+ "title": "SubItem",
+ },
+ "tests__test_pydantic_v1_v2_multifile__modelsv2b__Item": {
+ "properties": {
+ "dup_title": {
+ "type": "string",
+ "title": "Dup Title",
+ },
+ "dup_size": {
+ "type": "integer",
+ "title": "Dup Size",
+ },
+ "dup_description": {
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ "title": "Dup Description",
+ },
+ "dup_sub": {
+ "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv2b__SubItem"
+ },
+ "dup_multi": {
+ "items": {
+ "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv2b__SubItem"
+ },
+ "type": "array",
+ "title": "Dup Multi",
+ "default": [],
+ },
+ },
+ "type": "object",
+ "required": ["dup_title", "dup_size", "dup_sub"],
+ "title": "Item",
+ },
+ "tests__test_pydantic_v1_v2_multifile__modelsv2b__ItemInList": {
+ "properties": {
+ "dup_name2": {
+ "type": "string",
+ "title": "Dup Name2",
+ }
+ },
+ "type": "object",
+ "required": ["dup_name2"],
+ "title": "ItemInList",
+ },
+ "tests__test_pydantic_v1_v2_multifile__modelsv2b__SubItem": {
+ "properties": {
+ "dup_sub_name": {
+ "type": "string",
+ "title": "Dup Sub Name",
+ }
+ },
+ "type": "object",
+ "required": ["dup_sub_name"],
+ "title": "SubItem",
+ },
+ }
+ ),
+ ),
+ },
+ }
+ )
diff --git a/tests/test_pydantic_v1_v2_noneable.py b/tests/test_pydantic_v1_v2_noneable.py
new file mode 100644
index 0000000000..d2d6f6635b
--- /dev/null
+++ b/tests/test_pydantic_v1_v2_noneable.py
@@ -0,0 +1,766 @@
+import sys
+from typing import Any, List, Union
+
+from tests.utils import pydantic_snapshot, skip_module_if_py_gte_314
+
+if sys.version_info >= (3, 14):
+ skip_module_if_py_gte_314()
+
+from fastapi import FastAPI
+from fastapi._compat.v1 import BaseModel
+from fastapi.testclient import TestClient
+from inline_snapshot import snapshot
+from pydantic import BaseModel as NewBaseModel
+
+
+class SubItem(BaseModel):
+ name: str
+
+
+class Item(BaseModel):
+ title: str
+ size: int
+ description: Union[str, None] = None
+ sub: SubItem
+ multi: List[SubItem] = []
+
+
+class NewSubItem(NewBaseModel):
+ new_sub_name: str
+
+
+class NewItem(NewBaseModel):
+ new_title: str
+ new_size: int
+ new_description: Union[str, None] = None
+ new_sub: NewSubItem
+ new_multi: List[NewSubItem] = []
+
+
+app = FastAPI()
+
+
+@app.post("/v1-to-v2/")
+def handle_v1_item_to_v2(data: Item) -> Union[NewItem, None]:
+ if data.size < 0:
+ return None
+ return NewItem(
+ new_title=data.title,
+ new_size=data.size,
+ new_description=data.description,
+ new_sub=NewSubItem(new_sub_name=data.sub.name),
+ new_multi=[NewSubItem(new_sub_name=s.name) for s in data.multi],
+ )
+
+
+@app.post("/v1-to-v2/item-filter", response_model=Union[NewItem, None])
+def handle_v1_item_to_v2_filter(data: Item) -> Any:
+ if data.size < 0:
+ return None
+ result = {
+ "new_title": data.title,
+ "new_size": data.size,
+ "new_description": data.description,
+ "new_sub": {"new_sub_name": data.sub.name, "new_sub_secret": "sub_hidden"},
+ "new_multi": [
+ {"new_sub_name": s.name, "new_sub_secret": "sub_hidden"} for s in data.multi
+ ],
+ "secret": "hidden_v1_to_v2",
+ }
+ return result
+
+
+@app.post("/v2-to-v1/item")
+def handle_v2_item_to_v1(data: NewItem) -> Union[Item, None]:
+ if data.new_size < 0:
+ return None
+ return Item(
+ title=data.new_title,
+ size=data.new_size,
+ description=data.new_description,
+ sub=SubItem(name=data.new_sub.new_sub_name),
+ multi=[SubItem(name=s.new_sub_name) for s in data.new_multi],
+ )
+
+
+@app.post("/v2-to-v1/item-filter", response_model=Union[Item, None])
+def handle_v2_item_to_v1_filter(data: NewItem) -> Any:
+ if data.new_size < 0:
+ return None
+ result = {
+ "title": data.new_title,
+ "size": data.new_size,
+ "description": data.new_description,
+ "sub": {"name": data.new_sub.new_sub_name, "sub_secret": "sub_hidden"},
+ "multi": [
+ {"name": s.new_sub_name, "sub_secret": "sub_hidden"} for s in data.new_multi
+ ],
+ "secret": "hidden_v2_to_v1",
+ }
+ return result
+
+
+client = TestClient(app)
+
+
+def test_v1_to_v2_item_success():
+ response = client.post(
+ "/v1-to-v2/",
+ json={
+ "title": "Old Item",
+ "size": 100,
+ "description": "V1 description",
+ "sub": {"name": "V1 Sub"},
+ "multi": [{"name": "M1"}, {"name": "M2"}],
+ },
+ )
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "new_title": "Old Item",
+ "new_size": 100,
+ "new_description": "V1 description",
+ "new_sub": {"new_sub_name": "V1 Sub"},
+ "new_multi": [{"new_sub_name": "M1"}, {"new_sub_name": "M2"}],
+ }
+
+
+def test_v1_to_v2_item_returns_none():
+ response = client.post(
+ "/v1-to-v2/",
+ json={"title": "Invalid Item", "size": -10, "sub": {"name": "Sub"}},
+ )
+ assert response.status_code == 200, response.text
+ assert response.json() is None
+
+
+def test_v1_to_v2_item_minimal():
+ response = client.post(
+ "/v1-to-v2/", json={"title": "Minimal", "size": 50, "sub": {"name": "MinSub"}}
+ )
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "new_title": "Minimal",
+ "new_size": 50,
+ "new_description": None,
+ "new_sub": {"new_sub_name": "MinSub"},
+ "new_multi": [],
+ }
+
+
+def test_v1_to_v2_item_filter_success():
+ response = client.post(
+ "/v1-to-v2/item-filter",
+ json={
+ "title": "Filtered Item",
+ "size": 50,
+ "sub": {"name": "Sub"},
+ "multi": [{"name": "Multi1"}],
+ },
+ )
+ assert response.status_code == 200, response.text
+ result = response.json()
+ assert result["new_title"] == "Filtered Item"
+ assert result["new_size"] == 50
+ assert result["new_sub"]["new_sub_name"] == "Sub"
+ assert result["new_multi"][0]["new_sub_name"] == "Multi1"
+ # Verify secret fields are filtered out
+ assert "secret" not in result
+ assert "new_sub_secret" not in result["new_sub"]
+ assert "new_sub_secret" not in result["new_multi"][0]
+
+
+def test_v1_to_v2_item_filter_returns_none():
+ response = client.post(
+ "/v1-to-v2/item-filter",
+ json={"title": "Invalid", "size": -1, "sub": {"name": "Sub"}},
+ )
+ assert response.status_code == 200, response.text
+ assert response.json() is None
+
+
+def test_v2_to_v1_item_success():
+ response = client.post(
+ "/v2-to-v1/item",
+ json={
+ "new_title": "New Item",
+ "new_size": 200,
+ "new_description": "V2 description",
+ "new_sub": {"new_sub_name": "V2 Sub"},
+ "new_multi": [{"new_sub_name": "N1"}, {"new_sub_name": "N2"}],
+ },
+ )
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "title": "New Item",
+ "size": 200,
+ "description": "V2 description",
+ "sub": {"name": "V2 Sub"},
+ "multi": [{"name": "N1"}, {"name": "N2"}],
+ }
+
+
+def test_v2_to_v1_item_returns_none():
+ response = client.post(
+ "/v2-to-v1/item",
+ json={
+ "new_title": "Invalid New",
+ "new_size": -5,
+ "new_sub": {"new_sub_name": "NewSub"},
+ },
+ )
+ assert response.status_code == 200, response.text
+ assert response.json() is None
+
+
+def test_v2_to_v1_item_minimal():
+ response = client.post(
+ "/v2-to-v1/item",
+ json={
+ "new_title": "MinimalNew",
+ "new_size": 75,
+ "new_sub": {"new_sub_name": "MinNewSub"},
+ },
+ )
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "title": "MinimalNew",
+ "size": 75,
+ "description": None,
+ "sub": {"name": "MinNewSub"},
+ "multi": [],
+ }
+
+
+def test_v2_to_v1_item_filter_success():
+ response = client.post(
+ "/v2-to-v1/item-filter",
+ json={
+ "new_title": "Filtered New",
+ "new_size": 75,
+ "new_sub": {"new_sub_name": "NewSub"},
+ "new_multi": [],
+ },
+ )
+ assert response.status_code == 200, response.text
+ result = response.json()
+ assert result["title"] == "Filtered New"
+ assert result["size"] == 75
+ assert result["sub"]["name"] == "NewSub"
+ # Verify secret fields are filtered out
+ assert "secret" not in result
+ assert "sub_secret" not in result["sub"]
+
+
+def test_v2_to_v1_item_filter_returns_none():
+ response = client.post(
+ "/v2-to-v1/item-filter",
+ json={
+ "new_title": "Invalid Filtered",
+ "new_size": -100,
+ "new_sub": {"new_sub_name": "Sub"},
+ },
+ )
+ assert response.status_code == 200, response.text
+ assert response.json() is None
+
+
+def test_v1_to_v2_validation_error():
+ response = client.post("/v1-to-v2/", json={"title": "Missing fields"})
+ assert response.status_code == 422, response.text
+ assert response.json() == snapshot(
+ {
+ "detail": [
+ {
+ "loc": ["body", "size"],
+ "msg": "field required",
+ "type": "value_error.missing",
+ },
+ {
+ "loc": ["body", "sub"],
+ "msg": "field required",
+ "type": "value_error.missing",
+ },
+ ]
+ }
+ )
+
+
+def test_v1_to_v2_nested_validation_error():
+ response = client.post(
+ "/v1-to-v2/",
+ json={"title": "Bad sub", "size": 100, "sub": {"wrong_field": "value"}},
+ )
+ assert response.status_code == 422, response.text
+ error_detail = response.json()["detail"]
+ assert len(error_detail) == 1
+ assert error_detail[0]["loc"] == ["body", "sub", "name"]
+
+
+def test_v1_to_v2_type_validation_error():
+ response = client.post(
+ "/v1-to-v2/",
+ json={"title": "Bad type", "size": "not_a_number", "sub": {"name": "Sub"}},
+ )
+ assert response.status_code == 422, response.text
+ error_detail = response.json()["detail"]
+ assert len(error_detail) == 1
+ assert error_detail[0]["loc"] == ["body", "size"]
+
+
+def test_v2_to_v1_validation_error():
+ response = client.post("/v2-to-v1/item", json={"new_title": "Missing fields"})
+ assert response.status_code == 422, response.text
+ assert response.json() == snapshot(
+ {
+ "detail": pydantic_snapshot(
+ v2=snapshot(
+ [
+ {
+ "type": "missing",
+ "loc": ["body", "new_size"],
+ "msg": "Field required",
+ "input": {"new_title": "Missing fields"},
+ },
+ {
+ "type": "missing",
+ "loc": ["body", "new_sub"],
+ "msg": "Field required",
+ "input": {"new_title": "Missing fields"},
+ },
+ ]
+ ),
+ v1=snapshot(
+ [
+ {
+ "loc": ["body", "new_size"],
+ "msg": "field required",
+ "type": "value_error.missing",
+ },
+ {
+ "loc": ["body", "new_sub"],
+ "msg": "field required",
+ "type": "value_error.missing",
+ },
+ ]
+ ),
+ )
+ }
+ )
+
+
+def test_v2_to_v1_nested_validation_error():
+ response = client.post(
+ "/v2-to-v1/item",
+ json={
+ "new_title": "Bad sub",
+ "new_size": 200,
+ "new_sub": {"wrong_field": "value"},
+ },
+ )
+ assert response.status_code == 422, response.text
+ assert response.json() == snapshot(
+ {
+ "detail": [
+ pydantic_snapshot(
+ v2=snapshot(
+ {
+ "type": "missing",
+ "loc": ["body", "new_sub", "new_sub_name"],
+ "msg": "Field required",
+ "input": {"wrong_field": "value"},
+ }
+ ),
+ v1=snapshot(
+ {
+ "loc": ["body", "new_sub", "new_sub_name"],
+ "msg": "field required",
+ "type": "value_error.missing",
+ }
+ ),
+ )
+ ]
+ }
+ )
+
+
+def test_v2_to_v1_type_validation_error():
+ response = client.post(
+ "/v2-to-v1/item",
+ json={
+ "new_title": "Bad type",
+ "new_size": "not_a_number",
+ "new_sub": {"new_sub_name": "Sub"},
+ },
+ )
+ assert response.status_code == 422, response.text
+ assert response.json() == snapshot(
+ {
+ "detail": [
+ pydantic_snapshot(
+ v2=snapshot(
+ {
+ "type": "int_parsing",
+ "loc": ["body", "new_size"],
+ "msg": "Input should be a valid integer, unable to parse string as an integer",
+ "input": "not_a_number",
+ }
+ ),
+ v1=snapshot(
+ {
+ "loc": ["body", "new_size"],
+ "msg": "value is not a valid integer",
+ "type": "type_error.integer",
+ }
+ ),
+ )
+ ]
+ }
+ )
+
+
+def test_v1_to_v2_with_multi_items():
+ response = client.post(
+ "/v1-to-v2/",
+ json={
+ "title": "Complex Item",
+ "size": 300,
+ "description": "Item with multiple sub-items",
+ "sub": {"name": "Main Sub"},
+ "multi": [{"name": "Sub1"}, {"name": "Sub2"}, {"name": "Sub3"}],
+ },
+ )
+ assert response.status_code == 200, response.text
+ assert response.json() == snapshot(
+ {
+ "new_title": "Complex Item",
+ "new_size": 300,
+ "new_description": "Item with multiple sub-items",
+ "new_sub": {"new_sub_name": "Main Sub"},
+ "new_multi": [
+ {"new_sub_name": "Sub1"},
+ {"new_sub_name": "Sub2"},
+ {"new_sub_name": "Sub3"},
+ ],
+ }
+ )
+
+
+def test_v2_to_v1_with_multi_items():
+ response = client.post(
+ "/v2-to-v1/item",
+ json={
+ "new_title": "Complex New Item",
+ "new_size": 400,
+ "new_description": "New item with multiple sub-items",
+ "new_sub": {"new_sub_name": "Main New Sub"},
+ "new_multi": [{"new_sub_name": "NewSub1"}, {"new_sub_name": "NewSub2"}],
+ },
+ )
+ assert response.status_code == 200, response.text
+ assert response.json() == snapshot(
+ {
+ "title": "Complex New Item",
+ "size": 400,
+ "description": "New item with multiple sub-items",
+ "sub": {"name": "Main New Sub"},
+ "multi": [{"name": "NewSub1"}, {"name": "NewSub2"}],
+ }
+ )
+
+
+def test_openapi_schema():
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == snapshot(
+ {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/v1-to-v2/": {
+ "post": {
+ "summary": "Handle V1 Item To V2",
+ "operationId": "handle_v1_item_to_v2_v1_to_v2__post",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": pydantic_snapshot(
+ v2=snapshot(
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/Item"
+ }
+ ],
+ "title": "Data",
+ }
+ ),
+ v1=snapshot(
+ {"$ref": "#/components/schemas/Item"}
+ ),
+ )
+ }
+ },
+ "required": True,
+ },
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": pydantic_snapshot(
+ v2=snapshot(
+ {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/NewItem"
+ },
+ {"type": "null"},
+ ],
+ "title": "Response Handle V1 Item To V2 V1 To V2 Post",
+ }
+ ),
+ v1=snapshot(
+ {"$ref": "#/components/schemas/NewItem"}
+ ),
+ )
+ }
+ },
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ }
+ },
+ "/v1-to-v2/item-filter": {
+ "post": {
+ "summary": "Handle V1 Item To V2 Filter",
+ "operationId": "handle_v1_item_to_v2_filter_v1_to_v2_item_filter_post",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": pydantic_snapshot(
+ v2=snapshot(
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/Item"
+ }
+ ],
+ "title": "Data",
+ }
+ ),
+ v1=snapshot(
+ {"$ref": "#/components/schemas/Item"}
+ ),
+ )
+ }
+ },
+ "required": True,
+ },
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": pydantic_snapshot(
+ v2=snapshot(
+ {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/NewItem"
+ },
+ {"type": "null"},
+ ],
+ "title": "Response Handle V1 Item To V2 Filter V1 To V2 Item Filter Post",
+ }
+ ),
+ v1=snapshot(
+ {"$ref": "#/components/schemas/NewItem"}
+ ),
+ )
+ }
+ },
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ }
+ },
+ "/v2-to-v1/item": {
+ "post": {
+ "summary": "Handle V2 Item To V1",
+ "operationId": "handle_v2_item_to_v1_v2_to_v1_item_post",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {"$ref": "#/components/schemas/NewItem"}
+ }
+ },
+ "required": True,
+ },
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {"$ref": "#/components/schemas/Item"}
+ }
+ },
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ }
+ },
+ "/v2-to-v1/item-filter": {
+ "post": {
+ "summary": "Handle V2 Item To V1 Filter",
+ "operationId": "handle_v2_item_to_v1_filter_v2_to_v1_item_filter_post",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {"$ref": "#/components/schemas/NewItem"}
+ }
+ },
+ "required": True,
+ },
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {"$ref": "#/components/schemas/Item"}
+ }
+ },
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ }
+ },
+ },
+ "components": {
+ "schemas": {
+ "HTTPValidationError": {
+ "properties": {
+ "detail": {
+ "items": {
+ "$ref": "#/components/schemas/ValidationError"
+ },
+ "type": "array",
+ "title": "Detail",
+ }
+ },
+ "type": "object",
+ "title": "HTTPValidationError",
+ },
+ "Item": {
+ "properties": {
+ "title": {"type": "string", "title": "Title"},
+ "size": {"type": "integer", "title": "Size"},
+ "description": {"type": "string", "title": "Description"},
+ "sub": {"$ref": "#/components/schemas/SubItem"},
+ "multi": {
+ "items": {"$ref": "#/components/schemas/SubItem"},
+ "type": "array",
+ "title": "Multi",
+ "default": [],
+ },
+ },
+ "type": "object",
+ "required": ["title", "size", "sub"],
+ "title": "Item",
+ },
+ "NewItem": {
+ "properties": {
+ "new_title": {"type": "string", "title": "New Title"},
+ "new_size": {"type": "integer", "title": "New Size"},
+ "new_description": pydantic_snapshot(
+ v2=snapshot(
+ {
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ "title": "New Description",
+ }
+ ),
+ v1=snapshot(
+ {"type": "string", "title": "New Description"}
+ ),
+ ),
+ "new_sub": {"$ref": "#/components/schemas/NewSubItem"},
+ "new_multi": {
+ "items": {"$ref": "#/components/schemas/NewSubItem"},
+ "type": "array",
+ "title": "New Multi",
+ "default": [],
+ },
+ },
+ "type": "object",
+ "required": ["new_title", "new_size", "new_sub"],
+ "title": "NewItem",
+ },
+ "NewSubItem": {
+ "properties": {
+ "new_sub_name": {"type": "string", "title": "New Sub Name"}
+ },
+ "type": "object",
+ "required": ["new_sub_name"],
+ "title": "NewSubItem",
+ },
+ "SubItem": {
+ "properties": {"name": {"type": "string", "title": "Name"}},
+ "type": "object",
+ "required": ["name"],
+ "title": "SubItem",
+ },
+ "ValidationError": {
+ "properties": {
+ "loc": {
+ "items": {
+ "anyOf": [{"type": "string"}, {"type": "integer"}]
+ },
+ "type": "array",
+ "title": "Location",
+ },
+ "msg": {"type": "string", "title": "Message"},
+ "type": {"type": "string", "title": "Error Type"},
+ },
+ "type": "object",
+ "required": ["loc", "msg", "type"],
+ "title": "ValidationError",
+ },
+ }
+ },
+ }
+ )
diff --git a/tests/test_repeated_dependency_schema.py b/tests/test_repeated_dependency_schema.py
index d7d0dfa052..c21829bd97 100644
--- a/tests/test_repeated_dependency_schema.py
+++ b/tests/test_repeated_dependency_schema.py
@@ -41,7 +41,7 @@ schema = {
"type": "array",
},
"msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error " "Type", "type": "string"},
+ "type": {"title": "Error Type", "type": "string"},
},
"required": ["loc", "msg", "type"],
"title": "ValidationError",
@@ -66,7 +66,7 @@ schema = {
"responses": {
"200": {
"content": {"application/json": {"schema": {}}},
- "description": "Successful " "Response",
+ "description": "Successful Response",
},
"422": {
"content": {
@@ -76,7 +76,7 @@ schema = {
}
}
},
- "description": "Validation " "Error",
+ "description": "Validation Error",
},
},
"summary": "Get Deps",
diff --git a/tests/test_response_model_as_return_annotation.py b/tests/test_response_model_as_return_annotation.py
index 6948430a13..1745c69b60 100644
--- a/tests/test_response_model_as_return_annotation.py
+++ b/tests/test_response_model_as_return_annotation.py
@@ -7,6 +7,8 @@ from fastapi.responses import JSONResponse, Response
from fastapi.testclient import TestClient
from pydantic import BaseModel
+from tests.utils import needs_pydanticv1
+
class BaseUser(BaseModel):
name: str
@@ -509,6 +511,26 @@ def test_invalid_response_model_field():
assert "parameter response_model=None" in e.value.args[0]
+# TODO: remove when dropping Pydantic v1 support
+@needs_pydanticv1
+def test_invalid_response_model_field_pv1():
+ from fastapi._compat import v1
+
+ app = FastAPI()
+
+ class Model(v1.BaseModel):
+ foo: str
+
+ with pytest.raises(FastAPIError) as e:
+
+ @app.get("/")
+ def read_root() -> Union[Response, Model, None]:
+ return Response(content="Foo") # pragma: no cover
+
+ assert "valid Pydantic field type" in e.value.args[0]
+ assert "parameter response_model=None" in e.value.args[0]
+
+
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
diff --git a/tests/test_response_model_default_factory.py b/tests/test_response_model_default_factory.py
new file mode 100644
index 0000000000..13c1f442ba
--- /dev/null
+++ b/tests/test_response_model_default_factory.py
@@ -0,0 +1,47 @@
+from fastapi import FastAPI
+from fastapi.testclient import TestClient
+from pydantic import BaseModel, Field
+
+app = FastAPI()
+
+
+class ResponseModel(BaseModel):
+ code: int = 200
+ message: str = Field(default_factory=lambda: "Successful operation.")
+
+
+@app.get(
+ "/response_model_has_default_factory_return_dict",
+ response_model=ResponseModel,
+)
+async def response_model_has_default_factory_return_dict():
+ return {"code": 200}
+
+
+@app.get(
+ "/response_model_has_default_factory_return_model",
+ response_model=ResponseModel,
+)
+async def response_model_has_default_factory_return_model():
+ return ResponseModel()
+
+
+client = TestClient(app)
+
+
+def test_response_model_has_default_factory_return_dict():
+ response = client.get("/response_model_has_default_factory_return_dict")
+
+ assert response.status_code == 200, response.text
+
+ assert response.json()["code"] == 200
+ assert response.json()["message"] == "Successful operation."
+
+
+def test_response_model_has_default_factory_return_model():
+ response = client.get("/response_model_has_default_factory_return_model")
+
+ assert response.status_code == 200, response.text
+
+ assert response.json()["code"] == 200
+ assert response.json()["message"] == "Successful operation."
diff --git a/tests/test_return_none_stringified_annotations.py b/tests/test_return_none_stringified_annotations.py
new file mode 100644
index 0000000000..be052d532e
--- /dev/null
+++ b/tests/test_return_none_stringified_annotations.py
@@ -0,0 +1,17 @@
+import http
+
+from fastapi import FastAPI
+from fastapi.testclient import TestClient
+
+
+def test_no_content():
+ app = FastAPI()
+
+ @app.get("/no-content", status_code=http.HTTPStatus.NO_CONTENT)
+ def return_no_content() -> "None":
+ return
+
+ client = TestClient(app)
+ response = client.get("/no-content")
+ assert response.status_code == http.HTTPStatus.NO_CONTENT, response.text
+ assert not response.content
diff --git a/tests/test_route_scope.py b/tests/test_route_scope.py
index 2021c828f4..792ea66c3a 100644
--- a/tests/test_route_scope.py
+++ b/tests/test_route_scope.py
@@ -47,4 +47,4 @@ def test_websocket():
def test_websocket_invalid_path_doesnt_match():
with pytest.raises(WebSocketDisconnect):
with client.websocket_connect("/itemsx/portal-gun"):
- pass
+ pass # pragma: no cover
diff --git a/tests/test_security_http_digest_optional.py b/tests/test_security_http_digest_optional.py
index 1e6eb8bd7f..0d66f9c72e 100644
--- a/tests/test_security_http_digest_optional.py
+++ b/tests/test_security_http_digest_optional.py
@@ -37,8 +37,8 @@ def test_security_http_digest_incorrect_scheme_credentials():
response = client.get(
"/users/me", headers={"Authorization": "Other invalidauthorization"}
)
- assert response.status_code == 403, response.text
- assert response.json() == {"detail": "Invalid authentication credentials"}
+ assert response.status_code == 200, response.text
+ assert response.json() == {"msg": "Create an account first"}
def test_openapi_schema():
diff --git a/tests/test_security_oauth2.py b/tests/test_security_oauth2.py
index 7d914d0345..2b7e3457a9 100644
--- a/tests/test_security_oauth2.py
+++ b/tests/test_security_oauth2.py
@@ -1,3 +1,4 @@
+import pytest
from dirty_equals import IsDict
from fastapi import Depends, FastAPI, Security
from fastapi.security import OAuth2, OAuth2PasswordRequestFormStrict
@@ -137,10 +138,18 @@ def test_strict_login_no_grant_type():
)
-def test_strict_login_incorrect_grant_type():
+@pytest.mark.parametrize(
+ argnames=["grant_type"],
+ argvalues=[
+ pytest.param("incorrect", id="incorrect value"),
+ pytest.param("passwordblah", id="password with suffix"),
+ pytest.param("blahpassword", id="password with prefix"),
+ ],
+)
+def test_strict_login_incorrect_grant_type(grant_type: str):
response = client.post(
"/login",
- data={"username": "johndoe", "password": "secret", "grant_type": "incorrect"},
+ data={"username": "johndoe", "password": "secret", "grant_type": grant_type},
)
assert response.status_code == 422
assert response.json() == IsDict(
@@ -149,9 +158,9 @@ def test_strict_login_incorrect_grant_type():
{
"type": "string_pattern_mismatch",
"loc": ["body", "grant_type"],
- "msg": "String should match pattern 'password'",
- "input": "incorrect",
- "ctx": {"pattern": "password"},
+ "msg": "String should match pattern '^password$'",
+ "input": grant_type,
+ "ctx": {"pattern": "^password$"},
}
]
}
@@ -161,9 +170,9 @@ def test_strict_login_incorrect_grant_type():
"detail": [
{
"loc": ["body", "grant_type"],
- "msg": 'string does not match regex "password"',
+ "msg": 'string does not match regex "^password$"',
"type": "value_error.str.regex",
- "ctx": {"pattern": "password"},
+ "ctx": {"pattern": "^password$"},
}
]
}
@@ -248,7 +257,7 @@ def test_openapi_schema():
"properties": {
"grant_type": {
"title": "Grant Type",
- "pattern": "password",
+ "pattern": "^password$",
"type": "string",
},
"username": {"title": "Username", "type": "string"},
diff --git a/tests/test_security_oauth2_optional.py b/tests/test_security_oauth2_optional.py
index 0da3b911e8..046ac57637 100644
--- a/tests/test_security_oauth2_optional.py
+++ b/tests/test_security_oauth2_optional.py
@@ -1,5 +1,6 @@
from typing import Optional
+import pytest
from dirty_equals import IsDict
from fastapi import Depends, FastAPI, Security
from fastapi.security import OAuth2, OAuth2PasswordRequestFormStrict
@@ -141,10 +142,18 @@ def test_strict_login_no_grant_type():
)
-def test_strict_login_incorrect_grant_type():
+@pytest.mark.parametrize(
+ argnames=["grant_type"],
+ argvalues=[
+ pytest.param("incorrect", id="incorrect value"),
+ pytest.param("passwordblah", id="password with suffix"),
+ pytest.param("blahpassword", id="password with prefix"),
+ ],
+)
+def test_strict_login_incorrect_grant_type(grant_type: str):
response = client.post(
"/login",
- data={"username": "johndoe", "password": "secret", "grant_type": "incorrect"},
+ data={"username": "johndoe", "password": "secret", "grant_type": grant_type},
)
assert response.status_code == 422
assert response.json() == IsDict(
@@ -153,9 +162,9 @@ def test_strict_login_incorrect_grant_type():
{
"type": "string_pattern_mismatch",
"loc": ["body", "grant_type"],
- "msg": "String should match pattern 'password'",
- "input": "incorrect",
- "ctx": {"pattern": "password"},
+ "msg": "String should match pattern '^password$'",
+ "input": grant_type,
+ "ctx": {"pattern": "^password$"},
}
]
}
@@ -165,9 +174,9 @@ def test_strict_login_incorrect_grant_type():
"detail": [
{
"loc": ["body", "grant_type"],
- "msg": 'string does not match regex "password"',
+ "msg": 'string does not match regex "^password$"',
"type": "value_error.str.regex",
- "ctx": {"pattern": "password"},
+ "ctx": {"pattern": "^password$"},
}
]
}
@@ -252,7 +261,7 @@ def test_openapi_schema():
"properties": {
"grant_type": {
"title": "Grant Type",
- "pattern": "password",
+ "pattern": "^password$",
"type": "string",
},
"username": {"title": "Username", "type": "string"},
diff --git a/tests/test_security_oauth2_optional_description.py b/tests/test_security_oauth2_optional_description.py
index 85a9f9b391..629cddca2f 100644
--- a/tests/test_security_oauth2_optional_description.py
+++ b/tests/test_security_oauth2_optional_description.py
@@ -1,5 +1,6 @@
from typing import Optional
+import pytest
from dirty_equals import IsDict
from fastapi import Depends, FastAPI, Security
from fastapi.security import OAuth2, OAuth2PasswordRequestFormStrict
@@ -142,10 +143,18 @@ def test_strict_login_no_grant_type():
)
-def test_strict_login_incorrect_grant_type():
+@pytest.mark.parametrize(
+ argnames=["grant_type"],
+ argvalues=[
+ pytest.param("incorrect", id="incorrect value"),
+ pytest.param("passwordblah", id="password with suffix"),
+ pytest.param("blahpassword", id="password with prefix"),
+ ],
+)
+def test_strict_login_incorrect_grant_type(grant_type: str):
response = client.post(
"/login",
- data={"username": "johndoe", "password": "secret", "grant_type": "incorrect"},
+ data={"username": "johndoe", "password": "secret", "grant_type": grant_type},
)
assert response.status_code == 422
assert response.json() == IsDict(
@@ -154,9 +163,9 @@ def test_strict_login_incorrect_grant_type():
{
"type": "string_pattern_mismatch",
"loc": ["body", "grant_type"],
- "msg": "String should match pattern 'password'",
- "input": "incorrect",
- "ctx": {"pattern": "password"},
+ "msg": "String should match pattern '^password$'",
+ "input": grant_type,
+ "ctx": {"pattern": "^password$"},
}
]
}
@@ -166,9 +175,9 @@ def test_strict_login_incorrect_grant_type():
"detail": [
{
"loc": ["body", "grant_type"],
- "msg": 'string does not match regex "password"',
+ "msg": 'string does not match regex "^password$"',
"type": "value_error.str.regex",
- "ctx": {"pattern": "password"},
+ "ctx": {"pattern": "^password$"},
}
]
}
@@ -253,7 +262,7 @@ def test_openapi_schema():
"properties": {
"grant_type": {
"title": "Grant Type",
- "pattern": "password",
+ "pattern": "^password$",
"type": "string",
},
"username": {"title": "Username", "type": "string"},
diff --git a/tests/test_tutorial/test_additional_status_codes/test_tutorial001.py b/tests/test_tutorial/test_additional_status_codes/test_tutorial001.py
index c382cb5fe2..b304f70153 100644
--- a/tests/test_tutorial/test_additional_status_codes/test_tutorial001.py
+++ b/tests/test_tutorial/test_additional_status_codes/test_tutorial001.py
@@ -1,17 +1,35 @@
+import importlib
+
+import pytest
from fastapi.testclient import TestClient
-from docs_src.additional_status_codes.tutorial001 import app
-
-client = TestClient(app)
+from ...utils import needs_py39, needs_py310
-def test_update():
+@pytest.fixture(
+ name="client",
+ params=[
+ "tutorial001",
+ pytest.param("tutorial001_py310", marks=needs_py310),
+ "tutorial001_an",
+ pytest.param("tutorial001_an_py39", marks=needs_py39),
+ pytest.param("tutorial001_an_py310", marks=needs_py310),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.additional_status_codes.{request.param}")
+
+ client = TestClient(mod.app)
+ return client
+
+
+def test_update(client: TestClient):
response = client.put("/items/foo", json={"name": "Wrestlers"})
assert response.status_code == 200, response.text
assert response.json() == {"name": "Wrestlers", "size": None}
-def test_create():
+def test_create(client: TestClient):
response = client.put("/items/red", json={"name": "Chillies"})
assert response.status_code == 201, response.text
assert response.json() == {"name": "Chillies", "size": None}
diff --git a/tests/test_tutorial/test_additional_status_codes/test_tutorial001_an.py b/tests/test_tutorial/test_additional_status_codes/test_tutorial001_an.py
deleted file mode 100644
index 2cb2bb9930..0000000000
--- a/tests/test_tutorial/test_additional_status_codes/test_tutorial001_an.py
+++ /dev/null
@@ -1,17 +0,0 @@
-from fastapi.testclient import TestClient
-
-from docs_src.additional_status_codes.tutorial001_an import app
-
-client = TestClient(app)
-
-
-def test_update():
- response = client.put("/items/foo", json={"name": "Wrestlers"})
- assert response.status_code == 200, response.text
- assert response.json() == {"name": "Wrestlers", "size": None}
-
-
-def test_create():
- response = client.put("/items/red", json={"name": "Chillies"})
- assert response.status_code == 201, response.text
- assert response.json() == {"name": "Chillies", "size": None}
diff --git a/tests/test_tutorial/test_additional_status_codes/test_tutorial001_an_py310.py b/tests/test_tutorial/test_additional_status_codes/test_tutorial001_an_py310.py
deleted file mode 100644
index c7660a3925..0000000000
--- a/tests/test_tutorial/test_additional_status_codes/test_tutorial001_an_py310.py
+++ /dev/null
@@ -1,26 +0,0 @@
-import pytest
-from fastapi.testclient import TestClient
-
-from ...utils import needs_py310
-
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.additional_status_codes.tutorial001_an_py310 import app
-
- client = TestClient(app)
- return client
-
-
-@needs_py310
-def test_update(client: TestClient):
- response = client.put("/items/foo", json={"name": "Wrestlers"})
- assert response.status_code == 200, response.text
- assert response.json() == {"name": "Wrestlers", "size": None}
-
-
-@needs_py310
-def test_create(client: TestClient):
- response = client.put("/items/red", json={"name": "Chillies"})
- assert response.status_code == 201, response.text
- assert response.json() == {"name": "Chillies", "size": None}
diff --git a/tests/test_tutorial/test_additional_status_codes/test_tutorial001_an_py39.py b/tests/test_tutorial/test_additional_status_codes/test_tutorial001_an_py39.py
deleted file mode 100644
index 303c5dbae2..0000000000
--- a/tests/test_tutorial/test_additional_status_codes/test_tutorial001_an_py39.py
+++ /dev/null
@@ -1,26 +0,0 @@
-import pytest
-from fastapi.testclient import TestClient
-
-from ...utils import needs_py39
-
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.additional_status_codes.tutorial001_an_py39 import app
-
- client = TestClient(app)
- return client
-
-
-@needs_py39
-def test_update(client: TestClient):
- response = client.put("/items/foo", json={"name": "Wrestlers"})
- assert response.status_code == 200, response.text
- assert response.json() == {"name": "Wrestlers", "size": None}
-
-
-@needs_py39
-def test_create(client: TestClient):
- response = client.put("/items/red", json={"name": "Chillies"})
- assert response.status_code == 201, response.text
- assert response.json() == {"name": "Chillies", "size": None}
diff --git a/tests/test_tutorial/test_additional_status_codes/test_tutorial001_py310.py b/tests/test_tutorial/test_additional_status_codes/test_tutorial001_py310.py
deleted file mode 100644
index 02f2e188c7..0000000000
--- a/tests/test_tutorial/test_additional_status_codes/test_tutorial001_py310.py
+++ /dev/null
@@ -1,26 +0,0 @@
-import pytest
-from fastapi.testclient import TestClient
-
-from ...utils import needs_py310
-
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.additional_status_codes.tutorial001_py310 import app
-
- client = TestClient(app)
- return client
-
-
-@needs_py310
-def test_update(client: TestClient):
- response = client.put("/items/foo", json={"name": "Wrestlers"})
- assert response.status_code == 200, response.text
- assert response.json() == {"name": "Wrestlers", "size": None}
-
-
-@needs_py310
-def test_create(client: TestClient):
- response = client.put("/items/red", json={"name": "Chillies"})
- assert response.status_code == 201, response.text
- assert response.json() == {"name": "Chillies", "size": None}
diff --git a/tests/test_tutorial/test_async_sql_databases/__init__.py b/tests/test_tutorial/test_async_sql_databases/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/tests/test_tutorial/test_background_tasks/test_tutorial002.py b/tests/test_tutorial/test_background_tasks/test_tutorial002.py
index 74de1314bc..d5ef51ee2b 100644
--- a/tests/test_tutorial/test_background_tasks/test_tutorial002.py
+++ b/tests/test_tutorial/test_background_tasks/test_tutorial002.py
@@ -1,14 +1,31 @@
+import importlib
import os
from pathlib import Path
+import pytest
from fastapi.testclient import TestClient
-from docs_src.background_tasks.tutorial002 import app
-
-client = TestClient(app)
+from ...utils import needs_py39, needs_py310
-def test():
+@pytest.fixture(
+ name="client",
+ params=[
+ "tutorial002",
+ pytest.param("tutorial002_py310", marks=needs_py310),
+ "tutorial002_an",
+ pytest.param("tutorial002_an_py39", marks=needs_py39),
+ pytest.param("tutorial002_an_py310", marks=needs_py310),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.background_tasks.{request.param}")
+
+ client = TestClient(mod.app)
+ return client
+
+
+def test(client: TestClient):
log = Path("log.txt")
if log.is_file():
os.remove(log) # pragma: no cover
diff --git a/tests/test_tutorial/test_background_tasks/test_tutorial002_an.py b/tests/test_tutorial/test_background_tasks/test_tutorial002_an.py
deleted file mode 100644
index af682ecff1..0000000000
--- a/tests/test_tutorial/test_background_tasks/test_tutorial002_an.py
+++ /dev/null
@@ -1,19 +0,0 @@
-import os
-from pathlib import Path
-
-from fastapi.testclient import TestClient
-
-from docs_src.background_tasks.tutorial002_an import app
-
-client = TestClient(app)
-
-
-def test():
- log = Path("log.txt")
- if log.is_file():
- os.remove(log) # pragma: no cover
- response = client.post("/send-notification/foo@example.com?q=some-query")
- assert response.status_code == 200, response.text
- assert response.json() == {"message": "Message sent"}
- with open("./log.txt") as f:
- assert "found query: some-query\nmessage to foo@example.com" in f.read()
diff --git a/tests/test_tutorial/test_background_tasks/test_tutorial002_an_py310.py b/tests/test_tutorial/test_background_tasks/test_tutorial002_an_py310.py
deleted file mode 100644
index 067b2787e9..0000000000
--- a/tests/test_tutorial/test_background_tasks/test_tutorial002_an_py310.py
+++ /dev/null
@@ -1,21 +0,0 @@
-import os
-from pathlib import Path
-
-from fastapi.testclient import TestClient
-
-from ...utils import needs_py310
-
-
-@needs_py310
-def test():
- from docs_src.background_tasks.tutorial002_an_py310 import app
-
- client = TestClient(app)
- log = Path("log.txt")
- if log.is_file():
- os.remove(log) # pragma: no cover
- response = client.post("/send-notification/foo@example.com?q=some-query")
- assert response.status_code == 200, response.text
- assert response.json() == {"message": "Message sent"}
- with open("./log.txt") as f:
- assert "found query: some-query\nmessage to foo@example.com" in f.read()
diff --git a/tests/test_tutorial/test_background_tasks/test_tutorial002_an_py39.py b/tests/test_tutorial/test_background_tasks/test_tutorial002_an_py39.py
deleted file mode 100644
index 06b5a2f57a..0000000000
--- a/tests/test_tutorial/test_background_tasks/test_tutorial002_an_py39.py
+++ /dev/null
@@ -1,21 +0,0 @@
-import os
-from pathlib import Path
-
-from fastapi.testclient import TestClient
-
-from ...utils import needs_py39
-
-
-@needs_py39
-def test():
- from docs_src.background_tasks.tutorial002_an_py39 import app
-
- client = TestClient(app)
- log = Path("log.txt")
- if log.is_file():
- os.remove(log) # pragma: no cover
- response = client.post("/send-notification/foo@example.com?q=some-query")
- assert response.status_code == 200, response.text
- assert response.json() == {"message": "Message sent"}
- with open("./log.txt") as f:
- assert "found query: some-query\nmessage to foo@example.com" in f.read()
diff --git a/tests/test_tutorial/test_background_tasks/test_tutorial002_py310.py b/tests/test_tutorial/test_background_tasks/test_tutorial002_py310.py
deleted file mode 100644
index 6937c8fab4..0000000000
--- a/tests/test_tutorial/test_background_tasks/test_tutorial002_py310.py
+++ /dev/null
@@ -1,21 +0,0 @@
-import os
-from pathlib import Path
-
-from fastapi.testclient import TestClient
-
-from ...utils import needs_py310
-
-
-@needs_py310
-def test():
- from docs_src.background_tasks.tutorial002_py310 import app
-
- client = TestClient(app)
- log = Path("log.txt")
- if log.is_file():
- os.remove(log) # pragma: no cover
- response = client.post("/send-notification/foo@example.com?q=some-query")
- assert response.status_code == 200, response.text
- assert response.json() == {"message": "Message sent"}
- with open("./log.txt") as f:
- assert "found query: some-query\nmessage to foo@example.com" in f.read()
diff --git a/tests/test_tutorial/test_behind_a_proxy/test_tutorial001_01.py b/tests/test_tutorial/test_behind_a_proxy/test_tutorial001_01.py
new file mode 100644
index 0000000000..f13046e018
--- /dev/null
+++ b/tests/test_tutorial/test_behind_a_proxy/test_tutorial001_01.py
@@ -0,0 +1,21 @@
+from fastapi.testclient import TestClient
+
+from docs_src.behind_a_proxy.tutorial001_01 import app
+
+client = TestClient(
+ app,
+ base_url="https://example.com",
+ follow_redirects=False,
+)
+
+
+def test_redirect() -> None:
+ response = client.get("/items")
+ assert response.status_code == 307
+ assert response.headers["location"] == "https://example.com/items/"
+
+
+def test_no_redirect() -> None:
+ response = client.get("/items/")
+ assert response.status_code == 200
+ assert response.json() == ["plumbus", "portal gun"]
diff --git a/tests/test_tutorial/test_bigger_applications/test_main.py b/tests/test_tutorial/test_bigger_applications/test_main.py
index 35fdfa4a66..fe40fad7d0 100644
--- a/tests/test_tutorial/test_bigger_applications/test_main.py
+++ b/tests/test_tutorial/test_bigger_applications/test_main.py
@@ -1,13 +1,24 @@
+import importlib
+
import pytest
from dirty_equals import IsDict
from fastapi.testclient import TestClient
+from ...utils import needs_py39
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.bigger_applications.app.main import app
- client = TestClient(app)
+@pytest.fixture(
+ name="client",
+ params=[
+ "app_an.main",
+ pytest.param("app_an_py39.main", marks=needs_py39),
+ "app.main",
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.bigger_applications.{request.param}")
+
+ client = TestClient(mod.app)
return client
diff --git a/tests/test_tutorial/test_bigger_applications/test_main_an.py b/tests/test_tutorial/test_bigger_applications/test_main_an.py
deleted file mode 100644
index 4e2e3e74d7..0000000000
--- a/tests/test_tutorial/test_bigger_applications/test_main_an.py
+++ /dev/null
@@ -1,715 +0,0 @@
-import pytest
-from dirty_equals import IsDict
-from fastapi.testclient import TestClient
-
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.bigger_applications.app_an.main import app
-
- client = TestClient(app)
- return client
-
-
-def test_users_token_jessica(client: TestClient):
- response = client.get("/users?token=jessica")
- assert response.status_code == 200
- assert response.json() == [{"username": "Rick"}, {"username": "Morty"}]
-
-
-def test_users_with_no_token(client: TestClient):
- response = client.get("/users")
- assert response.status_code == 422
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "missing",
- "loc": ["query", "token"],
- "msg": "Field required",
- "input": None,
- }
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "loc": ["query", "token"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- ]
- }
- )
-
-
-def test_users_foo_token_jessica(client: TestClient):
- response = client.get("/users/foo?token=jessica")
- assert response.status_code == 200
- assert response.json() == {"username": "foo"}
-
-
-def test_users_foo_with_no_token(client: TestClient):
- response = client.get("/users/foo")
- assert response.status_code == 422
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "missing",
- "loc": ["query", "token"],
- "msg": "Field required",
- "input": None,
- }
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "loc": ["query", "token"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- ]
- }
- )
-
-
-def test_users_me_token_jessica(client: TestClient):
- response = client.get("/users/me?token=jessica")
- assert response.status_code == 200
- assert response.json() == {"username": "fakecurrentuser"}
-
-
-def test_users_me_with_no_token(client: TestClient):
- response = client.get("/users/me")
- assert response.status_code == 422
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "missing",
- "loc": ["query", "token"],
- "msg": "Field required",
- "input": None,
- }
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "loc": ["query", "token"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- ]
- }
- )
-
-
-def test_users_token_monica_with_no_jessica(client: TestClient):
- response = client.get("/users?token=monica")
- assert response.status_code == 400
- assert response.json() == {"detail": "No Jessica token provided"}
-
-
-def test_items_token_jessica(client: TestClient):
- response = client.get(
- "/items?token=jessica", headers={"X-Token": "fake-super-secret-token"}
- )
- assert response.status_code == 200
- assert response.json() == {
- "plumbus": {"name": "Plumbus"},
- "gun": {"name": "Portal Gun"},
- }
-
-
-def test_items_with_no_token_jessica(client: TestClient):
- response = client.get("/items", headers={"X-Token": "fake-super-secret-token"})
- assert response.status_code == 422
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "missing",
- "loc": ["query", "token"],
- "msg": "Field required",
- "input": None,
- }
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "loc": ["query", "token"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- ]
- }
- )
-
-
-def test_items_plumbus_token_jessica(client: TestClient):
- response = client.get(
- "/items/plumbus?token=jessica", headers={"X-Token": "fake-super-secret-token"}
- )
- assert response.status_code == 200
- assert response.json() == {"name": "Plumbus", "item_id": "plumbus"}
-
-
-def test_items_bar_token_jessica(client: TestClient):
- response = client.get(
- "/items/bar?token=jessica", headers={"X-Token": "fake-super-secret-token"}
- )
- assert response.status_code == 404
- assert response.json() == {"detail": "Item not found"}
-
-
-def test_items_plumbus_with_no_token(client: TestClient):
- response = client.get(
- "/items/plumbus", headers={"X-Token": "fake-super-secret-token"}
- )
- assert response.status_code == 422
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "missing",
- "loc": ["query", "token"],
- "msg": "Field required",
- "input": None,
- }
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "loc": ["query", "token"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- ]
- }
- )
-
-
-def test_items_with_invalid_token(client: TestClient):
- response = client.get("/items?token=jessica", headers={"X-Token": "invalid"})
- assert response.status_code == 400
- assert response.json() == {"detail": "X-Token header invalid"}
-
-
-def test_items_bar_with_invalid_token(client: TestClient):
- response = client.get("/items/bar?token=jessica", headers={"X-Token": "invalid"})
- assert response.status_code == 400
- assert response.json() == {"detail": "X-Token header invalid"}
-
-
-def test_items_with_missing_x_token_header(client: TestClient):
- response = client.get("/items?token=jessica")
- assert response.status_code == 422
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "missing",
- "loc": ["header", "x-token"],
- "msg": "Field required",
- "input": None,
- }
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "loc": ["header", "x-token"],
- "msg": "field required",
- "type": "value_error.missing",
- }
- ]
- }
- )
-
-
-def test_items_plumbus_with_missing_x_token_header(client: TestClient):
- response = client.get("/items/plumbus?token=jessica")
- assert response.status_code == 422
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "missing",
- "loc": ["header", "x-token"],
- "msg": "Field required",
- "input": None,
- }
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "loc": ["header", "x-token"],
- "msg": "field required",
- "type": "value_error.missing",
- }
- ]
- }
- )
-
-
-def test_root_token_jessica(client: TestClient):
- response = client.get("/?token=jessica")
- assert response.status_code == 200
- assert response.json() == {"message": "Hello Bigger Applications!"}
-
-
-def test_root_with_no_token(client: TestClient):
- response = client.get("/")
- assert response.status_code == 422
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "missing",
- "loc": ["query", "token"],
- "msg": "Field required",
- "input": None,
- }
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "loc": ["query", "token"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- ]
- }
- )
-
-
-def test_put_no_header(client: TestClient):
- response = client.put("/items/foo")
- assert response.status_code == 422, response.text
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "missing",
- "loc": ["query", "token"],
- "msg": "Field required",
- "input": None,
- },
- {
- "type": "missing",
- "loc": ["header", "x-token"],
- "msg": "Field required",
- "input": None,
- },
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "loc": ["query", "token"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- {
- "loc": ["header", "x-token"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- ]
- }
- )
-
-
-def test_put_invalid_header(client: TestClient):
- response = client.put("/items/foo", headers={"X-Token": "invalid"})
- assert response.status_code == 400, response.text
- assert response.json() == {"detail": "X-Token header invalid"}
-
-
-def test_put(client: TestClient):
- response = client.put(
- "/items/plumbus?token=jessica", headers={"X-Token": "fake-super-secret-token"}
- )
- assert response.status_code == 200, response.text
- assert response.json() == {"item_id": "plumbus", "name": "The great Plumbus"}
-
-
-def test_put_forbidden(client: TestClient):
- response = client.put(
- "/items/bar?token=jessica", headers={"X-Token": "fake-super-secret-token"}
- )
- assert response.status_code == 403, response.text
- assert response.json() == {"detail": "You can only update the item: plumbus"}
-
-
-def test_admin(client: TestClient):
- response = client.post(
- "/admin/?token=jessica", headers={"X-Token": "fake-super-secret-token"}
- )
- assert response.status_code == 200, response.text
- assert response.json() == {"message": "Admin getting schwifty"}
-
-
-def test_admin_invalid_header(client: TestClient):
- response = client.post("/admin/", headers={"X-Token": "invalid"})
- assert response.status_code == 400, response.text
- assert response.json() == {"detail": "X-Token header invalid"}
-
-
-def test_openapi_schema(client: TestClient):
- response = client.get("/openapi.json")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/users/": {
- "get": {
- "tags": ["users"],
- "summary": "Read Users",
- "operationId": "read_users_users__get",
- "parameters": [
- {
- "required": True,
- "schema": {"title": "Token", "type": "string"},
- "name": "token",
- "in": "query",
- }
- ],
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- }
- },
- "/users/me": {
- "get": {
- "tags": ["users"],
- "summary": "Read User Me",
- "operationId": "read_user_me_users_me_get",
- "parameters": [
- {
- "required": True,
- "schema": {"title": "Token", "type": "string"},
- "name": "token",
- "in": "query",
- }
- ],
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- }
- },
- "/users/{username}": {
- "get": {
- "tags": ["users"],
- "summary": "Read User",
- "operationId": "read_user_users__username__get",
- "parameters": [
- {
- "required": True,
- "schema": {"title": "Username", "type": "string"},
- "name": "username",
- "in": "path",
- },
- {
- "required": True,
- "schema": {"title": "Token", "type": "string"},
- "name": "token",
- "in": "query",
- },
- ],
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- }
- },
- "/items/": {
- "get": {
- "tags": ["items"],
- "summary": "Read Items",
- "operationId": "read_items_items__get",
- "parameters": [
- {
- "required": True,
- "schema": {"title": "Token", "type": "string"},
- "name": "token",
- "in": "query",
- },
- {
- "required": True,
- "schema": {"title": "X-Token", "type": "string"},
- "name": "x-token",
- "in": "header",
- },
- ],
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "404": {"description": "Not found"},
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- }
- },
- "/items/{item_id}": {
- "get": {
- "tags": ["items"],
- "summary": "Read Item",
- "operationId": "read_item_items__item_id__get",
- "parameters": [
- {
- "required": True,
- "schema": {"title": "Item Id", "type": "string"},
- "name": "item_id",
- "in": "path",
- },
- {
- "required": True,
- "schema": {"title": "Token", "type": "string"},
- "name": "token",
- "in": "query",
- },
- {
- "required": True,
- "schema": {"title": "X-Token", "type": "string"},
- "name": "x-token",
- "in": "header",
- },
- ],
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "404": {"description": "Not found"},
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- },
- "put": {
- "tags": ["items", "custom"],
- "summary": "Update Item",
- "operationId": "update_item_items__item_id__put",
- "parameters": [
- {
- "required": True,
- "schema": {"title": "Item Id", "type": "string"},
- "name": "item_id",
- "in": "path",
- },
- {
- "required": True,
- "schema": {"title": "Token", "type": "string"},
- "name": "token",
- "in": "query",
- },
- {
- "required": True,
- "schema": {"title": "X-Token", "type": "string"},
- "name": "x-token",
- "in": "header",
- },
- ],
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "404": {"description": "Not found"},
- "403": {"description": "Operation forbidden"},
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- },
- },
- "/admin/": {
- "post": {
- "tags": ["admin"],
- "summary": "Update Admin",
- "operationId": "update_admin_admin__post",
- "parameters": [
- {
- "required": True,
- "schema": {"title": "Token", "type": "string"},
- "name": "token",
- "in": "query",
- },
- {
- "required": True,
- "schema": {"title": "X-Token", "type": "string"},
- "name": "x-token",
- "in": "header",
- },
- ],
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "418": {"description": "I'm a teapot"},
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- }
- },
- "/": {
- "get": {
- "summary": "Root",
- "operationId": "root__get",
- "parameters": [
- {
- "required": True,
- "schema": {"title": "Token", "type": "string"},
- "name": "token",
- "in": "query",
- }
- ],
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- }
- },
- },
- "components": {
- "schemas": {
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_bigger_applications/test_main_an_py39.py b/tests/test_tutorial/test_bigger_applications/test_main_an_py39.py
deleted file mode 100644
index 8c9e976df2..0000000000
--- a/tests/test_tutorial/test_bigger_applications/test_main_an_py39.py
+++ /dev/null
@@ -1,742 +0,0 @@
-import pytest
-from dirty_equals import IsDict
-from fastapi.testclient import TestClient
-
-from ...utils import needs_py39
-
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.bigger_applications.app_an_py39.main import app
-
- client = TestClient(app)
- return client
-
-
-@needs_py39
-def test_users_token_jessica(client: TestClient):
- response = client.get("/users?token=jessica")
- assert response.status_code == 200
- assert response.json() == [{"username": "Rick"}, {"username": "Morty"}]
-
-
-@needs_py39
-def test_users_with_no_token(client: TestClient):
- response = client.get("/users")
- assert response.status_code == 422
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "missing",
- "loc": ["query", "token"],
- "msg": "Field required",
- "input": None,
- }
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "loc": ["query", "token"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- ]
- }
- )
-
-
-@needs_py39
-def test_users_foo_token_jessica(client: TestClient):
- response = client.get("/users/foo?token=jessica")
- assert response.status_code == 200
- assert response.json() == {"username": "foo"}
-
-
-@needs_py39
-def test_users_foo_with_no_token(client: TestClient):
- response = client.get("/users/foo")
- assert response.status_code == 422
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "missing",
- "loc": ["query", "token"],
- "msg": "Field required",
- "input": None,
- }
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "loc": ["query", "token"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- ]
- }
- )
-
-
-@needs_py39
-def test_users_me_token_jessica(client: TestClient):
- response = client.get("/users/me?token=jessica")
- assert response.status_code == 200
- assert response.json() == {"username": "fakecurrentuser"}
-
-
-@needs_py39
-def test_users_me_with_no_token(client: TestClient):
- response = client.get("/users/me")
- assert response.status_code == 422
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "missing",
- "loc": ["query", "token"],
- "msg": "Field required",
- "input": None,
- }
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "loc": ["query", "token"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- ]
- }
- )
-
-
-@needs_py39
-def test_users_token_monica_with_no_jessica(client: TestClient):
- response = client.get("/users?token=monica")
- assert response.status_code == 400
- assert response.json() == {"detail": "No Jessica token provided"}
-
-
-@needs_py39
-def test_items_token_jessica(client: TestClient):
- response = client.get(
- "/items?token=jessica", headers={"X-Token": "fake-super-secret-token"}
- )
- assert response.status_code == 200
- assert response.json() == {
- "plumbus": {"name": "Plumbus"},
- "gun": {"name": "Portal Gun"},
- }
-
-
-@needs_py39
-def test_items_with_no_token_jessica(client: TestClient):
- response = client.get("/items", headers={"X-Token": "fake-super-secret-token"})
- assert response.status_code == 422
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "missing",
- "loc": ["query", "token"],
- "msg": "Field required",
- "input": None,
- }
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "loc": ["query", "token"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- ]
- }
- )
-
-
-@needs_py39
-def test_items_plumbus_token_jessica(client: TestClient):
- response = client.get(
- "/items/plumbus?token=jessica", headers={"X-Token": "fake-super-secret-token"}
- )
- assert response.status_code == 200
- assert response.json() == {"name": "Plumbus", "item_id": "plumbus"}
-
-
-@needs_py39
-def test_items_bar_token_jessica(client: TestClient):
- response = client.get(
- "/items/bar?token=jessica", headers={"X-Token": "fake-super-secret-token"}
- )
- assert response.status_code == 404
- assert response.json() == {"detail": "Item not found"}
-
-
-@needs_py39
-def test_items_plumbus_with_no_token(client: TestClient):
- response = client.get(
- "/items/plumbus", headers={"X-Token": "fake-super-secret-token"}
- )
- assert response.status_code == 422
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "missing",
- "loc": ["query", "token"],
- "msg": "Field required",
- "input": None,
- }
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "loc": ["query", "token"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- ]
- }
- )
-
-
-@needs_py39
-def test_items_with_invalid_token(client: TestClient):
- response = client.get("/items?token=jessica", headers={"X-Token": "invalid"})
- assert response.status_code == 400
- assert response.json() == {"detail": "X-Token header invalid"}
-
-
-@needs_py39
-def test_items_bar_with_invalid_token(client: TestClient):
- response = client.get("/items/bar?token=jessica", headers={"X-Token": "invalid"})
- assert response.status_code == 400
- assert response.json() == {"detail": "X-Token header invalid"}
-
-
-@needs_py39
-def test_items_with_missing_x_token_header(client: TestClient):
- response = client.get("/items?token=jessica")
- assert response.status_code == 422
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "missing",
- "loc": ["header", "x-token"],
- "msg": "Field required",
- "input": None,
- }
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "loc": ["header", "x-token"],
- "msg": "field required",
- "type": "value_error.missing",
- }
- ]
- }
- )
-
-
-@needs_py39
-def test_items_plumbus_with_missing_x_token_header(client: TestClient):
- response = client.get("/items/plumbus?token=jessica")
- assert response.status_code == 422
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "missing",
- "loc": ["header", "x-token"],
- "msg": "Field required",
- "input": None,
- }
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "loc": ["header", "x-token"],
- "msg": "field required",
- "type": "value_error.missing",
- }
- ]
- }
- )
-
-
-@needs_py39
-def test_root_token_jessica(client: TestClient):
- response = client.get("/?token=jessica")
- assert response.status_code == 200
- assert response.json() == {"message": "Hello Bigger Applications!"}
-
-
-@needs_py39
-def test_root_with_no_token(client: TestClient):
- response = client.get("/")
- assert response.status_code == 422
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "missing",
- "loc": ["query", "token"],
- "msg": "Field required",
- "input": None,
- }
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "loc": ["query", "token"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- ]
- }
- )
-
-
-@needs_py39
-def test_put_no_header(client: TestClient):
- response = client.put("/items/foo")
- assert response.status_code == 422, response.text
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "missing",
- "loc": ["query", "token"],
- "msg": "Field required",
- "input": None,
- },
- {
- "type": "missing",
- "loc": ["header", "x-token"],
- "msg": "Field required",
- "input": None,
- },
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "loc": ["query", "token"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- {
- "loc": ["header", "x-token"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- ]
- }
- )
-
-
-@needs_py39
-def test_put_invalid_header(client: TestClient):
- response = client.put("/items/foo", headers={"X-Token": "invalid"})
- assert response.status_code == 400, response.text
- assert response.json() == {"detail": "X-Token header invalid"}
-
-
-@needs_py39
-def test_put(client: TestClient):
- response = client.put(
- "/items/plumbus?token=jessica", headers={"X-Token": "fake-super-secret-token"}
- )
- assert response.status_code == 200, response.text
- assert response.json() == {"item_id": "plumbus", "name": "The great Plumbus"}
-
-
-@needs_py39
-def test_put_forbidden(client: TestClient):
- response = client.put(
- "/items/bar?token=jessica", headers={"X-Token": "fake-super-secret-token"}
- )
- assert response.status_code == 403, response.text
- assert response.json() == {"detail": "You can only update the item: plumbus"}
-
-
-@needs_py39
-def test_admin(client: TestClient):
- response = client.post(
- "/admin/?token=jessica", headers={"X-Token": "fake-super-secret-token"}
- )
- assert response.status_code == 200, response.text
- assert response.json() == {"message": "Admin getting schwifty"}
-
-
-@needs_py39
-def test_admin_invalid_header(client: TestClient):
- response = client.post("/admin/", headers={"X-Token": "invalid"})
- assert response.status_code == 400, response.text
- assert response.json() == {"detail": "X-Token header invalid"}
-
-
-@needs_py39
-def test_openapi_schema(client: TestClient):
- response = client.get("/openapi.json")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/users/": {
- "get": {
- "tags": ["users"],
- "summary": "Read Users",
- "operationId": "read_users_users__get",
- "parameters": [
- {
- "required": True,
- "schema": {"title": "Token", "type": "string"},
- "name": "token",
- "in": "query",
- }
- ],
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- }
- },
- "/users/me": {
- "get": {
- "tags": ["users"],
- "summary": "Read User Me",
- "operationId": "read_user_me_users_me_get",
- "parameters": [
- {
- "required": True,
- "schema": {"title": "Token", "type": "string"},
- "name": "token",
- "in": "query",
- }
- ],
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- }
- },
- "/users/{username}": {
- "get": {
- "tags": ["users"],
- "summary": "Read User",
- "operationId": "read_user_users__username__get",
- "parameters": [
- {
- "required": True,
- "schema": {"title": "Username", "type": "string"},
- "name": "username",
- "in": "path",
- },
- {
- "required": True,
- "schema": {"title": "Token", "type": "string"},
- "name": "token",
- "in": "query",
- },
- ],
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- }
- },
- "/items/": {
- "get": {
- "tags": ["items"],
- "summary": "Read Items",
- "operationId": "read_items_items__get",
- "parameters": [
- {
- "required": True,
- "schema": {"title": "Token", "type": "string"},
- "name": "token",
- "in": "query",
- },
- {
- "required": True,
- "schema": {"title": "X-Token", "type": "string"},
- "name": "x-token",
- "in": "header",
- },
- ],
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "404": {"description": "Not found"},
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- }
- },
- "/items/{item_id}": {
- "get": {
- "tags": ["items"],
- "summary": "Read Item",
- "operationId": "read_item_items__item_id__get",
- "parameters": [
- {
- "required": True,
- "schema": {"title": "Item Id", "type": "string"},
- "name": "item_id",
- "in": "path",
- },
- {
- "required": True,
- "schema": {"title": "Token", "type": "string"},
- "name": "token",
- "in": "query",
- },
- {
- "required": True,
- "schema": {"title": "X-Token", "type": "string"},
- "name": "x-token",
- "in": "header",
- },
- ],
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "404": {"description": "Not found"},
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- },
- "put": {
- "tags": ["items", "custom"],
- "summary": "Update Item",
- "operationId": "update_item_items__item_id__put",
- "parameters": [
- {
- "required": True,
- "schema": {"title": "Item Id", "type": "string"},
- "name": "item_id",
- "in": "path",
- },
- {
- "required": True,
- "schema": {"title": "Token", "type": "string"},
- "name": "token",
- "in": "query",
- },
- {
- "required": True,
- "schema": {"title": "X-Token", "type": "string"},
- "name": "x-token",
- "in": "header",
- },
- ],
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "404": {"description": "Not found"},
- "403": {"description": "Operation forbidden"},
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- },
- },
- "/admin/": {
- "post": {
- "tags": ["admin"],
- "summary": "Update Admin",
- "operationId": "update_admin_admin__post",
- "parameters": [
- {
- "required": True,
- "schema": {"title": "Token", "type": "string"},
- "name": "token",
- "in": "query",
- },
- {
- "required": True,
- "schema": {"title": "X-Token", "type": "string"},
- "name": "x-token",
- "in": "header",
- },
- ],
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "418": {"description": "I'm a teapot"},
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- }
- },
- "/": {
- "get": {
- "summary": "Root",
- "operationId": "root__get",
- "parameters": [
- {
- "required": True,
- "schema": {"title": "Token", "type": "string"},
- "name": "token",
- "in": "query",
- }
- ],
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- }
- },
- },
- "components": {
- "schemas": {
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_body/test_tutorial001.py b/tests/test_tutorial/test_body/test_tutorial001.py
index 0d55d73ebe..f8b5aee8d2 100644
--- a/tests/test_tutorial/test_body/test_tutorial001.py
+++ b/tests/test_tutorial/test_body/test_tutorial001.py
@@ -1,15 +1,24 @@
+import importlib
from unittest.mock import patch
import pytest
from dirty_equals import IsDict
from fastapi.testclient import TestClient
+from ...utils import needs_py310
-@pytest.fixture
-def client():
- from docs_src.body.tutorial001 import app
- client = TestClient(app)
+@pytest.fixture(
+ name="client",
+ params=[
+ "tutorial001",
+ pytest.param("tutorial001_py310", marks=needs_py310),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.body.{request.param}")
+
+ client = TestClient(mod.app)
return client
diff --git a/tests/test_tutorial/test_body/test_tutorial001_py310.py b/tests/test_tutorial/test_body/test_tutorial001_py310.py
deleted file mode 100644
index 4b9c128063..0000000000
--- a/tests/test_tutorial/test_body/test_tutorial001_py310.py
+++ /dev/null
@@ -1,498 +0,0 @@
-from unittest.mock import patch
-
-import pytest
-from dirty_equals import IsDict
-from fastapi.testclient import TestClient
-
-from ...utils import needs_py310
-
-
-@pytest.fixture
-def client():
- from docs_src.body.tutorial001_py310 import app
-
- client = TestClient(app)
- return client
-
-
-@needs_py310
-def test_body_float(client: TestClient):
- response = client.post("/items/", json={"name": "Foo", "price": 50.5})
- assert response.status_code == 200
- assert response.json() == {
- "name": "Foo",
- "price": 50.5,
- "description": None,
- "tax": None,
- }
-
-
-@needs_py310
-def test_post_with_str_float(client: TestClient):
- response = client.post("/items/", json={"name": "Foo", "price": "50.5"})
- assert response.status_code == 200
- assert response.json() == {
- "name": "Foo",
- "price": 50.5,
- "description": None,
- "tax": None,
- }
-
-
-@needs_py310
-def test_post_with_str_float_description(client: TestClient):
- response = client.post(
- "/items/", json={"name": "Foo", "price": "50.5", "description": "Some Foo"}
- )
- assert response.status_code == 200
- assert response.json() == {
- "name": "Foo",
- "price": 50.5,
- "description": "Some Foo",
- "tax": None,
- }
-
-
-@needs_py310
-def test_post_with_str_float_description_tax(client: TestClient):
- response = client.post(
- "/items/",
- json={"name": "Foo", "price": "50.5", "description": "Some Foo", "tax": 0.3},
- )
- assert response.status_code == 200
- assert response.json() == {
- "name": "Foo",
- "price": 50.5,
- "description": "Some Foo",
- "tax": 0.3,
- }
-
-
-@needs_py310
-def test_post_with_only_name(client: TestClient):
- response = client.post("/items/", json={"name": "Foo"})
- assert response.status_code == 422
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "missing",
- "loc": ["body", "price"],
- "msg": "Field required",
- "input": {"name": "Foo"},
- }
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "loc": ["body", "price"],
- "msg": "field required",
- "type": "value_error.missing",
- }
- ]
- }
- )
-
-
-@needs_py310
-def test_post_with_only_name_price(client: TestClient):
- response = client.post("/items/", json={"name": "Foo", "price": "twenty"})
- assert response.status_code == 422
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "float_parsing",
- "loc": ["body", "price"],
- "msg": "Input should be a valid number, unable to parse string as a number",
- "input": "twenty",
- }
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "loc": ["body", "price"],
- "msg": "value is not a valid float",
- "type": "type_error.float",
- }
- ]
- }
- )
-
-
-@needs_py310
-def test_post_with_no_data(client: TestClient):
- response = client.post("/items/", json={})
- assert response.status_code == 422
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "missing",
- "loc": ["body", "name"],
- "msg": "Field required",
- "input": {},
- },
- {
- "type": "missing",
- "loc": ["body", "price"],
- "msg": "Field required",
- "input": {},
- },
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "loc": ["body", "name"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- {
- "loc": ["body", "price"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- ]
- }
- )
-
-
-@needs_py310
-def test_post_with_none(client: TestClient):
- response = client.post("/items/", json=None)
- assert response.status_code == 422
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "missing",
- "loc": ["body"],
- "msg": "Field required",
- "input": None,
- }
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "loc": ["body"],
- "msg": "field required",
- "type": "value_error.missing",
- }
- ]
- }
- )
-
-
-@needs_py310
-def test_post_broken_body(client: TestClient):
- response = client.post(
- "/items/",
- headers={"content-type": "application/json"},
- content="{some broken json}",
- )
- assert response.status_code == 422, response.text
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "json_invalid",
- "loc": ["body", 1],
- "msg": "JSON decode error",
- "input": {},
- "ctx": {
- "error": "Expecting property name enclosed in double quotes"
- },
- }
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "loc": ["body", 1],
- "msg": "Expecting property name enclosed in double quotes: line 1 column 2 (char 1)",
- "type": "value_error.jsondecode",
- "ctx": {
- "msg": "Expecting property name enclosed in double quotes",
- "doc": "{some broken json}",
- "pos": 1,
- "lineno": 1,
- "colno": 2,
- },
- }
- ]
- }
- )
-
-
-@needs_py310
-def test_post_form_for_json(client: TestClient):
- response = client.post("/items/", data={"name": "Foo", "price": 50.5})
- assert response.status_code == 422, response.text
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "model_attributes_type",
- "loc": ["body"],
- "msg": "Input should be a valid dictionary or object to extract fields from",
- "input": "name=Foo&price=50.5",
- }
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "loc": ["body"],
- "msg": "value is not a valid dict",
- "type": "type_error.dict",
- }
- ]
- }
- )
-
-
-@needs_py310
-def test_explicit_content_type(client: TestClient):
- response = client.post(
- "/items/",
- content='{"name": "Foo", "price": 50.5}',
- headers={"Content-Type": "application/json"},
- )
- assert response.status_code == 200, response.text
-
-
-@needs_py310
-def test_geo_json(client: TestClient):
- response = client.post(
- "/items/",
- content='{"name": "Foo", "price": 50.5}',
- headers={"Content-Type": "application/geo+json"},
- )
- assert response.status_code == 200, response.text
-
-
-@needs_py310
-def test_no_content_type_is_json(client: TestClient):
- response = client.post(
- "/items/",
- content='{"name": "Foo", "price": 50.5}',
- )
- assert response.status_code == 200, response.text
- assert response.json() == {
- "name": "Foo",
- "description": None,
- "price": 50.5,
- "tax": None,
- }
-
-
-@needs_py310
-def test_wrong_headers(client: TestClient):
- data = '{"name": "Foo", "price": 50.5}'
- response = client.post(
- "/items/", content=data, headers={"Content-Type": "text/plain"}
- )
- assert response.status_code == 422, response.text
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "model_attributes_type",
- "loc": ["body"],
- "msg": "Input should be a valid dictionary or object to extract fields from",
- "input": '{"name": "Foo", "price": 50.5}',
- }
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "loc": ["body"],
- "msg": "value is not a valid dict",
- "type": "type_error.dict",
- }
- ]
- }
- )
-
- response = client.post(
- "/items/", content=data, headers={"Content-Type": "application/geo+json-seq"}
- )
- assert response.status_code == 422, response.text
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "model_attributes_type",
- "loc": ["body"],
- "msg": "Input should be a valid dictionary or object to extract fields from",
- "input": '{"name": "Foo", "price": 50.5}',
- }
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "loc": ["body"],
- "msg": "value is not a valid dict",
- "type": "type_error.dict",
- }
- ]
- }
- )
- response = client.post(
- "/items/", content=data, headers={"Content-Type": "application/not-really-json"}
- )
- assert response.status_code == 422, response.text
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "model_attributes_type",
- "loc": ["body"],
- "msg": "Input should be a valid dictionary or object to extract fields from",
- "input": '{"name": "Foo", "price": 50.5}',
- }
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "loc": ["body"],
- "msg": "value is not a valid dict",
- "type": "type_error.dict",
- }
- ]
- }
- )
-
-
-@needs_py310
-def test_other_exceptions(client: TestClient):
- with patch("json.loads", side_effect=Exception):
- response = client.post("/items/", json={"test": "test2"})
- assert response.status_code == 400, response.text
-
-
-@needs_py310
-def test_openapi_schema(client: TestClient):
- response = client.get("/openapi.json")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/": {
- "post": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Create Item",
- "operationId": "create_item_items__post",
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {"$ref": "#/components/schemas/Item"}
- }
- },
- "required": True,
- },
- }
- }
- },
- "components": {
- "schemas": {
- "Item": {
- "title": "Item",
- "required": ["name", "price"],
- "type": "object",
- "properties": {
- "name": {"title": "Name", "type": "string"},
- "price": {"title": "Price", "type": "number"},
- "description": IsDict(
- {
- "title": "Description",
- "anyOf": [{"type": "string"}, {"type": "null"}],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Description", "type": "string"}
- ),
- "tax": IsDict(
- {
- "title": "Tax",
- "anyOf": [{"type": "number"}, {"type": "null"}],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Tax", "type": "number"}
- ),
- },
- },
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_body_fields/test_tutorial001.py b/tests/test_tutorial/test_body_fields/test_tutorial001.py
index fd6139eb9b..fb68f28689 100644
--- a/tests/test_tutorial/test_body_fields/test_tutorial001.py
+++ b/tests/test_tutorial/test_body_fields/test_tutorial001.py
@@ -1,13 +1,26 @@
+import importlib
+
import pytest
from dirty_equals import IsDict
from fastapi.testclient import TestClient
+from ...utils import needs_py39, needs_py310
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.body_fields.tutorial001 import app
- client = TestClient(app)
+@pytest.fixture(
+ name="client",
+ params=[
+ "tutorial001",
+ pytest.param("tutorial001_py310", marks=needs_py310),
+ "tutorial001_an",
+ pytest.param("tutorial001_an_py39", marks=needs_py39),
+ pytest.param("tutorial001_an_py310", marks=needs_py310),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.body_fields.{request.param}")
+
+ client = TestClient(mod.app)
return client
diff --git a/tests/test_tutorial/test_body_fields/test_tutorial001_an.py b/tests/test_tutorial/test_body_fields/test_tutorial001_an.py
deleted file mode 100644
index 72c18c1f73..0000000000
--- a/tests/test_tutorial/test_body_fields/test_tutorial001_an.py
+++ /dev/null
@@ -1,203 +0,0 @@
-import pytest
-from dirty_equals import IsDict
-from fastapi.testclient import TestClient
-
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.body_fields.tutorial001_an import app
-
- client = TestClient(app)
- return client
-
-
-def test_items_5(client: TestClient):
- response = client.put("/items/5", json={"item": {"name": "Foo", "price": 3.0}})
- assert response.status_code == 200
- assert response.json() == {
- "item_id": 5,
- "item": {"name": "Foo", "price": 3.0, "description": None, "tax": None},
- }
-
-
-def test_items_6(client: TestClient):
- response = client.put(
- "/items/6",
- json={
- "item": {
- "name": "Bar",
- "price": 0.2,
- "description": "Some bar",
- "tax": "5.4",
- }
- },
- )
- assert response.status_code == 200
- assert response.json() == {
- "item_id": 6,
- "item": {
- "name": "Bar",
- "price": 0.2,
- "description": "Some bar",
- "tax": 5.4,
- },
- }
-
-
-def test_invalid_price(client: TestClient):
- response = client.put("/items/5", json={"item": {"name": "Foo", "price": -3.0}})
- assert response.status_code == 422
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "greater_than",
- "loc": ["body", "item", "price"],
- "msg": "Input should be greater than 0",
- "input": -3.0,
- "ctx": {"gt": 0.0},
- }
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "ctx": {"limit_value": 0},
- "loc": ["body", "item", "price"],
- "msg": "ensure this value is greater than 0",
- "type": "value_error.number.not_gt",
- }
- ]
- }
- )
-
-
-def test_openapi_schema(client: TestClient):
- response = client.get("/openapi.json")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/{item_id}": {
- "put": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Update Item",
- "operationId": "update_item_items__item_id__put",
- "parameters": [
- {
- "required": True,
- "schema": {"title": "Item Id", "type": "integer"},
- "name": "item_id",
- "in": "path",
- }
- ],
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/Body_update_item_items__item_id__put"
- }
- }
- },
- "required": True,
- },
- }
- }
- },
- "components": {
- "schemas": {
- "Item": {
- "title": "Item",
- "required": ["name", "price"],
- "type": "object",
- "properties": {
- "name": {"title": "Name", "type": "string"},
- "description": IsDict(
- {
- "title": "The description of the item",
- "anyOf": [
- {"maxLength": 300, "type": "string"},
- {"type": "null"},
- ],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "title": "The description of the item",
- "maxLength": 300,
- "type": "string",
- }
- ),
- "price": {
- "title": "Price",
- "exclusiveMinimum": 0.0,
- "type": "number",
- "description": "The price must be greater than zero",
- },
- "tax": IsDict(
- {
- "title": "Tax",
- "anyOf": [{"type": "number"}, {"type": "null"}],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Tax", "type": "number"}
- ),
- },
- },
- "Body_update_item_items__item_id__put": {
- "title": "Body_update_item_items__item_id__put",
- "required": ["item"],
- "type": "object",
- "properties": {"item": {"$ref": "#/components/schemas/Item"}},
- },
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_body_fields/test_tutorial001_an_py310.py b/tests/test_tutorial/test_body_fields/test_tutorial001_an_py310.py
deleted file mode 100644
index 1bc62868fd..0000000000
--- a/tests/test_tutorial/test_body_fields/test_tutorial001_an_py310.py
+++ /dev/null
@@ -1,209 +0,0 @@
-import pytest
-from dirty_equals import IsDict
-from fastapi.testclient import TestClient
-
-from ...utils import needs_py310
-
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.body_fields.tutorial001_an_py310 import app
-
- client = TestClient(app)
- return client
-
-
-@needs_py310
-def test_items_5(client: TestClient):
- response = client.put("/items/5", json={"item": {"name": "Foo", "price": 3.0}})
- assert response.status_code == 200
- assert response.json() == {
- "item_id": 5,
- "item": {"name": "Foo", "price": 3.0, "description": None, "tax": None},
- }
-
-
-@needs_py310
-def test_items_6(client: TestClient):
- response = client.put(
- "/items/6",
- json={
- "item": {
- "name": "Bar",
- "price": 0.2,
- "description": "Some bar",
- "tax": "5.4",
- }
- },
- )
- assert response.status_code == 200
- assert response.json() == {
- "item_id": 6,
- "item": {
- "name": "Bar",
- "price": 0.2,
- "description": "Some bar",
- "tax": 5.4,
- },
- }
-
-
-@needs_py310
-def test_invalid_price(client: TestClient):
- response = client.put("/items/5", json={"item": {"name": "Foo", "price": -3.0}})
- assert response.status_code == 422
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "greater_than",
- "loc": ["body", "item", "price"],
- "msg": "Input should be greater than 0",
- "input": -3.0,
- "ctx": {"gt": 0.0},
- }
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "ctx": {"limit_value": 0},
- "loc": ["body", "item", "price"],
- "msg": "ensure this value is greater than 0",
- "type": "value_error.number.not_gt",
- }
- ]
- }
- )
-
-
-@needs_py310
-def test_openapi_schema(client: TestClient):
- response = client.get("/openapi.json")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/{item_id}": {
- "put": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Update Item",
- "operationId": "update_item_items__item_id__put",
- "parameters": [
- {
- "required": True,
- "schema": {"title": "Item Id", "type": "integer"},
- "name": "item_id",
- "in": "path",
- }
- ],
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/Body_update_item_items__item_id__put"
- }
- }
- },
- "required": True,
- },
- }
- }
- },
- "components": {
- "schemas": {
- "Item": {
- "title": "Item",
- "required": ["name", "price"],
- "type": "object",
- "properties": {
- "name": {"title": "Name", "type": "string"},
- "description": IsDict(
- {
- "title": "The description of the item",
- "anyOf": [
- {"maxLength": 300, "type": "string"},
- {"type": "null"},
- ],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "title": "The description of the item",
- "maxLength": 300,
- "type": "string",
- }
- ),
- "price": {
- "title": "Price",
- "exclusiveMinimum": 0.0,
- "type": "number",
- "description": "The price must be greater than zero",
- },
- "tax": IsDict(
- {
- "title": "Tax",
- "anyOf": [{"type": "number"}, {"type": "null"}],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Tax", "type": "number"}
- ),
- },
- },
- "Body_update_item_items__item_id__put": {
- "title": "Body_update_item_items__item_id__put",
- "required": ["item"],
- "type": "object",
- "properties": {"item": {"$ref": "#/components/schemas/Item"}},
- },
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_body_fields/test_tutorial001_an_py39.py b/tests/test_tutorial/test_body_fields/test_tutorial001_an_py39.py
deleted file mode 100644
index 3c5557a1b2..0000000000
--- a/tests/test_tutorial/test_body_fields/test_tutorial001_an_py39.py
+++ /dev/null
@@ -1,209 +0,0 @@
-import pytest
-from dirty_equals import IsDict
-from fastapi.testclient import TestClient
-
-from ...utils import needs_py39
-
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.body_fields.tutorial001_an_py39 import app
-
- client = TestClient(app)
- return client
-
-
-@needs_py39
-def test_items_5(client: TestClient):
- response = client.put("/items/5", json={"item": {"name": "Foo", "price": 3.0}})
- assert response.status_code == 200
- assert response.json() == {
- "item_id": 5,
- "item": {"name": "Foo", "price": 3.0, "description": None, "tax": None},
- }
-
-
-@needs_py39
-def test_items_6(client: TestClient):
- response = client.put(
- "/items/6",
- json={
- "item": {
- "name": "Bar",
- "price": 0.2,
- "description": "Some bar",
- "tax": "5.4",
- }
- },
- )
- assert response.status_code == 200
- assert response.json() == {
- "item_id": 6,
- "item": {
- "name": "Bar",
- "price": 0.2,
- "description": "Some bar",
- "tax": 5.4,
- },
- }
-
-
-@needs_py39
-def test_invalid_price(client: TestClient):
- response = client.put("/items/5", json={"item": {"name": "Foo", "price": -3.0}})
- assert response.status_code == 422
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "greater_than",
- "loc": ["body", "item", "price"],
- "msg": "Input should be greater than 0",
- "input": -3.0,
- "ctx": {"gt": 0.0},
- }
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "ctx": {"limit_value": 0},
- "loc": ["body", "item", "price"],
- "msg": "ensure this value is greater than 0",
- "type": "value_error.number.not_gt",
- }
- ]
- }
- )
-
-
-@needs_py39
-def test_openapi_schema(client: TestClient):
- response = client.get("/openapi.json")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/{item_id}": {
- "put": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Update Item",
- "operationId": "update_item_items__item_id__put",
- "parameters": [
- {
- "required": True,
- "schema": {"title": "Item Id", "type": "integer"},
- "name": "item_id",
- "in": "path",
- }
- ],
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/Body_update_item_items__item_id__put"
- }
- }
- },
- "required": True,
- },
- }
- }
- },
- "components": {
- "schemas": {
- "Item": {
- "title": "Item",
- "required": ["name", "price"],
- "type": "object",
- "properties": {
- "name": {"title": "Name", "type": "string"},
- "description": IsDict(
- {
- "title": "The description of the item",
- "anyOf": [
- {"maxLength": 300, "type": "string"},
- {"type": "null"},
- ],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "title": "The description of the item",
- "maxLength": 300,
- "type": "string",
- }
- ),
- "price": {
- "title": "Price",
- "exclusiveMinimum": 0.0,
- "type": "number",
- "description": "The price must be greater than zero",
- },
- "tax": IsDict(
- {
- "title": "Tax",
- "anyOf": [{"type": "number"}, {"type": "null"}],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Tax", "type": "number"}
- ),
- },
- },
- "Body_update_item_items__item_id__put": {
- "title": "Body_update_item_items__item_id__put",
- "required": ["item"],
- "type": "object",
- "properties": {"item": {"$ref": "#/components/schemas/Item"}},
- },
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_body_fields/test_tutorial001_py310.py b/tests/test_tutorial/test_body_fields/test_tutorial001_py310.py
deleted file mode 100644
index 8c1386aa67..0000000000
--- a/tests/test_tutorial/test_body_fields/test_tutorial001_py310.py
+++ /dev/null
@@ -1,209 +0,0 @@
-import pytest
-from dirty_equals import IsDict
-from fastapi.testclient import TestClient
-
-from ...utils import needs_py310
-
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.body_fields.tutorial001_py310 import app
-
- client = TestClient(app)
- return client
-
-
-@needs_py310
-def test_items_5(client: TestClient):
- response = client.put("/items/5", json={"item": {"name": "Foo", "price": 3.0}})
- assert response.status_code == 200
- assert response.json() == {
- "item_id": 5,
- "item": {"name": "Foo", "price": 3.0, "description": None, "tax": None},
- }
-
-
-@needs_py310
-def test_items_6(client: TestClient):
- response = client.put(
- "/items/6",
- json={
- "item": {
- "name": "Bar",
- "price": 0.2,
- "description": "Some bar",
- "tax": "5.4",
- }
- },
- )
- assert response.status_code == 200
- assert response.json() == {
- "item_id": 6,
- "item": {
- "name": "Bar",
- "price": 0.2,
- "description": "Some bar",
- "tax": 5.4,
- },
- }
-
-
-@needs_py310
-def test_invalid_price(client: TestClient):
- response = client.put("/items/5", json={"item": {"name": "Foo", "price": -3.0}})
- assert response.status_code == 422
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "greater_than",
- "loc": ["body", "item", "price"],
- "msg": "Input should be greater than 0",
- "input": -3.0,
- "ctx": {"gt": 0.0},
- }
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "ctx": {"limit_value": 0},
- "loc": ["body", "item", "price"],
- "msg": "ensure this value is greater than 0",
- "type": "value_error.number.not_gt",
- }
- ]
- }
- )
-
-
-@needs_py310
-def test_openapi_schema(client: TestClient):
- response = client.get("/openapi.json")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/{item_id}": {
- "put": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Update Item",
- "operationId": "update_item_items__item_id__put",
- "parameters": [
- {
- "required": True,
- "schema": {"title": "Item Id", "type": "integer"},
- "name": "item_id",
- "in": "path",
- }
- ],
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/Body_update_item_items__item_id__put"
- }
- }
- },
- "required": True,
- },
- }
- }
- },
- "components": {
- "schemas": {
- "Item": {
- "title": "Item",
- "required": ["name", "price"],
- "type": "object",
- "properties": {
- "name": {"title": "Name", "type": "string"},
- "description": IsDict(
- {
- "title": "The description of the item",
- "anyOf": [
- {"maxLength": 300, "type": "string"},
- {"type": "null"},
- ],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "title": "The description of the item",
- "maxLength": 300,
- "type": "string",
- }
- ),
- "price": {
- "title": "Price",
- "exclusiveMinimum": 0.0,
- "type": "number",
- "description": "The price must be greater than zero",
- },
- "tax": IsDict(
- {
- "title": "Tax",
- "anyOf": [{"type": "number"}, {"type": "null"}],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Tax", "type": "number"}
- ),
- },
- },
- "Body_update_item_items__item_id__put": {
- "title": "Body_update_item_items__item_id__put",
- "required": ["item"],
- "type": "object",
- "properties": {"item": {"$ref": "#/components/schemas/Item"}},
- },
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_body_multiple_params/test_tutorial001.py b/tests/test_tutorial/test_body_multiple_params/test_tutorial001.py
index 6275ebe95f..1424055952 100644
--- a/tests/test_tutorial/test_body_multiple_params/test_tutorial001.py
+++ b/tests/test_tutorial/test_body_multiple_params/test_tutorial001.py
@@ -1,13 +1,26 @@
+import importlib
+
import pytest
from dirty_equals import IsDict
from fastapi.testclient import TestClient
+from ...utils import needs_py39, needs_py310
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.body_multiple_params.tutorial001 import app
- client = TestClient(app)
+@pytest.fixture(
+ name="client",
+ params=[
+ "tutorial001",
+ pytest.param("tutorial001_py310", marks=needs_py310),
+ "tutorial001_an",
+ pytest.param("tutorial001_an_py39", marks=needs_py39),
+ pytest.param("tutorial001_an_py310", marks=needs_py310),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.body_multiple_params.{request.param}")
+
+ client = TestClient(mod.app)
return client
diff --git a/tests/test_tutorial/test_body_multiple_params/test_tutorial001_an.py b/tests/test_tutorial/test_body_multiple_params/test_tutorial001_an.py
deleted file mode 100644
index 5cd3e2c4aa..0000000000
--- a/tests/test_tutorial/test_body_multiple_params/test_tutorial001_an.py
+++ /dev/null
@@ -1,206 +0,0 @@
-import pytest
-from dirty_equals import IsDict
-from fastapi.testclient import TestClient
-
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.body_multiple_params.tutorial001_an import app
-
- client = TestClient(app)
- return client
-
-
-def test_post_body_q_bar_content(client: TestClient):
- response = client.put("/items/5?q=bar", json={"name": "Foo", "price": 50.5})
- assert response.status_code == 200
- assert response.json() == {
- "item_id": 5,
- "item": {
- "name": "Foo",
- "price": 50.5,
- "description": None,
- "tax": None,
- },
- "q": "bar",
- }
-
-
-def test_post_no_body_q_bar(client: TestClient):
- response = client.put("/items/5?q=bar", json=None)
- assert response.status_code == 200
- assert response.json() == {"item_id": 5, "q": "bar"}
-
-
-def test_post_no_body(client: TestClient):
- response = client.put("/items/5", json=None)
- assert response.status_code == 200
- assert response.json() == {"item_id": 5}
-
-
-def test_post_id_foo(client: TestClient):
- response = client.put("/items/foo", json=None)
- assert response.status_code == 422
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "int_parsing",
- "loc": ["path", "item_id"],
- "msg": "Input should be a valid integer, unable to parse string as an integer",
- "input": "foo",
- }
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "loc": ["path", "item_id"],
- "msg": "value is not a valid integer",
- "type": "type_error.integer",
- }
- ]
- }
- )
-
-
-def test_openapi_schema(client: TestClient):
- response = client.get("/openapi.json")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/{item_id}": {
- "put": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Update Item",
- "operationId": "update_item_items__item_id__put",
- "parameters": [
- {
- "required": True,
- "schema": {
- "title": "The ID of the item to get",
- "maximum": 1000.0,
- "minimum": 0.0,
- "type": "integer",
- },
- "name": "item_id",
- "in": "path",
- },
- {
- "required": False,
- "schema": IsDict(
- {
- "anyOf": [{"type": "string"}, {"type": "null"}],
- "title": "Q",
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Q", "type": "string"}
- ),
- "name": "q",
- "in": "query",
- },
- ],
- "requestBody": {
- "content": {
- "application/json": {
- "schema": IsDict(
- {
- "anyOf": [
- {"$ref": "#/components/schemas/Item"},
- {"type": "null"},
- ],
- "title": "Item",
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"$ref": "#/components/schemas/Item"}
- )
- }
- }
- },
- }
- }
- },
- "components": {
- "schemas": {
- "Item": {
- "title": "Item",
- "required": ["name", "price"],
- "type": "object",
- "properties": {
- "name": {"title": "Name", "type": "string"},
- "description": IsDict(
- {
- "title": "Description",
- "anyOf": [{"type": "string"}, {"type": "null"}],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Description", "type": "string"}
- ),
- "price": {"title": "Price", "type": "number"},
- "tax": IsDict(
- {
- "title": "Tax",
- "anyOf": [{"type": "number"}, {"type": "null"}],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Tax", "type": "number"}
- ),
- },
- },
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_body_multiple_params/test_tutorial001_an_py310.py b/tests/test_tutorial/test_body_multiple_params/test_tutorial001_an_py310.py
deleted file mode 100644
index 0173ab21b3..0000000000
--- a/tests/test_tutorial/test_body_multiple_params/test_tutorial001_an_py310.py
+++ /dev/null
@@ -1,213 +0,0 @@
-import pytest
-from dirty_equals import IsDict
-from fastapi.testclient import TestClient
-
-from ...utils import needs_py310
-
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.body_multiple_params.tutorial001_an_py310 import app
-
- client = TestClient(app)
- return client
-
-
-@needs_py310
-def test_post_body_q_bar_content(client: TestClient):
- response = client.put("/items/5?q=bar", json={"name": "Foo", "price": 50.5})
- assert response.status_code == 200
- assert response.json() == {
- "item_id": 5,
- "item": {
- "name": "Foo",
- "price": 50.5,
- "description": None,
- "tax": None,
- },
- "q": "bar",
- }
-
-
-@needs_py310
-def test_post_no_body_q_bar(client: TestClient):
- response = client.put("/items/5?q=bar", json=None)
- assert response.status_code == 200
- assert response.json() == {"item_id": 5, "q": "bar"}
-
-
-@needs_py310
-def test_post_no_body(client: TestClient):
- response = client.put("/items/5", json=None)
- assert response.status_code == 200
- assert response.json() == {"item_id": 5}
-
-
-@needs_py310
-def test_post_id_foo(client: TestClient):
- response = client.put("/items/foo", json=None)
- assert response.status_code == 422
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "int_parsing",
- "loc": ["path", "item_id"],
- "msg": "Input should be a valid integer, unable to parse string as an integer",
- "input": "foo",
- }
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "loc": ["path", "item_id"],
- "msg": "value is not a valid integer",
- "type": "type_error.integer",
- }
- ]
- }
- )
-
-
-@needs_py310
-def test_openapi_schema(client: TestClient):
- response = client.get("/openapi.json")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/{item_id}": {
- "put": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Update Item",
- "operationId": "update_item_items__item_id__put",
- "parameters": [
- {
- "required": True,
- "schema": {
- "title": "The ID of the item to get",
- "maximum": 1000.0,
- "minimum": 0.0,
- "type": "integer",
- },
- "name": "item_id",
- "in": "path",
- },
- {
- "required": False,
- "schema": IsDict(
- {
- "anyOf": [{"type": "string"}, {"type": "null"}],
- "title": "Q",
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Q", "type": "string"}
- ),
- "name": "q",
- "in": "query",
- },
- ],
- "requestBody": {
- "content": {
- "application/json": {
- "schema": IsDict(
- {
- "anyOf": [
- {"$ref": "#/components/schemas/Item"},
- {"type": "null"},
- ],
- "title": "Item",
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"$ref": "#/components/schemas/Item"}
- )
- }
- }
- },
- }
- }
- },
- "components": {
- "schemas": {
- "Item": {
- "title": "Item",
- "required": ["name", "price"],
- "type": "object",
- "properties": {
- "name": {"title": "Name", "type": "string"},
- "description": IsDict(
- {
- "title": "Description",
- "anyOf": [{"type": "string"}, {"type": "null"}],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Description", "type": "string"}
- ),
- "price": {"title": "Price", "type": "number"},
- "tax": IsDict(
- {
- "title": "Tax",
- "anyOf": [{"type": "number"}, {"type": "null"}],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Tax", "type": "number"}
- ),
- },
- },
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_body_multiple_params/test_tutorial001_an_py39.py b/tests/test_tutorial/test_body_multiple_params/test_tutorial001_an_py39.py
deleted file mode 100644
index cda19918ac..0000000000
--- a/tests/test_tutorial/test_body_multiple_params/test_tutorial001_an_py39.py
+++ /dev/null
@@ -1,213 +0,0 @@
-import pytest
-from dirty_equals import IsDict
-from fastapi.testclient import TestClient
-
-from ...utils import needs_py39
-
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.body_multiple_params.tutorial001_an_py39 import app
-
- client = TestClient(app)
- return client
-
-
-@needs_py39
-def test_post_body_q_bar_content(client: TestClient):
- response = client.put("/items/5?q=bar", json={"name": "Foo", "price": 50.5})
- assert response.status_code == 200
- assert response.json() == {
- "item_id": 5,
- "item": {
- "name": "Foo",
- "price": 50.5,
- "description": None,
- "tax": None,
- },
- "q": "bar",
- }
-
-
-@needs_py39
-def test_post_no_body_q_bar(client: TestClient):
- response = client.put("/items/5?q=bar", json=None)
- assert response.status_code == 200
- assert response.json() == {"item_id": 5, "q": "bar"}
-
-
-@needs_py39
-def test_post_no_body(client: TestClient):
- response = client.put("/items/5", json=None)
- assert response.status_code == 200
- assert response.json() == {"item_id": 5}
-
-
-@needs_py39
-def test_post_id_foo(client: TestClient):
- response = client.put("/items/foo", json=None)
- assert response.status_code == 422
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "int_parsing",
- "loc": ["path", "item_id"],
- "msg": "Input should be a valid integer, unable to parse string as an integer",
- "input": "foo",
- }
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "loc": ["path", "item_id"],
- "msg": "value is not a valid integer",
- "type": "type_error.integer",
- }
- ]
- }
- )
-
-
-@needs_py39
-def test_openapi_schema(client: TestClient):
- response = client.get("/openapi.json")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/{item_id}": {
- "put": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Update Item",
- "operationId": "update_item_items__item_id__put",
- "parameters": [
- {
- "required": True,
- "schema": {
- "title": "The ID of the item to get",
- "maximum": 1000.0,
- "minimum": 0.0,
- "type": "integer",
- },
- "name": "item_id",
- "in": "path",
- },
- {
- "required": False,
- "schema": IsDict(
- {
- "anyOf": [{"type": "string"}, {"type": "null"}],
- "title": "Q",
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Q", "type": "string"}
- ),
- "name": "q",
- "in": "query",
- },
- ],
- "requestBody": {
- "content": {
- "application/json": {
- "schema": IsDict(
- {
- "anyOf": [
- {"$ref": "#/components/schemas/Item"},
- {"type": "null"},
- ],
- "title": "Item",
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"$ref": "#/components/schemas/Item"}
- )
- }
- }
- },
- }
- }
- },
- "components": {
- "schemas": {
- "Item": {
- "title": "Item",
- "required": ["name", "price"],
- "type": "object",
- "properties": {
- "name": {"title": "Name", "type": "string"},
- "description": IsDict(
- {
- "title": "Description",
- "anyOf": [{"type": "string"}, {"type": "null"}],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Description", "type": "string"}
- ),
- "price": {"title": "Price", "type": "number"},
- "tax": IsDict(
- {
- "title": "Tax",
- "anyOf": [{"type": "number"}, {"type": "null"}],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Tax", "type": "number"}
- ),
- },
- },
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_body_multiple_params/test_tutorial001_py310.py b/tests/test_tutorial/test_body_multiple_params/test_tutorial001_py310.py
deleted file mode 100644
index 6632919331..0000000000
--- a/tests/test_tutorial/test_body_multiple_params/test_tutorial001_py310.py
+++ /dev/null
@@ -1,213 +0,0 @@
-import pytest
-from dirty_equals import IsDict
-from fastapi.testclient import TestClient
-
-from ...utils import needs_py310
-
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.body_multiple_params.tutorial001_py310 import app
-
- client = TestClient(app)
- return client
-
-
-@needs_py310
-def test_post_body_q_bar_content(client: TestClient):
- response = client.put("/items/5?q=bar", json={"name": "Foo", "price": 50.5})
- assert response.status_code == 200
- assert response.json() == {
- "item_id": 5,
- "item": {
- "name": "Foo",
- "price": 50.5,
- "description": None,
- "tax": None,
- },
- "q": "bar",
- }
-
-
-@needs_py310
-def test_post_no_body_q_bar(client: TestClient):
- response = client.put("/items/5?q=bar", json=None)
- assert response.status_code == 200
- assert response.json() == {"item_id": 5, "q": "bar"}
-
-
-@needs_py310
-def test_post_no_body(client: TestClient):
- response = client.put("/items/5", json=None)
- assert response.status_code == 200
- assert response.json() == {"item_id": 5}
-
-
-@needs_py310
-def test_post_id_foo(client: TestClient):
- response = client.put("/items/foo", json=None)
- assert response.status_code == 422
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "int_parsing",
- "loc": ["path", "item_id"],
- "msg": "Input should be a valid integer, unable to parse string as an integer",
- "input": "foo",
- }
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "loc": ["path", "item_id"],
- "msg": "value is not a valid integer",
- "type": "type_error.integer",
- }
- ]
- }
- )
-
-
-@needs_py310
-def test_openapi_schema(client: TestClient):
- response = client.get("/openapi.json")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/{item_id}": {
- "put": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Update Item",
- "operationId": "update_item_items__item_id__put",
- "parameters": [
- {
- "required": True,
- "schema": {
- "title": "The ID of the item to get",
- "maximum": 1000.0,
- "minimum": 0.0,
- "type": "integer",
- },
- "name": "item_id",
- "in": "path",
- },
- {
- "required": False,
- "schema": IsDict(
- {
- "anyOf": [{"type": "string"}, {"type": "null"}],
- "title": "Q",
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Q", "type": "string"}
- ),
- "name": "q",
- "in": "query",
- },
- ],
- "requestBody": {
- "content": {
- "application/json": {
- "schema": IsDict(
- {
- "anyOf": [
- {"$ref": "#/components/schemas/Item"},
- {"type": "null"},
- ],
- "title": "Item",
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"$ref": "#/components/schemas/Item"}
- )
- }
- }
- },
- }
- }
- },
- "components": {
- "schemas": {
- "Item": {
- "title": "Item",
- "required": ["name", "price"],
- "type": "object",
- "properties": {
- "name": {"title": "Name", "type": "string"},
- "description": IsDict(
- {
- "title": "Description",
- "anyOf": [{"type": "string"}, {"type": "null"}],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Description", "type": "string"}
- ),
- "price": {"title": "Price", "type": "number"},
- "tax": IsDict(
- {
- "title": "Tax",
- "anyOf": [{"type": "number"}, {"type": "null"}],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Tax", "type": "number"}
- ),
- },
- },
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_body_multiple_params/test_tutorial003.py b/tests/test_tutorial/test_body_multiple_params/test_tutorial003.py
index c26f8b89bc..d18ceae486 100644
--- a/tests/test_tutorial/test_body_multiple_params/test_tutorial003.py
+++ b/tests/test_tutorial/test_body_multiple_params/test_tutorial003.py
@@ -1,13 +1,26 @@
+import importlib
+
import pytest
from dirty_equals import IsDict
from fastapi.testclient import TestClient
+from ...utils import needs_py39, needs_py310
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.body_multiple_params.tutorial003 import app
- client = TestClient(app)
+@pytest.fixture(
+ name="client",
+ params=[
+ "tutorial003",
+ pytest.param("tutorial003_py310", marks=needs_py310),
+ "tutorial003_an",
+ pytest.param("tutorial003_an_py39", marks=needs_py39),
+ pytest.param("tutorial003_an_py310", marks=needs_py310),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.body_multiple_params.{request.param}")
+
+ client = TestClient(mod.app)
return client
diff --git a/tests/test_tutorial/test_body_multiple_params/test_tutorial003_an.py b/tests/test_tutorial/test_body_multiple_params/test_tutorial003_an.py
deleted file mode 100644
index 62c7e2fad8..0000000000
--- a/tests/test_tutorial/test_body_multiple_params/test_tutorial003_an.py
+++ /dev/null
@@ -1,273 +0,0 @@
-import pytest
-from dirty_equals import IsDict
-from fastapi.testclient import TestClient
-
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.body_multiple_params.tutorial003_an import app
-
- client = TestClient(app)
- return client
-
-
-def test_post_body_valid(client: TestClient):
- response = client.put(
- "/items/5",
- json={
- "importance": 2,
- "item": {"name": "Foo", "price": 50.5},
- "user": {"username": "Dave"},
- },
- )
- assert response.status_code == 200
- assert response.json() == {
- "item_id": 5,
- "importance": 2,
- "item": {
- "name": "Foo",
- "price": 50.5,
- "description": None,
- "tax": None,
- },
- "user": {"username": "Dave", "full_name": None},
- }
-
-
-def test_post_body_no_data(client: TestClient):
- response = client.put("/items/5", json=None)
- assert response.status_code == 422
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "missing",
- "loc": ["body", "item"],
- "msg": "Field required",
- "input": None,
- },
- {
- "type": "missing",
- "loc": ["body", "user"],
- "msg": "Field required",
- "input": None,
- },
- {
- "type": "missing",
- "loc": ["body", "importance"],
- "msg": "Field required",
- "input": None,
- },
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "loc": ["body", "item"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- {
- "loc": ["body", "user"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- {
- "loc": ["body", "importance"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- ]
- }
- )
-
-
-def test_post_body_empty_list(client: TestClient):
- response = client.put("/items/5", json=[])
- assert response.status_code == 422
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "missing",
- "loc": ["body", "item"],
- "msg": "Field required",
- "input": None,
- },
- {
- "type": "missing",
- "loc": ["body", "user"],
- "msg": "Field required",
- "input": None,
- },
- {
- "type": "missing",
- "loc": ["body", "importance"],
- "msg": "Field required",
- "input": None,
- },
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "loc": ["body", "item"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- {
- "loc": ["body", "user"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- {
- "loc": ["body", "importance"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- ]
- }
- )
-
-
-def test_openapi_schema(client: TestClient):
- response = client.get("/openapi.json")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/{item_id}": {
- "put": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Update Item",
- "operationId": "update_item_items__item_id__put",
- "parameters": [
- {
- "required": True,
- "schema": {"title": "Item Id", "type": "integer"},
- "name": "item_id",
- "in": "path",
- }
- ],
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/Body_update_item_items__item_id__put"
- }
- }
- },
- "required": True,
- },
- }
- }
- },
- "components": {
- "schemas": {
- "Item": {
- "title": "Item",
- "required": ["name", "price"],
- "type": "object",
- "properties": {
- "name": {"title": "Name", "type": "string"},
- "description": IsDict(
- {
- "title": "Description",
- "anyOf": [{"type": "string"}, {"type": "null"}],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Description", "type": "string"}
- ),
- "price": {"title": "Price", "type": "number"},
- "tax": IsDict(
- {
- "title": "Tax",
- "anyOf": [{"type": "number"}, {"type": "null"}],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Tax", "type": "number"}
- ),
- },
- },
- "User": {
- "title": "User",
- "required": ["username"],
- "type": "object",
- "properties": {
- "username": {"title": "Username", "type": "string"},
- "full_name": IsDict(
- {
- "title": "Full Name",
- "anyOf": [{"type": "string"}, {"type": "null"}],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Full Name", "type": "string"}
- ),
- },
- },
- "Body_update_item_items__item_id__put": {
- "title": "Body_update_item_items__item_id__put",
- "required": ["item", "user", "importance"],
- "type": "object",
- "properties": {
- "item": {"$ref": "#/components/schemas/Item"},
- "user": {"$ref": "#/components/schemas/User"},
- "importance": {"title": "Importance", "type": "integer"},
- },
- },
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_body_multiple_params/test_tutorial003_an_py310.py b/tests/test_tutorial/test_body_multiple_params/test_tutorial003_an_py310.py
deleted file mode 100644
index f46430fb5a..0000000000
--- a/tests/test_tutorial/test_body_multiple_params/test_tutorial003_an_py310.py
+++ /dev/null
@@ -1,279 +0,0 @@
-import pytest
-from dirty_equals import IsDict
-from fastapi.testclient import TestClient
-
-from ...utils import needs_py310
-
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.body_multiple_params.tutorial003_an_py310 import app
-
- client = TestClient(app)
- return client
-
-
-@needs_py310
-def test_post_body_valid(client: TestClient):
- response = client.put(
- "/items/5",
- json={
- "importance": 2,
- "item": {"name": "Foo", "price": 50.5},
- "user": {"username": "Dave"},
- },
- )
- assert response.status_code == 200
- assert response.json() == {
- "item_id": 5,
- "importance": 2,
- "item": {
- "name": "Foo",
- "price": 50.5,
- "description": None,
- "tax": None,
- },
- "user": {"username": "Dave", "full_name": None},
- }
-
-
-@needs_py310
-def test_post_body_no_data(client: TestClient):
- response = client.put("/items/5", json=None)
- assert response.status_code == 422
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "missing",
- "loc": ["body", "item"],
- "msg": "Field required",
- "input": None,
- },
- {
- "type": "missing",
- "loc": ["body", "user"],
- "msg": "Field required",
- "input": None,
- },
- {
- "type": "missing",
- "loc": ["body", "importance"],
- "msg": "Field required",
- "input": None,
- },
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "loc": ["body", "item"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- {
- "loc": ["body", "user"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- {
- "loc": ["body", "importance"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- ]
- }
- )
-
-
-@needs_py310
-def test_post_body_empty_list(client: TestClient):
- response = client.put("/items/5", json=[])
- assert response.status_code == 422
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "missing",
- "loc": ["body", "item"],
- "msg": "Field required",
- "input": None,
- },
- {
- "type": "missing",
- "loc": ["body", "user"],
- "msg": "Field required",
- "input": None,
- },
- {
- "type": "missing",
- "loc": ["body", "importance"],
- "msg": "Field required",
- "input": None,
- },
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "loc": ["body", "item"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- {
- "loc": ["body", "user"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- {
- "loc": ["body", "importance"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- ]
- }
- )
-
-
-@needs_py310
-def test_openapi_schema(client: TestClient):
- response = client.get("/openapi.json")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/{item_id}": {
- "put": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Update Item",
- "operationId": "update_item_items__item_id__put",
- "parameters": [
- {
- "required": True,
- "schema": {"title": "Item Id", "type": "integer"},
- "name": "item_id",
- "in": "path",
- }
- ],
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/Body_update_item_items__item_id__put"
- }
- }
- },
- "required": True,
- },
- }
- }
- },
- "components": {
- "schemas": {
- "Item": {
- "title": "Item",
- "required": ["name", "price"],
- "type": "object",
- "properties": {
- "name": {"title": "Name", "type": "string"},
- "description": IsDict(
- {
- "title": "Description",
- "anyOf": [{"type": "string"}, {"type": "null"}],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Description", "type": "string"}
- ),
- "price": {"title": "Price", "type": "number"},
- "tax": IsDict(
- {
- "title": "Tax",
- "anyOf": [{"type": "number"}, {"type": "null"}],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Tax", "type": "number"}
- ),
- },
- },
- "User": {
- "title": "User",
- "required": ["username"],
- "type": "object",
- "properties": {
- "username": {"title": "Username", "type": "string"},
- "full_name": IsDict(
- {
- "title": "Full Name",
- "anyOf": [{"type": "string"}, {"type": "null"}],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Full Name", "type": "string"}
- ),
- },
- },
- "Body_update_item_items__item_id__put": {
- "title": "Body_update_item_items__item_id__put",
- "required": ["item", "user", "importance"],
- "type": "object",
- "properties": {
- "item": {"$ref": "#/components/schemas/Item"},
- "user": {"$ref": "#/components/schemas/User"},
- "importance": {"title": "Importance", "type": "integer"},
- },
- },
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_body_multiple_params/test_tutorial003_an_py39.py b/tests/test_tutorial/test_body_multiple_params/test_tutorial003_an_py39.py
deleted file mode 100644
index 29071cddca..0000000000
--- a/tests/test_tutorial/test_body_multiple_params/test_tutorial003_an_py39.py
+++ /dev/null
@@ -1,279 +0,0 @@
-import pytest
-from dirty_equals import IsDict
-from fastapi.testclient import TestClient
-
-from ...utils import needs_py39
-
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.body_multiple_params.tutorial003_an_py39 import app
-
- client = TestClient(app)
- return client
-
-
-@needs_py39
-def test_post_body_valid(client: TestClient):
- response = client.put(
- "/items/5",
- json={
- "importance": 2,
- "item": {"name": "Foo", "price": 50.5},
- "user": {"username": "Dave"},
- },
- )
- assert response.status_code == 200
- assert response.json() == {
- "item_id": 5,
- "importance": 2,
- "item": {
- "name": "Foo",
- "price": 50.5,
- "description": None,
- "tax": None,
- },
- "user": {"username": "Dave", "full_name": None},
- }
-
-
-@needs_py39
-def test_post_body_no_data(client: TestClient):
- response = client.put("/items/5", json=None)
- assert response.status_code == 422
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "missing",
- "loc": ["body", "item"],
- "msg": "Field required",
- "input": None,
- },
- {
- "type": "missing",
- "loc": ["body", "user"],
- "msg": "Field required",
- "input": None,
- },
- {
- "type": "missing",
- "loc": ["body", "importance"],
- "msg": "Field required",
- "input": None,
- },
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "loc": ["body", "item"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- {
- "loc": ["body", "user"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- {
- "loc": ["body", "importance"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- ]
- }
- )
-
-
-@needs_py39
-def test_post_body_empty_list(client: TestClient):
- response = client.put("/items/5", json=[])
- assert response.status_code == 422
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "missing",
- "loc": ["body", "item"],
- "msg": "Field required",
- "input": None,
- },
- {
- "type": "missing",
- "loc": ["body", "user"],
- "msg": "Field required",
- "input": None,
- },
- {
- "type": "missing",
- "loc": ["body", "importance"],
- "msg": "Field required",
- "input": None,
- },
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "loc": ["body", "item"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- {
- "loc": ["body", "user"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- {
- "loc": ["body", "importance"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- ]
- }
- )
-
-
-@needs_py39
-def test_openapi_schema(client: TestClient):
- response = client.get("/openapi.json")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/{item_id}": {
- "put": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Update Item",
- "operationId": "update_item_items__item_id__put",
- "parameters": [
- {
- "required": True,
- "schema": {"title": "Item Id", "type": "integer"},
- "name": "item_id",
- "in": "path",
- }
- ],
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/Body_update_item_items__item_id__put"
- }
- }
- },
- "required": True,
- },
- }
- }
- },
- "components": {
- "schemas": {
- "Item": {
- "title": "Item",
- "required": ["name", "price"],
- "type": "object",
- "properties": {
- "name": {"title": "Name", "type": "string"},
- "description": IsDict(
- {
- "title": "Description",
- "anyOf": [{"type": "string"}, {"type": "null"}],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Description", "type": "string"}
- ),
- "price": {"title": "Price", "type": "number"},
- "tax": IsDict(
- {
- "title": "Tax",
- "anyOf": [{"type": "number"}, {"type": "null"}],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Tax", "type": "number"}
- ),
- },
- },
- "User": {
- "title": "User",
- "required": ["username"],
- "type": "object",
- "properties": {
- "username": {"title": "Username", "type": "string"},
- "full_name": IsDict(
- {
- "title": "Full Name",
- "anyOf": [{"type": "string"}, {"type": "null"}],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Full Name", "type": "string"}
- ),
- },
- },
- "Body_update_item_items__item_id__put": {
- "title": "Body_update_item_items__item_id__put",
- "required": ["item", "user", "importance"],
- "type": "object",
- "properties": {
- "item": {"$ref": "#/components/schemas/Item"},
- "user": {"$ref": "#/components/schemas/User"},
- "importance": {"title": "Importance", "type": "integer"},
- },
- },
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_body_multiple_params/test_tutorial003_py310.py b/tests/test_tutorial/test_body_multiple_params/test_tutorial003_py310.py
deleted file mode 100644
index 133afe9b5e..0000000000
--- a/tests/test_tutorial/test_body_multiple_params/test_tutorial003_py310.py
+++ /dev/null
@@ -1,279 +0,0 @@
-import pytest
-from dirty_equals import IsDict
-from fastapi.testclient import TestClient
-
-from ...utils import needs_py310
-
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.body_multiple_params.tutorial003_py310 import app
-
- client = TestClient(app)
- return client
-
-
-@needs_py310
-def test_post_body_valid(client: TestClient):
- response = client.put(
- "/items/5",
- json={
- "importance": 2,
- "item": {"name": "Foo", "price": 50.5},
- "user": {"username": "Dave"},
- },
- )
- assert response.status_code == 200
- assert response.json() == {
- "item_id": 5,
- "importance": 2,
- "item": {
- "name": "Foo",
- "price": 50.5,
- "description": None,
- "tax": None,
- },
- "user": {"username": "Dave", "full_name": None},
- }
-
-
-@needs_py310
-def test_post_body_no_data(client: TestClient):
- response = client.put("/items/5", json=None)
- assert response.status_code == 422
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "missing",
- "loc": ["body", "item"],
- "msg": "Field required",
- "input": None,
- },
- {
- "type": "missing",
- "loc": ["body", "user"],
- "msg": "Field required",
- "input": None,
- },
- {
- "type": "missing",
- "loc": ["body", "importance"],
- "msg": "Field required",
- "input": None,
- },
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "loc": ["body", "item"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- {
- "loc": ["body", "user"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- {
- "loc": ["body", "importance"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- ]
- }
- )
-
-
-@needs_py310
-def test_post_body_empty_list(client: TestClient):
- response = client.put("/items/5", json=[])
- assert response.status_code == 422
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "missing",
- "loc": ["body", "item"],
- "msg": "Field required",
- "input": None,
- },
- {
- "type": "missing",
- "loc": ["body", "user"],
- "msg": "Field required",
- "input": None,
- },
- {
- "type": "missing",
- "loc": ["body", "importance"],
- "msg": "Field required",
- "input": None,
- },
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "loc": ["body", "item"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- {
- "loc": ["body", "user"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- {
- "loc": ["body", "importance"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- ]
- }
- )
-
-
-@needs_py310
-def test_openapi_schema(client: TestClient):
- response = client.get("/openapi.json")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/{item_id}": {
- "put": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Update Item",
- "operationId": "update_item_items__item_id__put",
- "parameters": [
- {
- "required": True,
- "schema": {"title": "Item Id", "type": "integer"},
- "name": "item_id",
- "in": "path",
- }
- ],
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/Body_update_item_items__item_id__put"
- }
- }
- },
- "required": True,
- },
- }
- }
- },
- "components": {
- "schemas": {
- "Item": {
- "title": "Item",
- "required": ["name", "price"],
- "type": "object",
- "properties": {
- "name": {"title": "Name", "type": "string"},
- "description": IsDict(
- {
- "title": "Description",
- "anyOf": [{"type": "string"}, {"type": "null"}],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Description", "type": "string"}
- ),
- "price": {"title": "Price", "type": "number"},
- "tax": IsDict(
- {
- "title": "Tax",
- "anyOf": [{"type": "number"}, {"type": "null"}],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Tax", "type": "number"}
- ),
- },
- },
- "User": {
- "title": "User",
- "required": ["username"],
- "type": "object",
- "properties": {
- "username": {"title": "Username", "type": "string"},
- "full_name": IsDict(
- {
- "title": "Full Name",
- "anyOf": [{"type": "string"}, {"type": "null"}],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Full Name", "type": "string"}
- ),
- },
- },
- "Body_update_item_items__item_id__put": {
- "title": "Body_update_item_items__item_id__put",
- "required": ["item", "user", "importance"],
- "type": "object",
- "properties": {
- "item": {"$ref": "#/components/schemas/Item"},
- "user": {"$ref": "#/components/schemas/User"},
- "importance": {"title": "Importance", "type": "integer"},
- },
- },
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_body_nested_models/test_tutorial009.py b/tests/test_tutorial/test_body_nested_models/test_tutorial009.py
index 762073aea6..38ba3c8875 100644
--- a/tests/test_tutorial/test_body_nested_models/test_tutorial009.py
+++ b/tests/test_tutorial/test_body_nested_models/test_tutorial009.py
@@ -1,13 +1,23 @@
+import importlib
+
import pytest
from dirty_equals import IsDict
from fastapi.testclient import TestClient
+from ...utils import needs_py39
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.body_nested_models.tutorial009 import app
- client = TestClient(app)
+@pytest.fixture(
+ name="client",
+ params=[
+ "tutorial009",
+ pytest.param("tutorial009_py39", marks=needs_py39),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.body_nested_models.{request.param}")
+
+ client = TestClient(mod.app)
return client
diff --git a/tests/test_tutorial/test_body_nested_models/test_tutorial009_py39.py b/tests/test_tutorial/test_body_nested_models/test_tutorial009_py39.py
deleted file mode 100644
index 24623ceccb..0000000000
--- a/tests/test_tutorial/test_body_nested_models/test_tutorial009_py39.py
+++ /dev/null
@@ -1,128 +0,0 @@
-import pytest
-from dirty_equals import IsDict
-from fastapi.testclient import TestClient
-
-from ...utils import needs_py39
-
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.body_nested_models.tutorial009_py39 import app
-
- client = TestClient(app)
- return client
-
-
-@needs_py39
-def test_post_body(client: TestClient):
- data = {"2": 2.2, "3": 3.3}
- response = client.post("/index-weights/", json=data)
- assert response.status_code == 200, response.text
- assert response.json() == data
-
-
-@needs_py39
-def test_post_invalid_body(client: TestClient):
- data = {"foo": 2.2, "3": 3.3}
- response = client.post("/index-weights/", json=data)
- assert response.status_code == 422, response.text
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "int_parsing",
- "loc": ["body", "foo", "[key]"],
- "msg": "Input should be a valid integer, unable to parse string as an integer",
- "input": "foo",
- }
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "loc": ["body", "__key__"],
- "msg": "value is not a valid integer",
- "type": "type_error.integer",
- }
- ]
- }
- )
-
-
-@needs_py39
-def test_openapi_schema(client: TestClient):
- response = client.get("/openapi.json")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/index-weights/": {
- "post": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Create Index Weights",
- "operationId": "create_index_weights_index_weights__post",
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "title": "Weights",
- "type": "object",
- "additionalProperties": {"type": "number"},
- }
- }
- },
- "required": True,
- },
- }
- }
- },
- "components": {
- "schemas": {
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_body_updates/test_tutorial001.py b/tests/test_tutorial/test_body_updates/test_tutorial001.py
index e586534a07..f874dc9bd6 100644
--- a/tests/test_tutorial/test_body_updates/test_tutorial001.py
+++ b/tests/test_tutorial/test_body_updates/test_tutorial001.py
@@ -1,14 +1,23 @@
+import importlib
+
import pytest
from fastapi.testclient import TestClient
-from ...utils import needs_pydanticv1, needs_pydanticv2
+from ...utils import needs_py39, needs_py310, needs_pydanticv1, needs_pydanticv2
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.body_updates.tutorial001 import app
+@pytest.fixture(
+ name="client",
+ params=[
+ "tutorial001",
+ pytest.param("tutorial001_py310", marks=needs_py310),
+ pytest.param("tutorial001_py39", marks=needs_py39),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.body_updates.{request.param}")
- client = TestClient(app)
+ client = TestClient(mod.app)
return client
diff --git a/tests/test_tutorial/test_body_updates/test_tutorial001_py310.py b/tests/test_tutorial/test_body_updates/test_tutorial001_py310.py
deleted file mode 100644
index 6bc969d43a..0000000000
--- a/tests/test_tutorial/test_body_updates/test_tutorial001_py310.py
+++ /dev/null
@@ -1,317 +0,0 @@
-import pytest
-from fastapi.testclient import TestClient
-
-from ...utils import needs_py310, needs_pydanticv1, needs_pydanticv2
-
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.body_updates.tutorial001_py310 import app
-
- client = TestClient(app)
- return client
-
-
-@needs_py310
-def test_get(client: TestClient):
- response = client.get("/items/baz")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "name": "Baz",
- "description": None,
- "price": 50.2,
- "tax": 10.5,
- "tags": [],
- }
-
-
-@needs_py310
-def test_put(client: TestClient):
- response = client.put(
- "/items/bar", json={"name": "Barz", "price": 3, "description": None}
- )
- assert response.json() == {
- "name": "Barz",
- "description": None,
- "price": 3,
- "tax": 10.5,
- "tags": [],
- }
-
-
-@needs_py310
-@needs_pydanticv2
-def test_openapi_schema(client: TestClient):
- response = client.get("/openapi.json")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/{item_id}": {
- "get": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {
- "application/json": {
- "schema": {"$ref": "#/components/schemas/Item"}
- }
- },
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Read Item",
- "operationId": "read_item_items__item_id__get",
- "parameters": [
- {
- "required": True,
- "schema": {"title": "Item Id", "type": "string"},
- "name": "item_id",
- "in": "path",
- }
- ],
- },
- "put": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {
- "application/json": {
- "schema": {"$ref": "#/components/schemas/Item"}
- }
- },
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Update Item",
- "operationId": "update_item_items__item_id__put",
- "parameters": [
- {
- "required": True,
- "schema": {"title": "Item Id", "type": "string"},
- "name": "item_id",
- "in": "path",
- }
- ],
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {"$ref": "#/components/schemas/Item"}
- }
- },
- "required": True,
- },
- },
- }
- },
- "components": {
- "schemas": {
- "Item": {
- "type": "object",
- "title": "Item",
- "properties": {
- "name": {
- "anyOf": [{"type": "string"}, {"type": "null"}],
- "title": "Name",
- },
- "description": {
- "anyOf": [{"type": "string"}, {"type": "null"}],
- "title": "Description",
- },
- "price": {
- "anyOf": [{"type": "number"}, {"type": "null"}],
- "title": "Price",
- },
- "tax": {"title": "Tax", "type": "number", "default": 10.5},
- "tags": {
- "title": "Tags",
- "type": "array",
- "items": {"type": "string"},
- "default": [],
- },
- },
- },
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- }
- },
- }
-
-
-# TODO: remove when deprecating Pydantic v1
-@needs_py310
-@needs_pydanticv1
-def test_openapi_schema_pv1(client: TestClient):
- response = client.get("/openapi.json")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/{item_id}": {
- "get": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {
- "application/json": {
- "schema": {"$ref": "#/components/schemas/Item"}
- }
- },
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Read Item",
- "operationId": "read_item_items__item_id__get",
- "parameters": [
- {
- "required": True,
- "schema": {"title": "Item Id", "type": "string"},
- "name": "item_id",
- "in": "path",
- }
- ],
- },
- "put": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {
- "application/json": {
- "schema": {"$ref": "#/components/schemas/Item"}
- }
- },
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Update Item",
- "operationId": "update_item_items__item_id__put",
- "parameters": [
- {
- "required": True,
- "schema": {"title": "Item Id", "type": "string"},
- "name": "item_id",
- "in": "path",
- }
- ],
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {"$ref": "#/components/schemas/Item"}
- }
- },
- "required": True,
- },
- },
- }
- },
- "components": {
- "schemas": {
- "Item": {
- "title": "Item",
- "type": "object",
- "properties": {
- "name": {"title": "Name", "type": "string"},
- "description": {"title": "Description", "type": "string"},
- "price": {"title": "Price", "type": "number"},
- "tax": {"title": "Tax", "type": "number", "default": 10.5},
- "tags": {
- "title": "Tags",
- "type": "array",
- "items": {"type": "string"},
- "default": [],
- },
- },
- },
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_body_updates/test_tutorial001_py39.py b/tests/test_tutorial/test_body_updates/test_tutorial001_py39.py
deleted file mode 100644
index a1edb33707..0000000000
--- a/tests/test_tutorial/test_body_updates/test_tutorial001_py39.py
+++ /dev/null
@@ -1,317 +0,0 @@
-import pytest
-from fastapi.testclient import TestClient
-
-from ...utils import needs_py39, needs_pydanticv1, needs_pydanticv2
-
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.body_updates.tutorial001_py39 import app
-
- client = TestClient(app)
- return client
-
-
-@needs_py39
-def test_get(client: TestClient):
- response = client.get("/items/baz")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "name": "Baz",
- "description": None,
- "price": 50.2,
- "tax": 10.5,
- "tags": [],
- }
-
-
-@needs_py39
-def test_put(client: TestClient):
- response = client.put(
- "/items/bar", json={"name": "Barz", "price": 3, "description": None}
- )
- assert response.json() == {
- "name": "Barz",
- "description": None,
- "price": 3,
- "tax": 10.5,
- "tags": [],
- }
-
-
-@needs_py39
-@needs_pydanticv2
-def test_openapi_schema(client: TestClient):
- response = client.get("/openapi.json")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/{item_id}": {
- "get": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {
- "application/json": {
- "schema": {"$ref": "#/components/schemas/Item"}
- }
- },
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Read Item",
- "operationId": "read_item_items__item_id__get",
- "parameters": [
- {
- "required": True,
- "schema": {"title": "Item Id", "type": "string"},
- "name": "item_id",
- "in": "path",
- }
- ],
- },
- "put": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {
- "application/json": {
- "schema": {"$ref": "#/components/schemas/Item"}
- }
- },
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Update Item",
- "operationId": "update_item_items__item_id__put",
- "parameters": [
- {
- "required": True,
- "schema": {"title": "Item Id", "type": "string"},
- "name": "item_id",
- "in": "path",
- }
- ],
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {"$ref": "#/components/schemas/Item"}
- }
- },
- "required": True,
- },
- },
- }
- },
- "components": {
- "schemas": {
- "Item": {
- "type": "object",
- "title": "Item",
- "properties": {
- "name": {
- "anyOf": [{"type": "string"}, {"type": "null"}],
- "title": "Name",
- },
- "description": {
- "anyOf": [{"type": "string"}, {"type": "null"}],
- "title": "Description",
- },
- "price": {
- "anyOf": [{"type": "number"}, {"type": "null"}],
- "title": "Price",
- },
- "tax": {"title": "Tax", "type": "number", "default": 10.5},
- "tags": {
- "title": "Tags",
- "type": "array",
- "items": {"type": "string"},
- "default": [],
- },
- },
- },
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- }
- },
- }
-
-
-# TODO: remove when deprecating Pydantic v1
-@needs_py39
-@needs_pydanticv1
-def test_openapi_schema_pv1(client: TestClient):
- response = client.get("/openapi.json")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/{item_id}": {
- "get": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {
- "application/json": {
- "schema": {"$ref": "#/components/schemas/Item"}
- }
- },
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Read Item",
- "operationId": "read_item_items__item_id__get",
- "parameters": [
- {
- "required": True,
- "schema": {"title": "Item Id", "type": "string"},
- "name": "item_id",
- "in": "path",
- }
- ],
- },
- "put": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {
- "application/json": {
- "schema": {"$ref": "#/components/schemas/Item"}
- }
- },
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Update Item",
- "operationId": "update_item_items__item_id__put",
- "parameters": [
- {
- "required": True,
- "schema": {"title": "Item Id", "type": "string"},
- "name": "item_id",
- "in": "path",
- }
- ],
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {"$ref": "#/components/schemas/Item"}
- }
- },
- "required": True,
- },
- },
- }
- },
- "components": {
- "schemas": {
- "Item": {
- "title": "Item",
- "type": "object",
- "properties": {
- "name": {"title": "Name", "type": "string"},
- "description": {"title": "Description", "type": "string"},
- "price": {"title": "Price", "type": "number"},
- "tax": {"title": "Tax", "type": "number", "default": 10.5},
- "tags": {
- "title": "Tags",
- "type": "array",
- "items": {"type": "string"},
- "default": [],
- },
- },
- },
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_configure_swagger_ui/test_tutorial001.py b/tests/test_tutorial/test_configure_swagger_ui/test_tutorial001.py
index 72db54bd20..a04dba2197 100644
--- a/tests/test_tutorial/test_configure_swagger_ui/test_tutorial001.py
+++ b/tests/test_tutorial/test_configure_swagger_ui/test_tutorial001.py
@@ -8,31 +8,31 @@ client = TestClient(app)
def test_swagger_ui():
response = client.get("/docs")
assert response.status_code == 200, response.text
- assert (
- '"syntaxHighlight": false' in response.text
- ), "syntaxHighlight should be included and converted to JSON"
- assert (
- '"dom_id": "#swagger-ui"' in response.text
- ), "default configs should be preserved"
+ assert '"syntaxHighlight": false' in response.text, (
+ "syntaxHighlight should be included and converted to JSON"
+ )
+ assert '"dom_id": "#swagger-ui"' in response.text, (
+ "default configs should be preserved"
+ )
assert "presets: [" in response.text, "default configs should be preserved"
- assert (
- "SwaggerUIBundle.presets.apis," in response.text
- ), "default configs should be preserved"
- assert (
- "SwaggerUIBundle.SwaggerUIStandalonePreset" in response.text
- ), "default configs should be preserved"
- assert (
- '"layout": "BaseLayout",' in response.text
- ), "default configs should be preserved"
- assert (
- '"deepLinking": true,' in response.text
- ), "default configs should be preserved"
- assert (
- '"showExtensions": true,' in response.text
- ), "default configs should be preserved"
- assert (
- '"showCommonExtensions": true,' in response.text
- ), "default configs should be preserved"
+ assert "SwaggerUIBundle.presets.apis," in response.text, (
+ "default configs should be preserved"
+ )
+ assert "SwaggerUIBundle.SwaggerUIStandalonePreset" in response.text, (
+ "default configs should be preserved"
+ )
+ assert '"layout": "BaseLayout",' in response.text, (
+ "default configs should be preserved"
+ )
+ assert '"deepLinking": true,' in response.text, (
+ "default configs should be preserved"
+ )
+ assert '"showExtensions": true,' in response.text, (
+ "default configs should be preserved"
+ )
+ assert '"showCommonExtensions": true,' in response.text, (
+ "default configs should be preserved"
+ )
def test_get_users():
diff --git a/tests/test_tutorial/test_configure_swagger_ui/test_tutorial002.py b/tests/test_tutorial/test_configure_swagger_ui/test_tutorial002.py
index 1669011885..ea56b6f21c 100644
--- a/tests/test_tutorial/test_configure_swagger_ui/test_tutorial002.py
+++ b/tests/test_tutorial/test_configure_swagger_ui/test_tutorial002.py
@@ -8,34 +8,34 @@ client = TestClient(app)
def test_swagger_ui():
response = client.get("/docs")
assert response.status_code == 200, response.text
- assert (
- '"syntaxHighlight": false' not in response.text
- ), "not used parameters should not be included"
- assert (
- '"syntaxHighlight.theme": "obsidian"' in response.text
- ), "parameters with middle dots should be included in a JSON compatible way"
- assert (
- '"dom_id": "#swagger-ui"' in response.text
- ), "default configs should be preserved"
+ assert '"syntaxHighlight": false' not in response.text, (
+ "not used parameters should not be included"
+ )
+ assert '"syntaxHighlight": {"theme": "obsidian"}' in response.text, (
+ "parameters with middle dots should be included in a JSON compatible way"
+ )
+ assert '"dom_id": "#swagger-ui"' in response.text, (
+ "default configs should be preserved"
+ )
assert "presets: [" in response.text, "default configs should be preserved"
- assert (
- "SwaggerUIBundle.presets.apis," in response.text
- ), "default configs should be preserved"
- assert (
- "SwaggerUIBundle.SwaggerUIStandalonePreset" in response.text
- ), "default configs should be preserved"
- assert (
- '"layout": "BaseLayout",' in response.text
- ), "default configs should be preserved"
- assert (
- '"deepLinking": true,' in response.text
- ), "default configs should be preserved"
- assert (
- '"showExtensions": true,' in response.text
- ), "default configs should be preserved"
- assert (
- '"showCommonExtensions": true,' in response.text
- ), "default configs should be preserved"
+ assert "SwaggerUIBundle.presets.apis," in response.text, (
+ "default configs should be preserved"
+ )
+ assert "SwaggerUIBundle.SwaggerUIStandalonePreset" in response.text, (
+ "default configs should be preserved"
+ )
+ assert '"layout": "BaseLayout",' in response.text, (
+ "default configs should be preserved"
+ )
+ assert '"deepLinking": true,' in response.text, (
+ "default configs should be preserved"
+ )
+ assert '"showExtensions": true,' in response.text, (
+ "default configs should be preserved"
+ )
+ assert '"showCommonExtensions": true,' in response.text, (
+ "default configs should be preserved"
+ )
def test_get_users():
diff --git a/tests/test_tutorial/test_configure_swagger_ui/test_tutorial003.py b/tests/test_tutorial/test_configure_swagger_ui/test_tutorial003.py
index 187e89ace0..926bbb14f0 100644
--- a/tests/test_tutorial/test_configure_swagger_ui/test_tutorial003.py
+++ b/tests/test_tutorial/test_configure_swagger_ui/test_tutorial003.py
@@ -8,34 +8,34 @@ client = TestClient(app)
def test_swagger_ui():
response = client.get("/docs")
assert response.status_code == 200, response.text
- assert (
- '"deepLinking": false,' in response.text
- ), "overridden configs should be preserved"
- assert (
- '"deepLinking": true' not in response.text
- ), "overridden configs should not include the old value"
- assert (
- '"syntaxHighlight": false' not in response.text
- ), "not used parameters should not be included"
- assert (
- '"dom_id": "#swagger-ui"' in response.text
- ), "default configs should be preserved"
+ assert '"deepLinking": false,' in response.text, (
+ "overridden configs should be preserved"
+ )
+ assert '"deepLinking": true' not in response.text, (
+ "overridden configs should not include the old value"
+ )
+ assert '"syntaxHighlight": false' not in response.text, (
+ "not used parameters should not be included"
+ )
+ assert '"dom_id": "#swagger-ui"' in response.text, (
+ "default configs should be preserved"
+ )
assert "presets: [" in response.text, "default configs should be preserved"
- assert (
- "SwaggerUIBundle.presets.apis," in response.text
- ), "default configs should be preserved"
- assert (
- "SwaggerUIBundle.SwaggerUIStandalonePreset" in response.text
- ), "default configs should be preserved"
- assert (
- '"layout": "BaseLayout",' in response.text
- ), "default configs should be preserved"
- assert (
- '"showExtensions": true,' in response.text
- ), "default configs should be preserved"
- assert (
- '"showCommonExtensions": true,' in response.text
- ), "default configs should be preserved"
+ assert "SwaggerUIBundle.presets.apis," in response.text, (
+ "default configs should be preserved"
+ )
+ assert "SwaggerUIBundle.SwaggerUIStandalonePreset" in response.text, (
+ "default configs should be preserved"
+ )
+ assert '"layout": "BaseLayout",' in response.text, (
+ "default configs should be preserved"
+ )
+ assert '"showExtensions": true,' in response.text, (
+ "default configs should be preserved"
+ )
+ assert '"showCommonExtensions": true,' in response.text, (
+ "default configs should be preserved"
+ )
def test_get_users():
diff --git a/docs_src/sql_databases/sql_app/tests/__init__.py b/tests/test_tutorial/test_cookie_param_models/__init__.py
similarity index 100%
rename from docs_src/sql_databases/sql_app/tests/__init__.py
rename to tests/test_tutorial/test_cookie_param_models/__init__.py
diff --git a/tests/test_tutorial/test_cookie_param_models/test_tutorial001.py b/tests/test_tutorial/test_cookie_param_models/test_tutorial001.py
new file mode 100644
index 0000000000..60643185a4
--- /dev/null
+++ b/tests/test_tutorial/test_cookie_param_models/test_tutorial001.py
@@ -0,0 +1,205 @@
+import importlib
+
+import pytest
+from dirty_equals import IsDict
+from fastapi.testclient import TestClient
+from inline_snapshot import snapshot
+
+from tests.utils import needs_py39, needs_py310
+
+
+@pytest.fixture(
+ name="client",
+ params=[
+ "tutorial001",
+ pytest.param("tutorial001_py310", marks=needs_py310),
+ "tutorial001_an",
+ pytest.param("tutorial001_an_py39", marks=needs_py39),
+ pytest.param("tutorial001_an_py310", marks=needs_py310),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.cookie_param_models.{request.param}")
+
+ client = TestClient(mod.app)
+ return client
+
+
+def test_cookie_param_model(client: TestClient):
+ with client as c:
+ c.cookies.set("session_id", "123")
+ c.cookies.set("fatebook_tracker", "456")
+ c.cookies.set("googall_tracker", "789")
+ response = c.get("/items/")
+ assert response.status_code == 200
+ assert response.json() == {
+ "session_id": "123",
+ "fatebook_tracker": "456",
+ "googall_tracker": "789",
+ }
+
+
+def test_cookie_param_model_defaults(client: TestClient):
+ with client as c:
+ c.cookies.set("session_id", "123")
+ response = c.get("/items/")
+ assert response.status_code == 200
+ assert response.json() == {
+ "session_id": "123",
+ "fatebook_tracker": None,
+ "googall_tracker": None,
+ }
+
+
+def test_cookie_param_model_invalid(client: TestClient):
+ response = client.get("/items/")
+ assert response.status_code == 422
+ assert response.json() == snapshot(
+ IsDict(
+ {
+ "detail": [
+ {
+ "type": "missing",
+ "loc": ["cookie", "session_id"],
+ "msg": "Field required",
+ "input": {},
+ }
+ ]
+ }
+ )
+ | IsDict(
+ # TODO: remove when deprecating Pydantic v1
+ {
+ "detail": [
+ {
+ "type": "value_error.missing",
+ "loc": ["cookie", "session_id"],
+ "msg": "field required",
+ }
+ ]
+ }
+ )
+ )
+
+
+def test_cookie_param_model_extra(client: TestClient):
+ with client as c:
+ c.cookies.set("session_id", "123")
+ c.cookies.set("extra", "track-me-here-too")
+ response = c.get("/items/")
+ assert response.status_code == 200
+ assert response.json() == snapshot(
+ {"session_id": "123", "fatebook_tracker": None, "googall_tracker": None}
+ )
+
+
+def test_openapi_schema(client: TestClient):
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == snapshot(
+ {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/items/": {
+ "get": {
+ "summary": "Read Items",
+ "operationId": "read_items_items__get",
+ "parameters": [
+ {
+ "name": "session_id",
+ "in": "cookie",
+ "required": True,
+ "schema": {"type": "string", "title": "Session Id"},
+ },
+ {
+ "name": "fatebook_tracker",
+ "in": "cookie",
+ "required": False,
+ "schema": IsDict(
+ {
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ "title": "Fatebook Tracker",
+ }
+ )
+ | IsDict(
+ # TODO: remove when deprecating Pydantic v1
+ {
+ "type": "string",
+ "title": "Fatebook Tracker",
+ }
+ ),
+ },
+ {
+ "name": "googall_tracker",
+ "in": "cookie",
+ "required": False,
+ "schema": IsDict(
+ {
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ "title": "Googall Tracker",
+ }
+ )
+ | IsDict(
+ # TODO: remove when deprecating Pydantic v1
+ {
+ "type": "string",
+ "title": "Googall Tracker",
+ }
+ ),
+ },
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "HTTPValidationError": {
+ "properties": {
+ "detail": {
+ "items": {
+ "$ref": "#/components/schemas/ValidationError"
+ },
+ "type": "array",
+ "title": "Detail",
+ }
+ },
+ "type": "object",
+ "title": "HTTPValidationError",
+ },
+ "ValidationError": {
+ "properties": {
+ "loc": {
+ "items": {
+ "anyOf": [{"type": "string"}, {"type": "integer"}]
+ },
+ "type": "array",
+ "title": "Location",
+ },
+ "msg": {"type": "string", "title": "Message"},
+ "type": {"type": "string", "title": "Error Type"},
+ },
+ "type": "object",
+ "required": ["loc", "msg", "type"],
+ "title": "ValidationError",
+ },
+ }
+ },
+ }
+ )
diff --git a/tests/test_tutorial/test_cookie_param_models/test_tutorial002.py b/tests/test_tutorial/test_cookie_param_models/test_tutorial002.py
new file mode 100644
index 0000000000..cef6f66309
--- /dev/null
+++ b/tests/test_tutorial/test_cookie_param_models/test_tutorial002.py
@@ -0,0 +1,243 @@
+import importlib
+
+import pytest
+from dirty_equals import IsDict
+from fastapi.testclient import TestClient
+from inline_snapshot import snapshot
+
+from tests.utils import (
+ needs_py39,
+ needs_py310,
+ needs_pydanticv1,
+ needs_pydanticv2,
+ pydantic_snapshot,
+)
+
+
+@pytest.fixture(
+ name="client",
+ params=[
+ pytest.param("tutorial002", marks=needs_pydanticv2),
+ pytest.param("tutorial002_py310", marks=[needs_py310, needs_pydanticv2]),
+ pytest.param("tutorial002_an", marks=needs_pydanticv2),
+ pytest.param("tutorial002_an_py39", marks=[needs_py39, needs_pydanticv2]),
+ pytest.param("tutorial002_an_py310", marks=[needs_py310, needs_pydanticv2]),
+ pytest.param("tutorial002_pv1", marks=[needs_pydanticv1, needs_pydanticv1]),
+ pytest.param("tutorial002_pv1_py310", marks=[needs_py310, needs_pydanticv1]),
+ pytest.param("tutorial002_pv1_an", marks=[needs_pydanticv1]),
+ pytest.param("tutorial002_pv1_an_py39", marks=[needs_py39, needs_pydanticv1]),
+ pytest.param("tutorial002_pv1_an_py310", marks=[needs_py310, needs_pydanticv1]),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.cookie_param_models.{request.param}")
+
+ client = TestClient(mod.app)
+ return client
+
+
+def test_cookie_param_model(client: TestClient):
+ with client as c:
+ c.cookies.set("session_id", "123")
+ c.cookies.set("fatebook_tracker", "456")
+ c.cookies.set("googall_tracker", "789")
+ response = c.get("/items/")
+ assert response.status_code == 200
+ assert response.json() == {
+ "session_id": "123",
+ "fatebook_tracker": "456",
+ "googall_tracker": "789",
+ }
+
+
+def test_cookie_param_model_defaults(client: TestClient):
+ with client as c:
+ c.cookies.set("session_id", "123")
+ response = c.get("/items/")
+ assert response.status_code == 200
+ assert response.json() == {
+ "session_id": "123",
+ "fatebook_tracker": None,
+ "googall_tracker": None,
+ }
+
+
+def test_cookie_param_model_invalid(client: TestClient):
+ response = client.get("/items/")
+ assert response.status_code == 422
+ assert response.json() == pydantic_snapshot(
+ v2=snapshot(
+ {
+ "detail": [
+ {
+ "type": "missing",
+ "loc": ["cookie", "session_id"],
+ "msg": "Field required",
+ "input": {},
+ }
+ ]
+ }
+ ),
+ v1=snapshot(
+ {
+ "detail": [
+ {
+ "type": "value_error.missing",
+ "loc": ["cookie", "session_id"],
+ "msg": "field required",
+ }
+ ]
+ }
+ ),
+ )
+
+
+def test_cookie_param_model_extra(client: TestClient):
+ with client as c:
+ c.cookies.set("session_id", "123")
+ c.cookies.set("extra", "track-me-here-too")
+ response = c.get("/items/")
+ assert response.status_code == 422
+ assert response.json() == snapshot(
+ IsDict(
+ {
+ "detail": [
+ {
+ "type": "extra_forbidden",
+ "loc": ["cookie", "extra"],
+ "msg": "Extra inputs are not permitted",
+ "input": "track-me-here-too",
+ }
+ ]
+ }
+ )
+ | IsDict(
+ # TODO: remove when deprecating Pydantic v1
+ {
+ "detail": [
+ {
+ "type": "value_error.extra",
+ "loc": ["cookie", "extra"],
+ "msg": "extra fields not permitted",
+ }
+ ]
+ }
+ )
+ )
+
+
+def test_openapi_schema(client: TestClient):
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == snapshot(
+ {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/items/": {
+ "get": {
+ "summary": "Read Items",
+ "operationId": "read_items_items__get",
+ "parameters": [
+ {
+ "name": "session_id",
+ "in": "cookie",
+ "required": True,
+ "schema": {"type": "string", "title": "Session Id"},
+ },
+ {
+ "name": "fatebook_tracker",
+ "in": "cookie",
+ "required": False,
+ "schema": pydantic_snapshot(
+ v2=snapshot(
+ {
+ "anyOf": [
+ {"type": "string"},
+ {"type": "null"},
+ ],
+ "title": "Fatebook Tracker",
+ }
+ ),
+ v1=snapshot(
+ # TODO: remove when deprecating Pydantic v1
+ {
+ "type": "string",
+ "title": "Fatebook Tracker",
+ }
+ ),
+ ),
+ },
+ {
+ "name": "googall_tracker",
+ "in": "cookie",
+ "required": False,
+ "schema": IsDict(
+ {
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ "title": "Googall Tracker",
+ }
+ )
+ | IsDict(
+ # TODO: remove when deprecating Pydantic v1
+ {
+ "type": "string",
+ "title": "Googall Tracker",
+ }
+ ),
+ },
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "HTTPValidationError": {
+ "properties": {
+ "detail": {
+ "items": {
+ "$ref": "#/components/schemas/ValidationError"
+ },
+ "type": "array",
+ "title": "Detail",
+ }
+ },
+ "type": "object",
+ "title": "HTTPValidationError",
+ },
+ "ValidationError": {
+ "properties": {
+ "loc": {
+ "items": {
+ "anyOf": [{"type": "string"}, {"type": "integer"}]
+ },
+ "type": "array",
+ "title": "Location",
+ },
+ "msg": {"type": "string", "title": "Message"},
+ "type": {"type": "string", "title": "Error Type"},
+ },
+ "type": "object",
+ "required": ["loc", "msg", "type"],
+ "title": "ValidationError",
+ },
+ }
+ },
+ }
+ )
diff --git a/tests/test_tutorial/test_cookie_params/test_tutorial001.py b/tests/test_tutorial/test_cookie_params/test_tutorial001.py
index 7d0e669aba..90e8dfd37c 100644
--- a/tests/test_tutorial/test_cookie_params/test_tutorial001.py
+++ b/tests/test_tutorial/test_cookie_params/test_tutorial001.py
@@ -1,8 +1,27 @@
+import importlib
+from types import ModuleType
+
import pytest
from dirty_equals import IsDict
from fastapi.testclient import TestClient
-from docs_src.cookie_params.tutorial001 import app
+from ...utils import needs_py39, needs_py310
+
+
+@pytest.fixture(
+ name="mod",
+ params=[
+ "tutorial001",
+ pytest.param("tutorial001_py310", marks=needs_py310),
+ "tutorial001_an",
+ pytest.param("tutorial001_an_py39", marks=needs_py39),
+ pytest.param("tutorial001_an_py310", marks=needs_py310),
+ ],
+)
+def get_mod(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.cookie_params.{request.param}")
+
+ return mod
@pytest.mark.parametrize(
@@ -19,15 +38,15 @@ from docs_src.cookie_params.tutorial001 import app
("/items", {"session": "cookiesession"}, 200, {"ads_id": None}),
],
)
-def test(path, cookies, expected_status, expected_response):
- client = TestClient(app, cookies=cookies)
+def test(path, cookies, expected_status, expected_response, mod: ModuleType):
+ client = TestClient(mod.app, cookies=cookies)
response = client.get(path)
assert response.status_code == expected_status
assert response.json() == expected_response
-def test_openapi_schema():
- client = TestClient(app)
+def test_openapi_schema(mod: ModuleType):
+ client = TestClient(mod.app)
response = client.get("/openapi.json")
assert response.status_code == 200
assert response.json() == {
diff --git a/tests/test_tutorial/test_cookie_params/test_tutorial001_an.py b/tests/test_tutorial/test_cookie_params/test_tutorial001_an.py
deleted file mode 100644
index 2505876c87..0000000000
--- a/tests/test_tutorial/test_cookie_params/test_tutorial001_an.py
+++ /dev/null
@@ -1,108 +0,0 @@
-import pytest
-from dirty_equals import IsDict
-from fastapi.testclient import TestClient
-
-from docs_src.cookie_params.tutorial001_an import app
-
-
-@pytest.mark.parametrize(
- "path,cookies,expected_status,expected_response",
- [
- ("/items", None, 200, {"ads_id": None}),
- ("/items", {"ads_id": "ads_track"}, 200, {"ads_id": "ads_track"}),
- (
- "/items",
- {"ads_id": "ads_track", "session": "cookiesession"},
- 200,
- {"ads_id": "ads_track"},
- ),
- ("/items", {"session": "cookiesession"}, 200, {"ads_id": None}),
- ],
-)
-def test(path, cookies, expected_status, expected_response):
- client = TestClient(app, cookies=cookies)
- response = client.get(path)
- assert response.status_code == expected_status
- assert response.json() == expected_response
-
-
-def test_openapi_schema():
- client = TestClient(app)
- response = client.get("/openapi.json")
- assert response.status_code == 200
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/": {
- "get": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Read Items",
- "operationId": "read_items_items__get",
- "parameters": [
- {
- "required": False,
- "schema": IsDict(
- {
- "anyOf": [{"type": "string"}, {"type": "null"}],
- "title": "Ads Id",
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Ads Id", "type": "string"}
- ),
- "name": "ads_id",
- "in": "cookie",
- }
- ],
- }
- }
- },
- "components": {
- "schemas": {
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_cookie_params/test_tutorial001_an_py310.py b/tests/test_tutorial/test_cookie_params/test_tutorial001_an_py310.py
deleted file mode 100644
index 108f78b9c8..0000000000
--- a/tests/test_tutorial/test_cookie_params/test_tutorial001_an_py310.py
+++ /dev/null
@@ -1,114 +0,0 @@
-import pytest
-from dirty_equals import IsDict
-from fastapi.testclient import TestClient
-
-from ...utils import needs_py310
-
-
-@needs_py310
-@pytest.mark.parametrize(
- "path,cookies,expected_status,expected_response",
- [
- ("/items", None, 200, {"ads_id": None}),
- ("/items", {"ads_id": "ads_track"}, 200, {"ads_id": "ads_track"}),
- (
- "/items",
- {"ads_id": "ads_track", "session": "cookiesession"},
- 200,
- {"ads_id": "ads_track"},
- ),
- ("/items", {"session": "cookiesession"}, 200, {"ads_id": None}),
- ],
-)
-def test(path, cookies, expected_status, expected_response):
- from docs_src.cookie_params.tutorial001_an_py310 import app
-
- client = TestClient(app, cookies=cookies)
- response = client.get(path)
- assert response.status_code == expected_status
- assert response.json() == expected_response
-
-
-@needs_py310
-def test_openapi_schema():
- from docs_src.cookie_params.tutorial001_an_py310 import app
-
- client = TestClient(app)
- response = client.get("/openapi.json")
- assert response.status_code == 200
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/": {
- "get": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Read Items",
- "operationId": "read_items_items__get",
- "parameters": [
- {
- "required": False,
- "schema": IsDict(
- {
- "anyOf": [{"type": "string"}, {"type": "null"}],
- "title": "Ads Id",
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Ads Id", "type": "string"}
- ),
- "name": "ads_id",
- "in": "cookie",
- }
- ],
- }
- }
- },
- "components": {
- "schemas": {
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_cookie_params/test_tutorial001_an_py39.py b/tests/test_tutorial/test_cookie_params/test_tutorial001_an_py39.py
deleted file mode 100644
index 8126a10523..0000000000
--- a/tests/test_tutorial/test_cookie_params/test_tutorial001_an_py39.py
+++ /dev/null
@@ -1,114 +0,0 @@
-import pytest
-from dirty_equals import IsDict
-from fastapi.testclient import TestClient
-
-from ...utils import needs_py39
-
-
-@needs_py39
-@pytest.mark.parametrize(
- "path,cookies,expected_status,expected_response",
- [
- ("/items", None, 200, {"ads_id": None}),
- ("/items", {"ads_id": "ads_track"}, 200, {"ads_id": "ads_track"}),
- (
- "/items",
- {"ads_id": "ads_track", "session": "cookiesession"},
- 200,
- {"ads_id": "ads_track"},
- ),
- ("/items", {"session": "cookiesession"}, 200, {"ads_id": None}),
- ],
-)
-def test(path, cookies, expected_status, expected_response):
- from docs_src.cookie_params.tutorial001_an_py39 import app
-
- client = TestClient(app, cookies=cookies)
- response = client.get(path)
- assert response.status_code == expected_status
- assert response.json() == expected_response
-
-
-@needs_py39
-def test_openapi_schema():
- from docs_src.cookie_params.tutorial001_an_py39 import app
-
- client = TestClient(app)
- response = client.get("/openapi.json")
- assert response.status_code == 200
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/": {
- "get": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Read Items",
- "operationId": "read_items_items__get",
- "parameters": [
- {
- "required": False,
- "schema": IsDict(
- {
- "anyOf": [{"type": "string"}, {"type": "null"}],
- "title": "Ads Id",
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Ads Id", "type": "string"}
- ),
- "name": "ads_id",
- "in": "cookie",
- }
- ],
- }
- }
- },
- "components": {
- "schemas": {
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_cookie_params/test_tutorial001_py310.py b/tests/test_tutorial/test_cookie_params/test_tutorial001_py310.py
deleted file mode 100644
index 6711fa5818..0000000000
--- a/tests/test_tutorial/test_cookie_params/test_tutorial001_py310.py
+++ /dev/null
@@ -1,114 +0,0 @@
-import pytest
-from dirty_equals import IsDict
-from fastapi.testclient import TestClient
-
-from ...utils import needs_py310
-
-
-@needs_py310
-@pytest.mark.parametrize(
- "path,cookies,expected_status,expected_response",
- [
- ("/items", None, 200, {"ads_id": None}),
- ("/items", {"ads_id": "ads_track"}, 200, {"ads_id": "ads_track"}),
- (
- "/items",
- {"ads_id": "ads_track", "session": "cookiesession"},
- 200,
- {"ads_id": "ads_track"},
- ),
- ("/items", {"session": "cookiesession"}, 200, {"ads_id": None}),
- ],
-)
-def test(path, cookies, expected_status, expected_response):
- from docs_src.cookie_params.tutorial001_py310 import app
-
- client = TestClient(app, cookies=cookies)
- response = client.get(path)
- assert response.status_code == expected_status
- assert response.json() == expected_response
-
-
-@needs_py310
-def test_openapi_schema():
- from docs_src.cookie_params.tutorial001_py310 import app
-
- client = TestClient(app)
- response = client.get("/openapi.json")
- assert response.status_code == 200
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/": {
- "get": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Read Items",
- "operationId": "read_items_items__get",
- "parameters": [
- {
- "required": False,
- "schema": IsDict(
- {
- "anyOf": [{"type": "string"}, {"type": "null"}],
- "title": "Ads Id",
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Ads Id", "type": "string"}
- ),
- "name": "ads_id",
- "in": "cookie",
- }
- ],
- }
- }
- },
- "components": {
- "schemas": {
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_custom_docs_ui/test_tutorial001.py b/tests/test_tutorial/test_custom_docs_ui/test_tutorial001.py
index aff070d747..cb8e8c2248 100644
--- a/tests/test_tutorial/test_custom_docs_ui/test_tutorial001.py
+++ b/tests/test_tutorial/test_custom_docs_ui/test_tutorial001.py
@@ -33,7 +33,7 @@ def test_swagger_ui_oauth2_redirect_html(client: TestClient):
def test_redoc_html(client: TestClient):
response = client.get("/redoc")
assert response.status_code == 200, response.text
- assert "https://unpkg.com/redoc@next/bundles/redoc.standalone.js" in response.text
+ assert "https://unpkg.com/redoc@2/bundles/redoc.standalone.js" in response.text
def test_api(client: TestClient):
diff --git a/tests/test_tutorial/test_custom_request_and_route/test_tutorial002.py b/tests/test_tutorial/test_custom_request_and_route/test_tutorial002.py
index 6f7355aaa1..647f1c5ddf 100644
--- a/tests/test_tutorial/test_custom_request_and_route/test_tutorial002.py
+++ b/tests/test_tutorial/test_custom_request_and_route/test_tutorial002.py
@@ -1,4 +1,4 @@
-from dirty_equals import IsDict
+from dirty_equals import IsDict, IsOneOf
from fastapi.testclient import TestClient
from docs_src.custom_request_and_route.tutorial002 import app
@@ -24,14 +24,16 @@ def test_exception_handler_body_access():
"input": {"numbers": [1, 2, 3]},
}
],
- "body": '{"numbers": [1, 2, 3]}',
+ # httpx 0.28.0 switches to compact JSON https://github.com/encode/httpx/issues/3363
+ "body": IsOneOf('{"numbers": [1, 2, 3]}', '{"numbers":[1,2,3]}'),
}
}
) | IsDict(
# TODO: remove when deprecating Pydantic v1
{
"detail": {
- "body": '{"numbers": [1, 2, 3]}',
+ # httpx 0.28.0 switches to compact JSON https://github.com/encode/httpx/issues/3363
+ "body": IsOneOf('{"numbers": [1, 2, 3]}', '{"numbers":[1,2,3]}'),
"errors": [
{
"loc": ["body"],
diff --git a/tests/test_tutorial/test_dependencies/test_tutorial001.py b/tests/test_tutorial/test_dependencies/test_tutorial001.py
index d1324a6411..ed9944912e 100644
--- a/tests/test_tutorial/test_dependencies/test_tutorial001.py
+++ b/tests/test_tutorial/test_dependencies/test_tutorial001.py
@@ -1,10 +1,27 @@
+import importlib
+
import pytest
from dirty_equals import IsDict
from fastapi.testclient import TestClient
-from docs_src.dependencies.tutorial001 import app
+from ...utils import needs_py39, needs_py310
-client = TestClient(app)
+
+@pytest.fixture(
+ name="client",
+ params=[
+ "tutorial001",
+ pytest.param("tutorial001_py310", marks=needs_py310),
+ "tutorial001_an",
+ pytest.param("tutorial001_an_py39", marks=needs_py39),
+ pytest.param("tutorial001_an_py310", marks=needs_py310),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.dependencies.{request.param}")
+
+ client = TestClient(mod.app)
+ return client
@pytest.mark.parametrize(
@@ -17,13 +34,13 @@ client = TestClient(app)
("/users", 200, {"q": None, "skip": 0, "limit": 100}),
],
)
-def test_get(path, expected_status, expected_response):
+def test_get(path, expected_status, expected_response, client: TestClient):
response = client.get(path)
assert response.status_code == expected_status
assert response.json() == expected_response
-def test_openapi_schema():
+def test_openapi_schema(client: TestClient):
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
diff --git a/tests/test_tutorial/test_dependencies/test_tutorial001_an.py b/tests/test_tutorial/test_dependencies/test_tutorial001_an.py
deleted file mode 100644
index 79c2a1e10e..0000000000
--- a/tests/test_tutorial/test_dependencies/test_tutorial001_an.py
+++ /dev/null
@@ -1,183 +0,0 @@
-import pytest
-from dirty_equals import IsDict
-from fastapi.testclient import TestClient
-
-from docs_src.dependencies.tutorial001_an import app
-
-client = TestClient(app)
-
-
-@pytest.mark.parametrize(
- "path,expected_status,expected_response",
- [
- ("/items", 200, {"q": None, "skip": 0, "limit": 100}),
- ("/items?q=foo", 200, {"q": "foo", "skip": 0, "limit": 100}),
- ("/items?q=foo&skip=5", 200, {"q": "foo", "skip": 5, "limit": 100}),
- ("/items?q=foo&skip=5&limit=30", 200, {"q": "foo", "skip": 5, "limit": 30}),
- ("/users", 200, {"q": None, "skip": 0, "limit": 100}),
- ],
-)
-def test_get(path, expected_status, expected_response):
- response = client.get(path)
- assert response.status_code == expected_status
- assert response.json() == expected_response
-
-
-def test_openapi_schema():
- response = client.get("/openapi.json")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/": {
- "get": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Read Items",
- "operationId": "read_items_items__get",
- "parameters": [
- {
- "required": False,
- "schema": IsDict(
- {
- "anyOf": [{"type": "string"}, {"type": "null"}],
- "title": "Q",
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Q", "type": "string"}
- ),
- "name": "q",
- "in": "query",
- },
- {
- "required": False,
- "schema": {
- "title": "Skip",
- "type": "integer",
- "default": 0,
- },
- "name": "skip",
- "in": "query",
- },
- {
- "required": False,
- "schema": {
- "title": "Limit",
- "type": "integer",
- "default": 100,
- },
- "name": "limit",
- "in": "query",
- },
- ],
- }
- },
- "/users/": {
- "get": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Read Users",
- "operationId": "read_users_users__get",
- "parameters": [
- {
- "required": False,
- "schema": IsDict(
- {
- "anyOf": [{"type": "string"}, {"type": "null"}],
- "title": "Q",
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Q", "type": "string"}
- ),
- "name": "q",
- "in": "query",
- },
- {
- "required": False,
- "schema": {
- "title": "Skip",
- "type": "integer",
- "default": 0,
- },
- "name": "skip",
- "in": "query",
- },
- {
- "required": False,
- "schema": {
- "title": "Limit",
- "type": "integer",
- "default": 100,
- },
- "name": "limit",
- "in": "query",
- },
- ],
- }
- },
- },
- "components": {
- "schemas": {
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_dependencies/test_tutorial001_an_py310.py b/tests/test_tutorial/test_dependencies/test_tutorial001_an_py310.py
deleted file mode 100644
index 7db55a1c55..0000000000
--- a/tests/test_tutorial/test_dependencies/test_tutorial001_an_py310.py
+++ /dev/null
@@ -1,191 +0,0 @@
-import pytest
-from dirty_equals import IsDict
-from fastapi.testclient import TestClient
-
-from ...utils import needs_py310
-
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.dependencies.tutorial001_an_py310 import app
-
- client = TestClient(app)
- return client
-
-
-@needs_py310
-@pytest.mark.parametrize(
- "path,expected_status,expected_response",
- [
- ("/items", 200, {"q": None, "skip": 0, "limit": 100}),
- ("/items?q=foo", 200, {"q": "foo", "skip": 0, "limit": 100}),
- ("/items?q=foo&skip=5", 200, {"q": "foo", "skip": 5, "limit": 100}),
- ("/items?q=foo&skip=5&limit=30", 200, {"q": "foo", "skip": 5, "limit": 30}),
- ("/users", 200, {"q": None, "skip": 0, "limit": 100}),
- ],
-)
-def test_get(path, expected_status, expected_response, client: TestClient):
- response = client.get(path)
- assert response.status_code == expected_status
- assert response.json() == expected_response
-
-
-@needs_py310
-def test_openapi_schema(client: TestClient):
- response = client.get("/openapi.json")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/": {
- "get": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Read Items",
- "operationId": "read_items_items__get",
- "parameters": [
- {
- "required": False,
- "schema": IsDict(
- {
- "anyOf": [{"type": "string"}, {"type": "null"}],
- "title": "Q",
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Q", "type": "string"}
- ),
- "name": "q",
- "in": "query",
- },
- {
- "required": False,
- "schema": {
- "title": "Skip",
- "type": "integer",
- "default": 0,
- },
- "name": "skip",
- "in": "query",
- },
- {
- "required": False,
- "schema": {
- "title": "Limit",
- "type": "integer",
- "default": 100,
- },
- "name": "limit",
- "in": "query",
- },
- ],
- }
- },
- "/users/": {
- "get": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Read Users",
- "operationId": "read_users_users__get",
- "parameters": [
- {
- "required": False,
- "schema": IsDict(
- {
- "anyOf": [{"type": "string"}, {"type": "null"}],
- "title": "Q",
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Q", "type": "string"}
- ),
- "name": "q",
- "in": "query",
- },
- {
- "required": False,
- "schema": {
- "title": "Skip",
- "type": "integer",
- "default": 0,
- },
- "name": "skip",
- "in": "query",
- },
- {
- "required": False,
- "schema": {
- "title": "Limit",
- "type": "integer",
- "default": 100,
- },
- "name": "limit",
- "in": "query",
- },
- ],
- }
- },
- },
- "components": {
- "schemas": {
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_dependencies/test_tutorial001_an_py39.py b/tests/test_tutorial/test_dependencies/test_tutorial001_an_py39.py
deleted file mode 100644
index 68c2dedb1b..0000000000
--- a/tests/test_tutorial/test_dependencies/test_tutorial001_an_py39.py
+++ /dev/null
@@ -1,191 +0,0 @@
-import pytest
-from dirty_equals import IsDict
-from fastapi.testclient import TestClient
-
-from ...utils import needs_py39
-
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.dependencies.tutorial001_an_py39 import app
-
- client = TestClient(app)
- return client
-
-
-@needs_py39
-@pytest.mark.parametrize(
- "path,expected_status,expected_response",
- [
- ("/items", 200, {"q": None, "skip": 0, "limit": 100}),
- ("/items?q=foo", 200, {"q": "foo", "skip": 0, "limit": 100}),
- ("/items?q=foo&skip=5", 200, {"q": "foo", "skip": 5, "limit": 100}),
- ("/items?q=foo&skip=5&limit=30", 200, {"q": "foo", "skip": 5, "limit": 30}),
- ("/users", 200, {"q": None, "skip": 0, "limit": 100}),
- ],
-)
-def test_get(path, expected_status, expected_response, client: TestClient):
- response = client.get(path)
- assert response.status_code == expected_status
- assert response.json() == expected_response
-
-
-@needs_py39
-def test_openapi_schema(client: TestClient):
- response = client.get("/openapi.json")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/": {
- "get": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Read Items",
- "operationId": "read_items_items__get",
- "parameters": [
- {
- "required": False,
- "schema": IsDict(
- {
- "anyOf": [{"type": "string"}, {"type": "null"}],
- "title": "Q",
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Q", "type": "string"}
- ),
- "name": "q",
- "in": "query",
- },
- {
- "required": False,
- "schema": {
- "title": "Skip",
- "type": "integer",
- "default": 0,
- },
- "name": "skip",
- "in": "query",
- },
- {
- "required": False,
- "schema": {
- "title": "Limit",
- "type": "integer",
- "default": 100,
- },
- "name": "limit",
- "in": "query",
- },
- ],
- }
- },
- "/users/": {
- "get": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Read Users",
- "operationId": "read_users_users__get",
- "parameters": [
- {
- "required": False,
- "schema": IsDict(
- {
- "anyOf": [{"type": "string"}, {"type": "null"}],
- "title": "Q",
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Q", "type": "string"}
- ),
- "name": "q",
- "in": "query",
- },
- {
- "required": False,
- "schema": {
- "title": "Skip",
- "type": "integer",
- "default": 0,
- },
- "name": "skip",
- "in": "query",
- },
- {
- "required": False,
- "schema": {
- "title": "Limit",
- "type": "integer",
- "default": 100,
- },
- "name": "limit",
- "in": "query",
- },
- ],
- }
- },
- },
- "components": {
- "schemas": {
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_dependencies/test_tutorial001_py310.py b/tests/test_tutorial/test_dependencies/test_tutorial001_py310.py
deleted file mode 100644
index 381eecb639..0000000000
--- a/tests/test_tutorial/test_dependencies/test_tutorial001_py310.py
+++ /dev/null
@@ -1,191 +0,0 @@
-import pytest
-from dirty_equals import IsDict
-from fastapi.testclient import TestClient
-
-from ...utils import needs_py310
-
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.dependencies.tutorial001_py310 import app
-
- client = TestClient(app)
- return client
-
-
-@needs_py310
-@pytest.mark.parametrize(
- "path,expected_status,expected_response",
- [
- ("/items", 200, {"q": None, "skip": 0, "limit": 100}),
- ("/items?q=foo", 200, {"q": "foo", "skip": 0, "limit": 100}),
- ("/items?q=foo&skip=5", 200, {"q": "foo", "skip": 5, "limit": 100}),
- ("/items?q=foo&skip=5&limit=30", 200, {"q": "foo", "skip": 5, "limit": 30}),
- ("/users", 200, {"q": None, "skip": 0, "limit": 100}),
- ],
-)
-def test_get(path, expected_status, expected_response, client: TestClient):
- response = client.get(path)
- assert response.status_code == expected_status
- assert response.json() == expected_response
-
-
-@needs_py310
-def test_openapi_schema(client: TestClient):
- response = client.get("/openapi.json")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/": {
- "get": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Read Items",
- "operationId": "read_items_items__get",
- "parameters": [
- {
- "required": False,
- "schema": IsDict(
- {
- "anyOf": [{"type": "string"}, {"type": "null"}],
- "title": "Q",
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Q", "type": "string"}
- ),
- "name": "q",
- "in": "query",
- },
- {
- "required": False,
- "schema": {
- "title": "Skip",
- "type": "integer",
- "default": 0,
- },
- "name": "skip",
- "in": "query",
- },
- {
- "required": False,
- "schema": {
- "title": "Limit",
- "type": "integer",
- "default": 100,
- },
- "name": "limit",
- "in": "query",
- },
- ],
- }
- },
- "/users/": {
- "get": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Read Users",
- "operationId": "read_users_users__get",
- "parameters": [
- {
- "required": False,
- "schema": IsDict(
- {
- "anyOf": [{"type": "string"}, {"type": "null"}],
- "title": "Q",
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Q", "type": "string"}
- ),
- "name": "q",
- "in": "query",
- },
- {
- "required": False,
- "schema": {
- "title": "Skip",
- "type": "integer",
- "default": 0,
- },
- "name": "skip",
- "in": "query",
- },
- {
- "required": False,
- "schema": {
- "title": "Limit",
- "type": "integer",
- "default": 100,
- },
- "name": "limit",
- "in": "query",
- },
- ],
- }
- },
- },
- "components": {
- "schemas": {
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_dependencies/test_tutorial004.py b/tests/test_tutorial/test_dependencies/test_tutorial004.py
index 5c5d34cfcc..8221c83d44 100644
--- a/tests/test_tutorial/test_dependencies/test_tutorial004.py
+++ b/tests/test_tutorial/test_dependencies/test_tutorial004.py
@@ -1,10 +1,27 @@
+import importlib
+
import pytest
from dirty_equals import IsDict
from fastapi.testclient import TestClient
-from docs_src.dependencies.tutorial004 import app
+from ...utils import needs_py39, needs_py310
-client = TestClient(app)
+
+@pytest.fixture(
+ name="client",
+ params=[
+ "tutorial004",
+ pytest.param("tutorial004_py310", marks=needs_py310),
+ "tutorial004_an",
+ pytest.param("tutorial004_an_py39", marks=needs_py39),
+ pytest.param("tutorial004_an_py310", marks=needs_py310),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.dependencies.{request.param}")
+
+ client = TestClient(mod.app)
+ return client
@pytest.mark.parametrize(
@@ -55,13 +72,13 @@ client = TestClient(app)
),
],
)
-def test_get(path, expected_status, expected_response):
+def test_get(path, expected_status, expected_response, client: TestClient):
response = client.get(path)
assert response.status_code == expected_status
assert response.json() == expected_response
-def test_openapi_schema():
+def test_openapi_schema(client: TestClient):
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
diff --git a/tests/test_tutorial/test_dependencies/test_tutorial004_an.py b/tests/test_tutorial/test_dependencies/test_tutorial004_an.py
deleted file mode 100644
index c5c1a1fb88..0000000000
--- a/tests/test_tutorial/test_dependencies/test_tutorial004_an.py
+++ /dev/null
@@ -1,162 +0,0 @@
-import pytest
-from dirty_equals import IsDict
-from fastapi.testclient import TestClient
-
-from docs_src.dependencies.tutorial004_an import app
-
-client = TestClient(app)
-
-
-@pytest.mark.parametrize(
- "path,expected_status,expected_response",
- [
- (
- "/items",
- 200,
- {
- "items": [
- {"item_name": "Foo"},
- {"item_name": "Bar"},
- {"item_name": "Baz"},
- ]
- },
- ),
- (
- "/items?q=foo",
- 200,
- {
- "items": [
- {"item_name": "Foo"},
- {"item_name": "Bar"},
- {"item_name": "Baz"},
- ],
- "q": "foo",
- },
- ),
- (
- "/items?q=foo&skip=1",
- 200,
- {"items": [{"item_name": "Bar"}, {"item_name": "Baz"}], "q": "foo"},
- ),
- (
- "/items?q=bar&limit=2",
- 200,
- {"items": [{"item_name": "Foo"}, {"item_name": "Bar"}], "q": "bar"},
- ),
- (
- "/items?q=bar&skip=1&limit=1",
- 200,
- {"items": [{"item_name": "Bar"}], "q": "bar"},
- ),
- (
- "/items?limit=1&q=bar&skip=1",
- 200,
- {"items": [{"item_name": "Bar"}], "q": "bar"},
- ),
- ],
-)
-def test_get(path, expected_status, expected_response):
- response = client.get(path)
- assert response.status_code == expected_status
- assert response.json() == expected_response
-
-
-def test_openapi_schema():
- response = client.get("/openapi.json")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/": {
- "get": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Read Items",
- "operationId": "read_items_items__get",
- "parameters": [
- {
- "required": False,
- "schema": IsDict(
- {
- "anyOf": [{"type": "string"}, {"type": "null"}],
- "title": "Q",
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Q", "type": "string"}
- ),
- "name": "q",
- "in": "query",
- },
- {
- "required": False,
- "schema": {
- "title": "Skip",
- "type": "integer",
- "default": 0,
- },
- "name": "skip",
- "in": "query",
- },
- {
- "required": False,
- "schema": {
- "title": "Limit",
- "type": "integer",
- "default": 100,
- },
- "name": "limit",
- "in": "query",
- },
- ],
- }
- }
- },
- "components": {
- "schemas": {
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_dependencies/test_tutorial004_an_py310.py b/tests/test_tutorial/test_dependencies/test_tutorial004_an_py310.py
deleted file mode 100644
index 6fd093ddb1..0000000000
--- a/tests/test_tutorial/test_dependencies/test_tutorial004_an_py310.py
+++ /dev/null
@@ -1,170 +0,0 @@
-import pytest
-from dirty_equals import IsDict
-from fastapi.testclient import TestClient
-
-from ...utils import needs_py310
-
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.dependencies.tutorial004_an_py310 import app
-
- client = TestClient(app)
- return client
-
-
-@needs_py310
-@pytest.mark.parametrize(
- "path,expected_status,expected_response",
- [
- (
- "/items",
- 200,
- {
- "items": [
- {"item_name": "Foo"},
- {"item_name": "Bar"},
- {"item_name": "Baz"},
- ]
- },
- ),
- (
- "/items?q=foo",
- 200,
- {
- "items": [
- {"item_name": "Foo"},
- {"item_name": "Bar"},
- {"item_name": "Baz"},
- ],
- "q": "foo",
- },
- ),
- (
- "/items?q=foo&skip=1",
- 200,
- {"items": [{"item_name": "Bar"}, {"item_name": "Baz"}], "q": "foo"},
- ),
- (
- "/items?q=bar&limit=2",
- 200,
- {"items": [{"item_name": "Foo"}, {"item_name": "Bar"}], "q": "bar"},
- ),
- (
- "/items?q=bar&skip=1&limit=1",
- 200,
- {"items": [{"item_name": "Bar"}], "q": "bar"},
- ),
- (
- "/items?limit=1&q=bar&skip=1",
- 200,
- {"items": [{"item_name": "Bar"}], "q": "bar"},
- ),
- ],
-)
-def test_get(path, expected_status, expected_response, client: TestClient):
- response = client.get(path)
- assert response.status_code == expected_status
- assert response.json() == expected_response
-
-
-@needs_py310
-def test_openapi_schema(client: TestClient):
- response = client.get("/openapi.json")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/": {
- "get": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Read Items",
- "operationId": "read_items_items__get",
- "parameters": [
- {
- "required": False,
- "schema": IsDict(
- {
- "anyOf": [{"type": "string"}, {"type": "null"}],
- "title": "Q",
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Q", "type": "string"}
- ),
- "name": "q",
- "in": "query",
- },
- {
- "required": False,
- "schema": {
- "title": "Skip",
- "type": "integer",
- "default": 0,
- },
- "name": "skip",
- "in": "query",
- },
- {
- "required": False,
- "schema": {
- "title": "Limit",
- "type": "integer",
- "default": 100,
- },
- "name": "limit",
- "in": "query",
- },
- ],
- }
- }
- },
- "components": {
- "schemas": {
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_dependencies/test_tutorial004_an_py39.py b/tests/test_tutorial/test_dependencies/test_tutorial004_an_py39.py
deleted file mode 100644
index fbbe84cc94..0000000000
--- a/tests/test_tutorial/test_dependencies/test_tutorial004_an_py39.py
+++ /dev/null
@@ -1,170 +0,0 @@
-import pytest
-from dirty_equals import IsDict
-from fastapi.testclient import TestClient
-
-from ...utils import needs_py39
-
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.dependencies.tutorial004_an_py39 import app
-
- client = TestClient(app)
- return client
-
-
-@needs_py39
-@pytest.mark.parametrize(
- "path,expected_status,expected_response",
- [
- (
- "/items",
- 200,
- {
- "items": [
- {"item_name": "Foo"},
- {"item_name": "Bar"},
- {"item_name": "Baz"},
- ]
- },
- ),
- (
- "/items?q=foo",
- 200,
- {
- "items": [
- {"item_name": "Foo"},
- {"item_name": "Bar"},
- {"item_name": "Baz"},
- ],
- "q": "foo",
- },
- ),
- (
- "/items?q=foo&skip=1",
- 200,
- {"items": [{"item_name": "Bar"}, {"item_name": "Baz"}], "q": "foo"},
- ),
- (
- "/items?q=bar&limit=2",
- 200,
- {"items": [{"item_name": "Foo"}, {"item_name": "Bar"}], "q": "bar"},
- ),
- (
- "/items?q=bar&skip=1&limit=1",
- 200,
- {"items": [{"item_name": "Bar"}], "q": "bar"},
- ),
- (
- "/items?limit=1&q=bar&skip=1",
- 200,
- {"items": [{"item_name": "Bar"}], "q": "bar"},
- ),
- ],
-)
-def test_get(path, expected_status, expected_response, client: TestClient):
- response = client.get(path)
- assert response.status_code == expected_status
- assert response.json() == expected_response
-
-
-@needs_py39
-def test_openapi_schema(client: TestClient):
- response = client.get("/openapi.json")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/": {
- "get": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Read Items",
- "operationId": "read_items_items__get",
- "parameters": [
- {
- "required": False,
- "schema": IsDict(
- {
- "anyOf": [{"type": "string"}, {"type": "null"}],
- "title": "Q",
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Q", "type": "string"}
- ),
- "name": "q",
- "in": "query",
- },
- {
- "required": False,
- "schema": {
- "title": "Skip",
- "type": "integer",
- "default": 0,
- },
- "name": "skip",
- "in": "query",
- },
- {
- "required": False,
- "schema": {
- "title": "Limit",
- "type": "integer",
- "default": 100,
- },
- "name": "limit",
- "in": "query",
- },
- ],
- }
- }
- },
- "components": {
- "schemas": {
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_dependencies/test_tutorial004_py310.py b/tests/test_tutorial/test_dependencies/test_tutorial004_py310.py
deleted file mode 100644
index 845b098e79..0000000000
--- a/tests/test_tutorial/test_dependencies/test_tutorial004_py310.py
+++ /dev/null
@@ -1,170 +0,0 @@
-import pytest
-from dirty_equals import IsDict
-from fastapi.testclient import TestClient
-
-from ...utils import needs_py310
-
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.dependencies.tutorial004_py310 import app
-
- client = TestClient(app)
- return client
-
-
-@needs_py310
-@pytest.mark.parametrize(
- "path,expected_status,expected_response",
- [
- (
- "/items",
- 200,
- {
- "items": [
- {"item_name": "Foo"},
- {"item_name": "Bar"},
- {"item_name": "Baz"},
- ]
- },
- ),
- (
- "/items?q=foo",
- 200,
- {
- "items": [
- {"item_name": "Foo"},
- {"item_name": "Bar"},
- {"item_name": "Baz"},
- ],
- "q": "foo",
- },
- ),
- (
- "/items?q=foo&skip=1",
- 200,
- {"items": [{"item_name": "Bar"}, {"item_name": "Baz"}], "q": "foo"},
- ),
- (
- "/items?q=bar&limit=2",
- 200,
- {"items": [{"item_name": "Foo"}, {"item_name": "Bar"}], "q": "bar"},
- ),
- (
- "/items?q=bar&skip=1&limit=1",
- 200,
- {"items": [{"item_name": "Bar"}], "q": "bar"},
- ),
- (
- "/items?limit=1&q=bar&skip=1",
- 200,
- {"items": [{"item_name": "Bar"}], "q": "bar"},
- ),
- ],
-)
-def test_get(path, expected_status, expected_response, client: TestClient):
- response = client.get(path)
- assert response.status_code == expected_status
- assert response.json() == expected_response
-
-
-@needs_py310
-def test_openapi_schema(client: TestClient):
- response = client.get("/openapi.json")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/": {
- "get": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Read Items",
- "operationId": "read_items_items__get",
- "parameters": [
- {
- "required": False,
- "schema": IsDict(
- {
- "anyOf": [{"type": "string"}, {"type": "null"}],
- "title": "Q",
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Q", "type": "string"}
- ),
- "name": "q",
- "in": "query",
- },
- {
- "required": False,
- "schema": {
- "title": "Skip",
- "type": "integer",
- "default": 0,
- },
- "name": "skip",
- "in": "query",
- },
- {
- "required": False,
- "schema": {
- "title": "Limit",
- "type": "integer",
- "default": 100,
- },
- "name": "limit",
- "in": "query",
- },
- ],
- }
- }
- },
- "components": {
- "schemas": {
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_dependencies/test_tutorial006.py b/tests/test_tutorial/test_dependencies/test_tutorial006.py
index 5f14d9a3ba..4530762f76 100644
--- a/tests/test_tutorial/test_dependencies/test_tutorial006.py
+++ b/tests/test_tutorial/test_dependencies/test_tutorial006.py
@@ -1,12 +1,28 @@
+import importlib
+
+import pytest
from dirty_equals import IsDict
from fastapi.testclient import TestClient
-from docs_src.dependencies.tutorial006 import app
-
-client = TestClient(app)
+from ...utils import needs_py39
-def test_get_no_headers():
+@pytest.fixture(
+ name="client",
+ params=[
+ "tutorial006",
+ "tutorial006_an",
+ pytest.param("tutorial006_an_py39", marks=needs_py39),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.dependencies.{request.param}")
+
+ client = TestClient(mod.app)
+ return client
+
+
+def test_get_no_headers(client: TestClient):
response = client.get("/items/")
assert response.status_code == 422, response.text
assert response.json() == IsDict(
@@ -45,13 +61,13 @@ def test_get_no_headers():
)
-def test_get_invalid_one_header():
+def test_get_invalid_one_header(client: TestClient):
response = client.get("/items/", headers={"X-Token": "invalid"})
assert response.status_code == 400, response.text
assert response.json() == {"detail": "X-Token header invalid"}
-def test_get_invalid_second_header():
+def test_get_invalid_second_header(client: TestClient):
response = client.get(
"/items/", headers={"X-Token": "fake-super-secret-token", "X-Key": "invalid"}
)
@@ -59,7 +75,7 @@ def test_get_invalid_second_header():
assert response.json() == {"detail": "X-Key header invalid"}
-def test_get_valid_headers():
+def test_get_valid_headers(client: TestClient):
response = client.get(
"/items/",
headers={
@@ -71,7 +87,7 @@ def test_get_valid_headers():
assert response.json() == [{"item": "Foo"}, {"item": "Bar"}]
-def test_openapi_schema():
+def test_openapi_schema(client: TestClient):
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
diff --git a/tests/test_tutorial/test_dependencies/test_tutorial006_an.py b/tests/test_tutorial/test_dependencies/test_tutorial006_an.py
deleted file mode 100644
index a307ff8087..0000000000
--- a/tests/test_tutorial/test_dependencies/test_tutorial006_an.py
+++ /dev/null
@@ -1,149 +0,0 @@
-from dirty_equals import IsDict
-from fastapi.testclient import TestClient
-
-from docs_src.dependencies.tutorial006_an import app
-
-client = TestClient(app)
-
-
-def test_get_no_headers():
- response = client.get("/items/")
- assert response.status_code == 422, response.text
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "missing",
- "loc": ["header", "x-token"],
- "msg": "Field required",
- "input": None,
- },
- {
- "type": "missing",
- "loc": ["header", "x-key"],
- "msg": "Field required",
- "input": None,
- },
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "loc": ["header", "x-token"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- {
- "loc": ["header", "x-key"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- ]
- }
- )
-
-
-def test_get_invalid_one_header():
- response = client.get("/items/", headers={"X-Token": "invalid"})
- assert response.status_code == 400, response.text
- assert response.json() == {"detail": "X-Token header invalid"}
-
-
-def test_get_invalid_second_header():
- response = client.get(
- "/items/", headers={"X-Token": "fake-super-secret-token", "X-Key": "invalid"}
- )
- assert response.status_code == 400, response.text
- assert response.json() == {"detail": "X-Key header invalid"}
-
-
-def test_get_valid_headers():
- response = client.get(
- "/items/",
- headers={
- "X-Token": "fake-super-secret-token",
- "X-Key": "fake-super-secret-key",
- },
- )
- assert response.status_code == 200, response.text
- assert response.json() == [{"item": "Foo"}, {"item": "Bar"}]
-
-
-def test_openapi_schema():
- response = client.get("/openapi.json")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/": {
- "get": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Read Items",
- "operationId": "read_items_items__get",
- "parameters": [
- {
- "required": True,
- "schema": {"title": "X-Token", "type": "string"},
- "name": "x-token",
- "in": "header",
- },
- {
- "required": True,
- "schema": {"title": "X-Key", "type": "string"},
- "name": "x-key",
- "in": "header",
- },
- ],
- }
- }
- },
- "components": {
- "schemas": {
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_dependencies/test_tutorial006_an_py39.py b/tests/test_tutorial/test_dependencies/test_tutorial006_an_py39.py
deleted file mode 100644
index b41b1537ec..0000000000
--- a/tests/test_tutorial/test_dependencies/test_tutorial006_an_py39.py
+++ /dev/null
@@ -1,161 +0,0 @@
-import pytest
-from dirty_equals import IsDict
-from fastapi.testclient import TestClient
-
-from ...utils import needs_py39
-
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.dependencies.tutorial006_an_py39 import app
-
- client = TestClient(app)
- return client
-
-
-@needs_py39
-def test_get_no_headers(client: TestClient):
- response = client.get("/items/")
- assert response.status_code == 422, response.text
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "missing",
- "loc": ["header", "x-token"],
- "msg": "Field required",
- "input": None,
- },
- {
- "type": "missing",
- "loc": ["header", "x-key"],
- "msg": "Field required",
- "input": None,
- },
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "loc": ["header", "x-token"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- {
- "loc": ["header", "x-key"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- ]
- }
- )
-
-
-@needs_py39
-def test_get_invalid_one_header(client: TestClient):
- response = client.get("/items/", headers={"X-Token": "invalid"})
- assert response.status_code == 400, response.text
- assert response.json() == {"detail": "X-Token header invalid"}
-
-
-@needs_py39
-def test_get_invalid_second_header(client: TestClient):
- response = client.get(
- "/items/", headers={"X-Token": "fake-super-secret-token", "X-Key": "invalid"}
- )
- assert response.status_code == 400, response.text
- assert response.json() == {"detail": "X-Key header invalid"}
-
-
-@needs_py39
-def test_get_valid_headers(client: TestClient):
- response = client.get(
- "/items/",
- headers={
- "X-Token": "fake-super-secret-token",
- "X-Key": "fake-super-secret-key",
- },
- )
- assert response.status_code == 200, response.text
- assert response.json() == [{"item": "Foo"}, {"item": "Bar"}]
-
-
-@needs_py39
-def test_openapi_schema(client: TestClient):
- response = client.get("/openapi.json")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/": {
- "get": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Read Items",
- "operationId": "read_items_items__get",
- "parameters": [
- {
- "required": True,
- "schema": {"title": "X-Token", "type": "string"},
- "name": "x-token",
- "in": "header",
- },
- {
- "required": True,
- "schema": {"title": "X-Key", "type": "string"},
- "name": "x-key",
- "in": "header",
- },
- ],
- }
- }
- },
- "components": {
- "schemas": {
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_dependencies/test_tutorial008b.py b/tests/test_tutorial/test_dependencies/test_tutorial008b.py
index 86acba9e4f..4d70922657 100644
--- a/tests/test_tutorial/test_dependencies/test_tutorial008b.py
+++ b/tests/test_tutorial/test_dependencies/test_tutorial008b.py
@@ -1,23 +1,39 @@
+import importlib
+
+import pytest
from fastapi.testclient import TestClient
-from docs_src.dependencies.tutorial008b import app
-
-client = TestClient(app)
+from ...utils import needs_py39
-def test_get_no_item():
+@pytest.fixture(
+ name="client",
+ params=[
+ "tutorial008b",
+ "tutorial008b_an",
+ pytest.param("tutorial008b_an_py39", marks=needs_py39),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.dependencies.{request.param}")
+
+ client = TestClient(mod.app)
+ return client
+
+
+def test_get_no_item(client: TestClient):
response = client.get("/items/foo")
assert response.status_code == 404, response.text
assert response.json() == {"detail": "Item not found"}
-def test_owner_error():
+def test_owner_error(client: TestClient):
response = client.get("/items/plumbus")
assert response.status_code == 400, response.text
assert response.json() == {"detail": "Owner error: Rick"}
-def test_get_item():
+def test_get_item(client: TestClient):
response = client.get("/items/portal-gun")
assert response.status_code == 200, response.text
assert response.json() == {"description": "Gun to create portals", "owner": "Rick"}
diff --git a/tests/test_tutorial/test_dependencies/test_tutorial008b_an.py b/tests/test_tutorial/test_dependencies/test_tutorial008b_an.py
deleted file mode 100644
index 7f51fc52a5..0000000000
--- a/tests/test_tutorial/test_dependencies/test_tutorial008b_an.py
+++ /dev/null
@@ -1,23 +0,0 @@
-from fastapi.testclient import TestClient
-
-from docs_src.dependencies.tutorial008b_an import app
-
-client = TestClient(app)
-
-
-def test_get_no_item():
- response = client.get("/items/foo")
- assert response.status_code == 404, response.text
- assert response.json() == {"detail": "Item not found"}
-
-
-def test_owner_error():
- response = client.get("/items/plumbus")
- assert response.status_code == 400, response.text
- assert response.json() == {"detail": "Owner error: Rick"}
-
-
-def test_get_item():
- response = client.get("/items/portal-gun")
- assert response.status_code == 200, response.text
- assert response.json() == {"description": "Gun to create portals", "owner": "Rick"}
diff --git a/tests/test_tutorial/test_dependencies/test_tutorial008b_an_py39.py b/tests/test_tutorial/test_dependencies/test_tutorial008b_an_py39.py
deleted file mode 100644
index 7d24809a8a..0000000000
--- a/tests/test_tutorial/test_dependencies/test_tutorial008b_an_py39.py
+++ /dev/null
@@ -1,33 +0,0 @@
-import pytest
-from fastapi.testclient import TestClient
-
-from ...utils import needs_py39
-
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.dependencies.tutorial008b_an_py39 import app
-
- client = TestClient(app)
- return client
-
-
-@needs_py39
-def test_get_no_item(client: TestClient):
- response = client.get("/items/foo")
- assert response.status_code == 404, response.text
- assert response.json() == {"detail": "Item not found"}
-
-
-@needs_py39
-def test_owner_error(client: TestClient):
- response = client.get("/items/plumbus")
- assert response.status_code == 400, response.text
- assert response.json() == {"detail": "Owner error: Rick"}
-
-
-@needs_py39
-def test_get_item(client: TestClient):
- response = client.get("/items/portal-gun")
- assert response.status_code == 200, response.text
- assert response.json() == {"description": "Gun to create portals", "owner": "Rick"}
diff --git a/tests/test_tutorial/test_dependencies/test_tutorial008c.py b/tests/test_tutorial/test_dependencies/test_tutorial008c.py
index 27be8895a9..369b0a221d 100644
--- a/tests/test_tutorial/test_dependencies/test_tutorial008c.py
+++ b/tests/test_tutorial/test_dependencies/test_tutorial008c.py
@@ -1,38 +1,50 @@
+import importlib
+from types import ModuleType
+
import pytest
from fastapi.exceptions import FastAPIError
from fastapi.testclient import TestClient
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.dependencies.tutorial008c import app
-
- client = TestClient(app)
- return client
+from ...utils import needs_py39
-def test_get_no_item(client: TestClient):
+@pytest.fixture(
+ name="mod",
+ params=[
+ "tutorial008c",
+ "tutorial008c_an",
+ pytest.param("tutorial008c_an_py39", marks=needs_py39),
+ ],
+)
+def get_mod(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.dependencies.{request.param}")
+
+ return mod
+
+
+def test_get_no_item(mod: ModuleType):
+ client = TestClient(mod.app)
response = client.get("/items/foo")
assert response.status_code == 404, response.text
assert response.json() == {"detail": "Item not found, there's only a plumbus here"}
-def test_get(client: TestClient):
+def test_get(mod: ModuleType):
+ client = TestClient(mod.app)
response = client.get("/items/plumbus")
assert response.status_code == 200, response.text
assert response.json() == "plumbus"
-def test_fastapi_error(client: TestClient):
+def test_fastapi_error(mod: ModuleType):
+ client = TestClient(mod.app)
with pytest.raises(FastAPIError) as exc_info:
client.get("/items/portal-gun")
- assert "No response object was returned" in exc_info.value.args[0]
+ assert "raising an exception and a dependency with yield" in exc_info.value.args[0]
-def test_internal_server_error():
- from docs_src.dependencies.tutorial008c import app
-
- client = TestClient(app, raise_server_exceptions=False)
+def test_internal_server_error(mod: ModuleType):
+ client = TestClient(mod.app, raise_server_exceptions=False)
response = client.get("/items/portal-gun")
assert response.status_code == 500, response.text
assert response.text == "Internal Server Error"
diff --git a/tests/test_tutorial/test_dependencies/test_tutorial008c_an.py b/tests/test_tutorial/test_dependencies/test_tutorial008c_an.py
deleted file mode 100644
index 10fa1ab508..0000000000
--- a/tests/test_tutorial/test_dependencies/test_tutorial008c_an.py
+++ /dev/null
@@ -1,38 +0,0 @@
-import pytest
-from fastapi.exceptions import FastAPIError
-from fastapi.testclient import TestClient
-
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.dependencies.tutorial008c_an import app
-
- client = TestClient(app)
- return client
-
-
-def test_get_no_item(client: TestClient):
- response = client.get("/items/foo")
- assert response.status_code == 404, response.text
- assert response.json() == {"detail": "Item not found, there's only a plumbus here"}
-
-
-def test_get(client: TestClient):
- response = client.get("/items/plumbus")
- assert response.status_code == 200, response.text
- assert response.json() == "plumbus"
-
-
-def test_fastapi_error(client: TestClient):
- with pytest.raises(FastAPIError) as exc_info:
- client.get("/items/portal-gun")
- assert "No response object was returned" in exc_info.value.args[0]
-
-
-def test_internal_server_error():
- from docs_src.dependencies.tutorial008c_an import app
-
- client = TestClient(app, raise_server_exceptions=False)
- response = client.get("/items/portal-gun")
- assert response.status_code == 500, response.text
- assert response.text == "Internal Server Error"
diff --git a/tests/test_tutorial/test_dependencies/test_tutorial008c_an_py39.py b/tests/test_tutorial/test_dependencies/test_tutorial008c_an_py39.py
deleted file mode 100644
index 6c3acff509..0000000000
--- a/tests/test_tutorial/test_dependencies/test_tutorial008c_an_py39.py
+++ /dev/null
@@ -1,44 +0,0 @@
-import pytest
-from fastapi.exceptions import FastAPIError
-from fastapi.testclient import TestClient
-
-from ...utils import needs_py39
-
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.dependencies.tutorial008c_an_py39 import app
-
- client = TestClient(app)
- return client
-
-
-@needs_py39
-def test_get_no_item(client: TestClient):
- response = client.get("/items/foo")
- assert response.status_code == 404, response.text
- assert response.json() == {"detail": "Item not found, there's only a plumbus here"}
-
-
-@needs_py39
-def test_get(client: TestClient):
- response = client.get("/items/plumbus")
- assert response.status_code == 200, response.text
- assert response.json() == "plumbus"
-
-
-@needs_py39
-def test_fastapi_error(client: TestClient):
- with pytest.raises(FastAPIError) as exc_info:
- client.get("/items/portal-gun")
- assert "No response object was returned" in exc_info.value.args[0]
-
-
-@needs_py39
-def test_internal_server_error():
- from docs_src.dependencies.tutorial008c_an_py39 import app
-
- client = TestClient(app, raise_server_exceptions=False)
- response = client.get("/items/portal-gun")
- assert response.status_code == 500, response.text
- assert response.text == "Internal Server Error"
diff --git a/tests/test_tutorial/test_dependencies/test_tutorial008d.py b/tests/test_tutorial/test_dependencies/test_tutorial008d.py
index 0434961123..bc99bb3835 100644
--- a/tests/test_tutorial/test_dependencies/test_tutorial008d.py
+++ b/tests/test_tutorial/test_dependencies/test_tutorial008d.py
@@ -1,41 +1,51 @@
+import importlib
+from types import ModuleType
+
import pytest
from fastapi.testclient import TestClient
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.dependencies.tutorial008d import app
-
- client = TestClient(app)
- return client
+from ...utils import needs_py39
-def test_get_no_item(client: TestClient):
+@pytest.fixture(
+ name="mod",
+ params=[
+ "tutorial008d",
+ "tutorial008d_an",
+ pytest.param("tutorial008d_an_py39", marks=needs_py39),
+ ],
+)
+def get_mod(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.dependencies.{request.param}")
+
+ return mod
+
+
+def test_get_no_item(mod: ModuleType):
+ client = TestClient(mod.app)
response = client.get("/items/foo")
assert response.status_code == 404, response.text
assert response.json() == {"detail": "Item not found, there's only a plumbus here"}
-def test_get(client: TestClient):
+def test_get(mod: ModuleType):
+ client = TestClient(mod.app)
response = client.get("/items/plumbus")
assert response.status_code == 200, response.text
assert response.json() == "plumbus"
-def test_internal_error(client: TestClient):
- from docs_src.dependencies.tutorial008d import InternalError
-
- with pytest.raises(InternalError) as exc_info:
+def test_internal_error(mod: ModuleType):
+ client = TestClient(mod.app)
+ with pytest.raises(mod.InternalError) as exc_info:
client.get("/items/portal-gun")
assert (
exc_info.value.args[0] == "The portal gun is too dangerous to be owned by Rick"
)
-def test_internal_server_error():
- from docs_src.dependencies.tutorial008d import app
-
- client = TestClient(app, raise_server_exceptions=False)
+def test_internal_server_error(mod: ModuleType):
+ client = TestClient(mod.app, raise_server_exceptions=False)
response = client.get("/items/portal-gun")
assert response.status_code == 500, response.text
assert response.text == "Internal Server Error"
diff --git a/tests/test_tutorial/test_dependencies/test_tutorial008d_an.py b/tests/test_tutorial/test_dependencies/test_tutorial008d_an.py
deleted file mode 100644
index f29d8cdbe5..0000000000
--- a/tests/test_tutorial/test_dependencies/test_tutorial008d_an.py
+++ /dev/null
@@ -1,41 +0,0 @@
-import pytest
-from fastapi.testclient import TestClient
-
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.dependencies.tutorial008d_an import app
-
- client = TestClient(app)
- return client
-
-
-def test_get_no_item(client: TestClient):
- response = client.get("/items/foo")
- assert response.status_code == 404, response.text
- assert response.json() == {"detail": "Item not found, there's only a plumbus here"}
-
-
-def test_get(client: TestClient):
- response = client.get("/items/plumbus")
- assert response.status_code == 200, response.text
- assert response.json() == "plumbus"
-
-
-def test_internal_error(client: TestClient):
- from docs_src.dependencies.tutorial008d_an import InternalError
-
- with pytest.raises(InternalError) as exc_info:
- client.get("/items/portal-gun")
- assert (
- exc_info.value.args[0] == "The portal gun is too dangerous to be owned by Rick"
- )
-
-
-def test_internal_server_error():
- from docs_src.dependencies.tutorial008d_an import app
-
- client = TestClient(app, raise_server_exceptions=False)
- response = client.get("/items/portal-gun")
- assert response.status_code == 500, response.text
- assert response.text == "Internal Server Error"
diff --git a/tests/test_tutorial/test_dependencies/test_tutorial008d_an_py39.py b/tests/test_tutorial/test_dependencies/test_tutorial008d_an_py39.py
deleted file mode 100644
index 0a585f4adb..0000000000
--- a/tests/test_tutorial/test_dependencies/test_tutorial008d_an_py39.py
+++ /dev/null
@@ -1,47 +0,0 @@
-import pytest
-from fastapi.testclient import TestClient
-
-from ...utils import needs_py39
-
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.dependencies.tutorial008d_an_py39 import app
-
- client = TestClient(app)
- return client
-
-
-@needs_py39
-def test_get_no_item(client: TestClient):
- response = client.get("/items/foo")
- assert response.status_code == 404, response.text
- assert response.json() == {"detail": "Item not found, there's only a plumbus here"}
-
-
-@needs_py39
-def test_get(client: TestClient):
- response = client.get("/items/plumbus")
- assert response.status_code == 200, response.text
- assert response.json() == "plumbus"
-
-
-@needs_py39
-def test_internal_error(client: TestClient):
- from docs_src.dependencies.tutorial008d_an_py39 import InternalError
-
- with pytest.raises(InternalError) as exc_info:
- client.get("/items/portal-gun")
- assert (
- exc_info.value.args[0] == "The portal gun is too dangerous to be owned by Rick"
- )
-
-
-@needs_py39
-def test_internal_server_error():
- from docs_src.dependencies.tutorial008d_an_py39 import app
-
- client = TestClient(app, raise_server_exceptions=False)
- response = client.get("/items/portal-gun")
- assert response.status_code == 500, response.text
- assert response.text == "Internal Server Error"
diff --git a/tests/test_tutorial/test_dependencies/test_tutorial012.py b/tests/test_tutorial/test_dependencies/test_tutorial012.py
index 6b53c83bb5..0af17e9bc5 100644
--- a/tests/test_tutorial/test_dependencies/test_tutorial012.py
+++ b/tests/test_tutorial/test_dependencies/test_tutorial012.py
@@ -1,12 +1,28 @@
+import importlib
+
+import pytest
from dirty_equals import IsDict
from fastapi.testclient import TestClient
-from docs_src.dependencies.tutorial012 import app
-
-client = TestClient(app)
+from ...utils import needs_py39
-def test_get_no_headers_items():
+@pytest.fixture(
+ name="client",
+ params=[
+ "tutorial012",
+ "tutorial012_an",
+ pytest.param("tutorial012_an_py39", marks=needs_py39),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.dependencies.{request.param}")
+
+ client = TestClient(mod.app)
+ return client
+
+
+def test_get_no_headers_items(client: TestClient):
response = client.get("/items/")
assert response.status_code == 422, response.text
assert response.json() == IsDict(
@@ -45,7 +61,7 @@ def test_get_no_headers_items():
)
-def test_get_no_headers_users():
+def test_get_no_headers_users(client: TestClient):
response = client.get("/users/")
assert response.status_code == 422, response.text
assert response.json() == IsDict(
@@ -84,19 +100,19 @@ def test_get_no_headers_users():
)
-def test_get_invalid_one_header_items():
+def test_get_invalid_one_header_items(client: TestClient):
response = client.get("/items/", headers={"X-Token": "invalid"})
assert response.status_code == 400, response.text
assert response.json() == {"detail": "X-Token header invalid"}
-def test_get_invalid_one_users():
+def test_get_invalid_one_users(client: TestClient):
response = client.get("/users/", headers={"X-Token": "invalid"})
assert response.status_code == 400, response.text
assert response.json() == {"detail": "X-Token header invalid"}
-def test_get_invalid_second_header_items():
+def test_get_invalid_second_header_items(client: TestClient):
response = client.get(
"/items/", headers={"X-Token": "fake-super-secret-token", "X-Key": "invalid"}
)
@@ -104,7 +120,7 @@ def test_get_invalid_second_header_items():
assert response.json() == {"detail": "X-Key header invalid"}
-def test_get_invalid_second_header_users():
+def test_get_invalid_second_header_users(client: TestClient):
response = client.get(
"/users/", headers={"X-Token": "fake-super-secret-token", "X-Key": "invalid"}
)
@@ -112,7 +128,7 @@ def test_get_invalid_second_header_users():
assert response.json() == {"detail": "X-Key header invalid"}
-def test_get_valid_headers_items():
+def test_get_valid_headers_items(client: TestClient):
response = client.get(
"/items/",
headers={
@@ -124,7 +140,7 @@ def test_get_valid_headers_items():
assert response.json() == [{"item": "Portal Gun"}, {"item": "Plumbus"}]
-def test_get_valid_headers_users():
+def test_get_valid_headers_users(client: TestClient):
response = client.get(
"/users/",
headers={
@@ -136,7 +152,7 @@ def test_get_valid_headers_users():
assert response.json() == [{"username": "Rick"}, {"username": "Morty"}]
-def test_openapi_schema():
+def test_openapi_schema(client: TestClient):
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
diff --git a/tests/test_tutorial/test_dependencies/test_tutorial012_an.py b/tests/test_tutorial/test_dependencies/test_tutorial012_an.py
deleted file mode 100644
index 75adb69fc7..0000000000
--- a/tests/test_tutorial/test_dependencies/test_tutorial012_an.py
+++ /dev/null
@@ -1,250 +0,0 @@
-from dirty_equals import IsDict
-from fastapi.testclient import TestClient
-
-from docs_src.dependencies.tutorial012_an import app
-
-client = TestClient(app)
-
-
-def test_get_no_headers_items():
- response = client.get("/items/")
- assert response.status_code == 422, response.text
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "missing",
- "loc": ["header", "x-token"],
- "msg": "Field required",
- "input": None,
- },
- {
- "type": "missing",
- "loc": ["header", "x-key"],
- "msg": "Field required",
- "input": None,
- },
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "loc": ["header", "x-token"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- {
- "loc": ["header", "x-key"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- ]
- }
- )
-
-
-def test_get_no_headers_users():
- response = client.get("/users/")
- assert response.status_code == 422, response.text
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "missing",
- "loc": ["header", "x-token"],
- "msg": "Field required",
- "input": None,
- },
- {
- "type": "missing",
- "loc": ["header", "x-key"],
- "msg": "Field required",
- "input": None,
- },
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "loc": ["header", "x-token"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- {
- "loc": ["header", "x-key"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- ]
- }
- )
-
-
-def test_get_invalid_one_header_items():
- response = client.get("/items/", headers={"X-Token": "invalid"})
- assert response.status_code == 400, response.text
- assert response.json() == {"detail": "X-Token header invalid"}
-
-
-def test_get_invalid_one_users():
- response = client.get("/users/", headers={"X-Token": "invalid"})
- assert response.status_code == 400, response.text
- assert response.json() == {"detail": "X-Token header invalid"}
-
-
-def test_get_invalid_second_header_items():
- response = client.get(
- "/items/", headers={"X-Token": "fake-super-secret-token", "X-Key": "invalid"}
- )
- assert response.status_code == 400, response.text
- assert response.json() == {"detail": "X-Key header invalid"}
-
-
-def test_get_invalid_second_header_users():
- response = client.get(
- "/users/", headers={"X-Token": "fake-super-secret-token", "X-Key": "invalid"}
- )
- assert response.status_code == 400, response.text
- assert response.json() == {"detail": "X-Key header invalid"}
-
-
-def test_get_valid_headers_items():
- response = client.get(
- "/items/",
- headers={
- "X-Token": "fake-super-secret-token",
- "X-Key": "fake-super-secret-key",
- },
- )
- assert response.status_code == 200, response.text
- assert response.json() == [{"item": "Portal Gun"}, {"item": "Plumbus"}]
-
-
-def test_get_valid_headers_users():
- response = client.get(
- "/users/",
- headers={
- "X-Token": "fake-super-secret-token",
- "X-Key": "fake-super-secret-key",
- },
- )
- assert response.status_code == 200, response.text
- assert response.json() == [{"username": "Rick"}, {"username": "Morty"}]
-
-
-def test_openapi_schema():
- response = client.get("/openapi.json")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/": {
- "get": {
- "summary": "Read Items",
- "operationId": "read_items_items__get",
- "parameters": [
- {
- "required": True,
- "schema": {"title": "X-Token", "type": "string"},
- "name": "x-token",
- "in": "header",
- },
- {
- "required": True,
- "schema": {"title": "X-Key", "type": "string"},
- "name": "x-key",
- "in": "header",
- },
- ],
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- }
- },
- "/users/": {
- "get": {
- "summary": "Read Users",
- "operationId": "read_users_users__get",
- "parameters": [
- {
- "required": True,
- "schema": {"title": "X-Token", "type": "string"},
- "name": "x-token",
- "in": "header",
- },
- {
- "required": True,
- "schema": {"title": "X-Key", "type": "string"},
- "name": "x-key",
- "in": "header",
- },
- ],
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- }
- },
- },
- "components": {
- "schemas": {
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_dependencies/test_tutorial012_an_py39.py b/tests/test_tutorial/test_dependencies/test_tutorial012_an_py39.py
deleted file mode 100644
index e0a3d1ec27..0000000000
--- a/tests/test_tutorial/test_dependencies/test_tutorial012_an_py39.py
+++ /dev/null
@@ -1,266 +0,0 @@
-import pytest
-from dirty_equals import IsDict
-from fastapi.testclient import TestClient
-
-from ...utils import needs_py39
-
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.dependencies.tutorial012_an_py39 import app
-
- client = TestClient(app)
- return client
-
-
-@needs_py39
-def test_get_no_headers_items(client: TestClient):
- response = client.get("/items/")
- assert response.status_code == 422, response.text
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "missing",
- "loc": ["header", "x-token"],
- "msg": "Field required",
- "input": None,
- },
- {
- "type": "missing",
- "loc": ["header", "x-key"],
- "msg": "Field required",
- "input": None,
- },
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "loc": ["header", "x-token"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- {
- "loc": ["header", "x-key"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- ]
- }
- )
-
-
-@needs_py39
-def test_get_no_headers_users(client: TestClient):
- response = client.get("/users/")
- assert response.status_code == 422, response.text
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "missing",
- "loc": ["header", "x-token"],
- "msg": "Field required",
- "input": None,
- },
- {
- "type": "missing",
- "loc": ["header", "x-key"],
- "msg": "Field required",
- "input": None,
- },
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "loc": ["header", "x-token"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- {
- "loc": ["header", "x-key"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- ]
- }
- )
-
-
-@needs_py39
-def test_get_invalid_one_header_items(client: TestClient):
- response = client.get("/items/", headers={"X-Token": "invalid"})
- assert response.status_code == 400, response.text
- assert response.json() == {"detail": "X-Token header invalid"}
-
-
-@needs_py39
-def test_get_invalid_one_users(client: TestClient):
- response = client.get("/users/", headers={"X-Token": "invalid"})
- assert response.status_code == 400, response.text
- assert response.json() == {"detail": "X-Token header invalid"}
-
-
-@needs_py39
-def test_get_invalid_second_header_items(client: TestClient):
- response = client.get(
- "/items/", headers={"X-Token": "fake-super-secret-token", "X-Key": "invalid"}
- )
- assert response.status_code == 400, response.text
- assert response.json() == {"detail": "X-Key header invalid"}
-
-
-@needs_py39
-def test_get_invalid_second_header_users(client: TestClient):
- response = client.get(
- "/users/", headers={"X-Token": "fake-super-secret-token", "X-Key": "invalid"}
- )
- assert response.status_code == 400, response.text
- assert response.json() == {"detail": "X-Key header invalid"}
-
-
-@needs_py39
-def test_get_valid_headers_items(client: TestClient):
- response = client.get(
- "/items/",
- headers={
- "X-Token": "fake-super-secret-token",
- "X-Key": "fake-super-secret-key",
- },
- )
- assert response.status_code == 200, response.text
- assert response.json() == [{"item": "Portal Gun"}, {"item": "Plumbus"}]
-
-
-@needs_py39
-def test_get_valid_headers_users(client: TestClient):
- response = client.get(
- "/users/",
- headers={
- "X-Token": "fake-super-secret-token",
- "X-Key": "fake-super-secret-key",
- },
- )
- assert response.status_code == 200, response.text
- assert response.json() == [{"username": "Rick"}, {"username": "Morty"}]
-
-
-@needs_py39
-def test_openapi_schema(client: TestClient):
- response = client.get("/openapi.json")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/": {
- "get": {
- "summary": "Read Items",
- "operationId": "read_items_items__get",
- "parameters": [
- {
- "required": True,
- "schema": {"title": "X-Token", "type": "string"},
- "name": "x-token",
- "in": "header",
- },
- {
- "required": True,
- "schema": {"title": "X-Key", "type": "string"},
- "name": "x-key",
- "in": "header",
- },
- ],
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- }
- },
- "/users/": {
- "get": {
- "summary": "Read Users",
- "operationId": "read_users_users__get",
- "parameters": [
- {
- "required": True,
- "schema": {"title": "X-Token", "type": "string"},
- "name": "x-token",
- "in": "header",
- },
- {
- "required": True,
- "schema": {"title": "X-Key", "type": "string"},
- "name": "x-key",
- "in": "header",
- },
- ],
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- }
- },
- },
- "components": {
- "schemas": {
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_extra_data_types/test_tutorial001.py b/tests/test_tutorial/test_extra_data_types/test_tutorial001.py
index 5558671b91..b816c9cab5 100644
--- a/tests/test_tutorial/test_extra_data_types/test_tutorial001.py
+++ b/tests/test_tutorial/test_extra_data_types/test_tutorial001.py
@@ -1,12 +1,30 @@
+import importlib
+
+import pytest
from dirty_equals import IsDict
from fastapi.testclient import TestClient
-from docs_src.extra_data_types.tutorial001 import app
-
-client = TestClient(app)
+from ...utils import needs_py39, needs_py310
-def test_extra_types():
+@pytest.fixture(
+ name="client",
+ params=[
+ "tutorial001",
+ pytest.param("tutorial001_py310", marks=needs_py310),
+ "tutorial001_an",
+ pytest.param("tutorial001_an_py39", marks=needs_py39),
+ pytest.param("tutorial001_an_py310", marks=needs_py310),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.extra_data_types.{request.param}")
+
+ client = TestClient(mod.app)
+ return client
+
+
+def test_extra_types(client: TestClient):
item_id = "ff97dd87-a4a5-4a12-b412-cde99f33e00e"
data = {
"start_datetime": "2018-12-22T14:00:00+00:00",
@@ -27,7 +45,7 @@ def test_extra_types():
assert response.json() == expected_response
-def test_openapi_schema():
+def test_openapi_schema(client: TestClient):
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
diff --git a/tests/test_tutorial/test_extra_data_types/test_tutorial001_an.py b/tests/test_tutorial/test_extra_data_types/test_tutorial001_an.py
deleted file mode 100644
index e309f8bd6b..0000000000
--- a/tests/test_tutorial/test_extra_data_types/test_tutorial001_an.py
+++ /dev/null
@@ -1,175 +0,0 @@
-from dirty_equals import IsDict
-from fastapi.testclient import TestClient
-
-from docs_src.extra_data_types.tutorial001_an import app
-
-client = TestClient(app)
-
-
-def test_extra_types():
- item_id = "ff97dd87-a4a5-4a12-b412-cde99f33e00e"
- data = {
- "start_datetime": "2018-12-22T14:00:00+00:00",
- "end_datetime": "2018-12-24T15:00:00+00:00",
- "repeat_at": "15:30:00",
- "process_after": 300,
- }
- expected_response = data.copy()
- expected_response.update(
- {
- "start_process": "2018-12-22T14:05:00+00:00",
- "duration": 176_100,
- "item_id": item_id,
- }
- )
- response = client.put(f"/items/{item_id}", json=data)
- assert response.status_code == 200, response.text
- assert response.json() == expected_response
-
-
-def test_openapi_schema():
- response = client.get("/openapi.json")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/{item_id}": {
- "put": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Read Items",
- "operationId": "read_items_items__item_id__put",
- "parameters": [
- {
- "required": True,
- "schema": {
- "title": "Item Id",
- "type": "string",
- "format": "uuid",
- },
- "name": "item_id",
- "in": "path",
- }
- ],
- "requestBody": {
- "required": True,
- "content": {
- "application/json": {
- "schema": IsDict(
- {
- "allOf": [
- {
- "$ref": "#/components/schemas/Body_read_items_items__item_id__put"
- }
- ],
- "title": "Body",
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "$ref": "#/components/schemas/Body_read_items_items__item_id__put"
- }
- )
- }
- },
- },
- }
- }
- },
- "components": {
- "schemas": {
- "Body_read_items_items__item_id__put": {
- "title": "Body_read_items_items__item_id__put",
- "type": "object",
- "properties": {
- "start_datetime": {
- "title": "Start Datetime",
- "type": "string",
- "format": "date-time",
- },
- "end_datetime": {
- "title": "End Datetime",
- "type": "string",
- "format": "date-time",
- },
- "repeat_at": IsDict(
- {
- "title": "Repeat At",
- "anyOf": [
- {"type": "string", "format": "time"},
- {"type": "null"},
- ],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "title": "Repeat At",
- "type": "string",
- "format": "time",
- }
- ),
- "process_after": IsDict(
- {
- "title": "Process After",
- "type": "string",
- "format": "duration",
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "title": "Process After",
- "type": "number",
- "format": "time-delta",
- }
- ),
- },
- "required": ["start_datetime", "end_datetime", "process_after"],
- },
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_extra_data_types/test_tutorial001_an_py310.py b/tests/test_tutorial/test_extra_data_types/test_tutorial001_an_py310.py
deleted file mode 100644
index ca110dc00d..0000000000
--- a/tests/test_tutorial/test_extra_data_types/test_tutorial001_an_py310.py
+++ /dev/null
@@ -1,184 +0,0 @@
-import pytest
-from dirty_equals import IsDict
-from fastapi.testclient import TestClient
-
-from ...utils import needs_py310
-
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.extra_data_types.tutorial001_an_py310 import app
-
- client = TestClient(app)
- return client
-
-
-@needs_py310
-def test_extra_types(client: TestClient):
- item_id = "ff97dd87-a4a5-4a12-b412-cde99f33e00e"
- data = {
- "start_datetime": "2018-12-22T14:00:00+00:00",
- "end_datetime": "2018-12-24T15:00:00+00:00",
- "repeat_at": "15:30:00",
- "process_after": 300,
- }
- expected_response = data.copy()
- expected_response.update(
- {
- "start_process": "2018-12-22T14:05:00+00:00",
- "duration": 176_100,
- "item_id": item_id,
- }
- )
- response = client.put(f"/items/{item_id}", json=data)
- assert response.status_code == 200, response.text
- assert response.json() == expected_response
-
-
-@needs_py310
-def test_openapi_schema(client: TestClient):
- response = client.get("/openapi.json")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/{item_id}": {
- "put": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Read Items",
- "operationId": "read_items_items__item_id__put",
- "parameters": [
- {
- "required": True,
- "schema": {
- "title": "Item Id",
- "type": "string",
- "format": "uuid",
- },
- "name": "item_id",
- "in": "path",
- }
- ],
- "requestBody": {
- "required": True,
- "content": {
- "application/json": {
- "schema": IsDict(
- {
- "allOf": [
- {
- "$ref": "#/components/schemas/Body_read_items_items__item_id__put"
- }
- ],
- "title": "Body",
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "$ref": "#/components/schemas/Body_read_items_items__item_id__put"
- }
- )
- }
- },
- },
- }
- }
- },
- "components": {
- "schemas": {
- "Body_read_items_items__item_id__put": {
- "title": "Body_read_items_items__item_id__put",
- "type": "object",
- "properties": {
- "start_datetime": {
- "title": "Start Datetime",
- "type": "string",
- "format": "date-time",
- },
- "end_datetime": {
- "title": "End Datetime",
- "type": "string",
- "format": "date-time",
- },
- "repeat_at": IsDict(
- {
- "title": "Repeat At",
- "anyOf": [
- {"type": "string", "format": "time"},
- {"type": "null"},
- ],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "title": "Repeat At",
- "type": "string",
- "format": "time",
- }
- ),
- "process_after": IsDict(
- {
- "title": "Process After",
- "type": "string",
- "format": "duration",
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "title": "Process After",
- "type": "number",
- "format": "time-delta",
- }
- ),
- },
- "required": ["start_datetime", "end_datetime", "process_after"],
- },
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_extra_data_types/test_tutorial001_an_py39.py b/tests/test_tutorial/test_extra_data_types/test_tutorial001_an_py39.py
deleted file mode 100644
index 3386fb1fd8..0000000000
--- a/tests/test_tutorial/test_extra_data_types/test_tutorial001_an_py39.py
+++ /dev/null
@@ -1,184 +0,0 @@
-import pytest
-from dirty_equals import IsDict
-from fastapi.testclient import TestClient
-
-from ...utils import needs_py39
-
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.extra_data_types.tutorial001_an_py39 import app
-
- client = TestClient(app)
- return client
-
-
-@needs_py39
-def test_extra_types(client: TestClient):
- item_id = "ff97dd87-a4a5-4a12-b412-cde99f33e00e"
- data = {
- "start_datetime": "2018-12-22T14:00:00+00:00",
- "end_datetime": "2018-12-24T15:00:00+00:00",
- "repeat_at": "15:30:00",
- "process_after": 300,
- }
- expected_response = data.copy()
- expected_response.update(
- {
- "start_process": "2018-12-22T14:05:00+00:00",
- "duration": 176_100,
- "item_id": item_id,
- }
- )
- response = client.put(f"/items/{item_id}", json=data)
- assert response.status_code == 200, response.text
- assert response.json() == expected_response
-
-
-@needs_py39
-def test_openapi_schema(client: TestClient):
- response = client.get("/openapi.json")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/{item_id}": {
- "put": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Read Items",
- "operationId": "read_items_items__item_id__put",
- "parameters": [
- {
- "required": True,
- "schema": {
- "title": "Item Id",
- "type": "string",
- "format": "uuid",
- },
- "name": "item_id",
- "in": "path",
- }
- ],
- "requestBody": {
- "required": True,
- "content": {
- "application/json": {
- "schema": IsDict(
- {
- "allOf": [
- {
- "$ref": "#/components/schemas/Body_read_items_items__item_id__put"
- }
- ],
- "title": "Body",
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "$ref": "#/components/schemas/Body_read_items_items__item_id__put"
- }
- )
- }
- },
- },
- }
- }
- },
- "components": {
- "schemas": {
- "Body_read_items_items__item_id__put": {
- "title": "Body_read_items_items__item_id__put",
- "type": "object",
- "properties": {
- "start_datetime": {
- "title": "Start Datetime",
- "type": "string",
- "format": "date-time",
- },
- "end_datetime": {
- "title": "End Datetime",
- "type": "string",
- "format": "date-time",
- },
- "repeat_at": IsDict(
- {
- "title": "Repeat At",
- "anyOf": [
- {"type": "string", "format": "time"},
- {"type": "null"},
- ],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "title": "Repeat At",
- "type": "string",
- "format": "time",
- }
- ),
- "process_after": IsDict(
- {
- "title": "Process After",
- "type": "string",
- "format": "duration",
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "title": "Process After",
- "type": "number",
- "format": "time-delta",
- }
- ),
- },
- "required": ["start_datetime", "end_datetime", "process_after"],
- },
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_extra_data_types/test_tutorial001_py310.py b/tests/test_tutorial/test_extra_data_types/test_tutorial001_py310.py
deleted file mode 100644
index 50c9aefdf2..0000000000
--- a/tests/test_tutorial/test_extra_data_types/test_tutorial001_py310.py
+++ /dev/null
@@ -1,184 +0,0 @@
-import pytest
-from dirty_equals import IsDict
-from fastapi.testclient import TestClient
-
-from ...utils import needs_py310
-
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.extra_data_types.tutorial001_py310 import app
-
- client = TestClient(app)
- return client
-
-
-@needs_py310
-def test_extra_types(client: TestClient):
- item_id = "ff97dd87-a4a5-4a12-b412-cde99f33e00e"
- data = {
- "start_datetime": "2018-12-22T14:00:00+00:00",
- "end_datetime": "2018-12-24T15:00:00+00:00",
- "repeat_at": "15:30:00",
- "process_after": 300,
- }
- expected_response = data.copy()
- expected_response.update(
- {
- "start_process": "2018-12-22T14:05:00+00:00",
- "duration": 176_100,
- "item_id": item_id,
- }
- )
- response = client.put(f"/items/{item_id}", json=data)
- assert response.status_code == 200, response.text
- assert response.json() == expected_response
-
-
-@needs_py310
-def test_openapi_schema(client: TestClient):
- response = client.get("/openapi.json")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/{item_id}": {
- "put": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Read Items",
- "operationId": "read_items_items__item_id__put",
- "parameters": [
- {
- "required": True,
- "schema": {
- "title": "Item Id",
- "type": "string",
- "format": "uuid",
- },
- "name": "item_id",
- "in": "path",
- }
- ],
- "requestBody": {
- "required": True,
- "content": {
- "application/json": {
- "schema": IsDict(
- {
- "allOf": [
- {
- "$ref": "#/components/schemas/Body_read_items_items__item_id__put"
- }
- ],
- "title": "Body",
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "$ref": "#/components/schemas/Body_read_items_items__item_id__put"
- }
- )
- }
- },
- },
- }
- }
- },
- "components": {
- "schemas": {
- "Body_read_items_items__item_id__put": {
- "title": "Body_read_items_items__item_id__put",
- "type": "object",
- "properties": {
- "start_datetime": {
- "title": "Start Datetime",
- "type": "string",
- "format": "date-time",
- },
- "end_datetime": {
- "title": "End Datetime",
- "type": "string",
- "format": "date-time",
- },
- "repeat_at": IsDict(
- {
- "title": "Repeat At",
- "anyOf": [
- {"type": "string", "format": "time"},
- {"type": "null"},
- ],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "title": "Repeat At",
- "type": "string",
- "format": "time",
- }
- ),
- "process_after": IsDict(
- {
- "title": "Process After",
- "type": "string",
- "format": "duration",
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "title": "Process After",
- "type": "number",
- "format": "time-delta",
- }
- ),
- },
- "required": ["start_datetime", "end_datetime", "process_after"],
- },
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_extra_models/test_tutorial003.py b/tests/test_tutorial/test_extra_models/test_tutorial003.py
index 0ccb99948f..73aa299039 100644
--- a/tests/test_tutorial/test_extra_models/test_tutorial003.py
+++ b/tests/test_tutorial/test_extra_models/test_tutorial003.py
@@ -1,12 +1,27 @@
+import importlib
+
+import pytest
from dirty_equals import IsOneOf
from fastapi.testclient import TestClient
-from docs_src.extra_models.tutorial003 import app
-
-client = TestClient(app)
+from ...utils import needs_py310
-def test_get_car():
+@pytest.fixture(
+ name="client",
+ params=[
+ "tutorial003",
+ pytest.param("tutorial003_py310", marks=needs_py310),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.extra_models.{request.param}")
+
+ client = TestClient(mod.app)
+ return client
+
+
+def test_get_car(client: TestClient):
response = client.get("/items/item1")
assert response.status_code == 200, response.text
assert response.json() == {
@@ -15,7 +30,7 @@ def test_get_car():
}
-def test_get_plane():
+def test_get_plane(client: TestClient):
response = client.get("/items/item2")
assert response.status_code == 200, response.text
assert response.json() == {
@@ -25,7 +40,7 @@ def test_get_plane():
}
-def test_openapi_schema():
+def test_openapi_schema(client: TestClient):
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
diff --git a/tests/test_tutorial/test_extra_models/test_tutorial003_py310.py b/tests/test_tutorial/test_extra_models/test_tutorial003_py310.py
deleted file mode 100644
index b2fe65fd9f..0000000000
--- a/tests/test_tutorial/test_extra_models/test_tutorial003_py310.py
+++ /dev/null
@@ -1,144 +0,0 @@
-import pytest
-from dirty_equals import IsOneOf
-from fastapi.testclient import TestClient
-
-from ...utils import needs_py310
-
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.extra_models.tutorial003_py310 import app
-
- client = TestClient(app)
- return client
-
-
-@needs_py310
-def test_get_car(client: TestClient):
- response = client.get("/items/item1")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "description": "All my friends drive a low rider",
- "type": "car",
- }
-
-
-@needs_py310
-def test_get_plane(client: TestClient):
- response = client.get("/items/item2")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "description": "Music is my aeroplane, it's my aeroplane",
- "type": "plane",
- "size": 5,
- }
-
-
-@needs_py310
-def test_openapi_schema(client: TestClient):
- response = client.get("/openapi.json")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/{item_id}": {
- "get": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {
- "application/json": {
- "schema": {
- "title": "Response Read Item Items Item Id Get",
- "anyOf": [
- {"$ref": "#/components/schemas/PlaneItem"},
- {"$ref": "#/components/schemas/CarItem"},
- ],
- }
- }
- },
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Read Item",
- "operationId": "read_item_items__item_id__get",
- "parameters": [
- {
- "required": True,
- "schema": {"title": "Item Id", "type": "string"},
- "name": "item_id",
- "in": "path",
- }
- ],
- }
- }
- },
- "components": {
- "schemas": {
- "PlaneItem": {
- "title": "PlaneItem",
- "required": IsOneOf(
- ["description", "type", "size"],
- # TODO: remove when deprecating Pydantic v1
- ["description", "size"],
- ),
- "type": "object",
- "properties": {
- "description": {"title": "Description", "type": "string"},
- "type": {"title": "Type", "type": "string", "default": "plane"},
- "size": {"title": "Size", "type": "integer"},
- },
- },
- "CarItem": {
- "title": "CarItem",
- "required": IsOneOf(
- ["description", "type"],
- # TODO: remove when deprecating Pydantic v1
- ["description"],
- ),
- "type": "object",
- "properties": {
- "description": {"title": "Description", "type": "string"},
- "type": {"title": "Type", "type": "string", "default": "car"},
- },
- },
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_extra_models/test_tutorial004.py b/tests/test_tutorial/test_extra_models/test_tutorial004.py
index 71f6a8c700..7628db30c7 100644
--- a/tests/test_tutorial/test_extra_models/test_tutorial004.py
+++ b/tests/test_tutorial/test_extra_models/test_tutorial004.py
@@ -1,11 +1,26 @@
+import importlib
+
+import pytest
from fastapi.testclient import TestClient
-from docs_src.extra_models.tutorial004 import app
-
-client = TestClient(app)
+from ...utils import needs_py39
-def test_get_items():
+@pytest.fixture(
+ name="client",
+ params=[
+ "tutorial004",
+ pytest.param("tutorial004_py39", marks=needs_py39),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.extra_models.{request.param}")
+
+ client = TestClient(mod.app)
+ return client
+
+
+def test_get_items(client: TestClient):
response = client.get("/items/")
assert response.status_code == 200, response.text
assert response.json() == [
@@ -14,7 +29,7 @@ def test_get_items():
]
-def test_openapi_schema():
+def test_openapi_schema(client: TestClient):
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
diff --git a/tests/test_tutorial/test_extra_models/test_tutorial004_py39.py b/tests/test_tutorial/test_extra_models/test_tutorial004_py39.py
deleted file mode 100644
index 5475b92e10..0000000000
--- a/tests/test_tutorial/test_extra_models/test_tutorial004_py39.py
+++ /dev/null
@@ -1,67 +0,0 @@
-import pytest
-from fastapi.testclient import TestClient
-
-from ...utils import needs_py39
-
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.extra_models.tutorial004_py39 import app
-
- client = TestClient(app)
- return client
-
-
-@needs_py39
-def test_get_items(client: TestClient):
- response = client.get("/items/")
- assert response.status_code == 200, response.text
- assert response.json() == [
- {"name": "Foo", "description": "There comes my hero"},
- {"name": "Red", "description": "It's my aeroplane"},
- ]
-
-
-@needs_py39
-def test_openapi_schema(client: TestClient):
- response = client.get("/openapi.json")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/": {
- "get": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {
- "application/json": {
- "schema": {
- "title": "Response Read Items Items Get",
- "type": "array",
- "items": {"$ref": "#/components/schemas/Item"},
- }
- }
- },
- }
- },
- "summary": "Read Items",
- "operationId": "read_items_items__get",
- }
- }
- },
- "components": {
- "schemas": {
- "Item": {
- "title": "Item",
- "required": ["name", "description"],
- "type": "object",
- "properties": {
- "name": {"title": "Name", "type": "string"},
- "description": {"title": "Description", "type": "string"},
- },
- }
- }
- },
- }
diff --git a/tests/test_tutorial/test_extra_models/test_tutorial005.py b/tests/test_tutorial/test_extra_models/test_tutorial005.py
index b0861c37f7..553e442385 100644
--- a/tests/test_tutorial/test_extra_models/test_tutorial005.py
+++ b/tests/test_tutorial/test_extra_models/test_tutorial005.py
@@ -1,17 +1,32 @@
+import importlib
+
+import pytest
from fastapi.testclient import TestClient
-from docs_src.extra_models.tutorial005 import app
-
-client = TestClient(app)
+from ...utils import needs_py39
-def test_get_items():
+@pytest.fixture(
+ name="client",
+ params=[
+ "tutorial005",
+ pytest.param("tutorial005_py39", marks=needs_py39),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.extra_models.{request.param}")
+
+ client = TestClient(mod.app)
+ return client
+
+
+def test_get_items(client: TestClient):
response = client.get("/keyword-weights/")
assert response.status_code == 200, response.text
assert response.json() == {"foo": 2.3, "bar": 3.4}
-def test_openapi_schema():
+def test_openapi_schema(client: TestClient):
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
diff --git a/tests/test_tutorial/test_extra_models/test_tutorial005_py39.py b/tests/test_tutorial/test_extra_models/test_tutorial005_py39.py
deleted file mode 100644
index 7278e93c36..0000000000
--- a/tests/test_tutorial/test_extra_models/test_tutorial005_py39.py
+++ /dev/null
@@ -1,51 +0,0 @@
-import pytest
-from fastapi.testclient import TestClient
-
-from ...utils import needs_py39
-
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.extra_models.tutorial005_py39 import app
-
- client = TestClient(app)
- return client
-
-
-@needs_py39
-def test_get_items(client: TestClient):
- response = client.get("/keyword-weights/")
- assert response.status_code == 200, response.text
- assert response.json() == {"foo": 2.3, "bar": 3.4}
-
-
-@needs_py39
-def test_openapi_schema(client: TestClient):
- response = client.get("/openapi.json")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/keyword-weights/": {
- "get": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {
- "application/json": {
- "schema": {
- "title": "Response Read Keyword Weights Keyword Weights Get",
- "type": "object",
- "additionalProperties": {"type": "number"},
- }
- }
- },
- }
- },
- "summary": "Read Keyword Weights",
- "operationId": "read_keyword_weights_keyword_weights__get",
- }
- }
- },
- }
diff --git a/docs_src/sql_databases/sql_app_py310/__init__.py b/tests/test_tutorial/test_header_param_models/__init__.py
similarity index 100%
rename from docs_src/sql_databases/sql_app_py310/__init__.py
rename to tests/test_tutorial/test_header_param_models/__init__.py
diff --git a/tests/test_tutorial/test_header_param_models/test_tutorial001.py b/tests/test_tutorial/test_header_param_models/test_tutorial001.py
new file mode 100644
index 0000000000..bc876897b2
--- /dev/null
+++ b/tests/test_tutorial/test_header_param_models/test_tutorial001.py
@@ -0,0 +1,238 @@
+import importlib
+
+import pytest
+from dirty_equals import IsDict
+from fastapi.testclient import TestClient
+from inline_snapshot import snapshot
+
+from tests.utils import needs_py39, needs_py310
+
+
+@pytest.fixture(
+ name="client",
+ params=[
+ "tutorial001",
+ pytest.param("tutorial001_py39", marks=needs_py39),
+ pytest.param("tutorial001_py310", marks=needs_py310),
+ "tutorial001_an",
+ pytest.param("tutorial001_an_py39", marks=needs_py39),
+ pytest.param("tutorial001_an_py310", marks=needs_py310),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.header_param_models.{request.param}")
+
+ client = TestClient(mod.app)
+ return client
+
+
+def test_header_param_model(client: TestClient):
+ response = client.get(
+ "/items/",
+ headers=[
+ ("save-data", "true"),
+ ("if-modified-since", "yesterday"),
+ ("traceparent", "123"),
+ ("x-tag", "one"),
+ ("x-tag", "two"),
+ ],
+ )
+ assert response.status_code == 200
+ assert response.json() == {
+ "host": "testserver",
+ "save_data": True,
+ "if_modified_since": "yesterday",
+ "traceparent": "123",
+ "x_tag": ["one", "two"],
+ }
+
+
+def test_header_param_model_defaults(client: TestClient):
+ response = client.get("/items/", headers=[("save-data", "true")])
+ assert response.status_code == 200
+ assert response.json() == {
+ "host": "testserver",
+ "save_data": True,
+ "if_modified_since": None,
+ "traceparent": None,
+ "x_tag": [],
+ }
+
+
+def test_header_param_model_invalid(client: TestClient):
+ response = client.get("/items/")
+ assert response.status_code == 422
+ assert response.json() == snapshot(
+ {
+ "detail": [
+ IsDict(
+ {
+ "type": "missing",
+ "loc": ["header", "save_data"],
+ "msg": "Field required",
+ "input": {
+ "x_tag": [],
+ "host": "testserver",
+ "accept": "*/*",
+ "accept-encoding": "gzip, deflate",
+ "connection": "keep-alive",
+ "user-agent": "testclient",
+ },
+ }
+ )
+ | IsDict(
+ # TODO: remove when deprecating Pydantic v1
+ {
+ "type": "value_error.missing",
+ "loc": ["header", "save_data"],
+ "msg": "field required",
+ }
+ )
+ ]
+ }
+ )
+
+
+def test_header_param_model_extra(client: TestClient):
+ response = client.get(
+ "/items/", headers=[("save-data", "true"), ("tool", "plumbus")]
+ )
+ assert response.status_code == 200, response.text
+ assert response.json() == snapshot(
+ {
+ "host": "testserver",
+ "save_data": True,
+ "if_modified_since": None,
+ "traceparent": None,
+ "x_tag": [],
+ }
+ )
+
+
+def test_openapi_schema(client: TestClient):
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == snapshot(
+ {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/items/": {
+ "get": {
+ "summary": "Read Items",
+ "operationId": "read_items_items__get",
+ "parameters": [
+ {
+ "name": "host",
+ "in": "header",
+ "required": True,
+ "schema": {"type": "string", "title": "Host"},
+ },
+ {
+ "name": "save-data",
+ "in": "header",
+ "required": True,
+ "schema": {"type": "boolean", "title": "Save Data"},
+ },
+ {
+ "name": "if-modified-since",
+ "in": "header",
+ "required": False,
+ "schema": IsDict(
+ {
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ "title": "If Modified Since",
+ }
+ )
+ | IsDict(
+ # TODO: remove when deprecating Pydantic v1
+ {
+ "type": "string",
+ "title": "If Modified Since",
+ }
+ ),
+ },
+ {
+ "name": "traceparent",
+ "in": "header",
+ "required": False,
+ "schema": IsDict(
+ {
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ "title": "Traceparent",
+ }
+ )
+ | IsDict(
+ # TODO: remove when deprecating Pydantic v1
+ {
+ "type": "string",
+ "title": "Traceparent",
+ }
+ ),
+ },
+ {
+ "name": "x-tag",
+ "in": "header",
+ "required": False,
+ "schema": {
+ "type": "array",
+ "items": {"type": "string"},
+ "default": [],
+ "title": "X Tag",
+ },
+ },
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "HTTPValidationError": {
+ "properties": {
+ "detail": {
+ "items": {
+ "$ref": "#/components/schemas/ValidationError"
+ },
+ "type": "array",
+ "title": "Detail",
+ }
+ },
+ "type": "object",
+ "title": "HTTPValidationError",
+ },
+ "ValidationError": {
+ "properties": {
+ "loc": {
+ "items": {
+ "anyOf": [{"type": "string"}, {"type": "integer"}]
+ },
+ "type": "array",
+ "title": "Location",
+ },
+ "msg": {"type": "string", "title": "Message"},
+ "type": {"type": "string", "title": "Error Type"},
+ },
+ "type": "object",
+ "required": ["loc", "msg", "type"],
+ "title": "ValidationError",
+ },
+ }
+ },
+ }
+ )
diff --git a/tests/test_tutorial/test_header_param_models/test_tutorial002.py b/tests/test_tutorial/test_header_param_models/test_tutorial002.py
new file mode 100644
index 0000000000..0615521c43
--- /dev/null
+++ b/tests/test_tutorial/test_header_param_models/test_tutorial002.py
@@ -0,0 +1,249 @@
+import importlib
+
+import pytest
+from dirty_equals import IsDict
+from fastapi.testclient import TestClient
+from inline_snapshot import snapshot
+
+from tests.utils import needs_py39, needs_py310, needs_pydanticv1, needs_pydanticv2
+
+
+@pytest.fixture(
+ name="client",
+ params=[
+ pytest.param("tutorial002", marks=needs_pydanticv2),
+ pytest.param("tutorial002_py310", marks=[needs_py310, needs_pydanticv2]),
+ pytest.param("tutorial002_an", marks=needs_pydanticv2),
+ pytest.param("tutorial002_an_py39", marks=[needs_py39, needs_pydanticv2]),
+ pytest.param("tutorial002_an_py310", marks=[needs_py310, needs_pydanticv2]),
+ pytest.param("tutorial002_pv1", marks=[needs_pydanticv1, needs_pydanticv1]),
+ pytest.param("tutorial002_pv1_py310", marks=[needs_py310, needs_pydanticv1]),
+ pytest.param("tutorial002_pv1_an", marks=[needs_pydanticv1]),
+ pytest.param("tutorial002_pv1_an_py39", marks=[needs_py39, needs_pydanticv1]),
+ pytest.param("tutorial002_pv1_an_py310", marks=[needs_py310, needs_pydanticv1]),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.header_param_models.{request.param}")
+
+ client = TestClient(mod.app)
+ client.headers.clear()
+ return client
+
+
+def test_header_param_model(client: TestClient):
+ response = client.get(
+ "/items/",
+ headers=[
+ ("save-data", "true"),
+ ("if-modified-since", "yesterday"),
+ ("traceparent", "123"),
+ ("x-tag", "one"),
+ ("x-tag", "two"),
+ ],
+ )
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "host": "testserver",
+ "save_data": True,
+ "if_modified_since": "yesterday",
+ "traceparent": "123",
+ "x_tag": ["one", "two"],
+ }
+
+
+def test_header_param_model_defaults(client: TestClient):
+ response = client.get("/items/", headers=[("save-data", "true")])
+ assert response.status_code == 200
+ assert response.json() == {
+ "host": "testserver",
+ "save_data": True,
+ "if_modified_since": None,
+ "traceparent": None,
+ "x_tag": [],
+ }
+
+
+def test_header_param_model_invalid(client: TestClient):
+ response = client.get("/items/")
+ assert response.status_code == 422
+ assert response.json() == snapshot(
+ {
+ "detail": [
+ IsDict(
+ {
+ "type": "missing",
+ "loc": ["header", "save_data"],
+ "msg": "Field required",
+ "input": {"x_tag": [], "host": "testserver"},
+ }
+ )
+ | IsDict(
+ # TODO: remove when deprecating Pydantic v1
+ {
+ "type": "value_error.missing",
+ "loc": ["header", "save_data"],
+ "msg": "field required",
+ }
+ )
+ ]
+ }
+ )
+
+
+def test_header_param_model_extra(client: TestClient):
+ response = client.get(
+ "/items/", headers=[("save-data", "true"), ("tool", "plumbus")]
+ )
+ assert response.status_code == 422, response.text
+ assert response.json() == snapshot(
+ {
+ "detail": [
+ IsDict(
+ {
+ "type": "extra_forbidden",
+ "loc": ["header", "tool"],
+ "msg": "Extra inputs are not permitted",
+ "input": "plumbus",
+ }
+ )
+ | IsDict(
+ # TODO: remove when deprecating Pydantic v1
+ {
+ "type": "value_error.extra",
+ "loc": ["header", "tool"],
+ "msg": "extra fields not permitted",
+ }
+ )
+ ]
+ }
+ )
+
+
+def test_openapi_schema(client: TestClient):
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == snapshot(
+ {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/items/": {
+ "get": {
+ "summary": "Read Items",
+ "operationId": "read_items_items__get",
+ "parameters": [
+ {
+ "name": "host",
+ "in": "header",
+ "required": True,
+ "schema": {"type": "string", "title": "Host"},
+ },
+ {
+ "name": "save-data",
+ "in": "header",
+ "required": True,
+ "schema": {"type": "boolean", "title": "Save Data"},
+ },
+ {
+ "name": "if-modified-since",
+ "in": "header",
+ "required": False,
+ "schema": IsDict(
+ {
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ "title": "If Modified Since",
+ }
+ )
+ | IsDict(
+ # TODO: remove when deprecating Pydantic v1
+ {
+ "type": "string",
+ "title": "If Modified Since",
+ }
+ ),
+ },
+ {
+ "name": "traceparent",
+ "in": "header",
+ "required": False,
+ "schema": IsDict(
+ {
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ "title": "Traceparent",
+ }
+ )
+ | IsDict(
+ # TODO: remove when deprecating Pydantic v1
+ {
+ "type": "string",
+ "title": "Traceparent",
+ }
+ ),
+ },
+ {
+ "name": "x-tag",
+ "in": "header",
+ "required": False,
+ "schema": {
+ "type": "array",
+ "items": {"type": "string"},
+ "default": [],
+ "title": "X Tag",
+ },
+ },
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "HTTPValidationError": {
+ "properties": {
+ "detail": {
+ "items": {
+ "$ref": "#/components/schemas/ValidationError"
+ },
+ "type": "array",
+ "title": "Detail",
+ }
+ },
+ "type": "object",
+ "title": "HTTPValidationError",
+ },
+ "ValidationError": {
+ "properties": {
+ "loc": {
+ "items": {
+ "anyOf": [{"type": "string"}, {"type": "integer"}]
+ },
+ "type": "array",
+ "title": "Location",
+ },
+ "msg": {"type": "string", "title": "Message"},
+ "type": {"type": "string", "title": "Error Type"},
+ },
+ "type": "object",
+ "required": ["loc", "msg", "type"],
+ "title": "ValidationError",
+ },
+ }
+ },
+ }
+ )
diff --git a/tests/test_tutorial/test_header_param_models/test_tutorial003.py b/tests/test_tutorial/test_header_param_models/test_tutorial003.py
new file mode 100644
index 0000000000..60940e1da2
--- /dev/null
+++ b/tests/test_tutorial/test_header_param_models/test_tutorial003.py
@@ -0,0 +1,285 @@
+import importlib
+
+import pytest
+from dirty_equals import IsDict
+from fastapi.testclient import TestClient
+from inline_snapshot import snapshot
+
+from tests.utils import needs_py39, needs_py310
+
+
+@pytest.fixture(
+ name="client",
+ params=[
+ "tutorial003",
+ pytest.param("tutorial003_py39", marks=needs_py39),
+ pytest.param("tutorial003_py310", marks=needs_py310),
+ "tutorial003_an",
+ pytest.param("tutorial003_an_py39", marks=needs_py39),
+ pytest.param("tutorial003_an_py310", marks=needs_py310),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.header_param_models.{request.param}")
+
+ client = TestClient(mod.app)
+ return client
+
+
+def test_header_param_model(client: TestClient):
+ response = client.get(
+ "/items/",
+ headers=[
+ ("save_data", "true"),
+ ("if_modified_since", "yesterday"),
+ ("traceparent", "123"),
+ ("x_tag", "one"),
+ ("x_tag", "two"),
+ ],
+ )
+ assert response.status_code == 200
+ assert response.json() == {
+ "host": "testserver",
+ "save_data": True,
+ "if_modified_since": "yesterday",
+ "traceparent": "123",
+ "x_tag": ["one", "two"],
+ }
+
+
+def test_header_param_model_no_underscore(client: TestClient):
+ response = client.get(
+ "/items/",
+ headers=[
+ ("save-data", "true"),
+ ("if-modified-since", "yesterday"),
+ ("traceparent", "123"),
+ ("x-tag", "one"),
+ ("x-tag", "two"),
+ ],
+ )
+ assert response.status_code == 422
+ assert response.json() == snapshot(
+ {
+ "detail": [
+ IsDict(
+ {
+ "type": "missing",
+ "loc": ["header", "save_data"],
+ "msg": "Field required",
+ "input": {
+ "host": "testserver",
+ "traceparent": "123",
+ "x_tag": [],
+ "accept": "*/*",
+ "accept-encoding": "gzip, deflate",
+ "connection": "keep-alive",
+ "user-agent": "testclient",
+ "save-data": "true",
+ "if-modified-since": "yesterday",
+ "x-tag": "two",
+ },
+ }
+ )
+ | IsDict(
+ # TODO: remove when deprecating Pydantic v1
+ {
+ "type": "value_error.missing",
+ "loc": ["header", "save_data"],
+ "msg": "field required",
+ }
+ )
+ ]
+ }
+ )
+
+
+def test_header_param_model_defaults(client: TestClient):
+ response = client.get("/items/", headers=[("save_data", "true")])
+ assert response.status_code == 200
+ assert response.json() == {
+ "host": "testserver",
+ "save_data": True,
+ "if_modified_since": None,
+ "traceparent": None,
+ "x_tag": [],
+ }
+
+
+def test_header_param_model_invalid(client: TestClient):
+ response = client.get("/items/")
+ assert response.status_code == 422
+ assert response.json() == snapshot(
+ {
+ "detail": [
+ IsDict(
+ {
+ "type": "missing",
+ "loc": ["header", "save_data"],
+ "msg": "Field required",
+ "input": {
+ "x_tag": [],
+ "host": "testserver",
+ "accept": "*/*",
+ "accept-encoding": "gzip, deflate",
+ "connection": "keep-alive",
+ "user-agent": "testclient",
+ },
+ }
+ )
+ | IsDict(
+ # TODO: remove when deprecating Pydantic v1
+ {
+ "type": "value_error.missing",
+ "loc": ["header", "save_data"],
+ "msg": "field required",
+ }
+ )
+ ]
+ }
+ )
+
+
+def test_header_param_model_extra(client: TestClient):
+ response = client.get(
+ "/items/", headers=[("save_data", "true"), ("tool", "plumbus")]
+ )
+ assert response.status_code == 200, response.text
+ assert response.json() == snapshot(
+ {
+ "host": "testserver",
+ "save_data": True,
+ "if_modified_since": None,
+ "traceparent": None,
+ "x_tag": [],
+ }
+ )
+
+
+def test_openapi_schema(client: TestClient):
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == snapshot(
+ {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/items/": {
+ "get": {
+ "summary": "Read Items",
+ "operationId": "read_items_items__get",
+ "parameters": [
+ {
+ "name": "host",
+ "in": "header",
+ "required": True,
+ "schema": {"type": "string", "title": "Host"},
+ },
+ {
+ "name": "save_data",
+ "in": "header",
+ "required": True,
+ "schema": {"type": "boolean", "title": "Save Data"},
+ },
+ {
+ "name": "if_modified_since",
+ "in": "header",
+ "required": False,
+ "schema": IsDict(
+ {
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ "title": "If Modified Since",
+ }
+ )
+ | IsDict(
+ # TODO: remove when deprecating Pydantic v1
+ {
+ "type": "string",
+ "title": "If Modified Since",
+ }
+ ),
+ },
+ {
+ "name": "traceparent",
+ "in": "header",
+ "required": False,
+ "schema": IsDict(
+ {
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ "title": "Traceparent",
+ }
+ )
+ | IsDict(
+ # TODO: remove when deprecating Pydantic v1
+ {
+ "type": "string",
+ "title": "Traceparent",
+ }
+ ),
+ },
+ {
+ "name": "x_tag",
+ "in": "header",
+ "required": False,
+ "schema": {
+ "type": "array",
+ "items": {"type": "string"},
+ "default": [],
+ "title": "X Tag",
+ },
+ },
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "HTTPValidationError": {
+ "properties": {
+ "detail": {
+ "items": {
+ "$ref": "#/components/schemas/ValidationError"
+ },
+ "type": "array",
+ "title": "Detail",
+ }
+ },
+ "type": "object",
+ "title": "HTTPValidationError",
+ },
+ "ValidationError": {
+ "properties": {
+ "loc": {
+ "items": {
+ "anyOf": [{"type": "string"}, {"type": "integer"}]
+ },
+ "type": "array",
+ "title": "Location",
+ },
+ "msg": {"type": "string", "title": "Message"},
+ "type": {"type": "string", "title": "Error Type"},
+ },
+ "type": "object",
+ "required": ["loc", "msg", "type"],
+ "title": "ValidationError",
+ },
+ }
+ },
+ }
+ )
diff --git a/tests/test_tutorial/test_header_params/test_tutorial001.py b/tests/test_tutorial/test_header_params/test_tutorial001.py
index 746fc05024..d6f7fe6189 100644
--- a/tests/test_tutorial/test_header_params/test_tutorial001.py
+++ b/tests/test_tutorial/test_header_params/test_tutorial001.py
@@ -1,10 +1,26 @@
+import importlib
+
import pytest
from dirty_equals import IsDict
from fastapi.testclient import TestClient
-from docs_src.header_params.tutorial001 import app
+from ...utils import needs_py310
-client = TestClient(app)
+
+@pytest.fixture(
+ name="client",
+ params=[
+ "tutorial001",
+ pytest.param("tutorial001_py310", marks=needs_py310),
+ "tutorial001_an",
+ pytest.param("tutorial001_an_py310", marks=needs_py310),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.header_params.{request.param}")
+
+ client = TestClient(mod.app)
+ return client
@pytest.mark.parametrize(
@@ -15,13 +31,13 @@ client = TestClient(app)
("/items", {"User-Agent": "FastAPI test"}, 200, {"User-Agent": "FastAPI test"}),
],
)
-def test(path, headers, expected_status, expected_response):
+def test(path, headers, expected_status, expected_response, client: TestClient):
response = client.get(path, headers=headers)
assert response.status_code == expected_status
assert response.json() == expected_response
-def test_openapi_schema():
+def test_openapi_schema(client: TestClient):
response = client.get("/openapi.json")
assert response.status_code == 200
assert response.json() == {
diff --git a/tests/test_tutorial/test_header_params/test_tutorial001_an.py b/tests/test_tutorial/test_header_params/test_tutorial001_an.py
deleted file mode 100644
index a715228aa2..0000000000
--- a/tests/test_tutorial/test_header_params/test_tutorial001_an.py
+++ /dev/null
@@ -1,102 +0,0 @@
-import pytest
-from dirty_equals import IsDict
-from fastapi.testclient import TestClient
-
-from docs_src.header_params.tutorial001_an import app
-
-client = TestClient(app)
-
-
-@pytest.mark.parametrize(
- "path,headers,expected_status,expected_response",
- [
- ("/items", None, 200, {"User-Agent": "testclient"}),
- ("/items", {"X-Header": "notvalid"}, 200, {"User-Agent": "testclient"}),
- ("/items", {"User-Agent": "FastAPI test"}, 200, {"User-Agent": "FastAPI test"}),
- ],
-)
-def test(path, headers, expected_status, expected_response):
- response = client.get(path, headers=headers)
- assert response.status_code == expected_status
- assert response.json() == expected_response
-
-
-def test_openapi_schema():
- response = client.get("/openapi.json")
- assert response.status_code == 200
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/": {
- "get": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Read Items",
- "operationId": "read_items_items__get",
- "parameters": [
- {
- "required": False,
- "schema": IsDict(
- {
- "anyOf": [{"type": "string"}, {"type": "null"}],
- "title": "User-Agent",
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "User-Agent", "type": "string"}
- ),
- "name": "user-agent",
- "in": "header",
- }
- ],
- }
- }
- },
- "components": {
- "schemas": {
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_header_params/test_tutorial001_an_py310.py b/tests/test_tutorial/test_header_params/test_tutorial001_an_py310.py
deleted file mode 100644
index caf85bc6ca..0000000000
--- a/tests/test_tutorial/test_header_params/test_tutorial001_an_py310.py
+++ /dev/null
@@ -1,110 +0,0 @@
-import pytest
-from dirty_equals import IsDict
-from fastapi.testclient import TestClient
-
-from ...utils import needs_py310
-
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.header_params.tutorial001_an_py310 import app
-
- client = TestClient(app)
- return client
-
-
-@needs_py310
-@pytest.mark.parametrize(
- "path,headers,expected_status,expected_response",
- [
- ("/items", None, 200, {"User-Agent": "testclient"}),
- ("/items", {"X-Header": "notvalid"}, 200, {"User-Agent": "testclient"}),
- ("/items", {"User-Agent": "FastAPI test"}, 200, {"User-Agent": "FastAPI test"}),
- ],
-)
-def test(path, headers, expected_status, expected_response, client: TestClient):
- response = client.get(path, headers=headers)
- assert response.status_code == expected_status
- assert response.json() == expected_response
-
-
-@needs_py310
-def test_openapi_schema(client: TestClient):
- response = client.get("/openapi.json")
- assert response.status_code == 200
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/": {
- "get": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Read Items",
- "operationId": "read_items_items__get",
- "parameters": [
- {
- "required": False,
- "schema": IsDict(
- {
- "anyOf": [{"type": "string"}, {"type": "null"}],
- "title": "User-Agent",
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "User-Agent", "type": "string"}
- ),
- "name": "user-agent",
- "in": "header",
- }
- ],
- }
- }
- },
- "components": {
- "schemas": {
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_header_params/test_tutorial001_py310.py b/tests/test_tutorial/test_header_params/test_tutorial001_py310.py
deleted file mode 100644
index 57e0a296af..0000000000
--- a/tests/test_tutorial/test_header_params/test_tutorial001_py310.py
+++ /dev/null
@@ -1,110 +0,0 @@
-import pytest
-from dirty_equals import IsDict
-from fastapi.testclient import TestClient
-
-from ...utils import needs_py310
-
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.header_params.tutorial001_py310 import app
-
- client = TestClient(app)
- return client
-
-
-@needs_py310
-@pytest.mark.parametrize(
- "path,headers,expected_status,expected_response",
- [
- ("/items", None, 200, {"User-Agent": "testclient"}),
- ("/items", {"X-Header": "notvalid"}, 200, {"User-Agent": "testclient"}),
- ("/items", {"User-Agent": "FastAPI test"}, 200, {"User-Agent": "FastAPI test"}),
- ],
-)
-def test(path, headers, expected_status, expected_response, client: TestClient):
- response = client.get(path, headers=headers)
- assert response.status_code == expected_status
- assert response.json() == expected_response
-
-
-@needs_py310
-def test_openapi_schema(client: TestClient):
- response = client.get("/openapi.json")
- assert response.status_code == 200
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/": {
- "get": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Read Items",
- "operationId": "read_items_items__get",
- "parameters": [
- {
- "required": False,
- "schema": IsDict(
- {
- "anyOf": [{"type": "string"}, {"type": "null"}],
- "title": "User-Agent",
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "User-Agent", "type": "string"}
- ),
- "name": "user-agent",
- "in": "header",
- }
- ],
- }
- }
- },
- "components": {
- "schemas": {
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_header_params/test_tutorial002.py b/tests/test_tutorial/test_header_params/test_tutorial002.py
index 78bac838cf..7158f8651b 100644
--- a/tests/test_tutorial/test_header_params/test_tutorial002.py
+++ b/tests/test_tutorial/test_header_params/test_tutorial002.py
@@ -1,10 +1,27 @@
+import importlib
+
import pytest
from dirty_equals import IsDict
from fastapi.testclient import TestClient
-from docs_src.header_params.tutorial002 import app
+from ...utils import needs_py39, needs_py310
-client = TestClient(app)
+
+@pytest.fixture(
+ name="client",
+ params=[
+ "tutorial002",
+ pytest.param("tutorial002_py310", marks=needs_py310),
+ "tutorial002_an",
+ pytest.param("tutorial002_an_py39", marks=needs_py39),
+ pytest.param("tutorial002_an_py310", marks=needs_py310),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.header_params.{request.param}")
+
+ client = TestClient(mod.app)
+ return client
@pytest.mark.parametrize(
@@ -26,13 +43,13 @@ client = TestClient(app)
),
],
)
-def test(path, headers, expected_status, expected_response):
+def test(path, headers, expected_status, expected_response, client: TestClient):
response = client.get(path, headers=headers)
assert response.status_code == expected_status
assert response.json() == expected_response
-def test_openapi_schema():
+def test_openapi_schema(client: TestClient):
response = client.get("/openapi.json")
assert response.status_code == 200
assert response.json() == {
diff --git a/tests/test_tutorial/test_header_params/test_tutorial002_an.py b/tests/test_tutorial/test_header_params/test_tutorial002_an.py
deleted file mode 100644
index ffda8158fc..0000000000
--- a/tests/test_tutorial/test_header_params/test_tutorial002_an.py
+++ /dev/null
@@ -1,113 +0,0 @@
-import pytest
-from dirty_equals import IsDict
-from fastapi.testclient import TestClient
-
-from docs_src.header_params.tutorial002_an import app
-
-client = TestClient(app)
-
-
-@pytest.mark.parametrize(
- "path,headers,expected_status,expected_response",
- [
- ("/items", None, 200, {"strange_header": None}),
- ("/items", {"X-Header": "notvalid"}, 200, {"strange_header": None}),
- (
- "/items",
- {"strange_header": "FastAPI test"},
- 200,
- {"strange_header": "FastAPI test"},
- ),
- (
- "/items",
- {"strange-header": "Not really underscore"},
- 200,
- {"strange_header": None},
- ),
- ],
-)
-def test(path, headers, expected_status, expected_response):
- response = client.get(path, headers=headers)
- assert response.status_code == expected_status
- assert response.json() == expected_response
-
-
-def test_openapi_schema():
- response = client.get("/openapi.json")
- assert response.status_code == 200
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/": {
- "get": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Read Items",
- "operationId": "read_items_items__get",
- "parameters": [
- {
- "required": False,
- "schema": IsDict(
- {
- "anyOf": [{"type": "string"}, {"type": "null"}],
- "title": "Strange Header",
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Strange Header", "type": "string"}
- ),
- "name": "strange_header",
- "in": "header",
- }
- ],
- }
- }
- },
- "components": {
- "schemas": {
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_header_params/test_tutorial002_an_py310.py b/tests/test_tutorial/test_header_params/test_tutorial002_an_py310.py
deleted file mode 100644
index 6f332f3bac..0000000000
--- a/tests/test_tutorial/test_header_params/test_tutorial002_an_py310.py
+++ /dev/null
@@ -1,121 +0,0 @@
-import pytest
-from dirty_equals import IsDict
-from fastapi.testclient import TestClient
-
-from ...utils import needs_py310
-
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.header_params.tutorial002_an_py310 import app
-
- client = TestClient(app)
- return client
-
-
-@needs_py310
-@pytest.mark.parametrize(
- "path,headers,expected_status,expected_response",
- [
- ("/items", None, 200, {"strange_header": None}),
- ("/items", {"X-Header": "notvalid"}, 200, {"strange_header": None}),
- (
- "/items",
- {"strange_header": "FastAPI test"},
- 200,
- {"strange_header": "FastAPI test"},
- ),
- (
- "/items",
- {"strange-header": "Not really underscore"},
- 200,
- {"strange_header": None},
- ),
- ],
-)
-def test(path, headers, expected_status, expected_response, client: TestClient):
- response = client.get(path, headers=headers)
- assert response.status_code == expected_status
- assert response.json() == expected_response
-
-
-@needs_py310
-def test_openapi_schema(client: TestClient):
- response = client.get("/openapi.json")
- assert response.status_code == 200
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/": {
- "get": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Read Items",
- "operationId": "read_items_items__get",
- "parameters": [
- {
- "required": False,
- "schema": IsDict(
- {
- "anyOf": [{"type": "string"}, {"type": "null"}],
- "title": "Strange Header",
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Strange Header", "type": "string"}
- ),
- "name": "strange_header",
- "in": "header",
- }
- ],
- }
- }
- },
- "components": {
- "schemas": {
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_header_params/test_tutorial002_an_py39.py b/tests/test_tutorial/test_header_params/test_tutorial002_an_py39.py
deleted file mode 100644
index 8202bc671e..0000000000
--- a/tests/test_tutorial/test_header_params/test_tutorial002_an_py39.py
+++ /dev/null
@@ -1,124 +0,0 @@
-import pytest
-from dirty_equals import IsDict
-from fastapi.testclient import TestClient
-
-from ...utils import needs_py39
-
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.header_params.tutorial002_an_py39 import app
-
- client = TestClient(app)
- return client
-
-
-@needs_py39
-@pytest.mark.parametrize(
- "path,headers,expected_status,expected_response",
- [
- ("/items", None, 200, {"strange_header": None}),
- ("/items", {"X-Header": "notvalid"}, 200, {"strange_header": None}),
- (
- "/items",
- {"strange_header": "FastAPI test"},
- 200,
- {"strange_header": "FastAPI test"},
- ),
- (
- "/items",
- {"strange-header": "Not really underscore"},
- 200,
- {"strange_header": None},
- ),
- ],
-)
-def test(path, headers, expected_status, expected_response, client: TestClient):
- response = client.get(path, headers=headers)
- assert response.status_code == expected_status
- assert response.json() == expected_response
-
-
-@needs_py39
-def test_openapi_schema():
- from docs_src.header_params.tutorial002_an_py39 import app
-
- client = TestClient(app)
- response = client.get("/openapi.json")
- assert response.status_code == 200
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/": {
- "get": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Read Items",
- "operationId": "read_items_items__get",
- "parameters": [
- {
- "required": False,
- "schema": IsDict(
- {
- "anyOf": [{"type": "string"}, {"type": "null"}],
- "title": "Strange Header",
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Strange Header", "type": "string"}
- ),
- "name": "strange_header",
- "in": "header",
- }
- ],
- }
- }
- },
- "components": {
- "schemas": {
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_header_params/test_tutorial002_py310.py b/tests/test_tutorial/test_header_params/test_tutorial002_py310.py
deleted file mode 100644
index c113ed23e1..0000000000
--- a/tests/test_tutorial/test_header_params/test_tutorial002_py310.py
+++ /dev/null
@@ -1,124 +0,0 @@
-import pytest
-from dirty_equals import IsDict
-from fastapi.testclient import TestClient
-
-from ...utils import needs_py310
-
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.header_params.tutorial002_py310 import app
-
- client = TestClient(app)
- return client
-
-
-@needs_py310
-@pytest.mark.parametrize(
- "path,headers,expected_status,expected_response",
- [
- ("/items", None, 200, {"strange_header": None}),
- ("/items", {"X-Header": "notvalid"}, 200, {"strange_header": None}),
- (
- "/items",
- {"strange_header": "FastAPI test"},
- 200,
- {"strange_header": "FastAPI test"},
- ),
- (
- "/items",
- {"strange-header": "Not really underscore"},
- 200,
- {"strange_header": None},
- ),
- ],
-)
-def test(path, headers, expected_status, expected_response, client: TestClient):
- response = client.get(path, headers=headers)
- assert response.status_code == expected_status
- assert response.json() == expected_response
-
-
-@needs_py310
-def test_openapi_schema():
- from docs_src.header_params.tutorial002_py310 import app
-
- client = TestClient(app)
- response = client.get("/openapi.json")
- assert response.status_code == 200
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/": {
- "get": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Read Items",
- "operationId": "read_items_items__get",
- "parameters": [
- {
- "required": False,
- "schema": IsDict(
- {
- "anyOf": [{"type": "string"}, {"type": "null"}],
- "title": "Strange Header",
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Strange Header", "type": "string"}
- ),
- "name": "strange_header",
- "in": "header",
- }
- ],
- }
- }
- },
- "components": {
- "schemas": {
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_header_params/test_tutorial003.py b/tests/test_tutorial/test_header_params/test_tutorial003.py
index 6f7de8ed41..473b961236 100644
--- a/tests/test_tutorial/test_header_params/test_tutorial003.py
+++ b/tests/test_tutorial/test_header_params/test_tutorial003.py
@@ -1,10 +1,27 @@
+import importlib
+
import pytest
from dirty_equals import IsDict
from fastapi.testclient import TestClient
-from docs_src.header_params.tutorial003 import app
+from ...utils import needs_py39, needs_py310
-client = TestClient(app)
+
+@pytest.fixture(
+ name="client",
+ params=[
+ "tutorial003",
+ pytest.param("tutorial003_py310", marks=needs_py310),
+ "tutorial003_an",
+ pytest.param("tutorial003_an_py39", marks=needs_py39),
+ pytest.param("tutorial003_an_py310", marks=needs_py310),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.header_params.{request.param}")
+
+ client = TestClient(mod.app)
+ return client
@pytest.mark.parametrize(
@@ -20,13 +37,13 @@ client = TestClient(app)
),
],
)
-def test(path, headers, expected_status, expected_response):
+def test(path, headers, expected_status, expected_response, client: TestClient):
response = client.get(path, headers=headers)
assert response.status_code == expected_status
assert response.json() == expected_response
-def test_openapi_schema():
+def test_openapi_schema(client: TestClient):
response = client.get("/openapi.json")
assert response.status_code == 200
assert response.json() == {
diff --git a/tests/test_tutorial/test_header_params/test_tutorial003_an.py b/tests/test_tutorial/test_header_params/test_tutorial003_an.py
deleted file mode 100644
index 742ed41f48..0000000000
--- a/tests/test_tutorial/test_header_params/test_tutorial003_an.py
+++ /dev/null
@@ -1,110 +0,0 @@
-import pytest
-from dirty_equals import IsDict
-from fastapi.testclient import TestClient
-
-from docs_src.header_params.tutorial003_an import app
-
-client = TestClient(app)
-
-
-@pytest.mark.parametrize(
- "path,headers,expected_status,expected_response",
- [
- ("/items", None, 200, {"X-Token values": None}),
- ("/items", {"x-token": "foo"}, 200, {"X-Token values": ["foo"]}),
- # TODO: fix this, is it a bug?
- # ("/items", [("x-token", "foo"), ("x-token", "bar")], 200, {"X-Token values": ["foo", "bar"]}),
- ],
-)
-def test(path, headers, expected_status, expected_response):
- response = client.get(path, headers=headers)
- assert response.status_code == expected_status
- assert response.json() == expected_response
-
-
-def test_openapi_schema():
- response = client.get("/openapi.json")
- assert response.status_code == 200
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/": {
- "get": {
- "summary": "Read Items",
- "operationId": "read_items_items__get",
- "parameters": [
- {
- "required": False,
- "schema": IsDict(
- {
- "title": "X-Token",
- "anyOf": [
- {"type": "array", "items": {"type": "string"}},
- {"type": "null"},
- ],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "title": "X-Token",
- "type": "array",
- "items": {"type": "string"},
- }
- ),
- "name": "x-token",
- "in": "header",
- }
- ],
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- }
- }
- },
- "components": {
- "schemas": {
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_header_params/test_tutorial003_an_py310.py b/tests/test_tutorial/test_header_params/test_tutorial003_an_py310.py
deleted file mode 100644
index fdac4a416c..0000000000
--- a/tests/test_tutorial/test_header_params/test_tutorial003_an_py310.py
+++ /dev/null
@@ -1,118 +0,0 @@
-import pytest
-from dirty_equals import IsDict
-from fastapi.testclient import TestClient
-
-from ...utils import needs_py310
-
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.header_params.tutorial003_an_py310 import app
-
- client = TestClient(app)
- return client
-
-
-@needs_py310
-@pytest.mark.parametrize(
- "path,headers,expected_status,expected_response",
- [
- ("/items", None, 200, {"X-Token values": None}),
- ("/items", {"x-token": "foo"}, 200, {"X-Token values": ["foo"]}),
- # TODO: fix this, is it a bug?
- # ("/items", [("x-token", "foo"), ("x-token", "bar")], 200, {"X-Token values": ["foo", "bar"]}),
- ],
-)
-def test(path, headers, expected_status, expected_response, client: TestClient):
- response = client.get(path, headers=headers)
- assert response.status_code == expected_status
- assert response.json() == expected_response
-
-
-@needs_py310
-def test_openapi_schema(client: TestClient):
- response = client.get("/openapi.json")
- assert response.status_code == 200
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/": {
- "get": {
- "summary": "Read Items",
- "operationId": "read_items_items__get",
- "parameters": [
- {
- "required": False,
- "schema": IsDict(
- {
- "title": "X-Token",
- "anyOf": [
- {"type": "array", "items": {"type": "string"}},
- {"type": "null"},
- ],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "title": "X-Token",
- "type": "array",
- "items": {"type": "string"},
- }
- ),
- "name": "x-token",
- "in": "header",
- }
- ],
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- }
- }
- },
- "components": {
- "schemas": {
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_header_params/test_tutorial003_an_py39.py b/tests/test_tutorial/test_header_params/test_tutorial003_an_py39.py
deleted file mode 100644
index c50543cc88..0000000000
--- a/tests/test_tutorial/test_header_params/test_tutorial003_an_py39.py
+++ /dev/null
@@ -1,118 +0,0 @@
-import pytest
-from dirty_equals import IsDict
-from fastapi.testclient import TestClient
-
-from ...utils import needs_py39
-
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.header_params.tutorial003_an_py39 import app
-
- client = TestClient(app)
- return client
-
-
-@needs_py39
-@pytest.mark.parametrize(
- "path,headers,expected_status,expected_response",
- [
- ("/items", None, 200, {"X-Token values": None}),
- ("/items", {"x-token": "foo"}, 200, {"X-Token values": ["foo"]}),
- # TODO: fix this, is it a bug?
- # ("/items", [("x-token", "foo"), ("x-token", "bar")], 200, {"X-Token values": ["foo", "bar"]}),
- ],
-)
-def test(path, headers, expected_status, expected_response, client: TestClient):
- response = client.get(path, headers=headers)
- assert response.status_code == expected_status
- assert response.json() == expected_response
-
-
-@needs_py39
-def test_openapi_schema(client: TestClient):
- response = client.get("/openapi.json")
- assert response.status_code == 200
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/": {
- "get": {
- "summary": "Read Items",
- "operationId": "read_items_items__get",
- "parameters": [
- {
- "required": False,
- "schema": IsDict(
- {
- "title": "X-Token",
- "anyOf": [
- {"type": "array", "items": {"type": "string"}},
- {"type": "null"},
- ],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "title": "X-Token",
- "type": "array",
- "items": {"type": "string"},
- }
- ),
- "name": "x-token",
- "in": "header",
- }
- ],
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- }
- }
- },
- "components": {
- "schemas": {
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_header_params/test_tutorial003_py310.py b/tests/test_tutorial/test_header_params/test_tutorial003_py310.py
deleted file mode 100644
index 3afb355e94..0000000000
--- a/tests/test_tutorial/test_header_params/test_tutorial003_py310.py
+++ /dev/null
@@ -1,118 +0,0 @@
-import pytest
-from dirty_equals import IsDict
-from fastapi.testclient import TestClient
-
-from ...utils import needs_py310
-
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.header_params.tutorial003_py310 import app
-
- client = TestClient(app)
- return client
-
-
-@needs_py310
-@pytest.mark.parametrize(
- "path,headers,expected_status,expected_response",
- [
- ("/items", None, 200, {"X-Token values": None}),
- ("/items", {"x-token": "foo"}, 200, {"X-Token values": ["foo"]}),
- # TODO: fix this, is it a bug?
- # ("/items", [("x-token", "foo"), ("x-token", "bar")], 200, {"X-Token values": ["foo", "bar"]}),
- ],
-)
-def test(path, headers, expected_status, expected_response, client: TestClient):
- response = client.get(path, headers=headers)
- assert response.status_code == expected_status
- assert response.json() == expected_response
-
-
-@needs_py310
-def test_openapi_schema(client: TestClient):
- response = client.get("/openapi.json")
- assert response.status_code == 200
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/": {
- "get": {
- "summary": "Read Items",
- "operationId": "read_items_items__get",
- "parameters": [
- {
- "required": False,
- "schema": IsDict(
- {
- "title": "X-Token",
- "anyOf": [
- {"type": "array", "items": {"type": "string"}},
- {"type": "null"},
- ],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "title": "X-Token",
- "type": "array",
- "items": {"type": "string"},
- }
- ),
- "name": "x-token",
- "in": "header",
- }
- ],
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- }
- }
- },
- "components": {
- "schemas": {
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_path_operation_configurations/test_tutorial005.py b/tests/test_tutorial/test_path_operation_configurations/test_tutorial005.py
index d3792e701f..0742f5d0e8 100644
--- a/tests/test_tutorial/test_path_operation_configurations/test_tutorial005.py
+++ b/tests/test_tutorial/test_path_operation_configurations/test_tutorial005.py
@@ -1,13 +1,29 @@
+import importlib
+
+import pytest
from fastapi.testclient import TestClient
-from docs_src.path_operation_configuration.tutorial005 import app
-
-from ...utils import needs_pydanticv1, needs_pydanticv2
-
-client = TestClient(app)
+from ...utils import needs_py39, needs_py310, needs_pydanticv1, needs_pydanticv2
-def test_query_params_str_validations():
+@pytest.fixture(
+ name="client",
+ params=[
+ "tutorial005",
+ pytest.param("tutorial005_py39", marks=needs_py39),
+ pytest.param("tutorial005_py310", marks=needs_py310),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(
+ f"docs_src.path_operation_configuration.{request.param}"
+ )
+
+ client = TestClient(mod.app)
+ return client
+
+
+def test_query_params_str_validations(client: TestClient):
response = client.post("/items/", json={"name": "Foo", "price": 42})
assert response.status_code == 200, response.text
assert response.json() == {
@@ -20,7 +36,7 @@ def test_query_params_str_validations():
@needs_pydanticv2
-def test_openapi_schema():
+def test_openapi_schema(client: TestClient):
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
@@ -123,7 +139,7 @@ def test_openapi_schema():
# TODO: remove when deprecating Pydantic v1
@needs_pydanticv1
-def test_openapi_schema_pv1():
+def test_openapi_schema_pv1(client: TestClient):
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
diff --git a/tests/test_tutorial/test_path_operation_configurations/test_tutorial005_py310.py b/tests/test_tutorial/test_path_operation_configurations/test_tutorial005_py310.py
deleted file mode 100644
index a68deb3df5..0000000000
--- a/tests/test_tutorial/test_path_operation_configurations/test_tutorial005_py310.py
+++ /dev/null
@@ -1,226 +0,0 @@
-import pytest
-from fastapi.testclient import TestClient
-
-from ...utils import needs_py310, needs_pydanticv1, needs_pydanticv2
-
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.path_operation_configuration.tutorial005_py310 import app
-
- client = TestClient(app)
- return client
-
-
-@needs_py310
-def test_query_params_str_validations(client: TestClient):
- response = client.post("/items/", json={"name": "Foo", "price": 42})
- assert response.status_code == 200, response.text
- assert response.json() == {
- "name": "Foo",
- "price": 42,
- "description": None,
- "tax": None,
- "tags": [],
- }
-
-
-@needs_py310
-@needs_pydanticv2
-def test_openapi_schema(client: TestClient):
- response = client.get("/openapi.json")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/": {
- "post": {
- "responses": {
- "200": {
- "description": "The created item",
- "content": {
- "application/json": {
- "schema": {"$ref": "#/components/schemas/Item"}
- }
- },
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Create an item",
- "description": "Create an item with all the information:\n\n- **name**: each item must have a name\n- **description**: a long description\n- **price**: required\n- **tax**: if the item doesn't have tax, you can omit this\n- **tags**: a set of unique tag strings for this item",
- "operationId": "create_item_items__post",
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {"$ref": "#/components/schemas/Item"}
- }
- },
- "required": True,
- },
- }
- }
- },
- "components": {
- "schemas": {
- "Item": {
- "title": "Item",
- "required": ["name", "price"],
- "type": "object",
- "properties": {
- "name": {"title": "Name", "type": "string"},
- "description": {
- "title": "Description",
- "anyOf": [{"type": "string"}, {"type": "null"}],
- },
- "price": {"title": "Price", "type": "number"},
- "tax": {
- "title": "Tax",
- "anyOf": [{"type": "number"}, {"type": "null"}],
- },
- "tags": {
- "title": "Tags",
- "uniqueItems": True,
- "type": "array",
- "items": {"type": "string"},
- "default": [],
- },
- },
- },
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- }
- },
- }
-
-
-# TODO: remove when deprecating Pydantic v1
-@needs_py310
-@needs_pydanticv1
-def test_openapi_schema_pv1(client: TestClient):
- response = client.get("/openapi.json")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/": {
- "post": {
- "responses": {
- "200": {
- "description": "The created item",
- "content": {
- "application/json": {
- "schema": {"$ref": "#/components/schemas/Item"}
- }
- },
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Create an item",
- "description": "Create an item with all the information:\n\n- **name**: each item must have a name\n- **description**: a long description\n- **price**: required\n- **tax**: if the item doesn't have tax, you can omit this\n- **tags**: a set of unique tag strings for this item",
- "operationId": "create_item_items__post",
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {"$ref": "#/components/schemas/Item"}
- }
- },
- "required": True,
- },
- }
- }
- },
- "components": {
- "schemas": {
- "Item": {
- "title": "Item",
- "required": ["name", "price"],
- "type": "object",
- "properties": {
- "name": {"title": "Name", "type": "string"},
- "description": {"title": "Description", "type": "string"},
- "price": {"title": "Price", "type": "number"},
- "tax": {"title": "Tax", "type": "number"},
- "tags": {
- "title": "Tags",
- "uniqueItems": True,
- "type": "array",
- "items": {"type": "string"},
- "default": [],
- },
- },
- },
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_path_operation_configurations/test_tutorial005_py39.py b/tests/test_tutorial/test_path_operation_configurations/test_tutorial005_py39.py
deleted file mode 100644
index e17f2592dc..0000000000
--- a/tests/test_tutorial/test_path_operation_configurations/test_tutorial005_py39.py
+++ /dev/null
@@ -1,226 +0,0 @@
-import pytest
-from fastapi.testclient import TestClient
-
-from ...utils import needs_py39, needs_pydanticv1, needs_pydanticv2
-
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.path_operation_configuration.tutorial005_py39 import app
-
- client = TestClient(app)
- return client
-
-
-@needs_py39
-def test_query_params_str_validations(client: TestClient):
- response = client.post("/items/", json={"name": "Foo", "price": 42})
- assert response.status_code == 200, response.text
- assert response.json() == {
- "name": "Foo",
- "price": 42,
- "description": None,
- "tax": None,
- "tags": [],
- }
-
-
-@needs_py39
-@needs_pydanticv2
-def test_openapi_schema(client: TestClient):
- response = client.get("/openapi.json")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/": {
- "post": {
- "responses": {
- "200": {
- "description": "The created item",
- "content": {
- "application/json": {
- "schema": {"$ref": "#/components/schemas/Item"}
- }
- },
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Create an item",
- "description": "Create an item with all the information:\n\n- **name**: each item must have a name\n- **description**: a long description\n- **price**: required\n- **tax**: if the item doesn't have tax, you can omit this\n- **tags**: a set of unique tag strings for this item",
- "operationId": "create_item_items__post",
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {"$ref": "#/components/schemas/Item"}
- }
- },
- "required": True,
- },
- }
- }
- },
- "components": {
- "schemas": {
- "Item": {
- "title": "Item",
- "required": ["name", "price"],
- "type": "object",
- "properties": {
- "name": {"title": "Name", "type": "string"},
- "description": {
- "title": "Description",
- "anyOf": [{"type": "string"}, {"type": "null"}],
- },
- "price": {"title": "Price", "type": "number"},
- "tax": {
- "title": "Tax",
- "anyOf": [{"type": "number"}, {"type": "null"}],
- },
- "tags": {
- "title": "Tags",
- "uniqueItems": True,
- "type": "array",
- "items": {"type": "string"},
- "default": [],
- },
- },
- },
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- }
- },
- }
-
-
-# TODO: remove when deprecating Pydantic v1
-@needs_py39
-@needs_pydanticv1
-def test_openapi_schema_pv1(client: TestClient):
- response = client.get("/openapi.json")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/": {
- "post": {
- "responses": {
- "200": {
- "description": "The created item",
- "content": {
- "application/json": {
- "schema": {"$ref": "#/components/schemas/Item"}
- }
- },
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Create an item",
- "description": "Create an item with all the information:\n\n- **name**: each item must have a name\n- **description**: a long description\n- **price**: required\n- **tax**: if the item doesn't have tax, you can omit this\n- **tags**: a set of unique tag strings for this item",
- "operationId": "create_item_items__post",
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {"$ref": "#/components/schemas/Item"}
- }
- },
- "required": True,
- },
- }
- }
- },
- "components": {
- "schemas": {
- "Item": {
- "title": "Item",
- "required": ["name", "price"],
- "type": "object",
- "properties": {
- "name": {"title": "Name", "type": "string"},
- "description": {"title": "Description", "type": "string"},
- "price": {"title": "Price", "type": "number"},
- "tax": {"title": "Tax", "type": "number"},
- "tags": {
- "title": "Tags",
- "uniqueItems": True,
- "type": "array",
- "items": {"type": "string"},
- "default": [],
- },
- },
- },
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- }
- },
- }
diff --git a/docs_src/sql_databases/sql_app_py310/tests/__init__.py b/tests/test_tutorial/test_pydantic_v1_in_v2/__init__.py
similarity index 100%
rename from docs_src/sql_databases/sql_app_py310/tests/__init__.py
rename to tests/test_tutorial/test_pydantic_v1_in_v2/__init__.py
diff --git a/tests/test_tutorial/test_pydantic_v1_in_v2/test_tutorial001.py b/tests/test_tutorial/test_pydantic_v1_in_v2/test_tutorial001.py
new file mode 100644
index 0000000000..3075a05f51
--- /dev/null
+++ b/tests/test_tutorial/test_pydantic_v1_in_v2/test_tutorial001.py
@@ -0,0 +1,37 @@
+import sys
+from typing import Any
+
+import pytest
+from fastapi._compat import PYDANTIC_V2
+
+from tests.utils import skip_module_if_py_gte_314
+
+if sys.version_info >= (3, 14):
+ skip_module_if_py_gte_314()
+
+
+if not PYDANTIC_V2:
+ pytest.skip("This test is only for Pydantic v2", allow_module_level=True)
+
+import importlib
+
+import pytest
+
+from ...utils import needs_py310
+
+
+@pytest.fixture(
+ name="mod",
+ params=[
+ "tutorial001_an",
+ pytest.param("tutorial001_an_py310", marks=needs_py310),
+ ],
+)
+def get_mod(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.pydantic_v1_in_v2.{request.param}")
+ return mod
+
+
+def test_model(mod: Any):
+ item = mod.Item(name="Foo", size=3.4)
+ assert item.dict() == {"name": "Foo", "description": None, "size": 3.4}
diff --git a/tests/test_tutorial/test_pydantic_v1_in_v2/test_tutorial002.py b/tests/test_tutorial/test_pydantic_v1_in_v2/test_tutorial002.py
new file mode 100644
index 0000000000..a402c663d1
--- /dev/null
+++ b/tests/test_tutorial/test_pydantic_v1_in_v2/test_tutorial002.py
@@ -0,0 +1,140 @@
+import sys
+
+import pytest
+from fastapi._compat import PYDANTIC_V2
+from inline_snapshot import snapshot
+
+from tests.utils import skip_module_if_py_gte_314
+
+if sys.version_info >= (3, 14):
+ skip_module_if_py_gte_314()
+
+
+if not PYDANTIC_V2:
+ pytest.skip("This test is only for Pydantic v2", allow_module_level=True)
+
+import importlib
+
+import pytest
+from fastapi.testclient import TestClient
+
+from ...utils import needs_py310
+
+
+@pytest.fixture(
+ name="client",
+ params=[
+ "tutorial002_an",
+ pytest.param("tutorial002_an_py310", marks=needs_py310),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.pydantic_v1_in_v2.{request.param}")
+
+ c = TestClient(mod.app)
+ return c
+
+
+def test_call(client: TestClient):
+ response = client.post("/items/", json={"name": "Foo", "size": 3.4})
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "name": "Foo",
+ "description": None,
+ "size": 3.4,
+ }
+
+
+def test_openapi_schema(client: TestClient):
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == snapshot(
+ {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/items/": {
+ "post": {
+ "summary": "Create Item",
+ "operationId": "create_item_items__post",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "allOf": [
+ {"$ref": "#/components/schemas/Item"}
+ ],
+ "title": "Item",
+ }
+ }
+ },
+ "required": True,
+ },
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {"$ref": "#/components/schemas/Item"}
+ }
+ },
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "HTTPValidationError": {
+ "properties": {
+ "detail": {
+ "items": {
+ "$ref": "#/components/schemas/ValidationError"
+ },
+ "type": "array",
+ "title": "Detail",
+ }
+ },
+ "type": "object",
+ "title": "HTTPValidationError",
+ },
+ "Item": {
+ "properties": {
+ "name": {"type": "string", "title": "Name"},
+ "description": {"type": "string", "title": "Description"},
+ "size": {"type": "number", "title": "Size"},
+ },
+ "type": "object",
+ "required": ["name", "size"],
+ "title": "Item",
+ },
+ "ValidationError": {
+ "properties": {
+ "loc": {
+ "items": {
+ "anyOf": [{"type": "string"}, {"type": "integer"}]
+ },
+ "type": "array",
+ "title": "Location",
+ },
+ "msg": {"type": "string", "title": "Message"},
+ "type": {"type": "string", "title": "Error Type"},
+ },
+ "type": "object",
+ "required": ["loc", "msg", "type"],
+ "title": "ValidationError",
+ },
+ }
+ },
+ }
+ )
diff --git a/tests/test_tutorial/test_pydantic_v1_in_v2/test_tutorial003.py b/tests/test_tutorial/test_pydantic_v1_in_v2/test_tutorial003.py
new file mode 100644
index 0000000000..03155c9249
--- /dev/null
+++ b/tests/test_tutorial/test_pydantic_v1_in_v2/test_tutorial003.py
@@ -0,0 +1,154 @@
+import sys
+
+import pytest
+from fastapi._compat import PYDANTIC_V2
+from inline_snapshot import snapshot
+
+from tests.utils import skip_module_if_py_gte_314
+
+if sys.version_info >= (3, 14):
+ skip_module_if_py_gte_314()
+
+if not PYDANTIC_V2:
+ pytest.skip("This test is only for Pydantic v2", allow_module_level=True)
+
+
+import importlib
+
+from fastapi.testclient import TestClient
+
+from ...utils import needs_py310
+
+
+@pytest.fixture(
+ name="client",
+ params=[
+ "tutorial003_an",
+ pytest.param("tutorial003_an_py310", marks=needs_py310),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.pydantic_v1_in_v2.{request.param}")
+
+ c = TestClient(mod.app)
+ return c
+
+
+def test_call(client: TestClient):
+ response = client.post("/items/", json={"name": "Foo", "size": 3.4})
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "name": "Foo",
+ "description": None,
+ "size": 3.4,
+ }
+
+
+def test_openapi_schema(client: TestClient):
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == snapshot(
+ {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/items/": {
+ "post": {
+ "summary": "Create Item",
+ "operationId": "create_item_items__post",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "allOf": [
+ {"$ref": "#/components/schemas/Item"}
+ ],
+ "title": "Item",
+ }
+ }
+ },
+ "required": True,
+ },
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ItemV2"
+ }
+ }
+ },
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "HTTPValidationError": {
+ "properties": {
+ "detail": {
+ "items": {
+ "$ref": "#/components/schemas/ValidationError"
+ },
+ "type": "array",
+ "title": "Detail",
+ }
+ },
+ "type": "object",
+ "title": "HTTPValidationError",
+ },
+ "Item": {
+ "properties": {
+ "name": {"type": "string", "title": "Name"},
+ "description": {"type": "string", "title": "Description"},
+ "size": {"type": "number", "title": "Size"},
+ },
+ "type": "object",
+ "required": ["name", "size"],
+ "title": "Item",
+ },
+ "ItemV2": {
+ "properties": {
+ "name": {"type": "string", "title": "Name"},
+ "description": {
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ "title": "Description",
+ },
+ "size": {"type": "number", "title": "Size"},
+ },
+ "type": "object",
+ "required": ["name", "size"],
+ "title": "ItemV2",
+ },
+ "ValidationError": {
+ "properties": {
+ "loc": {
+ "items": {
+ "anyOf": [{"type": "string"}, {"type": "integer"}]
+ },
+ "type": "array",
+ "title": "Location",
+ },
+ "msg": {"type": "string", "title": "Message"},
+ "type": {"type": "string", "title": "Error Type"},
+ },
+ "type": "object",
+ "required": ["loc", "msg", "type"],
+ "title": "ValidationError",
+ },
+ }
+ },
+ }
+ )
diff --git a/tests/test_tutorial/test_async_sql_databases/test_tutorial001.py b/tests/test_tutorial/test_pydantic_v1_in_v2/test_tutorial004.py
similarity index 51%
rename from tests/test_tutorial/test_async_sql_databases/test_tutorial001.py
rename to tests/test_tutorial/test_pydantic_v1_in_v2/test_tutorial004.py
index 13568a5328..d2e204ddaf 100644
--- a/tests/test_tutorial/test_async_sql_databases/test_tutorial001.py
+++ b/tests/test_tutorial/test_pydantic_v1_in_v2/test_tutorial004.py
@@ -1,69 +1,83 @@
+import sys
+
import pytest
-from fastapi import FastAPI
+from fastapi._compat import PYDANTIC_V2
+from inline_snapshot import snapshot
+
+from tests.utils import skip_module_if_py_gte_314
+
+if sys.version_info >= (3, 14):
+ skip_module_if_py_gte_314()
+
+if not PYDANTIC_V2:
+ pytest.skip("This test is only for Pydantic v2", allow_module_level=True)
+
+
+import importlib
+
from fastapi.testclient import TestClient
-from ...utils import needs_pydanticv1
+from ...utils import needs_py39, needs_py310
-@pytest.fixture(name="app", scope="module")
-def get_app():
- with pytest.warns(DeprecationWarning):
- from docs_src.async_sql_databases.tutorial001 import app
- yield app
+@pytest.fixture(
+ name="client",
+ params=[
+ "tutorial004_an",
+ pytest.param("tutorial004_an_py39", marks=needs_py39),
+ pytest.param("tutorial004_an_py310", marks=needs_py310),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.pydantic_v1_in_v2.{request.param}")
+
+ c = TestClient(mod.app)
+ return c
-# TODO: pv2 add version with Pydantic v2
-@needs_pydanticv1
-def test_create_read(app: FastAPI):
- with TestClient(app) as client:
- note = {"text": "Foo bar", "completed": False}
- response = client.post("/notes/", json=note)
- assert response.status_code == 200, response.text
- data = response.json()
- assert data["text"] == note["text"]
- assert data["completed"] == note["completed"]
- assert "id" in data
- response = client.get("/notes/")
- assert response.status_code == 200, response.text
- assert data in response.json()
+def test_call(client: TestClient):
+ response = client.post("/items/", json={"item": {"name": "Foo", "size": 3.4}})
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "name": "Foo",
+ "description": None,
+ "size": 3.4,
+ }
-def test_openapi_schema(app: FastAPI):
- with TestClient(app) as client:
- response = client.get("/openapi.json")
- assert response.status_code == 200, response.text
- assert response.json() == {
+def test_openapi_schema(client: TestClient):
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == snapshot(
+ {
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
- "/notes/": {
- "get": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {
- "application/json": {
- "schema": {
- "title": "Response Read Notes Notes Get",
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/Note"
- },
- }
- }
- },
- }
- },
- "summary": "Read Notes",
- "operationId": "read_notes_notes__get",
- },
+ "/items/": {
"post": {
+ "summary": "Create Item",
+ "operationId": "create_item_items__post",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/Body_create_item_items__post"
+ }
+ ],
+ "title": "Body",
+ }
+ }
+ },
+ "required": True,
+ },
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
- "schema": {"$ref": "#/components/schemas/Note"}
+ "schema": {"$ref": "#/components/schemas/Item"}
}
},
},
@@ -78,69 +92,62 @@ def test_openapi_schema(app: FastAPI):
},
},
},
- "summary": "Create Note",
- "operationId": "create_note_notes__post",
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {"$ref": "#/components/schemas/NoteIn"}
- }
- },
- "required": True,
- },
- },
+ }
}
},
"components": {
"schemas": {
- "NoteIn": {
- "title": "NoteIn",
- "required": ["text", "completed"],
- "type": "object",
+ "Body_create_item_items__post": {
"properties": {
- "text": {"title": "Text", "type": "string"},
- "completed": {"title": "Completed", "type": "boolean"},
+ "item": {
+ "allOf": [{"$ref": "#/components/schemas/Item"}],
+ "title": "Item",
+ }
},
- },
- "Note": {
- "title": "Note",
- "required": ["id", "text", "completed"],
"type": "object",
- "properties": {
- "id": {"title": "Id", "type": "integer"},
- "text": {"title": "Text", "type": "string"},
- "completed": {"title": "Completed", "type": "boolean"},
- },
- },
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
+ "required": ["item"],
+ "title": "Body_create_item_items__post",
},
"HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
"properties": {
"detail": {
- "title": "Detail",
- "type": "array",
"items": {
"$ref": "#/components/schemas/ValidationError"
},
+ "type": "array",
+ "title": "Detail",
}
},
+ "type": "object",
+ "title": "HTTPValidationError",
+ },
+ "Item": {
+ "properties": {
+ "name": {"type": "string", "title": "Name"},
+ "description": {"type": "string", "title": "Description"},
+ "size": {"type": "number", "title": "Size"},
+ },
+ "type": "object",
+ "required": ["name", "size"],
+ "title": "Item",
+ },
+ "ValidationError": {
+ "properties": {
+ "loc": {
+ "items": {
+ "anyOf": [{"type": "string"}, {"type": "integer"}]
+ },
+ "type": "array",
+ "title": "Location",
+ },
+ "msg": {"type": "string", "title": "Message"},
+ "type": {"type": "string", "title": "Error Type"},
+ },
+ "type": "object",
+ "required": ["loc", "msg", "type"],
+ "title": "ValidationError",
},
}
},
}
+ )
diff --git a/docs_src/sql_databases/sql_app_py39/__init__.py b/tests/test_tutorial/test_query_param_models/__init__.py
similarity index 100%
rename from docs_src/sql_databases/sql_app_py39/__init__.py
rename to tests/test_tutorial/test_query_param_models/__init__.py
diff --git a/tests/test_tutorial/test_query_param_models/test_tutorial001.py b/tests/test_tutorial/test_query_param_models/test_tutorial001.py
new file mode 100644
index 0000000000..5b7bc7b424
--- /dev/null
+++ b/tests/test_tutorial/test_query_param_models/test_tutorial001.py
@@ -0,0 +1,260 @@
+import importlib
+
+import pytest
+from dirty_equals import IsDict
+from fastapi.testclient import TestClient
+from inline_snapshot import snapshot
+
+from tests.utils import needs_py39, needs_py310
+
+
+@pytest.fixture(
+ name="client",
+ params=[
+ "tutorial001",
+ pytest.param("tutorial001_py39", marks=needs_py39),
+ pytest.param("tutorial001_py310", marks=needs_py310),
+ "tutorial001_an",
+ pytest.param("tutorial001_an_py39", marks=needs_py39),
+ pytest.param("tutorial001_an_py310", marks=needs_py310),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.query_param_models.{request.param}")
+
+ client = TestClient(mod.app)
+ return client
+
+
+def test_query_param_model(client: TestClient):
+ response = client.get(
+ "/items/",
+ params={
+ "limit": 10,
+ "offset": 5,
+ "order_by": "updated_at",
+ "tags": ["tag1", "tag2"],
+ },
+ )
+ assert response.status_code == 200
+ assert response.json() == {
+ "limit": 10,
+ "offset": 5,
+ "order_by": "updated_at",
+ "tags": ["tag1", "tag2"],
+ }
+
+
+def test_query_param_model_defaults(client: TestClient):
+ response = client.get("/items/")
+ assert response.status_code == 200
+ assert response.json() == {
+ "limit": 100,
+ "offset": 0,
+ "order_by": "created_at",
+ "tags": [],
+ }
+
+
+def test_query_param_model_invalid(client: TestClient):
+ response = client.get(
+ "/items/",
+ params={
+ "limit": 150,
+ "offset": -1,
+ "order_by": "invalid",
+ },
+ )
+ assert response.status_code == 422
+ assert response.json() == snapshot(
+ IsDict(
+ {
+ "detail": [
+ {
+ "type": "less_than_equal",
+ "loc": ["query", "limit"],
+ "msg": "Input should be less than or equal to 100",
+ "input": "150",
+ "ctx": {"le": 100},
+ },
+ {
+ "type": "greater_than_equal",
+ "loc": ["query", "offset"],
+ "msg": "Input should be greater than or equal to 0",
+ "input": "-1",
+ "ctx": {"ge": 0},
+ },
+ {
+ "type": "literal_error",
+ "loc": ["query", "order_by"],
+ "msg": "Input should be 'created_at' or 'updated_at'",
+ "input": "invalid",
+ "ctx": {"expected": "'created_at' or 'updated_at'"},
+ },
+ ]
+ }
+ )
+ | IsDict(
+ # TODO: remove when deprecating Pydantic v1
+ {
+ "detail": [
+ {
+ "type": "value_error.number.not_le",
+ "loc": ["query", "limit"],
+ "msg": "ensure this value is less than or equal to 100",
+ "ctx": {"limit_value": 100},
+ },
+ {
+ "type": "value_error.number.not_ge",
+ "loc": ["query", "offset"],
+ "msg": "ensure this value is greater than or equal to 0",
+ "ctx": {"limit_value": 0},
+ },
+ {
+ "type": "value_error.const",
+ "loc": ["query", "order_by"],
+ "msg": "unexpected value; permitted: 'created_at', 'updated_at'",
+ "ctx": {
+ "given": "invalid",
+ "permitted": ["created_at", "updated_at"],
+ },
+ },
+ ]
+ }
+ )
+ )
+
+
+def test_query_param_model_extra(client: TestClient):
+ response = client.get(
+ "/items/",
+ params={
+ "limit": 10,
+ "offset": 5,
+ "order_by": "updated_at",
+ "tags": ["tag1", "tag2"],
+ "tool": "plumbus",
+ },
+ )
+ assert response.status_code == 200
+ assert response.json() == {
+ "limit": 10,
+ "offset": 5,
+ "order_by": "updated_at",
+ "tags": ["tag1", "tag2"],
+ }
+
+
+def test_openapi_schema(client: TestClient):
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == snapshot(
+ {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/items/": {
+ "get": {
+ "summary": "Read Items",
+ "operationId": "read_items_items__get",
+ "parameters": [
+ {
+ "name": "limit",
+ "in": "query",
+ "required": False,
+ "schema": {
+ "type": "integer",
+ "maximum": 100,
+ "exclusiveMinimum": 0,
+ "default": 100,
+ "title": "Limit",
+ },
+ },
+ {
+ "name": "offset",
+ "in": "query",
+ "required": False,
+ "schema": {
+ "type": "integer",
+ "minimum": 0,
+ "default": 0,
+ "title": "Offset",
+ },
+ },
+ {
+ "name": "order_by",
+ "in": "query",
+ "required": False,
+ "schema": {
+ "enum": ["created_at", "updated_at"],
+ "type": "string",
+ "default": "created_at",
+ "title": "Order By",
+ },
+ },
+ {
+ "name": "tags",
+ "in": "query",
+ "required": False,
+ "schema": {
+ "type": "array",
+ "items": {"type": "string"},
+ "default": [],
+ "title": "Tags",
+ },
+ },
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "HTTPValidationError": {
+ "properties": {
+ "detail": {
+ "items": {
+ "$ref": "#/components/schemas/ValidationError"
+ },
+ "type": "array",
+ "title": "Detail",
+ }
+ },
+ "type": "object",
+ "title": "HTTPValidationError",
+ },
+ "ValidationError": {
+ "properties": {
+ "loc": {
+ "items": {
+ "anyOf": [{"type": "string"}, {"type": "integer"}]
+ },
+ "type": "array",
+ "title": "Location",
+ },
+ "msg": {"type": "string", "title": "Message"},
+ "type": {"type": "string", "title": "Error Type"},
+ },
+ "type": "object",
+ "required": ["loc", "msg", "type"],
+ "title": "ValidationError",
+ },
+ }
+ },
+ }
+ )
diff --git a/tests/test_tutorial/test_query_param_models/test_tutorial002.py b/tests/test_tutorial/test_query_param_models/test_tutorial002.py
new file mode 100644
index 0000000000..4432c9d8a3
--- /dev/null
+++ b/tests/test_tutorial/test_query_param_models/test_tutorial002.py
@@ -0,0 +1,282 @@
+import importlib
+
+import pytest
+from dirty_equals import IsDict
+from fastapi.testclient import TestClient
+from inline_snapshot import snapshot
+
+from tests.utils import needs_py39, needs_py310, needs_pydanticv1, needs_pydanticv2
+
+
+@pytest.fixture(
+ name="client",
+ params=[
+ pytest.param("tutorial002", marks=needs_pydanticv2),
+ pytest.param("tutorial002_py39", marks=[needs_py39, needs_pydanticv2]),
+ pytest.param("tutorial002_py310", marks=[needs_py310, needs_pydanticv2]),
+ pytest.param("tutorial002_an", marks=needs_pydanticv2),
+ pytest.param("tutorial002_an_py39", marks=[needs_py39, needs_pydanticv2]),
+ pytest.param("tutorial002_an_py310", marks=[needs_py310, needs_pydanticv2]),
+ pytest.param("tutorial002_pv1", marks=[needs_pydanticv1, needs_pydanticv1]),
+ pytest.param("tutorial002_pv1_py39", marks=[needs_py39, needs_pydanticv1]),
+ pytest.param("tutorial002_pv1_py310", marks=[needs_py310, needs_pydanticv1]),
+ pytest.param("tutorial002_pv1_an", marks=[needs_pydanticv1]),
+ pytest.param("tutorial002_pv1_an_py39", marks=[needs_py39, needs_pydanticv1]),
+ pytest.param("tutorial002_pv1_an_py310", marks=[needs_py310, needs_pydanticv1]),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.query_param_models.{request.param}")
+
+ client = TestClient(mod.app)
+ return client
+
+
+def test_query_param_model(client: TestClient):
+ response = client.get(
+ "/items/",
+ params={
+ "limit": 10,
+ "offset": 5,
+ "order_by": "updated_at",
+ "tags": ["tag1", "tag2"],
+ },
+ )
+ assert response.status_code == 200
+ assert response.json() == {
+ "limit": 10,
+ "offset": 5,
+ "order_by": "updated_at",
+ "tags": ["tag1", "tag2"],
+ }
+
+
+def test_query_param_model_defaults(client: TestClient):
+ response = client.get("/items/")
+ assert response.status_code == 200
+ assert response.json() == {
+ "limit": 100,
+ "offset": 0,
+ "order_by": "created_at",
+ "tags": [],
+ }
+
+
+def test_query_param_model_invalid(client: TestClient):
+ response = client.get(
+ "/items/",
+ params={
+ "limit": 150,
+ "offset": -1,
+ "order_by": "invalid",
+ },
+ )
+ assert response.status_code == 422
+ assert response.json() == snapshot(
+ IsDict(
+ {
+ "detail": [
+ {
+ "type": "less_than_equal",
+ "loc": ["query", "limit"],
+ "msg": "Input should be less than or equal to 100",
+ "input": "150",
+ "ctx": {"le": 100},
+ },
+ {
+ "type": "greater_than_equal",
+ "loc": ["query", "offset"],
+ "msg": "Input should be greater than or equal to 0",
+ "input": "-1",
+ "ctx": {"ge": 0},
+ },
+ {
+ "type": "literal_error",
+ "loc": ["query", "order_by"],
+ "msg": "Input should be 'created_at' or 'updated_at'",
+ "input": "invalid",
+ "ctx": {"expected": "'created_at' or 'updated_at'"},
+ },
+ ]
+ }
+ )
+ | IsDict(
+ # TODO: remove when deprecating Pydantic v1
+ {
+ "detail": [
+ {
+ "type": "value_error.number.not_le",
+ "loc": ["query", "limit"],
+ "msg": "ensure this value is less than or equal to 100",
+ "ctx": {"limit_value": 100},
+ },
+ {
+ "type": "value_error.number.not_ge",
+ "loc": ["query", "offset"],
+ "msg": "ensure this value is greater than or equal to 0",
+ "ctx": {"limit_value": 0},
+ },
+ {
+ "type": "value_error.const",
+ "loc": ["query", "order_by"],
+ "msg": "unexpected value; permitted: 'created_at', 'updated_at'",
+ "ctx": {
+ "given": "invalid",
+ "permitted": ["created_at", "updated_at"],
+ },
+ },
+ ]
+ }
+ )
+ )
+
+
+def test_query_param_model_extra(client: TestClient):
+ response = client.get(
+ "/items/",
+ params={
+ "limit": 10,
+ "offset": 5,
+ "order_by": "updated_at",
+ "tags": ["tag1", "tag2"],
+ "tool": "plumbus",
+ },
+ )
+ assert response.status_code == 422
+ assert response.json() == snapshot(
+ {
+ "detail": [
+ IsDict(
+ {
+ "type": "extra_forbidden",
+ "loc": ["query", "tool"],
+ "msg": "Extra inputs are not permitted",
+ "input": "plumbus",
+ }
+ )
+ | IsDict(
+ # TODO: remove when deprecating Pydantic v1
+ {
+ "type": "value_error.extra",
+ "loc": ["query", "tool"],
+ "msg": "extra fields not permitted",
+ }
+ )
+ ]
+ }
+ )
+
+
+def test_openapi_schema(client: TestClient):
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == snapshot(
+ {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/items/": {
+ "get": {
+ "summary": "Read Items",
+ "operationId": "read_items_items__get",
+ "parameters": [
+ {
+ "name": "limit",
+ "in": "query",
+ "required": False,
+ "schema": {
+ "type": "integer",
+ "maximum": 100,
+ "exclusiveMinimum": 0,
+ "default": 100,
+ "title": "Limit",
+ },
+ },
+ {
+ "name": "offset",
+ "in": "query",
+ "required": False,
+ "schema": {
+ "type": "integer",
+ "minimum": 0,
+ "default": 0,
+ "title": "Offset",
+ },
+ },
+ {
+ "name": "order_by",
+ "in": "query",
+ "required": False,
+ "schema": {
+ "enum": ["created_at", "updated_at"],
+ "type": "string",
+ "default": "created_at",
+ "title": "Order By",
+ },
+ },
+ {
+ "name": "tags",
+ "in": "query",
+ "required": False,
+ "schema": {
+ "type": "array",
+ "items": {"type": "string"},
+ "default": [],
+ "title": "Tags",
+ },
+ },
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "HTTPValidationError": {
+ "properties": {
+ "detail": {
+ "items": {
+ "$ref": "#/components/schemas/ValidationError"
+ },
+ "type": "array",
+ "title": "Detail",
+ }
+ },
+ "type": "object",
+ "title": "HTTPValidationError",
+ },
+ "ValidationError": {
+ "properties": {
+ "loc": {
+ "items": {
+ "anyOf": [{"type": "string"}, {"type": "integer"}]
+ },
+ "type": "array",
+ "title": "Location",
+ },
+ "msg": {"type": "string", "title": "Message"},
+ "type": {"type": "string", "title": "Error Type"},
+ },
+ "type": "object",
+ "required": ["loc", "msg", "type"],
+ "title": "ValidationError",
+ },
+ }
+ },
+ }
+ )
diff --git a/tests/test_tutorial/test_query_params/test_tutorial006.py b/tests/test_tutorial/test_query_params/test_tutorial006.py
index dbd63da160..a0b5ef4943 100644
--- a/tests/test_tutorial/test_query_params/test_tutorial006.py
+++ b/tests/test_tutorial/test_query_params/test_tutorial006.py
@@ -1,13 +1,23 @@
+import importlib
+
import pytest
from dirty_equals import IsDict
from fastapi.testclient import TestClient
+from ...utils import needs_py310
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.query_params.tutorial006 import app
- c = TestClient(app)
+@pytest.fixture(
+ name="client",
+ params=[
+ "tutorial006",
+ pytest.param("tutorial006_py310", marks=needs_py310),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.query_params.{request.param}")
+
+ c = TestClient(mod.app)
return c
diff --git a/tests/test_tutorial/test_query_params/test_tutorial006_py310.py b/tests/test_tutorial/test_query_params/test_tutorial006_py310.py
deleted file mode 100644
index 5055e38052..0000000000
--- a/tests/test_tutorial/test_query_params/test_tutorial006_py310.py
+++ /dev/null
@@ -1,180 +0,0 @@
-import pytest
-from dirty_equals import IsDict
-from fastapi.testclient import TestClient
-
-from ...utils import needs_py310
-
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.query_params.tutorial006_py310 import app
-
- c = TestClient(app)
- return c
-
-
-@needs_py310
-def test_foo_needy_very(client: TestClient):
- response = client.get("/items/foo?needy=very")
- assert response.status_code == 200
- assert response.json() == {
- "item_id": "foo",
- "needy": "very",
- "skip": 0,
- "limit": None,
- }
-
-
-@needs_py310
-def test_foo_no_needy(client: TestClient):
- response = client.get("/items/foo?skip=a&limit=b")
- assert response.status_code == 422
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "missing",
- "loc": ["query", "needy"],
- "msg": "Field required",
- "input": None,
- },
- {
- "type": "int_parsing",
- "loc": ["query", "skip"],
- "msg": "Input should be a valid integer, unable to parse string as an integer",
- "input": "a",
- },
- {
- "type": "int_parsing",
- "loc": ["query", "limit"],
- "msg": "Input should be a valid integer, unable to parse string as an integer",
- "input": "b",
- },
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "loc": ["query", "needy"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- {
- "loc": ["query", "skip"],
- "msg": "value is not a valid integer",
- "type": "type_error.integer",
- },
- {
- "loc": ["query", "limit"],
- "msg": "value is not a valid integer",
- "type": "type_error.integer",
- },
- ]
- }
- )
-
-
-@needs_py310
-def test_openapi_schema(client: TestClient):
- response = client.get("/openapi.json")
- assert response.status_code == 200
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/{item_id}": {
- "get": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Read User Item",
- "operationId": "read_user_item_items__item_id__get",
- "parameters": [
- {
- "required": True,
- "schema": {"title": "Item Id", "type": "string"},
- "name": "item_id",
- "in": "path",
- },
- {
- "required": True,
- "schema": {"title": "Needy", "type": "string"},
- "name": "needy",
- "in": "query",
- },
- {
- "required": False,
- "schema": {
- "title": "Skip",
- "type": "integer",
- "default": 0,
- },
- "name": "skip",
- "in": "query",
- },
- {
- "required": False,
- "schema": IsDict(
- {
- "anyOf": [{"type": "integer"}, {"type": "null"}],
- "title": "Limit",
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Limit", "type": "integer"}
- ),
- "name": "limit",
- "in": "query",
- },
- ],
- }
- }
- },
- "components": {
- "schemas": {
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_query_params_str_validations/test_tutorial010.py b/tests/test_tutorial/test_query_params_str_validations/test_tutorial010.py
index 945cee3d26..e08e169633 100644
--- a/tests/test_tutorial/test_query_params_str_validations/test_tutorial010.py
+++ b/tests/test_tutorial/test_query_params_str_validations/test_tutorial010.py
@@ -1,13 +1,29 @@
+import importlib
+
import pytest
from dirty_equals import IsDict
+from fastapi._compat import PYDANTIC_VERSION_MINOR_TUPLE
from fastapi.testclient import TestClient
+from ...utils import needs_py39, needs_py310
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.query_params_str_validations.tutorial010 import app
- client = TestClient(app)
+@pytest.fixture(
+ name="client",
+ params=[
+ "tutorial010",
+ pytest.param("tutorial010_py310", marks=needs_py310),
+ "tutorial010_an",
+ pytest.param("tutorial010_an_py39", marks=needs_py39),
+ pytest.param("tutorial010_an_py310", marks=needs_py310),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(
+ f"docs_src.query_params_str_validations.{request.param}"
+ )
+
+ client = TestClient(mod.app)
return client
@@ -107,6 +123,12 @@ def test_openapi_schema(client: TestClient):
],
"title": "Query string",
"description": "Query string for the items to search in the database that have a good match",
+ # See https://github.com/pydantic/pydantic/blob/80353c29a824c55dea4667b328ba8f329879ac9f/tests/test_fastapi.sh#L25-L34.
+ **(
+ {"deprecated": True}
+ if PYDANTIC_VERSION_MINOR_TUPLE >= (2, 10)
+ else {}
+ ),
}
)
| IsDict(
diff --git a/tests/test_tutorial/test_query_params_str_validations/test_tutorial010_an.py b/tests/test_tutorial/test_query_params_str_validations/test_tutorial010_an.py
deleted file mode 100644
index 23951a9aa3..0000000000
--- a/tests/test_tutorial/test_query_params_str_validations/test_tutorial010_an.py
+++ /dev/null
@@ -1,161 +0,0 @@
-import pytest
-from dirty_equals import IsDict
-from fastapi.testclient import TestClient
-
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.query_params_str_validations.tutorial010_an import app
-
- client = TestClient(app)
- return client
-
-
-def test_query_params_str_validations_no_query(client: TestClient):
- response = client.get("/items/")
- assert response.status_code == 200
- assert response.json() == {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
-
-
-def test_query_params_str_validations_item_query_fixedquery(client: TestClient):
- response = client.get("/items/", params={"item-query": "fixedquery"})
- assert response.status_code == 200
- assert response.json() == {
- "items": [{"item_id": "Foo"}, {"item_id": "Bar"}],
- "q": "fixedquery",
- }
-
-
-def test_query_params_str_validations_q_fixedquery(client: TestClient):
- response = client.get("/items/", params={"q": "fixedquery"})
- assert response.status_code == 200
- assert response.json() == {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
-
-
-def test_query_params_str_validations_item_query_nonregexquery(client: TestClient):
- response = client.get("/items/", params={"item-query": "nonregexquery"})
- assert response.status_code == 422
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "string_pattern_mismatch",
- "loc": ["query", "item-query"],
- "msg": "String should match pattern '^fixedquery$'",
- "input": "nonregexquery",
- "ctx": {"pattern": "^fixedquery$"},
- }
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "ctx": {"pattern": "^fixedquery$"},
- "loc": ["query", "item-query"],
- "msg": 'string does not match regex "^fixedquery$"',
- "type": "value_error.str.regex",
- }
- ]
- }
- )
-
-
-def test_openapi_schema(client: TestClient):
- response = client.get("/openapi.json")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/": {
- "get": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Read Items",
- "operationId": "read_items_items__get",
- "parameters": [
- {
- "description": "Query string for the items to search in the database that have a good match",
- "required": False,
- "deprecated": True,
- "schema": IsDict(
- {
- "anyOf": [
- {
- "type": "string",
- "minLength": 3,
- "maxLength": 50,
- "pattern": "^fixedquery$",
- },
- {"type": "null"},
- ],
- "title": "Query string",
- "description": "Query string for the items to search in the database that have a good match",
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "title": "Query string",
- "maxLength": 50,
- "minLength": 3,
- "pattern": "^fixedquery$",
- "type": "string",
- "description": "Query string for the items to search in the database that have a good match",
- }
- ),
- "name": "item-query",
- "in": "query",
- }
- ],
- }
- }
- },
- "components": {
- "schemas": {
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_query_params_str_validations/test_tutorial010_an_py310.py b/tests/test_tutorial/test_query_params_str_validations/test_tutorial010_an_py310.py
deleted file mode 100644
index 2968af563c..0000000000
--- a/tests/test_tutorial/test_query_params_str_validations/test_tutorial010_an_py310.py
+++ /dev/null
@@ -1,168 +0,0 @@
-import pytest
-from dirty_equals import IsDict
-from fastapi.testclient import TestClient
-
-from ...utils import needs_py310
-
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.query_params_str_validations.tutorial010_an_py310 import app
-
- client = TestClient(app)
- return client
-
-
-@needs_py310
-def test_query_params_str_validations_no_query(client: TestClient):
- response = client.get("/items/")
- assert response.status_code == 200
- assert response.json() == {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
-
-
-@needs_py310
-def test_query_params_str_validations_item_query_fixedquery(client: TestClient):
- response = client.get("/items/", params={"item-query": "fixedquery"})
- assert response.status_code == 200
- assert response.json() == {
- "items": [{"item_id": "Foo"}, {"item_id": "Bar"}],
- "q": "fixedquery",
- }
-
-
-@needs_py310
-def test_query_params_str_validations_q_fixedquery(client: TestClient):
- response = client.get("/items/", params={"q": "fixedquery"})
- assert response.status_code == 200
- assert response.json() == {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
-
-
-@needs_py310
-def test_query_params_str_validations_item_query_nonregexquery(client: TestClient):
- response = client.get("/items/", params={"item-query": "nonregexquery"})
- assert response.status_code == 422
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "string_pattern_mismatch",
- "loc": ["query", "item-query"],
- "msg": "String should match pattern '^fixedquery$'",
- "input": "nonregexquery",
- "ctx": {"pattern": "^fixedquery$"},
- }
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "ctx": {"pattern": "^fixedquery$"},
- "loc": ["query", "item-query"],
- "msg": 'string does not match regex "^fixedquery$"',
- "type": "value_error.str.regex",
- }
- ]
- }
- )
-
-
-@needs_py310
-def test_openapi_schema(client: TestClient):
- response = client.get("/openapi.json")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/": {
- "get": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Read Items",
- "operationId": "read_items_items__get",
- "parameters": [
- {
- "description": "Query string for the items to search in the database that have a good match",
- "required": False,
- "deprecated": True,
- "schema": IsDict(
- {
- "anyOf": [
- {
- "type": "string",
- "minLength": 3,
- "maxLength": 50,
- "pattern": "^fixedquery$",
- },
- {"type": "null"},
- ],
- "title": "Query string",
- "description": "Query string for the items to search in the database that have a good match",
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "title": "Query string",
- "maxLength": 50,
- "minLength": 3,
- "pattern": "^fixedquery$",
- "type": "string",
- "description": "Query string for the items to search in the database that have a good match",
- }
- ),
- "name": "item-query",
- "in": "query",
- }
- ],
- }
- }
- },
- "components": {
- "schemas": {
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_query_params_str_validations/test_tutorial010_an_py39.py b/tests/test_tutorial/test_query_params_str_validations/test_tutorial010_an_py39.py
deleted file mode 100644
index 534ba87594..0000000000
--- a/tests/test_tutorial/test_query_params_str_validations/test_tutorial010_an_py39.py
+++ /dev/null
@@ -1,168 +0,0 @@
-import pytest
-from dirty_equals import IsDict
-from fastapi.testclient import TestClient
-
-from ...utils import needs_py39
-
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.query_params_str_validations.tutorial010_an_py39 import app
-
- client = TestClient(app)
- return client
-
-
-@needs_py39
-def test_query_params_str_validations_no_query(client: TestClient):
- response = client.get("/items/")
- assert response.status_code == 200
- assert response.json() == {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
-
-
-@needs_py39
-def test_query_params_str_validations_item_query_fixedquery(client: TestClient):
- response = client.get("/items/", params={"item-query": "fixedquery"})
- assert response.status_code == 200
- assert response.json() == {
- "items": [{"item_id": "Foo"}, {"item_id": "Bar"}],
- "q": "fixedquery",
- }
-
-
-@needs_py39
-def test_query_params_str_validations_q_fixedquery(client: TestClient):
- response = client.get("/items/", params={"q": "fixedquery"})
- assert response.status_code == 200
- assert response.json() == {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
-
-
-@needs_py39
-def test_query_params_str_validations_item_query_nonregexquery(client: TestClient):
- response = client.get("/items/", params={"item-query": "nonregexquery"})
- assert response.status_code == 422
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "string_pattern_mismatch",
- "loc": ["query", "item-query"],
- "msg": "String should match pattern '^fixedquery$'",
- "input": "nonregexquery",
- "ctx": {"pattern": "^fixedquery$"},
- }
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "ctx": {"pattern": "^fixedquery$"},
- "loc": ["query", "item-query"],
- "msg": 'string does not match regex "^fixedquery$"',
- "type": "value_error.str.regex",
- }
- ]
- }
- )
-
-
-@needs_py39
-def test_openapi_schema(client: TestClient):
- response = client.get("/openapi.json")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/": {
- "get": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Read Items",
- "operationId": "read_items_items__get",
- "parameters": [
- {
- "description": "Query string for the items to search in the database that have a good match",
- "required": False,
- "deprecated": True,
- "schema": IsDict(
- {
- "anyOf": [
- {
- "type": "string",
- "minLength": 3,
- "maxLength": 50,
- "pattern": "^fixedquery$",
- },
- {"type": "null"},
- ],
- "title": "Query string",
- "description": "Query string for the items to search in the database that have a good match",
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "title": "Query string",
- "maxLength": 50,
- "minLength": 3,
- "pattern": "^fixedquery$",
- "type": "string",
- "description": "Query string for the items to search in the database that have a good match",
- }
- ),
- "name": "item-query",
- "in": "query",
- }
- ],
- }
- }
- },
- "components": {
- "schemas": {
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_query_params_str_validations/test_tutorial010_py310.py b/tests/test_tutorial/test_query_params_str_validations/test_tutorial010_py310.py
deleted file mode 100644
index 886bceca21..0000000000
--- a/tests/test_tutorial/test_query_params_str_validations/test_tutorial010_py310.py
+++ /dev/null
@@ -1,168 +0,0 @@
-import pytest
-from dirty_equals import IsDict
-from fastapi.testclient import TestClient
-
-from ...utils import needs_py310
-
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.query_params_str_validations.tutorial010_py310 import app
-
- client = TestClient(app)
- return client
-
-
-@needs_py310
-def test_query_params_str_validations_no_query(client: TestClient):
- response = client.get("/items/")
- assert response.status_code == 200
- assert response.json() == {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
-
-
-@needs_py310
-def test_query_params_str_validations_item_query_fixedquery(client: TestClient):
- response = client.get("/items/", params={"item-query": "fixedquery"})
- assert response.status_code == 200
- assert response.json() == {
- "items": [{"item_id": "Foo"}, {"item_id": "Bar"}],
- "q": "fixedquery",
- }
-
-
-@needs_py310
-def test_query_params_str_validations_q_fixedquery(client: TestClient):
- response = client.get("/items/", params={"q": "fixedquery"})
- assert response.status_code == 200
- assert response.json() == {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
-
-
-@needs_py310
-def test_query_params_str_validations_item_query_nonregexquery(client: TestClient):
- response = client.get("/items/", params={"item-query": "nonregexquery"})
- assert response.status_code == 422
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "string_pattern_mismatch",
- "loc": ["query", "item-query"],
- "msg": "String should match pattern '^fixedquery$'",
- "input": "nonregexquery",
- "ctx": {"pattern": "^fixedquery$"},
- }
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "ctx": {"pattern": "^fixedquery$"},
- "loc": ["query", "item-query"],
- "msg": 'string does not match regex "^fixedquery$"',
- "type": "value_error.str.regex",
- }
- ]
- }
- )
-
-
-@needs_py310
-def test_openapi_schema(client: TestClient):
- response = client.get("/openapi.json")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/": {
- "get": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Read Items",
- "operationId": "read_items_items__get",
- "parameters": [
- {
- "description": "Query string for the items to search in the database that have a good match",
- "required": False,
- "deprecated": True,
- "schema": IsDict(
- {
- "anyOf": [
- {
- "type": "string",
- "minLength": 3,
- "maxLength": 50,
- "pattern": "^fixedquery$",
- },
- {"type": "null"},
- ],
- "title": "Query string",
- "description": "Query string for the items to search in the database that have a good match",
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "title": "Query string",
- "maxLength": 50,
- "minLength": 3,
- "pattern": "^fixedquery$",
- "type": "string",
- "description": "Query string for the items to search in the database that have a good match",
- }
- ),
- "name": "item-query",
- "in": "query",
- }
- ],
- }
- }
- },
- "components": {
- "schemas": {
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_query_params_str_validations/test_tutorial011.py b/tests/test_tutorial/test_query_params_str_validations/test_tutorial011.py
index 5ba39b05d6..f4da25752b 100644
--- a/tests/test_tutorial/test_query_params_str_validations/test_tutorial011.py
+++ b/tests/test_tutorial/test_query_params_str_validations/test_tutorial011.py
@@ -1,26 +1,47 @@
+import importlib
+
+import pytest
from dirty_equals import IsDict
from fastapi.testclient import TestClient
-from docs_src.query_params_str_validations.tutorial011 import app
-
-client = TestClient(app)
+from ...utils import needs_py39, needs_py310
-def test_multi_query_values():
+@pytest.fixture(
+ name="client",
+ params=[
+ "tutorial011",
+ pytest.param("tutorial011_py39", marks=needs_py310),
+ pytest.param("tutorial011_py310", marks=needs_py310),
+ "tutorial011_an",
+ pytest.param("tutorial011_an_py39", marks=needs_py39),
+ pytest.param("tutorial011_an_py310", marks=needs_py310),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(
+ f"docs_src.query_params_str_validations.{request.param}"
+ )
+
+ client = TestClient(mod.app)
+ return client
+
+
+def test_multi_query_values(client: TestClient):
url = "/items/?q=foo&q=bar"
response = client.get(url)
assert response.status_code == 200, response.text
assert response.json() == {"q": ["foo", "bar"]}
-def test_query_no_values():
+def test_query_no_values(client: TestClient):
url = "/items/"
response = client.get(url)
assert response.status_code == 200, response.text
assert response.json() == {"q": None}
-def test_openapi_schema():
+def test_openapi_schema(client: TestClient):
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
diff --git a/tests/test_tutorial/test_query_params_str_validations/test_tutorial011_an.py b/tests/test_tutorial/test_query_params_str_validations/test_tutorial011_an.py
deleted file mode 100644
index 3942ea77a9..0000000000
--- a/tests/test_tutorial/test_query_params_str_validations/test_tutorial011_an.py
+++ /dev/null
@@ -1,108 +0,0 @@
-from dirty_equals import IsDict
-from fastapi.testclient import TestClient
-
-from docs_src.query_params_str_validations.tutorial011_an import app
-
-client = TestClient(app)
-
-
-def test_multi_query_values():
- url = "/items/?q=foo&q=bar"
- response = client.get(url)
- assert response.status_code == 200, response.text
- assert response.json() == {"q": ["foo", "bar"]}
-
-
-def test_query_no_values():
- url = "/items/"
- response = client.get(url)
- assert response.status_code == 200, response.text
- assert response.json() == {"q": None}
-
-
-def test_openapi_schema():
- response = client.get("/openapi.json")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/": {
- "get": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Read Items",
- "operationId": "read_items_items__get",
- "parameters": [
- {
- "required": False,
- "schema": IsDict(
- {
- "anyOf": [
- {"type": "array", "items": {"type": "string"}},
- {"type": "null"},
- ],
- "title": "Q",
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "title": "Q",
- "type": "array",
- "items": {"type": "string"},
- }
- ),
- "name": "q",
- "in": "query",
- }
- ],
- }
- }
- },
- "components": {
- "schemas": {
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_query_params_str_validations/test_tutorial011_an_py310.py b/tests/test_tutorial/test_query_params_str_validations/test_tutorial011_an_py310.py
deleted file mode 100644
index f2ec38c950..0000000000
--- a/tests/test_tutorial/test_query_params_str_validations/test_tutorial011_an_py310.py
+++ /dev/null
@@ -1,118 +0,0 @@
-import pytest
-from dirty_equals import IsDict
-from fastapi.testclient import TestClient
-
-from ...utils import needs_py310
-
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.query_params_str_validations.tutorial011_an_py310 import app
-
- client = TestClient(app)
- return client
-
-
-@needs_py310
-def test_multi_query_values(client: TestClient):
- url = "/items/?q=foo&q=bar"
- response = client.get(url)
- assert response.status_code == 200, response.text
- assert response.json() == {"q": ["foo", "bar"]}
-
-
-@needs_py310
-def test_query_no_values(client: TestClient):
- url = "/items/"
- response = client.get(url)
- assert response.status_code == 200, response.text
- assert response.json() == {"q": None}
-
-
-@needs_py310
-def test_openapi_schema(client: TestClient):
- response = client.get("/openapi.json")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/": {
- "get": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Read Items",
- "operationId": "read_items_items__get",
- "parameters": [
- {
- "required": False,
- "schema": IsDict(
- {
- "anyOf": [
- {"type": "array", "items": {"type": "string"}},
- {"type": "null"},
- ],
- "title": "Q",
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "title": "Q",
- "type": "array",
- "items": {"type": "string"},
- }
- ),
- "name": "q",
- "in": "query",
- }
- ],
- }
- }
- },
- "components": {
- "schemas": {
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_query_params_str_validations/test_tutorial011_an_py39.py b/tests/test_tutorial/test_query_params_str_validations/test_tutorial011_an_py39.py
deleted file mode 100644
index cd7b156798..0000000000
--- a/tests/test_tutorial/test_query_params_str_validations/test_tutorial011_an_py39.py
+++ /dev/null
@@ -1,118 +0,0 @@
-import pytest
-from dirty_equals import IsDict
-from fastapi.testclient import TestClient
-
-from ...utils import needs_py39
-
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.query_params_str_validations.tutorial011_an_py39 import app
-
- client = TestClient(app)
- return client
-
-
-@needs_py39
-def test_multi_query_values(client: TestClient):
- url = "/items/?q=foo&q=bar"
- response = client.get(url)
- assert response.status_code == 200, response.text
- assert response.json() == {"q": ["foo", "bar"]}
-
-
-@needs_py39
-def test_query_no_values(client: TestClient):
- url = "/items/"
- response = client.get(url)
- assert response.status_code == 200, response.text
- assert response.json() == {"q": None}
-
-
-@needs_py39
-def test_openapi_schema(client: TestClient):
- response = client.get("/openapi.json")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/": {
- "get": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Read Items",
- "operationId": "read_items_items__get",
- "parameters": [
- {
- "required": False,
- "schema": IsDict(
- {
- "anyOf": [
- {"type": "array", "items": {"type": "string"}},
- {"type": "null"},
- ],
- "title": "Q",
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "title": "Q",
- "type": "array",
- "items": {"type": "string"},
- }
- ),
- "name": "q",
- "in": "query",
- }
- ],
- }
- }
- },
- "components": {
- "schemas": {
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_query_params_str_validations/test_tutorial011_py310.py b/tests/test_tutorial/test_query_params_str_validations/test_tutorial011_py310.py
deleted file mode 100644
index bdc7295162..0000000000
--- a/tests/test_tutorial/test_query_params_str_validations/test_tutorial011_py310.py
+++ /dev/null
@@ -1,118 +0,0 @@
-import pytest
-from dirty_equals import IsDict
-from fastapi.testclient import TestClient
-
-from ...utils import needs_py310
-
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.query_params_str_validations.tutorial011_py310 import app
-
- client = TestClient(app)
- return client
-
-
-@needs_py310
-def test_multi_query_values(client: TestClient):
- url = "/items/?q=foo&q=bar"
- response = client.get(url)
- assert response.status_code == 200, response.text
- assert response.json() == {"q": ["foo", "bar"]}
-
-
-@needs_py310
-def test_query_no_values(client: TestClient):
- url = "/items/"
- response = client.get(url)
- assert response.status_code == 200, response.text
- assert response.json() == {"q": None}
-
-
-@needs_py310
-def test_openapi_schema(client: TestClient):
- response = client.get("/openapi.json")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/": {
- "get": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Read Items",
- "operationId": "read_items_items__get",
- "parameters": [
- {
- "required": False,
- "schema": IsDict(
- {
- "anyOf": [
- {"type": "array", "items": {"type": "string"}},
- {"type": "null"},
- ],
- "title": "Q",
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "title": "Q",
- "type": "array",
- "items": {"type": "string"},
- }
- ),
- "name": "q",
- "in": "query",
- }
- ],
- }
- }
- },
- "components": {
- "schemas": {
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_query_params_str_validations/test_tutorial011_py39.py b/tests/test_tutorial/test_query_params_str_validations/test_tutorial011_py39.py
deleted file mode 100644
index 26ac56b2f1..0000000000
--- a/tests/test_tutorial/test_query_params_str_validations/test_tutorial011_py39.py
+++ /dev/null
@@ -1,118 +0,0 @@
-import pytest
-from dirty_equals import IsDict
-from fastapi.testclient import TestClient
-
-from ...utils import needs_py39
-
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.query_params_str_validations.tutorial011_py39 import app
-
- client = TestClient(app)
- return client
-
-
-@needs_py39
-def test_multi_query_values(client: TestClient):
- url = "/items/?q=foo&q=bar"
- response = client.get(url)
- assert response.status_code == 200, response.text
- assert response.json() == {"q": ["foo", "bar"]}
-
-
-@needs_py39
-def test_query_no_values(client: TestClient):
- url = "/items/"
- response = client.get(url)
- assert response.status_code == 200, response.text
- assert response.json() == {"q": None}
-
-
-@needs_py39
-def test_openapi_schema(client: TestClient):
- response = client.get("/openapi.json")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/": {
- "get": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Read Items",
- "operationId": "read_items_items__get",
- "parameters": [
- {
- "required": False,
- "schema": IsDict(
- {
- "anyOf": [
- {"type": "array", "items": {"type": "string"}},
- {"type": "null"},
- ],
- "title": "Q",
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "title": "Q",
- "type": "array",
- "items": {"type": "string"},
- }
- ),
- "name": "q",
- "in": "query",
- }
- ],
- }
- }
- },
- "components": {
- "schemas": {
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_query_params_str_validations/test_tutorial012.py b/tests/test_tutorial/test_query_params_str_validations/test_tutorial012.py
index 1436db384c..549a90519e 100644
--- a/tests/test_tutorial/test_query_params_str_validations/test_tutorial012.py
+++ b/tests/test_tutorial/test_query_params_str_validations/test_tutorial012.py
@@ -1,25 +1,44 @@
+import importlib
+
+import pytest
from fastapi.testclient import TestClient
-from docs_src.query_params_str_validations.tutorial012 import app
-
-client = TestClient(app)
+from ...utils import needs_py39
-def test_default_query_values():
+@pytest.fixture(
+ name="client",
+ params=[
+ "tutorial012",
+ pytest.param("tutorial012_py39", marks=needs_py39),
+ "tutorial012_an",
+ pytest.param("tutorial012_an_py39", marks=needs_py39),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(
+ f"docs_src.query_params_str_validations.{request.param}"
+ )
+
+ client = TestClient(mod.app)
+ return client
+
+
+def test_default_query_values(client: TestClient):
url = "/items/"
response = client.get(url)
assert response.status_code == 200, response.text
assert response.json() == {"q": ["foo", "bar"]}
-def test_multi_query_values():
+def test_multi_query_values(client: TestClient):
url = "/items/?q=baz&q=foobar"
response = client.get(url)
assert response.status_code == 200, response.text
assert response.json() == {"q": ["baz", "foobar"]}
-def test_openapi_schema():
+def test_openapi_schema(client: TestClient):
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
diff --git a/tests/test_tutorial/test_query_params_str_validations/test_tutorial012_an.py b/tests/test_tutorial/test_query_params_str_validations/test_tutorial012_an.py
deleted file mode 100644
index 270763f1d1..0000000000
--- a/tests/test_tutorial/test_query_params_str_validations/test_tutorial012_an.py
+++ /dev/null
@@ -1,96 +0,0 @@
-from fastapi.testclient import TestClient
-
-from docs_src.query_params_str_validations.tutorial012_an import app
-
-client = TestClient(app)
-
-
-def test_default_query_values():
- url = "/items/"
- response = client.get(url)
- assert response.status_code == 200, response.text
- assert response.json() == {"q": ["foo", "bar"]}
-
-
-def test_multi_query_values():
- url = "/items/?q=baz&q=foobar"
- response = client.get(url)
- assert response.status_code == 200, response.text
- assert response.json() == {"q": ["baz", "foobar"]}
-
-
-def test_openapi_schema():
- response = client.get("/openapi.json")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/": {
- "get": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Read Items",
- "operationId": "read_items_items__get",
- "parameters": [
- {
- "required": False,
- "schema": {
- "title": "Q",
- "type": "array",
- "items": {"type": "string"},
- "default": ["foo", "bar"],
- },
- "name": "q",
- "in": "query",
- }
- ],
- }
- }
- },
- "components": {
- "schemas": {
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_query_params_str_validations/test_tutorial012_an_py39.py b/tests/test_tutorial/test_query_params_str_validations/test_tutorial012_an_py39.py
deleted file mode 100644
index 5483916839..0000000000
--- a/tests/test_tutorial/test_query_params_str_validations/test_tutorial012_an_py39.py
+++ /dev/null
@@ -1,106 +0,0 @@
-import pytest
-from fastapi.testclient import TestClient
-
-from ...utils import needs_py39
-
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.query_params_str_validations.tutorial012_an_py39 import app
-
- client = TestClient(app)
- return client
-
-
-@needs_py39
-def test_default_query_values(client: TestClient):
- url = "/items/"
- response = client.get(url)
- assert response.status_code == 200, response.text
- assert response.json() == {"q": ["foo", "bar"]}
-
-
-@needs_py39
-def test_multi_query_values(client: TestClient):
- url = "/items/?q=baz&q=foobar"
- response = client.get(url)
- assert response.status_code == 200, response.text
- assert response.json() == {"q": ["baz", "foobar"]}
-
-
-@needs_py39
-def test_openapi_schema(client: TestClient):
- response = client.get("/openapi.json")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/": {
- "get": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Read Items",
- "operationId": "read_items_items__get",
- "parameters": [
- {
- "required": False,
- "schema": {
- "title": "Q",
- "type": "array",
- "items": {"type": "string"},
- "default": ["foo", "bar"],
- },
- "name": "q",
- "in": "query",
- }
- ],
- }
- }
- },
- "components": {
- "schemas": {
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_query_params_str_validations/test_tutorial012_py39.py b/tests/test_tutorial/test_query_params_str_validations/test_tutorial012_py39.py
deleted file mode 100644
index e7d7451548..0000000000
--- a/tests/test_tutorial/test_query_params_str_validations/test_tutorial012_py39.py
+++ /dev/null
@@ -1,106 +0,0 @@
-import pytest
-from fastapi.testclient import TestClient
-
-from ...utils import needs_py39
-
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.query_params_str_validations.tutorial012_py39 import app
-
- client = TestClient(app)
- return client
-
-
-@needs_py39
-def test_default_query_values(client: TestClient):
- url = "/items/"
- response = client.get(url)
- assert response.status_code == 200, response.text
- assert response.json() == {"q": ["foo", "bar"]}
-
-
-@needs_py39
-def test_multi_query_values(client: TestClient):
- url = "/items/?q=baz&q=foobar"
- response = client.get(url)
- assert response.status_code == 200, response.text
- assert response.json() == {"q": ["baz", "foobar"]}
-
-
-@needs_py39
-def test_openapi_schema(client: TestClient):
- response = client.get("/openapi.json")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/": {
- "get": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Read Items",
- "operationId": "read_items_items__get",
- "parameters": [
- {
- "required": False,
- "schema": {
- "title": "Q",
- "type": "array",
- "items": {"type": "string"},
- "default": ["foo", "bar"],
- },
- "name": "q",
- "in": "query",
- }
- ],
- }
- }
- },
- "components": {
- "schemas": {
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_query_params_str_validations/test_tutorial013.py b/tests/test_tutorial/test_query_params_str_validations/test_tutorial013.py
index 1ba1fdf618..f2f5f7a858 100644
--- a/tests/test_tutorial/test_query_params_str_validations/test_tutorial013.py
+++ b/tests/test_tutorial/test_query_params_str_validations/test_tutorial013.py
@@ -1,25 +1,43 @@
+import importlib
+
+import pytest
from fastapi.testclient import TestClient
-from docs_src.query_params_str_validations.tutorial013 import app
-
-client = TestClient(app)
+from ...utils import needs_py39
-def test_multi_query_values():
+@pytest.fixture(
+ name="client",
+ params=[
+ "tutorial013",
+ "tutorial013_an",
+ pytest.param("tutorial013_an_py39", marks=needs_py39),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(
+ f"docs_src.query_params_str_validations.{request.param}"
+ )
+
+ client = TestClient(mod.app)
+ return client
+
+
+def test_multi_query_values(client: TestClient):
url = "/items/?q=foo&q=bar"
response = client.get(url)
assert response.status_code == 200, response.text
assert response.json() == {"q": ["foo", "bar"]}
-def test_query_no_values():
+def test_query_no_values(client: TestClient):
url = "/items/"
response = client.get(url)
assert response.status_code == 200, response.text
assert response.json() == {"q": []}
-def test_openapi_schema():
+def test_openapi_schema(client: TestClient):
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
diff --git a/tests/test_tutorial/test_query_params_str_validations/test_tutorial013_an.py b/tests/test_tutorial/test_query_params_str_validations/test_tutorial013_an.py
deleted file mode 100644
index 3432617481..0000000000
--- a/tests/test_tutorial/test_query_params_str_validations/test_tutorial013_an.py
+++ /dev/null
@@ -1,96 +0,0 @@
-from fastapi.testclient import TestClient
-
-from docs_src.query_params_str_validations.tutorial013_an import app
-
-client = TestClient(app)
-
-
-def test_multi_query_values():
- url = "/items/?q=foo&q=bar"
- response = client.get(url)
- assert response.status_code == 200, response.text
- assert response.json() == {"q": ["foo", "bar"]}
-
-
-def test_query_no_values():
- url = "/items/"
- response = client.get(url)
- assert response.status_code == 200, response.text
- assert response.json() == {"q": []}
-
-
-def test_openapi_schema():
- response = client.get("/openapi.json")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/": {
- "get": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Read Items",
- "operationId": "read_items_items__get",
- "parameters": [
- {
- "required": False,
- "schema": {
- "title": "Q",
- "type": "array",
- "items": {},
- "default": [],
- },
- "name": "q",
- "in": "query",
- }
- ],
- }
- }
- },
- "components": {
- "schemas": {
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_query_params_str_validations/test_tutorial013_an_py39.py b/tests/test_tutorial/test_query_params_str_validations/test_tutorial013_an_py39.py
deleted file mode 100644
index 537d6325b3..0000000000
--- a/tests/test_tutorial/test_query_params_str_validations/test_tutorial013_an_py39.py
+++ /dev/null
@@ -1,106 +0,0 @@
-import pytest
-from fastapi.testclient import TestClient
-
-from ...utils import needs_py39
-
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.query_params_str_validations.tutorial013_an_py39 import app
-
- client = TestClient(app)
- return client
-
-
-@needs_py39
-def test_multi_query_values(client: TestClient):
- url = "/items/?q=foo&q=bar"
- response = client.get(url)
- assert response.status_code == 200, response.text
- assert response.json() == {"q": ["foo", "bar"]}
-
-
-@needs_py39
-def test_query_no_values(client: TestClient):
- url = "/items/"
- response = client.get(url)
- assert response.status_code == 200, response.text
- assert response.json() == {"q": []}
-
-
-@needs_py39
-def test_openapi_schema(client: TestClient):
- response = client.get("/openapi.json")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/": {
- "get": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Read Items",
- "operationId": "read_items_items__get",
- "parameters": [
- {
- "required": False,
- "schema": {
- "title": "Q",
- "type": "array",
- "items": {},
- "default": [],
- },
- "name": "q",
- "in": "query",
- }
- ],
- }
- }
- },
- "components": {
- "schemas": {
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_query_params_str_validations/test_tutorial014.py b/tests/test_tutorial/test_query_params_str_validations/test_tutorial014.py
index 7bce7590c2..edd40bb1ab 100644
--- a/tests/test_tutorial/test_query_params_str_validations/test_tutorial014.py
+++ b/tests/test_tutorial/test_query_params_str_validations/test_tutorial014.py
@@ -1,23 +1,43 @@
+import importlib
+
+import pytest
from fastapi.testclient import TestClient
-from docs_src.query_params_str_validations.tutorial014 import app
-
-client = TestClient(app)
+from ...utils import needs_py39, needs_py310
-def test_hidden_query():
+@pytest.fixture(
+ name="client",
+ params=[
+ "tutorial014",
+ pytest.param("tutorial014_py310", marks=needs_py310),
+ "tutorial014_an",
+ pytest.param("tutorial014_an_py39", marks=needs_py39),
+ pytest.param("tutorial014_an_py310", marks=needs_py310),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(
+ f"docs_src.query_params_str_validations.{request.param}"
+ )
+
+ client = TestClient(mod.app)
+ return client
+
+
+def test_hidden_query(client: TestClient):
response = client.get("/items?hidden_query=somevalue")
assert response.status_code == 200, response.text
assert response.json() == {"hidden_query": "somevalue"}
-def test_no_hidden_query():
+def test_no_hidden_query(client: TestClient):
response = client.get("/items")
assert response.status_code == 200, response.text
assert response.json() == {"hidden_query": "Not found"}
-def test_openapi_schema():
+def test_openapi_schema(client: TestClient):
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
diff --git a/tests/test_tutorial/test_query_params_str_validations/test_tutorial014_an_py310.py b/tests/test_tutorial/test_query_params_str_validations/test_tutorial014_an_py310.py
deleted file mode 100644
index 344004d01b..0000000000
--- a/tests/test_tutorial/test_query_params_str_validations/test_tutorial014_an_py310.py
+++ /dev/null
@@ -1,91 +0,0 @@
-import pytest
-from fastapi.testclient import TestClient
-
-from ...utils import needs_py310
-
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.query_params_str_validations.tutorial014_an_py310 import app
-
- client = TestClient(app)
- return client
-
-
-@needs_py310
-def test_hidden_query(client: TestClient):
- response = client.get("/items?hidden_query=somevalue")
- assert response.status_code == 200, response.text
- assert response.json() == {"hidden_query": "somevalue"}
-
-
-@needs_py310
-def test_no_hidden_query(client: TestClient):
- response = client.get("/items")
- assert response.status_code == 200, response.text
- assert response.json() == {"hidden_query": "Not found"}
-
-
-@needs_py310
-def test_openapi_schema(client: TestClient):
- response = client.get("/openapi.json")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/": {
- "get": {
- "summary": "Read Items",
- "operationId": "read_items_items__get",
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- }
- }
- },
- "components": {
- "schemas": {
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_query_params_str_validations/test_tutorial014_an_py39.py b/tests/test_tutorial/test_query_params_str_validations/test_tutorial014_an_py39.py
deleted file mode 100644
index 5d4f6df3d0..0000000000
--- a/tests/test_tutorial/test_query_params_str_validations/test_tutorial014_an_py39.py
+++ /dev/null
@@ -1,91 +0,0 @@
-import pytest
-from fastapi.testclient import TestClient
-
-from ...utils import needs_py310
-
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.query_params_str_validations.tutorial014_an_py39 import app
-
- client = TestClient(app)
- return client
-
-
-@needs_py310
-def test_hidden_query(client: TestClient):
- response = client.get("/items?hidden_query=somevalue")
- assert response.status_code == 200, response.text
- assert response.json() == {"hidden_query": "somevalue"}
-
-
-@needs_py310
-def test_no_hidden_query(client: TestClient):
- response = client.get("/items")
- assert response.status_code == 200, response.text
- assert response.json() == {"hidden_query": "Not found"}
-
-
-@needs_py310
-def test_openapi_schema(client: TestClient):
- response = client.get("/openapi.json")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/": {
- "get": {
- "summary": "Read Items",
- "operationId": "read_items_items__get",
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- }
- }
- },
- "components": {
- "schemas": {
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_query_params_str_validations/test_tutorial014_py310.py b/tests/test_tutorial/test_query_params_str_validations/test_tutorial014_py310.py
deleted file mode 100644
index dad49fb12d..0000000000
--- a/tests/test_tutorial/test_query_params_str_validations/test_tutorial014_py310.py
+++ /dev/null
@@ -1,91 +0,0 @@
-import pytest
-from fastapi.testclient import TestClient
-
-from ...utils import needs_py310
-
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.query_params_str_validations.tutorial014_py310 import app
-
- client = TestClient(app)
- return client
-
-
-@needs_py310
-def test_hidden_query(client: TestClient):
- response = client.get("/items?hidden_query=somevalue")
- assert response.status_code == 200, response.text
- assert response.json() == {"hidden_query": "somevalue"}
-
-
-@needs_py310
-def test_no_hidden_query(client: TestClient):
- response = client.get("/items")
- assert response.status_code == 200, response.text
- assert response.json() == {"hidden_query": "Not found"}
-
-
-@needs_py310
-def test_openapi_schema(client: TestClient):
- response = client.get("/openapi.json")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/": {
- "get": {
- "summary": "Read Items",
- "operationId": "read_items_items__get",
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- }
- }
- },
- "components": {
- "schemas": {
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_query_params_str_validations/test_tutorial015.py b/tests/test_tutorial/test_query_params_str_validations/test_tutorial015.py
new file mode 100644
index 0000000000..ae1c40286d
--- /dev/null
+++ b/tests/test_tutorial/test_query_params_str_validations/test_tutorial015.py
@@ -0,0 +1,143 @@
+import importlib
+
+import pytest
+from dirty_equals import IsStr
+from fastapi.testclient import TestClient
+from inline_snapshot import snapshot
+
+from ...utils import needs_py39, needs_py310, needs_pydanticv2
+
+
+@pytest.fixture(
+ name="client",
+ params=[
+ pytest.param("tutorial015_an", marks=needs_pydanticv2),
+ pytest.param("tutorial015_an_py310", marks=(needs_py310, needs_pydanticv2)),
+ pytest.param("tutorial015_an_py39", marks=(needs_py39, needs_pydanticv2)),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(
+ f"docs_src.query_params_str_validations.{request.param}"
+ )
+
+ client = TestClient(mod.app)
+ return client
+
+
+def test_get_random_item(client: TestClient):
+ response = client.get("/items")
+ assert response.status_code == 200, response.text
+ assert response.json() == {"id": IsStr(), "name": IsStr()}
+
+
+def test_get_item(client: TestClient):
+ response = client.get("/items?id=isbn-9781529046137")
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "id": "isbn-9781529046137",
+ "name": "The Hitchhiker's Guide to the Galaxy",
+ }
+
+
+def test_get_item_does_not_exist(client: TestClient):
+ response = client.get("/items?id=isbn-nope")
+ assert response.status_code == 200, response.text
+ assert response.json() == {"id": "isbn-nope", "name": None}
+
+
+def test_get_invalid_item(client: TestClient):
+ response = client.get("/items?id=wtf-yes")
+ assert response.status_code == 422, response.text
+ assert response.json() == snapshot(
+ {
+ "detail": [
+ {
+ "type": "value_error",
+ "loc": ["query", "id"],
+ "msg": 'Value error, Invalid ID format, it must start with "isbn-" or "imdb-"',
+ "input": "wtf-yes",
+ "ctx": {"error": {}},
+ }
+ ]
+ }
+ )
+
+
+def test_openapi_schema(client: TestClient):
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == snapshot(
+ {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/items/": {
+ "get": {
+ "summary": "Read Items",
+ "operationId": "read_items_items__get",
+ "parameters": [
+ {
+ "name": "id",
+ "in": "query",
+ "required": False,
+ "schema": {
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ "title": "Id",
+ },
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "HTTPValidationError": {
+ "properties": {
+ "detail": {
+ "items": {
+ "$ref": "#/components/schemas/ValidationError"
+ },
+ "type": "array",
+ "title": "Detail",
+ }
+ },
+ "type": "object",
+ "title": "HTTPValidationError",
+ },
+ "ValidationError": {
+ "properties": {
+ "loc": {
+ "items": {
+ "anyOf": [{"type": "string"}, {"type": "integer"}]
+ },
+ "type": "array",
+ "title": "Location",
+ },
+ "msg": {"type": "string", "title": "Message"},
+ "type": {"type": "string", "title": "Error Type"},
+ },
+ "type": "object",
+ "required": ["loc", "msg", "type"],
+ "title": "ValidationError",
+ },
+ }
+ },
+ }
+ )
diff --git a/tests/test_tutorial/test_request_files/test_tutorial001.py b/tests/test_tutorial/test_request_files/test_tutorial001.py
index f5817593bb..b069199612 100644
--- a/tests/test_tutorial/test_request_files/test_tutorial001.py
+++ b/tests/test_tutorial/test_request_files/test_tutorial001.py
@@ -1,23 +1,28 @@
+import importlib
+
+import pytest
from dirty_equals import IsDict
from fastapi.testclient import TestClient
-from docs_src.request_files.tutorial001 import app
-
-client = TestClient(app)
+from ...utils import needs_py39
-file_required = {
- "detail": [
- {
- "loc": ["body", "file"],
- "msg": "field required",
- "type": "value_error.missing",
- }
- ]
-}
+@pytest.fixture(
+ name="client",
+ params=[
+ "tutorial001",
+ "tutorial001_an",
+ pytest.param("tutorial001_an_py39", marks=needs_py39),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.request_files.{request.param}")
+
+ client = TestClient(mod.app)
+ return client
-def test_post_form_no_body():
+def test_post_form_no_body(client: TestClient):
response = client.post("/files/")
assert response.status_code == 422, response.text
assert response.json() == IsDict(
@@ -45,7 +50,7 @@ def test_post_form_no_body():
)
-def test_post_body_json():
+def test_post_body_json(client: TestClient):
response = client.post("/files/", json={"file": "Foo"})
assert response.status_code == 422, response.text
assert response.json() == IsDict(
@@ -73,41 +78,38 @@ def test_post_body_json():
)
-def test_post_file(tmp_path):
+def test_post_file(tmp_path, client: TestClient):
path = tmp_path / "test.txt"
path.write_bytes(b"