committed by
GitHub
52 changed files with 2206 additions and 598 deletions
@ -0,0 +1,298 @@ |
|||
# এনভায়রনমেন্ট ভেরিয়েবলস |
|||
|
|||
/// tip |
|||
|
|||
আপনি যদি "এনভায়রনমেন্ট ভেরিয়েবলস" কী এবং সেগুলো কীভাবে ব্যবহার করতে হয় সেটা জানেন, তাহলে এই অংশটি স্কিপ করে যেতে পারেন। |
|||
|
|||
/// |
|||
|
|||
এনভায়রনমেন্ট ভেরিয়েবল (সংক্ষেপে "**env var**" নামেও পরিচিত) হলো এমন একটি ভেরিয়েবল যা পাইথন কোডের **বাইরে**, **অপারেটিং সিস্টেমে** থাকে এবং আপনার পাইথন কোড (বা অন্যান্য প্রোগ্রাম) দ্বারা যাকে রিড করা যায়। |
|||
|
|||
এনভায়রনমেন্ট ভেরিয়েবলস অ্যাপ্লিকেশনের **সেটিংস** পরিচালনা করতে, পাইথনের **ইনস্টলেশন** প্রক্রিয়ার অংশ হিসেবে, ইত্যাদি কাজে উপযোগী হতে পারে। |
|||
|
|||
## Env Vars তৈরী এবং ব্যবহার |
|||
|
|||
আপনি **শেল (টার্মিনাল)**-এ, পাইথনের প্রয়োজন ছাড়াই, এনভায়রনমেন্ট ভেরিয়েবলস **তৈরি** এবং ব্যবহার করতে পারবেনঃ |
|||
|
|||
//// tab | লিনাক্স, ম্যাকওএস, উইন্ডোজ Bash |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
// আপনি চাইলে MY_NAME নামে একটি env var তৈরি করতে পারেন |
|||
$ export MY_NAME="Wade Wilson" |
|||
|
|||
// তারপরে এটিকে চাইলে অন্যান্য প্রোগ্রামে ব্যবহার করতে পারেন |
|||
$ echo "Hello $MY_NAME" |
|||
|
|||
Hello Wade Wilson |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
//// |
|||
|
|||
//// tab | উইন্ডোজ পাওয়ারশেল |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
// MY_NAME নামে env var তৈরি |
|||
$ $Env:MY_NAME = "Wade Wilson" |
|||
|
|||
// অন্যান্য প্রোগ্রামে এটিকে ব্যবহার |
|||
$ echo "Hello $Env:MY_NAME" |
|||
|
|||
Hello Wade Wilson |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
//// |
|||
|
|||
## পাইথনে env vars রিড করা |
|||
|
|||
আপনি চাইলে পাইথনের **বাইরে**, টার্মিনালে (বা অন্য কোনো উপায়ে) এনভায়রনমেন্ট ভেরিয়েবলস তৈরি করতে পারেন, এবং পরে সেগুলো **পাইথনে রিড** (অ্যাক্সেস করতে) পারেন। |
|||
|
|||
উদাহরণস্বরূপ, আপনার `main.py` নামে একটি ফাইল থাকতে পারেঃ |
|||
|
|||
```Python hl_lines="3" |
|||
import os |
|||
|
|||
name = os.getenv("MY_NAME", "World") |
|||
print(f"Hello {name} from Python") |
|||
``` |
|||
|
|||
/// tip |
|||
|
|||
<a href="https://docs.python.org/3.8/library/os.html#os.getenv" class="external-link" target="_blank">`os.getenv()`</a> এর দ্বিতীয় আর্গুমেন্টটি হলো এর ডিফল্ট ভ্যালু যা রিটার্ন করা হবে। |
|||
|
|||
যদি এটি দেওয়া না হয়, ডিফল্টভাবে `None` ব্যবহৃত হবে, এখানে আমরা ডিফল্ট ভ্যালু হিসেবে `"World"` ব্যবহার করেছি। |
|||
|
|||
/// |
|||
|
|||
তারপরে পাইথন প্রোগ্রামটিকে নিম্নোক্তভাবে কল করা যাবেঃ |
|||
|
|||
//// tab | লিনাক্স, ম্যাকওএস, উইন্ডোজ Bash |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
// এখনো আমরা এনভায়রনমেন্ট ভেরিয়েবল সেট করিনি |
|||
$ python main.py |
|||
|
|||
// যেহেতু env var সেট করা হয়নি, তাই আমরা ডিফল্ট ভ্যালু পাচ্ছি |
|||
|
|||
Hello World from Python |
|||
|
|||
// কিন্তু আমরা প্রথমে যদি একটা এনভায়রনমেন্ট ভারিয়েবল তৈরি করে নেই |
|||
$ export MY_NAME="Wade Wilson" |
|||
|
|||
// এবং তারপর আবার প্রোগ্রাটিকে কল করি |
|||
$ python main.py |
|||
|
|||
// এখন এটি এনভায়রনমেন্ট ভেরিয়েবল রিড করতে পারবে |
|||
|
|||
Hello Wade Wilson from Python |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
//// |
|||
|
|||
//// tab | উইন্ডোজ পাওয়ারশেল |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
// এখনো আমরা এনভায়রনমেন্ট ভেরিয়েবল সেট করিনি |
|||
$ python main.py |
|||
|
|||
// যেহেতু env var সেট করা হয়নি, তাই আমরা ডিফল্ট ভ্যালু পাচ্ছি |
|||
|
|||
Hello World from Python |
|||
|
|||
// কিন্তু আমরা প্রথমে যদি একটা এনভায়রনমেন্ট ভারিয়েবল তৈরি করে নেই |
|||
$ $Env:MY_NAME = "Wade Wilson" |
|||
|
|||
// এবং তারপর আবার প্রোগ্রাটিকে কল করি |
|||
$ python main.py |
|||
|
|||
// এখন এটি এনভায়রনমেন্ট ভেরিয়েবল রিড করতে পারবে |
|||
|
|||
Hello Wade Wilson from Python |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
//// |
|||
|
|||
যেহেতু এনভায়রনমেন্ট ভেরিয়েবলস কোডের বাইরে সেট করা যায়, কিন্তু পরবর্তীতে কোড দ্বারা রিড করা যায়, এবং বাকি ফাইলগুলোর সাথে রাখতে (`git` এ কমিট) হয় না, তাই কনফিগারেশনস বা **সেটিংস** এর জন্য এগুলো সাধারণত ব্যবহৃত হয়ে থাকে। |
|||
|
|||
আপনি একটি এনভায়রনমেন্ট ভেরিয়েবল শুধুমাত্র একটি **নির্দিষ্ট প্রোগ্রাম ইনভোকেশনের** জন্যও তৈরি করতে পারেন, যা শুধুমাত্র সেই প্রোগ্রামের জন্যই এভেইলেবল থাকবে এবং শুধুমাত্র তার চলাকালীন সময় পর্যন্তই সক্রিয় থাকবে। |
|||
|
|||
এটি করতে, প্রোগ্রামটি রান করার ঠিক আগেই, একই লাইনে এনভায়রনমেন্ট ভেরিয়েবল তৈরি করুন: |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
// প্রোগ্রামটি কল করার সময় একই লাইনে MY_NAME এনভায়রনমেন্ট ভেরিয়েবল তৈরি করুন |
|||
$ MY_NAME="Wade Wilson" python main.py |
|||
|
|||
// এখন এটি এনভায়রনমেন্ট ভ্যরিয়েবলটিকে রিড করতে পারবে |
|||
|
|||
Hello Wade Wilson from Python |
|||
|
|||
// পরবর্তীতে এনভায়রনমেন্ট ভেরিয়েবলটিকে আর ব্যবহার করা যাচ্ছে না |
|||
$ python main.py |
|||
|
|||
Hello World from Python |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
/// tip |
|||
|
|||
এটি নিয়ে আরো বিস্তারিত পড়তে পারেন এখানে <a href="https://12factor.net/config" class="external-link" target="_blank">The Twelve-Factor App: Config</a>। |
|||
|
|||
/// |
|||
|
|||
## টাইপস এবং ভ্যালিডেশন |
|||
|
|||
এই এনভায়রনমেন্ট ভেরিয়েবলগুলো শুধুমাত্র **টেক্সট স্ট্রিংস** হ্যান্ডেল করতে পারে, যেহেতু এগুলো পাইথনের বাইরে অবস্থিত এবং অন্যান্য প্রোগ্রাম এবং সিস্টেমের বাকি অংশের (এমনকি বিভিন্ন অপারেটিং সিস্টেম যেমন লিনাক্স, উইন্ডোজ, ম্যাকওএস) সাথে সামঞ্জস্যপূর্ণ হতে হয়। |
|||
|
|||
এর অর্থ হচ্ছে পাইথনে এনভায়রনমেন্ট ভেরিয়েবল থেকে রিড করা **যেকোনো ভ্যালু** একটি `str` হবে, এবং অন্য কোনো টাইপে কনভার্সন বা যেকোনো ভেলিডেশন কোডে আলাদাভাবে করতে হবে। |
|||
|
|||
এনভায়রনমেন্ট ভেরিয়েবল ব্যবহার করে **এপ্লিকেশন সেটিংস** হ্যান্ডেল করা নিয়ে আরো বিস্তারিত জানা যাবে [Advanced User Guide - Settings and Environment Variables](./advanced/settings.md){.internal-link target=_blank}. |
|||
|
|||
## `PATH` এনভায়রনমেন্ট ভেরিয়েবল |
|||
|
|||
**`PATH`** নামে একটি **বিশেষ** এনভায়রনমেন্ট ভেরিয়েবল রয়েছে, যেটি প্রোগ্রাম রান করার জন্য অপারেটিং সিস্টেমস (লিনাক্স, ম্যাকওএস, উইন্ডোজ) দ্বারা ব্যবহৃত হয়। |
|||
|
|||
`PATH` ভেরিয়েবল এর ভ্যালু হচ্ছে একটি বিশাল স্ট্রিং যা ডিরেক্টরিকে কোলন `:` দিয়ে আলাদা করার মাধ্যমে লিনাক্সে ও ম্যাকওএস এ, এবং সেমিকোলন `;` এর মাধ্যমে উইন্ডোজ এ তৈরি করা থাকে। |
|||
|
|||
উদাহরণস্বরূপ, `PATH` ভেরিয়েবল নিচের মতো দেখতে হতে পারেঃ |
|||
|
|||
//// tab | লিনাক্স, ম্যাকওএস |
|||
|
|||
```plaintext |
|||
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin |
|||
``` |
|||
|
|||
তারমানে হলো সিস্টেম প্রোগ্রামগুলোকে নিচের ডিরেক্টরিগুলোতে খুঁজবেঃ |
|||
|
|||
* `/usr/local/bin` |
|||
* `/usr/bin` |
|||
* `/bin` |
|||
* `/usr/sbin` |
|||
* `/sbin` |
|||
|
|||
//// |
|||
|
|||
//// tab | উইন্ডোজ |
|||
|
|||
```plaintext |
|||
C:\Program Files\Python312\Scripts;C:\Program Files\Python312;C:\Windows\System32 |
|||
``` |
|||
|
|||
তারমানে হলো সিস্টেম প্রোগ্রামগুলোকে নিচের ডিরেক্টরিগুলোতে খুঁজবেঃ |
|||
|
|||
* `C:\Program Files\Python312\Scripts` |
|||
* `C:\Program Files\Python312` |
|||
* `C:\Windows\System32` |
|||
|
|||
//// |
|||
|
|||
যখন আপনি টার্মিনালে কোনো **কমান্ড** লিখবেন, অপারেটিং সিস্টেম **প্রত্যেকটি ডিরেক্টরিতে** প্রোগ্রামটি **খুঁজবে** যেগুলো `PATH` এনভায়রনমেন্ট ভেরিয়েবল এ লিস্ট করা আছে। |
|||
|
|||
উদাহরণস্বরূপ, যখন আপনি টার্মিনালে `python` টাইপ করবেন, অপারেটিং সিস্টেম এই লিস্ট এর **প্রথম ডিরেক্টরিতে** `python` নামের একটি প্রোগ্রাম খুঁজবে। |
|||
|
|||
যদি এটি খুঁজে পায়, তাহলে এটি প্রোগ্রামটিকে ব্যবহার করবে। অন্যথায় এটি **অন্যান্য ডিরেক্টরিগুলোতে** এটিকে খুঁজতে থাকবে। |
|||
|
|||
### পাইথন ইনস্টল এবং `PATH` আপডেট |
|||
|
|||
যখন আপনি পাইথন ইনস্টল করেন, আপনি `PATH` এনভায়রনমেন্ট ভেরিয়েবল আপডেট করতে চান কিনা সেটা জিজ্ঞেস করা হতে পারে। |
|||
|
|||
//// tab | লিনাক্স, ম্যাকওএস |
|||
|
|||
ধরা যাক আপনি পাইথন ইনস্টল করলেন এবং এটি `/opt/custompython/bin` ডিরেক্টরিতে ইনস্টল হচ্ছে। |
|||
|
|||
যদি আপনি "Yes" সিলেক্ট করে `PATH` এনভায়রনমেন্ট ভেরিয়েবল আপডেট করতে চান, তাহলে ইনস্টলার `/opt/custompython/bin` কে `PATH` এনভায়রনমেন্ট ভেরিয়েবল এ এড করে দিবে। |
|||
|
|||
এটা দেখতে এমনটা হতে পারেঃ |
|||
|
|||
```plaintext |
|||
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/custompython/bin |
|||
``` |
|||
|
|||
এইভাবে, আপনি যখন টার্মিনালে `python` টাইপ করেন, সিস্টেম পাইথন প্রোগ্রামটিকে `/opt/custompython/bin` (সর্বশেষ ডিরেক্টরি) তে খুঁজে পাবে এবং এটাকে ব্যবহার করবে। |
|||
|
|||
//// |
|||
|
|||
//// tab | উইন্ডোজ |
|||
|
|||
ধরা যাক আপনি পাইথন ইনস্টল করলেন এবং এটি `C:\opt\custompython\bin` ডিরেক্টরিতে ইনস্টল হচ্ছে। |
|||
|
|||
যদি আপনি "Yes" সিলেক্ট করে `PATH` এনভায়রনমেন্ট ভেরিয়েবল আপডেট করতে চান, তাহলে ইনস্টলার `C:\opt\custompython\bin` কে `PATH` এনভায়রনমেন্ট ভেরিয়েবল এ এড করে দিবে। |
|||
|
|||
```plaintext |
|||
C:\Program Files\Python312\Scripts;C:\Program Files\Python312;C:\Windows\System32;C:\opt\custompython\bin |
|||
``` |
|||
|
|||
এইভাবে, আপনি যখন টার্মিনালে `python` টাইপ করেন, সিস্টেম পাইথন প্রোগ্রামটিকে `C:\opt\custompython\bin` (সর্বশেষ ডিরেক্টরি) তে খুঁজে পাবে এবং এটাকে ব্যবহার করবে। |
|||
|
|||
//// |
|||
|
|||
তাই, আপনি যদি টাইপ করেনঃ |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ python |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
//// tab | লিনাক্স, ম্যাকওএস |
|||
|
|||
সিস্টেম `python` প্রোগ্রামকে `/opt/custompython/bin` এ **খুঁজে পাবে** এবং এটাকে রান করবে। |
|||
|
|||
এটা মোটামুটিভাবে নিচের মতো করে লেখার সমান হবেঃ |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ /opt/custompython/bin/python |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
//// |
|||
|
|||
//// tab | উইন্ডোজ |
|||
|
|||
সিস্টেম `python` প্রোগ্রামকে `C:\opt\custompython\bin\python` এ **খুঁজে পাবে** এবং এটাকে রান করবে। |
|||
|
|||
এটা মোটামুটিভাবে নিচের মতো করে লেখার সমান হবেঃ |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ C:\opt\custompython\bin\python |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
//// |
|||
|
|||
এই তথ্যগুলো [ভার্চুয়াল এনভায়রনমেন্টস](virtual-environments.md){.internal-link target=_blank} শেখার ক্ষেত্রে সহায়ক হবে। |
|||
|
|||
## উপসংহার |
|||
|
|||
এর মাধ্যমে আপনি **এনভায়রনমেন্ট ভেরিয়েবলস** কি এবং এটিকে পাইথনে কিভাবে ব্যবহার করতে হয় তার সম্পর্কে বেসিক ধারনা পেলেন। |
|||
|
|||
চাইলে এই সম্পর্কে আরো বিস্তারিত পড়তে পারেন <a href="https://en.wikipedia.org/wiki/Environment_variable" class="external-link" target="_blank">Wikipedia for Environment Variable</a> এ। |
|||
|
|||
অনেক ক্ষেত্রে, দেখা মাত্রই এনভায়রনমেন্ট ভেরিয়েবল কীভাবে প্রয়োজন হবে তা স্পষ্ট হয় না। কিন্তু ডেভেলপমেন্টের সময় আপনি নানা রকম পরিস্থিতিতে এগুলোর সম্মুখীন হবেন, তাই এগুলো সম্পর্কে জেনে রাখা ভালো। |
|||
|
|||
উদাহরণস্বরূপ, আপনার এই ইনফরমেশনটি পরবর্তী, [ভার্চুয়াল এনভায়রনমেন্টস](virtual-environments.md) অংশে দরকার হবে। |
After Width: | Height: | Size: 19 KiB |
@ -0,0 +1,5 @@ |
|||
# یادگیری |
|||
|
|||
اینجا بخشهای مقدماتی و آموزشهایی هستن که برای یادگیری **FastAPI** بهت کمک میکنن. |
|||
|
|||
میتونی اینو یه **کتاب**، یه **دوره آموزشی**، یا راه **رسمی** و پیشنهادی برای یادگیری FastAPI در نظر بگیری. 😎 |
@ -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`라면, 이렇게 실행합니다: |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ uvicorn main:app --reload |
|||
|
|||
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
그리고 <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>에서 문서를 여십시오. |
|||
|
|||
메인 응용프로그램의 *경로 동작*만을 포함하는, 메인 응용프로그램에 대한 자동 API 문서를 확인할 수 있습니다: |
|||
|
|||
<img src="https://fastapi.tiangolo.com//img/tutorial/sub-applications/image01.png"> |
|||
|
|||
다음으로, <a href="http://127.0.0.1:8000/subapi/docs" class="external-link" target="_blank">http://127.0.0.1:8000/subapi/docs</a>에서 하위 응용프로그램의 문서를 여십시오. |
|||
|
|||
하위 경로 접두사 `/subapi` 아래에 선언된 *경로 동작* 을 포함하는, 하위 응용프로그램에 대한 자동 API 문서를 확인할 수 있습니다: |
|||
|
|||
<img src="https://fastapi.tiangolo.com//img/tutorial/sub-applications/image02.png"> |
|||
|
|||
두 사용자 인터페이스 중 어느 하나를 사용해야하는 경우, 브라우저는 특정 응용프로그램 또는 하위 응용프로그램과 각각 통신할 수 있기 때문에 올바르게 동작할 것입니다. |
|||
|
|||
### 기술적 세부사항: `root_path` |
|||
|
|||
위에 설명된 것과 같이 하위 응용프로그램을 마운트하는 경우, FastAPI는 `root_path`라고 하는 ASGI 명세의 매커니즘을 사용하여 하위 응용프로그램에 대한 마운트 경로 통신을 처리합니다. |
|||
|
|||
이를 통해, 하위 응용프로그램은 문서 UI를 위해 경로 접두사를 사용해야 한다는 사실을 인지합니다. |
|||
|
|||
하위 응용프로그램에도 역시 다른 하위 응용프로그램을 마운트하는 것이 가능하며 FastAPI가 모든 `root_path` 들을 자동적으로 처리하기 때문에 모든 것은 올바르게 동작할 것입니다. |
|||
|
|||
`root_path`와 이것을 사용하는 방법에 대해서는 [프록시의 뒷단](./behind-a-proxy.md){.internal-link target=_blank} 섹션에서 배울 수 있습니다. |
@ -0,0 +1,21 @@ |
|||
# Расширенное руководство пользователя |
|||
|
|||
## Дополнительные возможности |
|||
|
|||
Основное [Учебник - Руководство пользователя](../tutorial/index.md){.internal-link target=_blank} должно быть достаточно, чтобы познакомить вас со всеми основными функциями **FastAPI**. |
|||
|
|||
В следующих разделах вы увидите другие варианты, конфигурации и дополнительные возможности. |
|||
|
|||
/// tip |
|||
|
|||
Следующие разделы **не обязательно являются "продвинутыми"**. |
|||
|
|||
И вполне возможно, что для вашего случая использования решение находится в одном из них. |
|||
|
|||
/// |
|||
|
|||
## Сначала прочитайте Учебник - Руководство пользователя |
|||
|
|||
Вы все еще можете использовать большинство функций **FastAPI** со знаниями из [Учебник - Руководство пользователя](../tutorial/index.md){.internal-link target=_blank}. |
|||
|
|||
И следующие разделы предполагают, что вы уже прочитали его, и предполагают, что вы знаете эти основные идеи. |
@ -0,0 +1,31 @@ |
|||
# Response - Изменение cтатус кода |
|||
|
|||
Вы, вероятно, уже читали о том, что можно установить [Состояние ответа по умолчанию](../tutorial/response-status-code.md){.internal-link target=_blank}. |
|||
|
|||
Но в некоторых случаях вам нужно вернуть код состояния, отличный от установленного по умолчанию. |
|||
|
|||
## Пример использования |
|||
|
|||
Например, представьте, что вы хотите возвращать HTTP код состояния "OK" `200` по умолчанию. |
|||
|
|||
Но если данные не существовали, вы хотите создать их и вернуть HTTP код состояния "CREATED" `201`. |
|||
|
|||
При этом вы всё ещё хотите иметь возможность фильтровать и преобразовывать возвращаемые данные с помощью `response_model`. |
|||
|
|||
Для таких случаев вы можете использовать параметр `Response`. |
|||
|
|||
## Использование параметра `Response` |
|||
|
|||
Вы можете объявить параметр типа `Response` в вашей *функции обработки пути* (так же как для cookies и headers). |
|||
|
|||
И затем вы можете установить `status_code` в этом *временном* объекте ответа. |
|||
|
|||
{* ../../docs_src/response_change_status_code/tutorial001.py hl[1,9,12] *} |
|||
|
|||
После этого вы можете вернуть любой объект, который вам нужен, как обычно (`dict`, модель базы данных и т.д.). |
|||
|
|||
И если вы объявили `response_model`, он всё равно будет использоваться для фильтрации и преобразования возвращаемого объекта. |
|||
|
|||
**FastAPI** будет использовать этот *временный* ответ для извлечения кода состояния (а также cookies и headers) и поместит их в финальный ответ, который содержит возвращаемое вами значение, отфильтрованное любым `response_model`. |
|||
|
|||
Вы также можете объявить параметр `Response` в зависимостях и установить код состояния в них. Но помните, что последнее установленное значение будет иметь приоритет. |
@ -0,0 +1,116 @@ |
|||
# Тіло – Оновлення |
|||
|
|||
## Оновлення з використанням `PUT` |
|||
|
|||
Щоб оновити елемент, Ви можете використати <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PUT" class="external-link" target="_blank">HTTP `PUT`</a> операцію. |
|||
|
|||
Ви можете використати `jsonable_encoder`, щоб перетворити вхідні дані на такі, які можна зберігати як JSON (наприклад, у NoSQL базі даних). Наприклад, перетворюючи `datetime` у `str`. |
|||
|
|||
{* ../../docs_src/body_updates/tutorial001_py310.py hl[28:33] *} |
|||
|
|||
`PUT` використовується для отримання даних, які мають замінити чинні дані. |
|||
|
|||
### Попередження про заміну |
|||
|
|||
Це означає, що якщо Ви хочете оновити елемент `bar`, використовуючи `PUT` з тілом: |
|||
|
|||
```Python |
|||
{ |
|||
"name": "Barz", |
|||
"price": 3, |
|||
"description": None, |
|||
} |
|||
``` |
|||
|
|||
оскільки він не містить вже збереженого атрибута `"tax": 20.2`, модель введення прийме значення за замовчуванням `"tax": 10.5`. |
|||
|
|||
І дані будуть збережені з цим "новим" значенням `tax` = `10.5`. |
|||
|
|||
## Часткові оновлення з `PATCH` |
|||
|
|||
Ви також можете використовувати операцію <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PATCH" class="external-link" target="_blank">HTTP `PATCH`</a> для *часткового* оновлення даних. |
|||
|
|||
Це означає, що Ви можете надіслати лише ті дані, які хочете оновити, залишаючи інші без змін. |
|||
|
|||
/// note | Примітка |
|||
|
|||
`PATCH` менш відомий і рідше використовується, ніж `PUT`. |
|||
|
|||
І багато команд використовують лише `PUT`, навіть для часткових оновлень. |
|||
|
|||
Ви **вільні** використовувати їх так, як хочете, **FastAPI** не накладає обмежень. |
|||
|
|||
Але цей посібник показує Вам більш-менш як їх задумано використовувати. |
|||
|
|||
/// |
|||
|
|||
### Використання параметра `exclude_unset` у Pydantic |
|||
|
|||
Якщо Ви хочете отримати часткові оновлення, дуже зручно використовувати параметр `exclude_unset` у методі `.model_dump()` моделі Pydantic. |
|||
|
|||
Наприклад: `item.model_dump(exclude_unset=True)`. |
|||
|
|||
/// info | Інформація |
|||
|
|||
У Pydantic v1 цей метод називався `.dict()`, він був застарілий (але все ще підтримується) у Pydantic v2, і був перейменований у `.model_dump()`. |
|||
|
|||
Приклади тут використовують `.dict()` для сумісності з Pydantic v1, але Вам слід використовувати `.model_dump()`, якщо можете використовувати Pydantic v2. |
|||
|
|||
/// |
|||
|
|||
Це створить `dict` лише з тими даними, які були явно встановлені під час створення моделі `item`, виключаючи значення за замовчуванням. |
|||
|
|||
Тоді Ви можете використовувати це, щоб створити `dict` лише з даними, які були встановлені (надіслані у запиті), пропускаючи значення за замовчуванням: |
|||
|
|||
{* ../../docs_src/body_updates/tutorial002_py310.py hl[32] *} |
|||
|
|||
### Використання параметра `update` у Pydantic |
|||
|
|||
Тепер Ви можете створити копію наявної моделі за допомогою `.model_copy()`, і передати параметр `update` з `dict` , який містить дані для оновлення. |
|||
|
|||
/// info | Інформація |
|||
|
|||
У Pydantic v1 метод називався `.copy()`, він був застарілий (але все ще підтримується) у Pydantic v2, і був перейменований у `.model_copy()`. |
|||
|
|||
Приклади тут використовують `.copy()` для сумісності з Pydantic v1, але якщо Ви можете використовувати Pydantic v2 — Вам слід використовувати `.model_copy()` замість цього. |
|||
|
|||
/// |
|||
|
|||
Наприклад: `stored_item_model.model_copy(update=update_data)`: |
|||
|
|||
{* ../../docs_src/body_updates/tutorial002_py310.py hl[33] *} |
|||
|
|||
### Підсумок часткових оновлень |
|||
|
|||
У підсумку, щоб застосувати часткові оновлення, Ви: |
|||
|
|||
* (Опціонально) використовуєте `PATCH` замість `PUT`. |
|||
* Отримуєте збережені дані. |
|||
* Поміщаєте ці дані в модель Pydantic. |
|||
* Генеруєте `dict` без значень за замовчуванням з моделі введення (використовуючи `exclude_unset`). |
|||
* Таким чином Ви оновите лише ті значення, які були явно задані користувачем, замість того, щоб перезаписувати вже збережені значення значеннями за замовчуванням з вашої моделі. |
|||
* Створюєте копію збереженої моделі, оновлюючи її атрибути отриманими частковими оновленнями (використовуючи параметр `update`). |
|||
* Перетворюєте скопійовану модель на щось, що можна зберегти у вашу БД (наприклад, використовуючи `jsonable_encoder`). |
|||
* Це можна порівняти з повторним використанням методу `.model_dump()` моделі, але це гарантує (і перетворює) значення у типи даних, які можна перетворити на JSON, наприклад, `datetime` на `str`. |
|||
* Зберігаєте дані у вашу БД. |
|||
* Повертаєте оновлену модель. |
|||
|
|||
{* ../../docs_src/body_updates/tutorial002_py310.py hl[28:35] *} |
|||
|
|||
/// tip | Порада |
|||
|
|||
Насправді Ви можете використовувати цю саму техніку і з операцією HTTP `PUT`. |
|||
|
|||
Але приклад тут використовує `PATCH`, тому що він був створений саме для таких випадків. |
|||
|
|||
/// |
|||
|
|||
/// note | Примітка |
|||
|
|||
Зверніть увагу, що модель запиту все ще проходить валідацію. |
|||
|
|||
Тож, якщо Ви хочете отримувати часткові оновлення, які можуть не містити жодного атрибута, Вам потрібно мати модель, де всі атрибути позначені як необов’язкові (зі значеннями за замовчуванням або `None`). |
|||
|
|||
Щоб розрізняти моделі з усіма необов’язковими значеннями для **оновлення** і моделі з обов’язковими значеннями для **створення**, Ви можете скористатись ідеями, описаними у [Додаткові моделі](extra-models.md){.internal-link target=_blank}. |
|||
|
|||
/// |
@ -0,0 +1,68 @@ |
|||
# Моделі Query параметрів |
|||
|
|||
Якщо у Вас є група **query параметрів**, які пов’язані між собою, Ви можете створити **Pydantic-модель** для їх оголошення. |
|||
|
|||
Це дозволить Вам **повторно використовувати модель** у **різних місцях**, а також оголошувати перевірки та метадані для всіх параметрів одночасно. 😎 |
|||
|
|||
/// note | Примітка |
|||
|
|||
Ця можливість підтримується, починаючи з версії FastAPI `0.115.0`. 🤓 |
|||
|
|||
/// |
|||
|
|||
## Query параметри з Pydantic-моделлю |
|||
|
|||
Оголосіть **query параметри**, які Вам потрібні, у **Pydantic-моделі**, а потім оголосіть цей параметр як `Query`: |
|||
|
|||
{* ../../docs_src/query_param_models/tutorial001_an_py310.py hl[9:13,17] *} |
|||
|
|||
**FastAPI** буде **витягувати** дані для **кожного поля** з **query параметрів** у запиті та передавати їх у визначену вами Pydantic-модель. |
|||
|
|||
## Перевірте документацію |
|||
|
|||
Ви можете побачити параметри запиту в UI документації за `/docs`: |
|||
|
|||
<div class="screenshot"> |
|||
<img src="/img/tutorial/query-param-models/image01.png"> |
|||
</div> |
|||
|
|||
## Заборона зайвих Query параметрів |
|||
|
|||
У деяких особливих випадках (ймовірно, не дуже поширених) Ви можете захотіти **обмежити** query параметри, які дозволено отримувати. |
|||
|
|||
Ви можете використати конфігурацію моделі Pydantic, щоб заборонити (`forbid`) будь-які зайві (`extra`) поля: |
|||
|
|||
{* ../../docs_src/query_param_models/tutorial002_an_py310.py hl[10] *} |
|||
|
|||
Якщо клієнт спробує надіслати **зайві** дані у **query параметрах**, він отримає **помилку**. |
|||
|
|||
Наприклад, якщо клієнт спробує надіслати query параметр `tool` зі значенням `plumbus`, як у цьому запиті: |
|||
|
|||
```http |
|||
https://example.com/items/?limit=10&tool=plumbus |
|||
``` |
|||
|
|||
Він отримає відповідь з **помилкою**, яка повідомить, що query параметр `tool ` не дозволено: |
|||
|
|||
```json |
|||
{ |
|||
"detail": [ |
|||
{ |
|||
"type": "extra_forbidden", |
|||
"loc": ["query", "tool"], |
|||
"msg": "Extra inputs are not permitted", |
|||
"input": "plumbus" |
|||
} |
|||
] |
|||
} |
|||
``` |
|||
|
|||
## Підсумок |
|||
|
|||
Ви можете використовувати **Pydantic-моделі** для оголошення **query параметрів** у **FastAPI**. 😎 |
|||
|
|||
/// tip | Підказка |
|||
|
|||
Спойлер: Ви також можете використовувати Pydantic-моделі для оголошення cookie та заголовків, але про це Ви дізнаєтеся пізніше в цьому посібнику. 🤫 |
|||
|
|||
/// |
@ -0,0 +1,358 @@ |
|||
# Модель відповіді — Тип, що повертається |
|||
|
|||
Ви можете оголосити тип, який використовуватиметься у відповіді, за допомогою *анотації типу, що повертається* *функцією операцією шляху* (path operation) |
|||
|
|||
**Анотацію типу** можна вказати так само як і для вхідних **параметрів** функції: це можуть бути моделі Pydantic, списки (lists), словники (dictionaries), скалярні значення, як-от цілі числа (integers), булеві значення (booleans) тощо. |
|||
|
|||
{* ../../docs_src/response_model/tutorial001_01_py310.py hl[16,21] *} |
|||
|
|||
FastAPI використовуватиме цей тип, щоб: |
|||
|
|||
* **Перевірити правильність** повернених даних. |
|||
* Якщо дані не валідні (наприклад, відсутнє поле), це означає, що Ваш код додатку працює некоректно і не повертає те, що повинен. У такому випадку FastAPI поверне помилку сервера, замість того щоб віддати недопустимі дані. Так Ви та Ваші клієнти будете впевнені, що отримуєте очікувані дані у правильному форматі. |
|||
|
|||
* Додати **JSON Schema** відповіді до специфікації OpenAPI в *операціях шляху*. |
|||
* Це буде використано в **автоматичній документації**. |
|||
* А також інструментами, які автоматично генерують клієнтський код. |
|||
|
|||
Але найголовніше: |
|||
|
|||
* FastAPI **обмежить та відфільтрує** вихідні дані відповідно до типу, вказаного у відповіді. |
|||
* Це особливо важливо для **безпеки**. Деталі нижче. |
|||
|
|||
## Параметр `response_model` |
|||
|
|||
Іноді Вам потрібно або зручно повертати інші типи даних, ніж ті, що зазначені як тип відповіді. |
|||
|
|||
Наприклад, Ви можете **повертати словник** або об’єкт бази даних, але **оголосити модель Pydantic** як модель відповіді. Тоді модель Pydantic автоматично оброблятиме валідацію, документацію тощо. |
|||
|
|||
Якщо Ви додасте анотацію типу для повернення, редактор коду або mypy можуть поскаржитися, що функція повертає інший тип (наприклад, dict замість Item). |
|||
|
|||
У таких випадках можна скористатися параметром `response_model` в декораторі маршруту (наприклад, @app.get()). |
|||
|
|||
Параметр `response_model` працює з будь-яким *оператором шляху*: |
|||
|
|||
* `@app.get()` |
|||
* `@app.post()` |
|||
* `@app.put()` |
|||
* `@app.delete()` |
|||
* тощо. |
|||
|
|||
{* ../../docs_src/response_model/tutorial001_py310.py hl[17,22,24:27] *} |
|||
|
|||
/// note | Примітка |
|||
|
|||
Зверніть увагу, що `response_model` є параметром методу-декоратора (`get`, `post`, тощо), а не *функцією операцією шляху* (path operation function), як це робиться з параметрами або тілом запиту. |
|||
|
|||
/// |
|||
|
|||
`response_model` приймає такий самий тип, який Ви б вказали для поля моделі Pydantic. Тобто це може бути як Pydantic-модель, так і, наприклад, `list` із моделей Pydantic — `List[Item]`. |
|||
|
|||
FastAPI використовуватиме `response_model` для створення документації, валідації даних та — найважливіше — **перетворення та фільтрації вихідних даних** згідно з оголошеним типом. |
|||
|
|||
/// tip | Порада |
|||
|
|||
Якщо у Вас увімкнено сувору перевірку типів у редакторі, mypy тощо, Ви можете оголосити тип повернення функції як `Any`. |
|||
|
|||
Таким чином, Ви повідомляєте редактору, що свідомо повертаєте будь-що. Але FastAPI усе одно виконуватиме створення документації, валідацію, фільтрацію тощо за допомогою параметра `response_model`. |
|||
|
|||
/// |
|||
|
|||
### Пріоритет `response_model` |
|||
|
|||
Якщо Ви вказуєте і тип повернення, і `response_model`, то FastAPI використовуватиме `response_model` з пріоритетом. |
|||
|
|||
Таким чином, Ви можете додати правильні анотації типів до ваших функцій, навіть якщо вони повертають тип, відмінний від `response_model`. Це буде корисно для редакторів коду та інструментів, таких як mypy. І при цьому FastAPI продовжить виконувати валідацію даних, генерувати документацію тощо на основі `response_model`. |
|||
|
|||
Ви також можете використати `response_model=None`, щоб вимкнути створення моделі відповіді для цієї *операції шляху*. Це може знадобитися, якщо Ви додаєте анотації типів до об'єктів, які не є допустимими полями Pydantic — приклад цього Ви побачите в одному з наступних розділів. |
|||
|
|||
## Повернути ті самі вхідні дані |
|||
|
|||
Тут ми оголошуємо модель `UserIn`, яка містить звичайний текстовий пароль: |
|||
|
|||
{* ../../docs_src/response_model/tutorial002_py310.py hl[7,9] *} |
|||
|
|||
/// info | Інформація |
|||
|
|||
Щоб використовувати `EmailStr`, спочатку встановіть <a href="https://github.com/JoshData/python-email-validator" class="external-link" target="_blank">`email-validator`</a>. |
|||
|
|||
Переконайтесь, що Ви створили [віртуальне середовище](../virtual-environments.md){.internal-link target=_blank}, активували його, а потім встановили пакет, наприклад: |
|||
|
|||
```console |
|||
$ pip install email-validator |
|||
``` |
|||
|
|||
or with: |
|||
|
|||
```console |
|||
$ pip install "pydantic[email]" |
|||
``` |
|||
|
|||
/// |
|||
|
|||
І ми використовуємо цю модель, щоб оголосити і вхідні, і вихідні дані: |
|||
|
|||
{* ../../docs_src/response_model/tutorial002_py310.py hl[16] *} |
|||
|
|||
Тепер, коли браузер створює користувача з паролем, API поверне той самий пароль у відповіді. |
|||
|
|||
У цьому випадку це може не бути проблемою, адже саме користувач надіслав пароль. |
|||
|
|||
Але якщо ми використаємо цю ж модель для іншої операції шляху, ми можемо випадково надіслати паролі наших користувачів кожному клієнту. |
|||
|
|||
/// danger | Обережно |
|||
|
|||
Ніколи не зберігайте пароль користувача у відкритому вигляді та не надсилайте його у відповіді, якщо тільки Ви не знаєте всі ризики і точно розумієте, що робите. |
|||
|
|||
/// |
|||
|
|||
## Додайте окрему вихідну модель |
|||
|
|||
Замість цього ми можемо створити вхідну модель з відкритим паролем і вихідну модель без нього: |
|||
|
|||
{* ../../docs_src/response_model/tutorial003_py310.py hl[9,11,16] *} |
|||
|
|||
Тут, навіть якщо *функція операції шляху* повертає об'єкт користувача, який містить пароль: |
|||
|
|||
{* ../../docs_src/response_model/tutorial003_py310.py hl[24] *} |
|||
|
|||
...ми оголосили `response_model` як нашу модель `UserOut`, яка не містить пароля: |
|||
|
|||
{* ../../docs_src/response_model/tutorial003_py310.py hl[22] *} |
|||
|
|||
Таким чином, **FastAPI** автоматично відфільтрує всі дані, які не вказані у вихідній моделі (за допомогою Pydantic). |
|||
|
|||
### `response_model` або тип повернення |
|||
|
|||
У цьому випадку, оскільки дві моделі різні, якщо ми анотуємо тип повернення функції як `UserOut`, редактор і такі інструменти, як mypy, видадуть помилку, бо фактично ми повертаємо інший тип. |
|||
|
|||
Тому в цьому прикладі ми використовуємо параметр `response_model`, а не анотацію типу повернення. |
|||
|
|||
...але читайте далі, щоб дізнатися, як обійти це обмеження. |
|||
|
|||
## Тип повернення і фільтрація даних |
|||
|
|||
Продовжимо з попереднього прикладу. Ми хотіли **анотувати функцію одним типом**, але при цьому повертати з неї більше даних. |
|||
|
|||
Ми хочемо, щоб FastAPI продовжував **фільтрувати** ці дані за допомогою response_model. Тобто навіть якщо функція повертає більше інформації, у відповіді будуть лише ті поля, які вказані у response_model. |
|||
|
|||
У попередньому прикладі, оскільки класи були різні, нам довелося використовувати параметр `response_model`. Але це означає, що ми не отримуємо підтримки з боку редактора коду та інструментів перевірки типів щодо типу, який повертає функція. |
|||
|
|||
Проте в більшості випадків, коли нам потрібно зробити щось подібне, ми просто хочемо, щоб модель **відфільтрувала або прибрала** частину даних, як у цьому прикладі. |
|||
|
|||
У таких випадках ми можемо використати класи та спадкування, щоб скористатися **анотаціями типів** функцій — це дає кращу підтримку з боку редактора та інструментів типу mypy, і при цьому FastAPI продовжує виконувати **фільтрацію даних** у відповіді. |
|||
|
|||
{* ../../docs_src/response_model/tutorial003_01_py310.py hl[7:10,13:14,18] *} |
|||
|
|||
Завдяки цьому ми отримуємо підтримку інструментів — від редакторів і mypy, оскільки цей код є коректним з точки зору типів, — але ми також отримуємо фільтрацію даних від FastAPI. |
|||
|
|||
Як це працює? Давайте розберемося. 🤓 |
|||
|
|||
### Типи та підтримка інструментів |
|||
|
|||
Спершу подивимось, як це бачать редактори, mypy та інші інструменти. |
|||
|
|||
`BaseUser` має базові поля. Потім `UserIn` успадковує `BaseUser` і додає поле `password`, отже, він матиме всі поля з обох моделей. |
|||
|
|||
Ми зазначаємо тип повернення функції як `BaseUser`, але фактично повертаємо екземпляр `UserIn`. |
|||
|
|||
Редактор, mypy та інші інструменти не скаржитимуться на це, тому що з точки зору типізації `UserIn` є підкласом `BaseUser`, а це означає, що він є `валідним` типом, коли очікується будь-що, що є `BaseUser`. |
|||
|
|||
### Фільтрація даних у FastAPI |
|||
|
|||
Тепер для FastAPI він бачить тип повернення і переконується, що те, що Ви повертаєте, містить **тільки** поля, які оголошені у цьому типі. |
|||
|
|||
FastAPI виконує кілька внутрішніх операцій з Pydantic, щоб гарантувати, що правила наслідування класів не застосовуються для фільтрації повернених даних, інакше Ви могли б повернути значно більше даних, ніж очікували. |
|||
|
|||
Таким чином, Ви отримуєте найкраще з двох світів: анотації типів **з підтримкою інструментів** і **фільтрацію даних**. |
|||
|
|||
## Подивитись у документації |
|||
|
|||
Коли Ви дивитесь автоматичну документацію, Ви можете побачити, що вхідна модель і вихідна модель мають власну JSON-схему: |
|||
|
|||
<img src="/img/tutorial/response-model/image01.png"> |
|||
|
|||
І обидві моделі використовуються для інтерактивної API-документації: |
|||
|
|||
<img src="/img/tutorial/response-model/image02.png"> |
|||
|
|||
## Інші анотації типів повернення |
|||
|
|||
Існують випадки, коли Ви повертаєте щось, що не є допустимим полем Pydantic, але анотуєте це у функції лише для того, щоб отримати підтримку від інструментів (редактора, mypy тощо). |
|||
|
|||
### Повернення Response напряму |
|||
|
|||
Найпоширенішим випадком буде [повернення Response напряму, як пояснюється пізніше у розширеній документації](../advanced/response-directly.md){.internal-link target=_blank}. |
|||
|
|||
{* ../../docs_src/response_model/tutorial003_02.py hl[8,10:11] *} |
|||
|
|||
Цей простий випадок автоматично обробляється FastAPI, тому що анотація типу повернення — це клас (або підклас) `Response`. |
|||
|
|||
І інструменти також будуть задоволені, бо і `RedirectResponse`, і `JSONResponse` є підкласами `Response`, отже анотація типу коректна. |
|||
|
|||
### Анотація підкласу Response |
|||
|
|||
Також можна використовувати підклас `Response` у анотації типу: |
|||
|
|||
{* ../../docs_src/response_model/tutorial003_03.py hl[8:9] *} |
|||
|
|||
Це теж працюватиме, бо `RedirectResponse` — підклас `Response`, і FastAPI автоматично обробить цей простий випадок. |
|||
|
|||
### Некоректні анотації типу повернення |
|||
|
|||
Але коли Ви повертаєте якийсь інший довільний об’єкт, що не є валідним типом Pydantic (наприклад, об’єкт бази даних), і анотуєте його так у функції, FastAPI спробує створити Pydantic модель відповіді на основі цієї анотації типу, і це завершиться помилкою. |
|||
|
|||
Те саме станеться, якщо Ви використовуєте <abbr title="Об'єднання (union) кількох типів означає: «будь-який з цих типів».">union</abbr> між різними типами, де один або більше не є валідними типами Pydantic, наприклад, це спричинить помилку 💥: |
|||
|
|||
{* ../../docs_src/response_model/tutorial003_04_py310.py hl[8] *} |
|||
|
|||
...це не працює, тому що тип анотації не є типом Pydantic і не є просто класом `Response` або його підкласом, а є об’єднанням (union) — або `Response`, або `dict`. |
|||
|
|||
### Відключення Моделі Відповіді |
|||
|
|||
Продовжуючи приклад вище, можливо, Ви не хочете використовувати стандартну валідацію даних, автоматичну документацію, фільтрацію тощо, які FastAPI виконує за замовчуванням. |
|||
|
|||
Але ви все одно можете залишити анотацію типу у функції, щоб зберегти підтримку з боку інструментів, таких як редактори коду або статичні перевірки типів (наприклад, mypy). |
|||
|
|||
У такому випадку ви можете вимкнути генерацію моделі відповіді, встановивши `response_model=None`: |
|||
|
|||
{* ../../docs_src/response_model/tutorial003_05_py310.py hl[7] *} |
|||
|
|||
Це змусить FastAPI пропустити генерацію моделі відповіді, і таким чином Ви зможете використовувати будь-які анотації типів повернення без впливу на вашу FastAPI аплікацію. 🤓 |
|||
|
|||
## Параметри кодування моделі відповіді |
|||
|
|||
Ваша модель відповіді може мати значення за замовчуванням, наприклад: |
|||
|
|||
{* ../../docs_src/response_model/tutorial004_py310.py hl[9,11:12] *} |
|||
|
|||
* `description: Union[str, None] = None` (або `str | None = None` у Python 3.10) має значення за замовчуванням `None`. |
|||
* `tax: float = 10.5` має значення за замовчуванням `10.5`. |
|||
* `tags: List[str] = []` має значення за замовчуванням порожній список: `[]`. |
|||
|
|||
Але Ви можете захотіти не включати їх у результат, якщо вони фактично не були збережені. |
|||
|
|||
Наприклад, якщо у Вас є моделі з багатьма необов’язковими атрибутами у NoSQL базі даних, але Ви не хочете відправляти дуже довгі JSON-відповіді, повні значень за замовчуванням. |
|||
|
|||
### Використовуйте параметр `response_model_exclude_unset` |
|||
|
|||
Ви можете встановити параметр декоратора шляху `response_model_exclude_unset=True`: |
|||
|
|||
{* ../../docs_src/response_model/tutorial004_py310.py hl[22] *} |
|||
|
|||
і ці значення за замовчуванням не будуть включені у відповідь, тільки фактично встановлені значення. |
|||
|
|||
Отже, якщо Ви надішлете запит до цього оператора шляху для елемента з item_id `foo`, відповідь (без включення значень за замовчуванням) буде: |
|||
|
|||
```JSON |
|||
{ |
|||
"name": "Foo", |
|||
"price": 50.2 |
|||
} |
|||
``` |
|||
|
|||
/// info | Інформація |
|||
|
|||
У Pydantic версії 1 метод називався `.dict()`, він був застарілий (але ще підтримується) у Pydantic версії 2 і перейменований у `.model_dump()`. |
|||
|
|||
Приклади тут використовують `.dict()` для сумісності з Pydantic v1, але Вам слід використовувати `.model_dump()`, якщо Ви можете використовувати Pydantic v2. |
|||
|
|||
/// |
|||
|
|||
/// info | Інформація |
|||
|
|||
FastAPI використовує `.dict()` моделі Pydantic з <a href="https://docs.pydantic.dev/1.10/usage/exporting_models/#modeldict" class="external-link" target="_blank">параметром `exclude_unset`</a>, щоб досягти цього. |
|||
|
|||
/// |
|||
|
|||
/// info | Інформація |
|||
|
|||
Ви також можете використовувати: |
|||
|
|||
* `response_model_exclude_defaults=True` |
|||
* `response_model_exclude_none=True` |
|||
|
|||
як описано в <a href="https://docs.pydantic.dev/1.10/usage/exporting_models/#modeldict" class="external-link" target="_blank">документації Pydantic</a> for `exclude_defaults` та `exclude_none`. |
|||
|
|||
/// |
|||
|
|||
#### Дані зі значеннями для полів із типовими значеннями |
|||
|
|||
Але якщо Ваші дані мають значення для полів моделі з типовими значеннями, як у елемента з item_id `bar`: |
|||
|
|||
```Python hl_lines="3 5" |
|||
{ |
|||
"name": "Bar", |
|||
"description": "The bartenders", |
|||
"price": 62, |
|||
"tax": 20.2 |
|||
} |
|||
``` |
|||
вони будуть включені у відповідь. |
|||
|
|||
#### Дані з тими самими значеннями, що й типові |
|||
|
|||
Якщо дані мають ті самі значення, що й типові, як у елемента з item_id `baz`: |
|||
|
|||
```Python hl_lines="3 5-6" |
|||
{ |
|||
"name": "Baz", |
|||
"description": None, |
|||
"price": 50.2, |
|||
"tax": 10.5, |
|||
"tags": [] |
|||
} |
|||
``` |
|||
|
|||
FastAPI достатньо розумний (насправді, Pydantic достатньо розумний), щоб зрозуміти, що, хоча `description`, `tax` і `tags` мають ті самі значення, що й типові, вони були встановлені явно (а не взяті як значення за замовчуванням). |
|||
|
|||
Отже, вони будуть включені у JSON-відповідь. |
|||
|
|||
/// tip | Порада |
|||
|
|||
Зверніть увагу, що типові значення можуть бути будь-якими, не лише `None`. |
|||
|
|||
Це може бути list (`[]`), `float` 10.5 тощо. |
|||
|
|||
/// |
|||
|
|||
### `response_model_include` та `response_model_exclude` |
|||
|
|||
Ви також можете використовувати параметри *декоратора операції шляху* `response_model_include` та `response_model_exclude`. |
|||
|
|||
Вони приймають `set` (множину) рядків (`str`) з іменами атрибутів, які потрібно включити (пропускаючи інші) або виключити (включаючи інші). |
|||
|
|||
Це можна використовувати як швидкий спосіб, якщо у Вас є лише одна модель Pydantic і Ви хочете видалити деякі дані з виводу. |
|||
|
|||
/// tip | Порада |
|||
|
|||
Але все ж рекомендується використовувати описані вище підходи, із застосуванням кількох класів, замість цих параметрів. |
|||
|
|||
|
|||
Це тому, що JSON Schema, який генерується у вашому OpenAPI додатку (і в документації), все одно буде відповідати повній моделі, навіть якщо Ви використовуєте `response_model_include` або `response_model_exclude` для виключення деяких атрибутів. |
|||
|
|||
Це також стосується `response_model_by_alias`, який працює подібним чином. |
|||
|
|||
/// |
|||
|
|||
{* ../../docs_src/response_model/tutorial005_py310.py hl[29,35] *} |
|||
|
|||
/// tip | Порада |
|||
|
|||
Синтаксис `{"name", "description"}` створює `set` з цими двома значеннями. |
|||
|
|||
Він еквівалентний `set(["name", "description"])`. |
|||
|
|||
/// |
|||
|
|||
#### Використання `list` замість `set` |
|||
|
|||
Якщо Ви забудете використати `set` і натомість застосуєте `list` або `tuple`, FastAPI все одно перетворить це на `set`, і все працюватиме правильно: |
|||
|
|||
{* ../../docs_src/response_model/tutorial006_py310.py hl[29,35] *} |
|||
|
|||
## Підсумок |
|||
|
|||
Використовуйте параметр `response_model` *декоратора операції шляху*, щоб визначати моделі відповіді, особливо щоб гарантувати фільтрацію приватних даних. |
|||
|
|||
Використовуйте `response_model_exclude_unset`, щоб повертати лише явно встановлені значення. |
@ -0,0 +1,222 @@ |
|||
# Декларування прикладів вхідних даних |
|||
|
|||
Ви можете задати приклади даних, які Ваш застосунок може отримувати. |
|||
|
|||
Ось кілька способів, як це зробити. |
|||
|
|||
## Додаткові дані JSON-схеми в моделях Pydantic |
|||
|
|||
Ви можете задати `examples` для моделі Pydantic, які буде додано до згенерованої JSON-схеми. |
|||
|
|||
//// tab | Pydantic v2 |
|||
|
|||
{* ../../docs_src/schema_extra_example/tutorial001_py310.py hl[13:24] *} |
|||
|
|||
//// |
|||
|
|||
//// tab | Pydantic v1 |
|||
|
|||
{* ../../docs_src/schema_extra_example/tutorial001_pv1_py310.py hl[13:23] *} |
|||
|
|||
//// |
|||
|
|||
Ця додаткова інформація буде додана як є до **JSON-схеми**, і вона буде використовуватися в документації до API. |
|||
|
|||
//// tab | Pydantic v2 |
|||
|
|||
У версії Pydantic 2 використовується атрибут `model_config`, який приймає `dict`, як описано в <a href="https://docs.pydantic.dev/latest/api/config/" class="external-link" target="_blank">документації Pydantic: Конфігурація</a>. |
|||
|
|||
Ви можете встановити `"json_schema_extra"` як `dict`, що містить будь-які додаткові дані, які Ви хочете відобразити у згенерованій JSON-схемі, включаючи `examples`. |
|||
|
|||
//// |
|||
|
|||
//// tab | Pydantic v1 |
|||
|
|||
У версії Pydantic 1 використовується внутрішній клас `Config` і параметр `schema_extra`, як описано в <a href="https://docs.pydantic.dev/1.10/usage/schema/#schema-customization" class="external-link" target="_blank">документації Pydantic: Налаштування схеми</a>. |
|||
|
|||
Ви можете задати `schema_extra` як `dict`, що містить будь-які додаткові дані, які Ви хочете бачити у згенерованій JSON-схемі, включаючи `examples`. |
|||
|
|||
//// |
|||
|
|||
/// tip | Підказка |
|||
|
|||
Ви можете використати ту ж техніку, щоб розширити JSON-схему і додати власну додаткову інформацію. |
|||
|
|||
Наприклад, Ви можете використати її для додавання метаданих для інтерфейсу користувача на фронтенді тощо. |
|||
|
|||
/// |
|||
|
|||
/// info | Інформація |
|||
|
|||
OpenAPI 3.1.0 (який використовується починаючи з FastAPI 0.99.0) додав підтримку `examples`, що є частиною стандарту **JSON-схеми**. |
|||
|
|||
До цього підтримувався лише ключ `example` з одним прикладом. Він все ще підтримується в OpenAPI 3.1.0, але є застарілим і не входить до стандарту JSON Schema. Тому рекомендується перейти з `example` на `examples`. 🤓 |
|||
|
|||
Більше про це можна прочитати в кінці цієї сторінки. |
|||
|
|||
/// |
|||
|
|||
## Додаткові аргументи `Field` |
|||
|
|||
Коли ви використовуєте `Field()` у моделях Pydantic, Ви також можете вказати додаткові `examples`: |
|||
|
|||
{* ../../docs_src/schema_extra_example/tutorial002_py310.py hl[2,8:11] *} |
|||
|
|||
## `examples` у JSON-схемі — OpenAPI |
|||
|
|||
При використанні будь-кого з наступного: |
|||
|
|||
* `Path()` |
|||
* `Query()` |
|||
* `Header()` |
|||
* `Cookie()` |
|||
* `Body()` |
|||
* `Form()` |
|||
* `File()` |
|||
|
|||
Ви також можете задати набір `examples` з додатковою інформацією, яка буде додана до їхніх **JSON-схем** у **OpenAPI**. |
|||
|
|||
### `Body` з `examples` |
|||
|
|||
Тут ми передаємо `examples`, які містять один приклад очікуваних даних у `Body()`: |
|||
|
|||
{* ../../docs_src/schema_extra_example/tutorial003_an_py310.py hl[22:29] *} |
|||
|
|||
### Приклад у UI документації |
|||
|
|||
За допомогою будь-якого з наведених вище методів це виглядатиме так у документації за `/docs`: |
|||
|
|||
<img src="/img/tutorial/body-fields/image01.png"> |
|||
|
|||
### `Body` з кількома `examples` |
|||
|
|||
Звичайно, Ви також можете передати кілька `examples`: |
|||
|
|||
{* ../../docs_src/schema_extra_example/tutorial004_an_py310.py hl[23:38] *} |
|||
|
|||
Коли Ви це робите, приклади будуть частиною внутрішньої **JSON-схеми** для цих даних. |
|||
|
|||
Втім, на момент написання цього (<abbr title="2023-08-26">26 серпня 2023</abbr>), Swagger UI — інструмент, який відповідає за відображення UI документації — не підтримує показ кількох прикладів у **JSON-схеми**. Але нижче можна прочитати про обхідний шлях. |
|||
|
|||
### Специфічні для OpenAPI `examples` |
|||
|
|||
Ще до того, як **JSON-схема** почала підтримувати `examples`, OpenAPI вже мала підтримку поля з такою ж назвою — `examples`. |
|||
|
|||
Це **специфічне для OpenAPI** поле `examples` розміщується в іншій частині специфікації OpenAPI — у **деталях кожної *операції шляху***, а не всередині самої JSON-схеми. |
|||
|
|||
Swagger UI вже давно підтримує це поле `examples`. Тому Ви можете використовувати його, щоб **відображати** кілька **прикладів у документації**. |
|||
|
|||
Це поле `examples` у специфікації OpenAPI — це `dict` (словник) з **кількома прикладами** (а не список `list`), кожен із яких може містити додаткову інформацію, що буде додана до **OpenAPI**. |
|||
|
|||
Воно не включається до JSON Schema кожного параметра, а розміщується зовні, безпосередньо в *операції шляху*. |
|||
|
|||
### Використання параметра `openapi_examples` |
|||
|
|||
Ви можете оголосити специфічні для OpenAPI `examples` у FastAPI за допомогою параметра `openapi_examples` для: |
|||
|
|||
* `Path()` |
|||
* `Query()` |
|||
* `Header()` |
|||
* `Cookie()` |
|||
* `Body()` |
|||
* `Form()` |
|||
* `File()` |
|||
|
|||
Ключі словника (`dict`) ідентифікують кожен приклад, а кожне значення `dict` — кожен специфічний словник `dict` в `examples` може містити: |
|||
|
|||
* `summary`: короткий опис прикладу. |
|||
* `description`: розгорнутий опис (може містити Markdown). |
|||
* `value`: сам приклад, наприклад, словник (`dict`). |
|||
* `externalValue`: альтернатива `value`, URL-адреса, що вказує на приклад. Проте ця опція може не підтримуватися більшістю інструментів, на відміну від `value`. |
|||
|
|||
Використання виглядає так: |
|||
|
|||
{* ../../docs_src/schema_extra_example/tutorial005_an_py310.py hl[23:49] *} |
|||
|
|||
### Приклади OpenAPI у UI документації |
|||
|
|||
З параметром `openapi_examples`, доданим до `Body()`, документація `/docs` виглядатиме так: |
|||
|
|||
<img src="/img/tutorial/body-fields/image02.png"> |
|||
|
|||
## Технічні деталі |
|||
|
|||
/// tip | Підказка |
|||
|
|||
Якщо Ви вже використовуєте **FastAPI** версії **0.99.0 або вище**, Ви можете **пропустити** цей розділ. |
|||
|
|||
Він більш актуальний для старих версій, до появи OpenAPI 3.1.0. |
|||
|
|||
Можна вважати це коротким **історичним екскурсом** у OpenAPI та JSON Schema. 🤓 |
|||
|
|||
/// |
|||
|
|||
/// warning | Попередження |
|||
|
|||
Це дуже технічна інформація про стандарти **JSON Schema** і **OpenAPI**. |
|||
|
|||
Якщо вищезгадані ідеї вже працюють у Вас — можете не заглиблюватися в ці деталі. |
|||
|
|||
/// |
|||
|
|||
До OpenAPI 3.1.0 специфікація використовувала стару та модифіковану версію **JSON Schema**. |
|||
|
|||
Оскільки JSON Schema раніше не підтримувала `examples`, OpenAPI додала власне поле `examples`. |
|||
|
|||
OpenAPI також додала `example` і `examples` до інших частин специфікації: |
|||
|
|||
* <a href="https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#parameter-object" class="external-link" target="_blank">`Parameter Object` (в специфікації)</a> використовується FastAPI для: |
|||
* `Path()` |
|||
* `Query()` |
|||
* `Header()` |
|||
* `Cookie()` |
|||
* <a href="https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#media-type-object" class="external-link" target="_blank">`Request Body Object`, в полі `content`, в `Media Type Object` (в специфікації)</a> використовується FastAPI для: |
|||
* `Body()` |
|||
* `File()` |
|||
* `Form()` |
|||
|
|||
/// info | Інформація |
|||
|
|||
Цей старий параметр `examples`, специфічний для OpenAPI, тепер називається `openapi_examples`, починаючи з FastAPI версії `0.103.0`. |
|||
|
|||
/// |
|||
|
|||
### Поле `examples` у JSON Schema |
|||
|
|||
Пізніше JSON Schema додала поле <a href="https://json-schema.org/draft/2019-09/json-schema-validation.html#rfc.section.9.5" class="external-link" target="_blank">`examples`</a> у нову версію специфікації. |
|||
|
|||
І вже OpenAPI 3.1.0 базується на цій новій версії (JSON Schema 2020-12), яка включає поле `examples`. |
|||
|
|||
Тепер це поле `examples` є пріоритетним і замінює старе (і кастомне) поле `example`, яке стало застарілим. |
|||
|
|||
Нове поле `examples` у JSON Schema — це **просто список (`list`)** прикладів, без додаткових метаданих (на відміну від OpenAPI). |
|||
|
|||
/// info | Інформація |
|||
|
|||
Навіть після того, як з'явився OpenAPI 3.1.0, який підтримував examples у JSON Schema, інструмент Swagger UI ще деякий час не підтримував цю версію (підтримка з’явилась з версії 5.0.0 🎉). |
|||
|
|||
Через це версії FastAPI до 0.99.0 все ще використовували версії OpenAPI нижчі за 3.1.0. |
|||
|
|||
/// |
|||
|
|||
### `Examples` в Pydantic і FastAPI |
|||
|
|||
Коли Ви додаєте `examples` у модель Pydantic через `schema_extra` або `Field(examples=["something"])`, ці приклади додаються до **JSON Schema** цієї моделі. |
|||
|
|||
І ця **JSON Schema** Pydantic-моделі включається до **OpenAPI** Вашого API, а потім використовується в UI документації (docs UI). |
|||
|
|||
У версіях FastAPI до 0.99.0 (починаючи з 0.99.0 використовується новіший OpenAPI 3.1.0), коли Ви використовували `example` або `examples` з іншими утилітами (`Query()`, `Body()` тощо), ці приклади не додавалися до JSON Schema, який описує ці дані (навіть не до власної версії JSON Schema у OpenAPI). Натомість вони додавалися безпосередньо до опису *обробника шляху* *(path operation)* в OpenAPI (тобто поза межами частин, які використовують JSON Schema). |
|||
|
|||
Але тепер, коли FastAPI 0.99.0 і вище використовують OpenAPI 3.1.0, а той — JSON Schema 2020-12, разом із Swagger UI 5.0.0 і вище — все стало більш узгодженим, і examples тепер включаються до JSON Schema. |
|||
|
|||
### Swagger UI та специфічні для OpenAPI `examples` |
|||
|
|||
Раніше (станом на 26 серпня 2023 року) Swagger UI не підтримував кілька прикладів у JSON Schema, тому користувачі не мали можливості показати декілька прикладів у документації. |
|||
|
|||
Щоб вирішити це, FastAPI починаючи з версії 0.103.0 **додав підтримку** старого **OpenAPI-специфічного** поля `examples` через новий параметр `openapi_examples`. 🤓 |
|||
|
|||
### Підсумок |
|||
|
|||
Раніше я казав, що не люблю історію... а тепер ось я — розповідаю "технічні історичні" лекції. 😅 |
|||
|
|||
Коротко: **оновіться до FastAPI 0.99.0 або вище** — і все стане значно **простішим, узгодженим та інтуїтивно зрозумілим**, і Вам не доведеться знати всі ці історичні деталі. 😎 |
@ -0,0 +1,104 @@ |
|||
# Безпека |
|||
|
|||
Існує багато способів реалізувати безпеку, автентифікацію та авторизацію. |
|||
|
|||
Це зазвичай складна і "непроста" тема. |
|||
|
|||
У багатьох фреймворках і системах забезпечення безпеки та автентифікації займає величезну частину зусиль і коду (іноді — понад 50% всього написаного коду). |
|||
|
|||
**FastAPI** надає кілька інструментів, які допоможуть Вам впоратися з **безпекою** легко, швидко, стандартним способом, без необхідності вивчати всі специфікації безпеки. |
|||
|
|||
Але спочатку — кілька коротких понять. |
|||
|
|||
## Поспішаєте? |
|||
|
|||
Якщо Вам не цікаві всі ці терміни й просто потрібно *швидко* додати автентифікацію за логіном і паролем — переходьте до наступних розділів. |
|||
|
|||
## OAuth2 |
|||
|
|||
OAuth2 — це специфікація, що описує кілька способів обробки автентифікації та авторизації. |
|||
|
|||
Це досить об'ємна специфікація, яка охоплює складні випадки використання. |
|||
|
|||
Вона включає способи автентифікації через "третю сторону". |
|||
|
|||
Саме це лежить в основі "входу через Google, Facebook, X (Twitter), GitHub" тощо. |
|||
|
|||
### OAuth 1 |
|||
|
|||
Раніше існував OAuth 1, який значно відрізняється від OAuth2 і є складнішим, оскільки містив специфікації для шифрування комунікацій. |
|||
|
|||
Зараз майже не використовується. |
|||
|
|||
OAuth2 не вказує, як саме шифрувати з'єднання — воно очікує, що ваш застосунок працює через HTTPS. |
|||
|
|||
/// tip | Порада |
|||
|
|||
У розділі про **деплой** Ви побачите, як налаштувати HTTPS безкоштовно з Traefik та Let's Encrypt. |
|||
|
|||
/// |
|||
|
|||
## OpenID Connect |
|||
|
|||
OpenID Connect — ще одна специфікація, побудована на основі **OAuth2**. |
|||
|
|||
Вона розширює OAuth2, уточнюючи деякі неоднозначності для досягнення кращої сумісності. |
|||
|
|||
Наприклад, вхід через Google використовує OpenID Connect (який базується на OAuth2). |
|||
|
|||
Але вхід через Facebook — ні. Він має власну реалізацію на базі OAuth2. |
|||
|
|||
### OpenID (не "OpenID Connect") |
|||
|
|||
Існувала також специфікація "OpenID", яка намагалася розвʼязати ті самі задачі, що й **OpenID Connect**, але не базувалась на OAuth2. |
|||
|
|||
Це була зовсім інша система, і сьогодні вона майже не використовується. |
|||
|
|||
## OpenAPI |
|||
|
|||
OpenAPI (раніше Swagger) — це специфікація для побудови API (тепер під егідою Linux Foundation). |
|||
|
|||
**FastAPI** базується на **OpenAPI**. |
|||
|
|||
Завдяки цьому Ви отримуєте автоматичну інтерактивну документацію, генерацію коду та багато іншого. |
|||
|
|||
OpenAPI дозволяє описувати різні "схеми" безпеки. |
|||
|
|||
Використовуючи їх, Ви можете скористатися всіма цими інструментами, що базуються на стандартах, зокрема інтерактивними системами документації. |
|||
|
|||
OpenAPI визначає такі схеми безпеки: |
|||
|
|||
* `apiKey`: специфічний для застосунку ключ, який може передаватися через: |
|||
* Параметр запиту. |
|||
* Заголовок. |
|||
* Cookie. |
|||
* `http`: стандартні методи HTTP-автентифікації, включаючи: |
|||
* `bearer`: заголовок `Authorization` зі значенням `Bearer` та токеном. Це успадковано з OAuth2. |
|||
* HTTP Basic автентифікація |
|||
* HTTP Digest, тощо. |
|||
* `oauth2`: усі способи обробки безпеки за допомогою OAuth2 (так звані «потоки»). |
|||
* Деякі з цих потоків підходять для створення власного провайдера автентифікації OAuth 2.0 (наприклад, Google, Facebook, X (Twitter), GitHub тощо): |
|||
* `implicit`— неявний |
|||
* `clientCredentials`— облікові дані клієнта |
|||
* `authorizationCode` — код авторизації |
|||
* Але є один окремий «потік», який ідеально підходить для реалізації автентифікації всередині одного додатку: |
|||
* `password`: у наступних розділах буде приклад використання цього потоку. |
|||
* `openIdConnect`: дозволяє автоматично виявляти параметри автентифікації OAuth2. |
|||
* Це автоматичне виявлення визначається у специфікації OpenID Connect. |
|||
|
|||
|
|||
/// tip | Порада |
|||
|
|||
Інтеграція інших провайдерів автентифікації/авторизації, таких як Google, Facebook, X (Twitter), GitHub тощо — також можлива і відносно проста. |
|||
|
|||
Найскладніше — це створити власного провайдера автентифікації/авторизації, як Google чи Facebook. Але **FastAPI** надає Вам інструменти, щоб зробити це легко, беручи на себе важку частину роботи. |
|||
|
|||
/// |
|||
|
|||
## Інструменти **FastAPI** |
|||
|
|||
FastAPI надає кілька інструментів для кожної з описаних схем безпеки в модулі `fastapi.security`, які спрощують використання цих механізмів захисту. |
|||
|
|||
У наступних розділах Ви побачите, як додати безпеку до свого API за допомогою цих інструментів **FastAPI**. |
|||
|
|||
А також побачите, як вона автоматично інтегрується в інтерактивну документацію вашого API. |
@ -0,0 +1,31 @@ |
|||
from fastapi import FastAPI |
|||
from fastapi.testclient import TestClient |
|||
from pydantic import BaseModel |
|||
|
|||
app = FastAPI() |
|||
|
|||
|
|||
class MyModel(BaseModel): |
|||
""" |
|||
A model with a form feed character in the title. |
|||
\f |
|||
Text after form feed character. |
|||
""" |
|||
|
|||
|
|||
@app.get("/foo") |
|||
def foo(v: MyModel): # pragma: no cover |
|||
pass |
|||
|
|||
|
|||
client = TestClient(app) |
|||
|
|||
|
|||
def test_openapi(): |
|||
response = client.get("/openapi.json") |
|||
assert response.status_code == 200, response.text |
|||
openapi_schema = response.json() |
|||
|
|||
assert openapi_schema["components"]["schemas"]["MyModel"]["description"] == ( |
|||
"A model with a form feed character in the title.\n" |
|||
) |
@ -1,19 +0,0 @@ |
|||
from fastapi.testclient import TestClient |
|||
from pytest import MonkeyPatch |
|||
|
|||
from ...utils import needs_pydanticv1 |
|||
|
|||
|
|||
@needs_pydanticv1 |
|||
def test_settings(monkeypatch: MonkeyPatch): |
|||
monkeypatch.setenv("ADMIN_EMAIL", "admin@example.com") |
|||
from docs_src.settings.tutorial001_pv1 import app |
|||
|
|||
client = TestClient(app) |
|||
response = client.get("/info") |
|||
assert response.status_code == 200, response.text |
|||
assert response.json() == { |
|||
"app_name": "Awesome API", |
|||
"admin_email": "admin@example.com", |
|||
"items_per_user": 50, |
|||
} |
@ -0,0 +1,156 @@ |
|||
from typing import Union |
|||
|
|||
from fastapi import FastAPI, Form |
|||
from fastapi.testclient import TestClient |
|||
from pydantic import BaseModel |
|||
from typing_extensions import Annotated |
|||
|
|||
app = FastAPI() |
|||
|
|||
|
|||
class UserForm(BaseModel): |
|||
name: str |
|||
email: str |
|||
|
|||
|
|||
class CompanyForm(BaseModel): |
|||
company_name: str |
|||
industry: str |
|||
|
|||
|
|||
@app.post("/form-union/") |
|||
def post_union_form(data: Annotated[Union[UserForm, CompanyForm], Form()]): |
|||
return {"received": data} |
|||
|
|||
|
|||
client = TestClient(app) |
|||
|
|||
|
|||
def test_post_user_form(): |
|||
response = client.post( |
|||
"/form-union/", data={"name": "John Doe", "email": "john@example.com"} |
|||
) |
|||
assert response.status_code == 200, response.text |
|||
assert response.json() == { |
|||
"received": {"name": "John Doe", "email": "john@example.com"} |
|||
} |
|||
|
|||
|
|||
def test_post_company_form(): |
|||
response = client.post( |
|||
"/form-union/", data={"company_name": "Tech Corp", "industry": "Technology"} |
|||
) |
|||
assert response.status_code == 200, response.text |
|||
assert response.json() == { |
|||
"received": {"company_name": "Tech Corp", "industry": "Technology"} |
|||
} |
|||
|
|||
|
|||
def test_invalid_form_data(): |
|||
response = client.post( |
|||
"/form-union/", |
|||
data={"name": "John", "company_name": "Tech Corp"}, |
|||
) |
|||
assert response.status_code == 422, response.text |
|||
|
|||
|
|||
def test_empty_form(): |
|||
response = client.post("/form-union/") |
|||
assert response.status_code == 422, response.text |
|||
|
|||
|
|||
def test_openapi_schema(): |
|||
response = client.get("/openapi.json") |
|||
assert response.status_code == 200, response.text |
|||
|
|||
assert response.json() == { |
|||
"openapi": "3.1.0", |
|||
"info": {"title": "FastAPI", "version": "0.1.0"}, |
|||
"paths": { |
|||
"/form-union/": { |
|||
"post": { |
|||
"summary": "Post Union Form", |
|||
"operationId": "post_union_form_form_union__post", |
|||
"requestBody": { |
|||
"content": { |
|||
"application/x-www-form-urlencoded": { |
|||
"schema": { |
|||
"anyOf": [ |
|||
{"$ref": "#/components/schemas/UserForm"}, |
|||
{"$ref": "#/components/schemas/CompanyForm"}, |
|||
], |
|||
"title": "Data", |
|||
} |
|||
} |
|||
}, |
|||
"required": True, |
|||
}, |
|||
"responses": { |
|||
"200": { |
|||
"description": "Successful Response", |
|||
"content": {"application/json": {"schema": {}}}, |
|||
}, |
|||
"422": { |
|||
"description": "Validation Error", |
|||
"content": { |
|||
"application/json": { |
|||
"schema": { |
|||
"$ref": "#/components/schemas/HTTPValidationError" |
|||
} |
|||
} |
|||
}, |
|||
}, |
|||
}, |
|||
} |
|||
} |
|||
}, |
|||
"components": { |
|||
"schemas": { |
|||
"CompanyForm": { |
|||
"properties": { |
|||
"company_name": {"type": "string", "title": "Company Name"}, |
|||
"industry": {"type": "string", "title": "Industry"}, |
|||
}, |
|||
"type": "object", |
|||
"required": ["company_name", "industry"], |
|||
"title": "CompanyForm", |
|||
}, |
|||
"HTTPValidationError": { |
|||
"properties": { |
|||
"detail": { |
|||
"items": {"$ref": "#/components/schemas/ValidationError"}, |
|||
"type": "array", |
|||
"title": "Detail", |
|||
} |
|||
}, |
|||
"type": "object", |
|||
"title": "HTTPValidationError", |
|||
}, |
|||
"UserForm": { |
|||
"properties": { |
|||
"name": {"type": "string", "title": "Name"}, |
|||
"email": {"type": "string", "title": "Email"}, |
|||
}, |
|||
"type": "object", |
|||
"required": ["name", "email"], |
|||
"title": "UserForm", |
|||
}, |
|||
"ValidationError": { |
|||
"properties": { |
|||
"loc": { |
|||
"items": { |
|||
"anyOf": [{"type": "string"}, {"type": "integer"}] |
|||
}, |
|||
"type": "array", |
|||
"title": "Location", |
|||
}, |
|||
"msg": {"type": "string", "title": "Message"}, |
|||
"type": {"type": "string", "title": "Error Type"}, |
|||
}, |
|||
"type": "object", |
|||
"required": ["loc", "msg", "type"], |
|||
"title": "ValidationError", |
|||
}, |
|||
} |
|||
}, |
|||
} |
@ -1,51 +0,0 @@ |
|||
from typing import List |
|||
|
|||
from fastapi import FastAPI |
|||
from pydantic import BaseModel |
|||
|
|||
app = FastAPI() |
|||
|
|||
|
|||
class RecursiveItem(BaseModel): |
|||
sub_items: List["RecursiveItem"] = [] |
|||
name: str |
|||
|
|||
|
|||
RecursiveItem.model_rebuild() |
|||
|
|||
|
|||
class RecursiveSubitemInSubmodel(BaseModel): |
|||
sub_items2: List["RecursiveItemViaSubmodel"] = [] |
|||
name: str |
|||
|
|||
|
|||
class RecursiveItemViaSubmodel(BaseModel): |
|||
sub_items1: List[RecursiveSubitemInSubmodel] = [] |
|||
name: str |
|||
|
|||
|
|||
RecursiveSubitemInSubmodel.model_rebuild() |
|||
RecursiveItemViaSubmodel.model_rebuild() |
|||
|
|||
|
|||
@app.get("/items/recursive", response_model=RecursiveItem) |
|||
def get_recursive(): |
|||
return {"name": "item", "sub_items": [{"name": "subitem", "sub_items": []}]} |
|||
|
|||
|
|||
@app.get("/items/recursive-submodel", response_model=RecursiveItemViaSubmodel) |
|||
def get_recursive_submodel(): |
|||
return { |
|||
"name": "item", |
|||
"sub_items1": [ |
|||
{ |
|||
"name": "subitem", |
|||
"sub_items2": [ |
|||
{ |
|||
"name": "subsubitem", |
|||
"sub_items1": [{"name": "subsubsubitem", "sub_items2": []}], |
|||
} |
|||
], |
|||
} |
|||
], |
|||
} |
@ -1,12 +1,9 @@ |
|||
from fastapi.testclient import TestClient |
|||
|
|||
from ..utils import needs_pydanticv2 |
|||
from .app import app |
|||
|
|||
|
|||
@needs_pydanticv2 |
|||
def test_recursive(): |
|||
from .app_pv2 import app |
|||
|
|||
client = TestClient(app) |
|||
response = client.get("/items/recursive") |
|||
assert response.status_code == 200, response.text |
@ -1,33 +0,0 @@ |
|||
from fastapi.testclient import TestClient |
|||
|
|||
from ..utils import needs_pydanticv1 |
|||
|
|||
|
|||
@needs_pydanticv1 |
|||
def test_recursive(): |
|||
from .app_pv1 import app |
|||
|
|||
client = TestClient(app) |
|||
response = client.get("/items/recursive") |
|||
assert response.status_code == 200, response.text |
|||
assert response.json() == { |
|||
"sub_items": [{"name": "subitem", "sub_items": []}], |
|||
"name": "item", |
|||
} |
|||
|
|||
response = client.get("/items/recursive-submodel") |
|||
assert response.status_code == 200, response.text |
|||
assert response.json() == { |
|||
"name": "item", |
|||
"sub_items1": [ |
|||
{ |
|||
"name": "subitem", |
|||
"sub_items2": [ |
|||
{ |
|||
"name": "subsubitem", |
|||
"sub_items1": [{"name": "subsubsubitem", "sub_items2": []}], |
|||
} |
|||
], |
|||
} |
|||
], |
|||
} |
Loading…
Reference in new issue