committed by
GitHub
101 changed files with 3633 additions and 228 deletions
@ -15,7 +15,7 @@ jobs: |
|||
run: echo "$GITHUB_CONTEXT" |
|||
- uses: actions/checkout@v4 |
|||
- name: Set up Python |
|||
uses: actions/setup-python@v4 |
|||
uses: actions/setup-python@v5 |
|||
with: |
|||
python-version: "3.10" |
|||
# Issue ref: https://github.com/actions/setup-python/issues/436 |
|||
@ -32,7 +32,7 @@ jobs: |
|||
- name: Build distribution |
|||
run: python -m build |
|||
- name: Publish |
|||
uses: pypa/[email protected]0 |
|||
uses: pypa/[email protected]1 |
|||
with: |
|||
password: ${{ secrets.PYPI_API_TOKEN }} |
|||
- name: Dump GitHub context |
|||
|
@ -0,0 +1,126 @@ |
|||
# Hintergrundtasks |
|||
|
|||
Sie können Hintergrundtasks (Hintergrund-Aufgaben) 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. |
|||
|
|||
Hierzu zählen beispielsweise: |
|||
|
|||
* E-Mail-Benachrichtigungen, die nach dem Ausführen einer Aktion gesendet werden: |
|||
* Da die Verbindung zu einem E-Mail-Server und das Senden einer E-Mail in der Regel „langsam“ ist (einige Sekunden), können Sie die Response sofort zurücksenden und die E-Mail-Benachrichtigung im Hintergrund senden. |
|||
* 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 |
|||
|
|||
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!} |
|||
``` |
|||
|
|||
**FastAPI** erstellt für Sie das Objekt vom Typ `BackgroundTasks` und übergibt es als diesen Parameter. |
|||
|
|||
## Eine Taskfunktion erstellen |
|||
|
|||
Erstellen Sie eine Funktion, die als Hintergrundtask ausgeführt werden soll. |
|||
|
|||
Es handelt sich schlicht um eine Standard-Funktion, die Parameter empfangen kann. |
|||
|
|||
Es kann sich um eine `async def`- oder normale `def`-Funktion handeln. **FastAPI** weiß, wie damit zu verfahren ist. |
|||
|
|||
In diesem Fall schreibt die Taskfunktion in eine Datei (den Versand einer E-Mail simulierend). |
|||
|
|||
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!} |
|||
``` |
|||
|
|||
## Den Hintergrundtask hinzufügen |
|||
|
|||
Ü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!} |
|||
``` |
|||
|
|||
`.add_task()` erhält als Argumente: |
|||
|
|||
* Eine Taskfunktion, die im Hintergrund ausgeführt wird (`write_notification`). |
|||
* 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 |
|||
|
|||
Die Verwendung von `BackgroundTasks` funktioniert auch mit dem <abbr title="Einbringen von Abhängigkeiten">Dependency Injection</abbr> 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: |
|||
|
|||
=== "Python 3.10+" |
|||
|
|||
```Python hl_lines="13 15 22 25" |
|||
{!> ../../../docs_src/background_tasks/tutorial002_an_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.9+" |
|||
|
|||
```Python hl_lines="13 15 22 25" |
|||
{!> ../../../docs_src/background_tasks/tutorial002_an_py39.py!} |
|||
``` |
|||
|
|||
=== "Python 3.8+" |
|||
|
|||
```Python hl_lines="14 16 23 26" |
|||
{!> ../../../docs_src/background_tasks/tutorial002_an.py!} |
|||
``` |
|||
|
|||
=== "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!} |
|||
``` |
|||
|
|||
=== "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. |
|||
|
|||
Wenn im Request ein Query-Parameter enthalten war, wird dieser in einem Hintergrundtask in das Log geschrieben. |
|||
|
|||
Und dann schreibt ein weiterer Hintergrundtask, der in der *Pfadoperation-Funktion* erstellt wird, eine Nachricht unter Verwendung des Pfad-Parameters `email`. |
|||
|
|||
## Technische Details |
|||
|
|||
Die Klasse `BackgroundTasks` stammt direkt von <a href="https://www.starlette.io/background/" class="external-link" target="_blank">`starlette.background`</a>. |
|||
|
|||
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. |
|||
|
|||
Indem Sie nur `BackgroundTasks` (und nicht `BackgroundTask`) verwenden, ist es dann möglich, es als *Pfadoperation-Funktion*-Parameter zu verwenden und **FastAPI** den Rest für Sie erledigen zu lassen, genau wie bei der direkten Verwendung des `Request`-Objekts. |
|||
|
|||
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 <a href="https://www.starlette.io/background/" class="external-link" target="_blank">offiziellen Starlette-Dokumentation für Hintergrundtasks</a>. |
|||
|
|||
## Vorbehalt |
|||
|
|||
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. <a href="https://docs.celeryq.dev" class="external-link" target="_blank">Celery</a> 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**-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 |
|||
|
|||
Importieren und verwenden Sie `BackgroundTasks` mit Parametern in *Pfadoperation-Funktionen* und Abhängigkeiten, um Hintergrundtasks hinzuzufügen. |
@ -0,0 +1,80 @@ |
|||
# Tutorial – Benutzerhandbuch |
|||
|
|||
Dieses Tutorial zeigt Ihnen Schritt für Schritt, wie Sie **FastAPI** und die meisten seiner Funktionen verwenden können. |
|||
|
|||
Jeder Abschnitt baut schrittweise auf den vorhergehenden auf. Diese Abschnitte sind aber nach einzelnen Themen gegliedert, sodass Sie direkt zu einem bestimmten Thema übergehen können, um Ihre speziellen API-Anforderungen zu lösen. |
|||
|
|||
Außerdem dienen diese als zukünftige Referenz. |
|||
|
|||
Dadurch können Sie jederzeit zurückkommen und sehen genau das, was Sie benötigen. |
|||
|
|||
## Den Code ausführen |
|||
|
|||
Alle Codeblöcke können kopiert und direkt verwendet werden (da es sich um getestete Python-Dateien handelt). |
|||
|
|||
Um eines der Beispiele auszuführen, kopieren Sie den Code in eine Datei `main.py`, und starten Sie `uvicorn` mit: |
|||
|
|||
<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) |
|||
<span style="color: green;">INFO</span>: Started reloader process [28720] |
|||
<span style="color: green;">INFO</span>: Started server process [28722] |
|||
<span style="color: green;">INFO</span>: Waiting for application startup. |
|||
<span style="color: green;">INFO</span>: Application startup complete. |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
Es wird **ausdrücklich empfohlen**, dass Sie den Code schreiben oder kopieren, ihn bearbeiten und lokal ausführen. |
|||
|
|||
Die Verwendung in Ihrem eigenen Editor zeigt Ihnen die Vorteile von FastAPI am besten, wenn Sie sehen, wie wenig Code Sie schreiben müssen, all die Typprüfungen, die automatische Vervollständigung usw. |
|||
|
|||
--- |
|||
|
|||
## FastAPI installieren |
|||
|
|||
Der erste Schritt besteht aus der Installation von FastAPI. |
|||
|
|||
Für dieses Tutorial empfiehlt es sich, FastAPI mit allen optionalen Abhängigkeiten und Funktionen zu installieren: |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ pip install "fastapi[all]" |
|||
|
|||
---> 100% |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
... das beinhaltet auch `uvicorn`, welchen Sie als Server verwenden können, der ihren Code ausführt. |
|||
|
|||
!!! note "Hinweis" |
|||
Sie können die einzelnen Teile auch separat installieren. |
|||
|
|||
Das folgende würden Sie wahrscheinlich tun, wenn Sie Ihre Anwendung in der Produktion einsetzen: |
|||
|
|||
``` |
|||
pip install fastapi |
|||
``` |
|||
|
|||
Installieren Sie auch `uvicorn` als Server: |
|||
|
|||
``` |
|||
pip install "uvicorn[standard]" |
|||
``` |
|||
|
|||
Das gleiche gilt für jede der optionalen Abhängigkeiten, die Sie verwenden möchten. |
|||
|
|||
## Handbuch für fortgeschrittene Benutzer |
|||
|
|||
Es gibt auch ein **Handbuch für fortgeschrittene Benutzer**, welches Sie später nach diesem **Tutorial – Benutzerhandbuch** lesen können. |
|||
|
|||
Das **Handbuch für fortgeschrittene Benutzer** baut auf diesem Tutorial auf, verwendet dieselben Konzepte und bringt Ihnen einige zusätzliche Funktionen bei. |
|||
|
|||
Allerdings sollten Sie zuerst das **Tutorial – Benutzerhandbuch** lesen (was Sie hier gerade tun). |
|||
|
|||
Die Dokumentation ist so konzipiert, dass Sie mit dem **Tutorial – Benutzerhandbuch** eine vollständige Anwendung erstellen können und diese dann je nach Bedarf mit einigen der zusätzlichen Ideen aus dem **Handbuch für fortgeschrittene Benutzer** vervollständigen können. |
@ -0,0 +1,315 @@ |
|||
# Pythonの型の紹介 |
|||
|
|||
**Python 3.6以降** では「型ヒント」オプションがサポートされています。 |
|||
|
|||
これらの **"型ヒント"** は変数の<abbr title="例: str, int, float, bool">型</abbr>を宣言することができる新しい構文です。(Python 3.6以降) |
|||
|
|||
変数に型を宣言することでエディターやツールがより良いサポートを提供することができます。 |
|||
|
|||
ここではPythonの型ヒントについての **クイックチュートリアル/リフレッシュ** で、**FastAPI**でそれらを使用するために必要な最低限のことだけをカバーしています。...実際には本当に少ないです。 |
|||
|
|||
**FastAPI** はすべてこれらの型ヒントに基づいており、多くの強みと利点を与えてくれます。 |
|||
|
|||
しかしたとえまったく **FastAPI** を使用しない場合でも、それらについて少し学ぶことで利点を得ることができるでしょう。 |
|||
|
|||
!!! note "備考" |
|||
もしあなたがPythonの専門家で、すでに型ヒントについてすべて知っているのであれば、次の章まで読み飛ばしてください。 |
|||
|
|||
## 動機 |
|||
|
|||
簡単な例から始めてみましょう: |
|||
|
|||
```Python |
|||
{!../../../docs_src/python_types/tutorial001.py!} |
|||
``` |
|||
|
|||
このプログラムを実行すると以下が出力されます: |
|||
|
|||
``` |
|||
John Doe |
|||
``` |
|||
|
|||
この関数は以下のようなことを行います: |
|||
|
|||
* `first_name`と`last_name`を取得します。 |
|||
* `title()`を用いて、それぞれの最初の文字を大文字に変換します。 |
|||
* 真ん中にスペースを入れて<abbr title="次から次へと中身を入れて一つにまとめる">連結</abbr>します。 |
|||
|
|||
```Python hl_lines="2" |
|||
{!../../../docs_src/python_types/tutorial001.py!} |
|||
``` |
|||
|
|||
### 編集 |
|||
|
|||
これはとても簡単なプログラムです。 |
|||
|
|||
しかし、今、あなたがそれを一から書いていたと想像してみてください。 |
|||
|
|||
パラメータの準備ができていたら、そのとき、関数の定義を始めていたことでしょう... |
|||
|
|||
しかし、そうすると「最初の文字を大文字に変換するあのメソッド」を呼び出す必要があります。 |
|||
|
|||
それは`upper`でしたか?`uppercase`でしたか?それとも`first_uppercase`?または`capitalize`? |
|||
|
|||
そして、古くからプログラマーの友人であるエディタで自動補完を試してみます。 |
|||
|
|||
関数の最初のパラメータ`first_name`を入力し、ドット(`.`)を入力してから、`Ctrl+Space`を押すと補完が実行されます。 |
|||
|
|||
しかし、悲しいことに、これはなんの役にも立ちません: |
|||
|
|||
<img src="https://fastapi.tiangolo.com/img/python-types/image01.png"> |
|||
|
|||
### 型の追加 |
|||
|
|||
先ほどのコードから一行変更してみましょう。 |
|||
|
|||
以下の関数のパラメータ部分を: |
|||
|
|||
```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`で自動補完を実行すると、以下のようになります: |
|||
|
|||
<img src="https://fastapi.tiangolo.com/img/python-types/image02.png"> |
|||
|
|||
これであれば、あなたは「ベルを鳴らす」一つを見つけるまで、オプションを見て、スクロールすることができます: |
|||
|
|||
<img src="https://fastapi.tiangolo.com/img/python-types/image03.png"> |
|||
|
|||
## より強い動機 |
|||
|
|||
この関数を見てください。すでに型ヒントを持っています: |
|||
|
|||
```Python hl_lines="1" |
|||
{!../../../docs_src/python_types/tutorial003.py!} |
|||
``` |
|||
|
|||
エディタは変数の型を知っているので、補完だけでなく、エラーチェックをすることもできます。 |
|||
|
|||
<img src="https://fastapi.tiangolo.com/img/python-types/image04.png"> |
|||
|
|||
これで`age`を`str(age)`で文字列に変換して修正する必要があることがわかります: |
|||
|
|||
```Python hl_lines="2" |
|||
{!../../../docs_src/python_types/tutorial004.py!} |
|||
``` |
|||
|
|||
## 型の宣言 |
|||
|
|||
関数のパラメータとして、型ヒントを宣言している主な場所を確認しました。 |
|||
|
|||
これは **FastAPI** で使用する主な場所でもあります。 |
|||
|
|||
### 単純な型 |
|||
|
|||
`str`だけでなく、Pythonの標準的な型すべてを宣言することができます。 |
|||
|
|||
例えば、以下を使用可能です: |
|||
|
|||
* `int` |
|||
* `float` |
|||
* `bool` |
|||
* `bytes` |
|||
|
|||
```Python hl_lines="1" |
|||
{!../../../docs_src/python_types/tutorial005.py!} |
|||
``` |
|||
|
|||
### 型パラメータを持つジェネリック型 |
|||
|
|||
データ構造の中には、`dict`、`list`、`set`、そして`tuple`のように他の値を含むことができるものがあります。また内部の値も独自の型を持つことができます。 |
|||
|
|||
これらの型や内部の型を宣言するには、Pythonの標準モジュール`typing`を使用します。 |
|||
|
|||
これらの型ヒントをサポートするために特別に存在しています。 |
|||
|
|||
#### `List` |
|||
|
|||
例えば、`str`の`list`の変数を定義してみましょう。 |
|||
|
|||
`typing`から`List`をインポートします(大文字の`L`を含む): |
|||
|
|||
```Python hl_lines="1" |
|||
{!../../../docs_src/python_types/tutorial006.py!} |
|||
``` |
|||
|
|||
同じようにコロン(`:`)の構文で変数を宣言します。 |
|||
|
|||
型として、`List`を入力します。 |
|||
|
|||
リストはいくつかの内部の型を含む型なので、それらを角括弧で囲んでいます。 |
|||
|
|||
```Python hl_lines="4" |
|||
{!../../../docs_src/python_types/tutorial006.py!} |
|||
``` |
|||
|
|||
!!! tip "豆知識" |
|||
角括弧内の内部の型は「型パラメータ」と呼ばれています。 |
|||
|
|||
この場合、`str`は`List`に渡される型パラメータです。 |
|||
|
|||
つまり: 変数`items`は`list`であり、このリストの各項目は`str`です。 |
|||
|
|||
そうすることで、エディタはリストの項目を処理している間にもサポートを提供できます。 |
|||
|
|||
<img src="https://fastapi.tiangolo.com/img/python-types/image05.png"> |
|||
|
|||
タイプがなければ、それはほぼ不可能です。 |
|||
|
|||
変数`item`はリスト`items`の要素の一つであることに注意してください。 |
|||
|
|||
それでも、エディタはそれが`str`であることを知っていて、そのためのサポートを提供しています。 |
|||
|
|||
#### `Tuple` と `Set` |
|||
|
|||
`tuple`と`set`の宣言も同様です: |
|||
|
|||
```Python hl_lines="1 4" |
|||
{!../../../docs_src/python_types/tutorial007.py!} |
|||
``` |
|||
|
|||
つまり: |
|||
|
|||
* 変数`items_t`は`int`、`int`、`str`の3つの項目を持つ`tuple`です |
|||
|
|||
* 変数`items_s`はそれぞれの項目が`bytes`型である`set`です。 |
|||
|
|||
#### `Dict` |
|||
|
|||
`dict`を宣言するためには、カンマ区切りで2つの型パラメータを渡します。 |
|||
|
|||
最初の型パラメータは`dict`のキーです。 |
|||
|
|||
2番目の型パラメータは`dict`の値です。 |
|||
|
|||
```Python hl_lines="1 4" |
|||
{!../../../docs_src/python_types/tutorial008.py!} |
|||
``` |
|||
|
|||
つまり: |
|||
|
|||
* 変数`prices`は`dict`であり: |
|||
* この`dict`のキーは`str`型です。(つまり、各項目の名前) |
|||
* この`dict`の値は`float`型です。(つまり、各項目の価格) |
|||
|
|||
#### `Optional` |
|||
|
|||
また、`Optional`を使用して、変数が`str`のような型を持つことを宣言することもできますが、それは「オプション」であり、`None`にすることもできます。 |
|||
|
|||
```Python hl_lines="1 4" |
|||
{!../../../docs_src/python_types/tutorial009.py!} |
|||
``` |
|||
|
|||
ただの`str`の代わりに`Optional[str]`を使用することで、エディタは値が常に`str`であると仮定している場合に実際には`None`である可能性があるエラーを検出するのに役立ちます。 |
|||
|
|||
#### ジェネリック型 |
|||
|
|||
以下のように角括弧で型パラメータを取る型を: |
|||
|
|||
* `List` |
|||
* `Tuple` |
|||
* `Set` |
|||
* `Dict` |
|||
* `Optional` |
|||
* ...など |
|||
|
|||
**ジェネリック型** または **ジェネリクス** と呼びます。 |
|||
|
|||
### 型としてのクラス |
|||
|
|||
変数の型としてクラスを宣言することもできます。 |
|||
|
|||
例えば、`Person`クラスという名前のクラスがあるとしましょう: |
|||
|
|||
```Python hl_lines="1 2 3" |
|||
{!../../../docs_src/python_types/tutorial010.py!} |
|||
``` |
|||
|
|||
変数の型を`Person`として宣言することができます: |
|||
|
|||
```Python hl_lines="6" |
|||
{!../../../docs_src/python_types/tutorial010.py!} |
|||
``` |
|||
|
|||
そして、再び、すべてのエディタのサポートを得ることができます: |
|||
|
|||
<img src="https://fastapi.tiangolo.com/img/python-types/image06.png"> |
|||
|
|||
## Pydanticのモデル |
|||
|
|||
<a href="https://pydantic-docs.helpmanual.io/" class="external-link" target="_blank">Pydantic</a> はデータ検証を行うためのPythonライブラリです。 |
|||
|
|||
データの「形」を属性付きのクラスとして宣言します。 |
|||
|
|||
そして、それぞれの属性は型を持ちます。 |
|||
|
|||
さらに、いくつかの値を持つクラスのインスタンスを作成すると、その値を検証し、適切な型に変換して(もしそうであれば)全てのデータを持つオブジェクトを提供してくれます。 |
|||
|
|||
また、その結果のオブジェクトですべてのエディタのサポートを受けることができます。 |
|||
|
|||
Pydanticの公式ドキュメントから引用: |
|||
|
|||
```Python |
|||
{!../../../docs_src/python_types/tutorial011.py!} |
|||
``` |
|||
|
|||
!!! info "情報" |
|||
Pydanticについてより学びたい方は<a href="https://pydantic-docs.helpmanual.io/" class="external-link" target="_blank">ドキュメントを参照してください</a>. |
|||
|
|||
**FastAPI** はすべてPydanticをベースにしています。 |
|||
|
|||
すべてのことは[チュートリアル - ユーザーガイド](tutorial/index.md){.internal-link target=_blank}で実際に見ることができます。 |
|||
|
|||
## **FastAPI**での型ヒント |
|||
|
|||
**FastAPI** はこれらの型ヒントを利用していくつかのことを行います。 |
|||
|
|||
**FastAPI** では型ヒントを使って型パラメータを宣言すると以下のものが得られます: |
|||
|
|||
* **エディタサポート**. |
|||
* **型チェック**. |
|||
|
|||
...そして **FastAPI** は同じように宣言をすると、以下のことを行います: |
|||
|
|||
* **要件の定義**: リクエストパスパラメータ、クエリパラメータ、ヘッダー、ボディ、依存関係などから要件を定義します。 |
|||
* **データの変換**: リクエストのデータを必要な型に変換します。 |
|||
* **データの検証**: リクエストごとに: |
|||
* データが無効な場合にクライアントに返される **自動エラー** を生成します。 |
|||
* **ドキュメント** OpenAPIを使用したAPI: |
|||
* 自動的に対話型ドキュメントのユーザーインターフェイスで使用されます。 |
|||
|
|||
すべてが抽象的に聞こえるかもしれません。心配しないでください。 この全ての動作は [チュートリアル - ユーザーガイド](tutorial/index.md){.internal-link target=_blank}で見ることができます。 |
|||
|
|||
重要なのは、Pythonの標準的な型を使うことで、(クラスやデコレータなどを追加するのではなく)1つの場所で **FastAPI** が多くの作業を代わりにやってくれているということです。 |
|||
|
|||
!!! info "情報" |
|||
すでにすべてのチュートリアルを終えて、型についての詳細を見るためにこのページに戻ってきた場合は、<a href="https://mypy.readthedocs.io/en/latest/cheat_sheet_py3.html" class="external-link" target="_blank">`mypy`のチートシートを参照してください</a> |
@ -0,0 +1,94 @@ |
|||
# バックグラウンドタスク |
|||
|
|||
レスポンスを返した *後に* 実行されるバックグラウンドタスクを定義できます。 |
|||
|
|||
これは、リクエスト後に処理を開始する必要があるが、クライアントがレスポンスを受け取る前に処理を終える必要のない操作に役立ちます。 |
|||
|
|||
これには、たとえば次のものが含まれます。 |
|||
|
|||
* 作業実行後のメール通知: |
|||
* メールサーバーへの接続とメールの送信は「遅い」(数秒) 傾向があるため、すぐにレスポンスを返し、バックグラウンドでメール通知ができます。 |
|||
* データ処理: |
|||
* たとえば、時間のかかる処理を必要とするファイル受信時には、「受信済み」(HTTP 202) のレスポンスを返し、バックグラウンドで処理できます。 |
|||
|
|||
## `BackgroundTasks` の使用 |
|||
|
|||
まず初めに、`BackgroundTasks` をインポートし、` BackgroundTasks` の型宣言と共に、*path operation 関数* のパラメーターを定義します: |
|||
|
|||
```Python hl_lines="1 13" |
|||
{!../../../docs_src/background_tasks/tutorial001.py!} |
|||
``` |
|||
|
|||
**FastAPI** は、`BackgroundTasks` 型のオブジェクトを作成し、そのパラメーターに渡します。 |
|||
|
|||
## タスク関数の作成 |
|||
|
|||
バックグラウンドタスクとして実行される関数を作成します。 |
|||
|
|||
これは、パラメーターを受け取ることができる単なる標準的な関数です。 |
|||
|
|||
これは `async def` または通常の `def` 関数であり、**FastAPI** はこれを正しく処理します。 |
|||
|
|||
ここで、タスク関数はファイル書き込みを実行します (メール送信のシミュレーション)。 |
|||
|
|||
また、書き込み操作では `async` と `await` を使用しないため、通常の `def` で関数を定義します。 |
|||
|
|||
```Python hl_lines="6-9" |
|||
{!../../../docs_src/background_tasks/tutorial001.py!} |
|||
``` |
|||
|
|||
## バックグラウンドタスクの追加 |
|||
|
|||
*path operations 関数* 内で、`.add_task()` メソッドを使用してタスク関数を *background tasks* オブジェクトに渡します。 |
|||
|
|||
```Python hl_lines="14" |
|||
{!../../../docs_src/background_tasks/tutorial001.py!} |
|||
``` |
|||
|
|||
`.add_task()` は以下の引数を受け取ります: |
|||
|
|||
* バックグラウンドで実行されるタスク関数 (`write_notification`)。 |
|||
* タスク関数に順番に渡す必要のある引数の列 (`email`)。 |
|||
* タスク関数に渡す必要のあるキーワード引数 (`message="some notification"`)。 |
|||
|
|||
## 依存性注入 |
|||
|
|||
`BackgroundTasks` の使用は依存性注入システムでも機能し、様々な階層 (*path operations 関数*、依存性 (依存可能性)、サブ依存性など) で `BackgroundTasks` 型のパラメーターを宣言できます。 |
|||
|
|||
**FastAPI** は、それぞれの場合の処理方法と同じオブジェクトの再利用方法を知っているため、すべてのバックグラウンドタスクがマージされ、バックグラウンドで後で実行されます。 |
|||
|
|||
```Python hl_lines="13 15 22 25" |
|||
{!../../../docs_src/background_tasks/tutorial002.py!} |
|||
``` |
|||
|
|||
この例では、レスポンスが送信された *後* にメッセージが `log.txt` ファイルに書き込まれます。 |
|||
|
|||
リクエストにクエリがあった場合、バックグラウンドタスクでログに書き込まれます。 |
|||
|
|||
そして、*path operations 関数* で生成された別のバックグラウンドタスクは、`email` パスパラメータを使用してメッセージを書き込みます。 |
|||
|
|||
## 技術的な詳細 |
|||
|
|||
`BackgroundTasks` クラスは、<a href="https://www.starlette.io/background/" class="external-link" target="_blank">`starlette.background`</a>から直接取得されます。 |
|||
|
|||
これは、FastAPI に直接インポート/インクルードされるため、`fastapi` からインポートできる上に、`starlette.background`から別の `BackgroundTask` (末尾に `s` がない) を誤ってインポートすることを回避できます。 |
|||
|
|||
`BackgroundTasks`のみを使用することで (`BackgroundTask` ではなく)、`Request` オブジェクトを直接使用する場合と同様に、それを *path operations 関数* パラメーターとして使用し、**FastAPI** に残りの処理を任せることができます。 |
|||
|
|||
それでも、FastAPI で `BackgroundTask` を単独で使用することは可能ですが、コード内でオブジェクトを作成し、それを含むStarlette `Response` を返す必要があります。 |
|||
|
|||
詳細については、<a href="https://www.starlette.io/background/" class="external-link" target="_blank">バックグラウンドタスクに関する Starlette の公式ドキュメント</a>を参照して下さい。 |
|||
|
|||
## 警告 |
|||
|
|||
大量のバックグラウンド計算が必要であり、必ずしも同じプロセスで実行する必要がない場合 (たとえば、メモリや変数などを共有する必要がない場合)、<a href="https://www.celeryproject.org/" class="external-link" target="_blank">Celery</a> のようなより大きな他のツールを使用するとメリットがあるかもしれません。 |
|||
|
|||
これらは、より複雑な構成、RabbitMQ や Redis などのメッセージ/ジョブキューマネージャーを必要とする傾向がありますが、複数のプロセス、特に複数のサーバーでバックグラウンドタスクを実行できます。 |
|||
|
|||
例を確認するには、[Project Generators](../project-generation.md){.internal-link target=_blank} を参照してください。これらにはすべて、Celery が構築済みです。 |
|||
|
|||
ただし、同じ **FastAPI** アプリから変数とオブジェクトにアクセスする必要がある場合、または小さなバックグラウンドタスク (電子メール通知の送信など) を実行する必要がある場合は、単に `BackgroundTasks` を使用できます。 |
|||
|
|||
## まとめ |
|||
|
|||
`BackgroundTasks` をインポートして、*path operations 関数* や依存関係のパラメータに `BackgroundTasks`を使用し、バックグラウンドタスクを追加して下さい。 |
@ -0,0 +1,48 @@ |
|||
# ボディ - フィールド |
|||
|
|||
`Query`や`Path`、`Body`を使って *path operation関数* のパラメータに追加のバリデーションやメタデータを宣言するのと同じように、Pydanticの`Field`を使ってPydanticモデルの内部でバリデーションやメタデータを宣言することができます。 |
|||
|
|||
## `Field`のインポート |
|||
|
|||
まず、以下のようにインポートします: |
|||
|
|||
```Python hl_lines="4" |
|||
{!../../../docs_src/body_fields/tutorial001.py!} |
|||
``` |
|||
|
|||
!!! warning "注意" |
|||
`Field`は他の全てのもの(`Query`、`Path`、`Body`など)とは違い、`fastapi`からではなく、`pydantic`から直接インポートされていることに注意してください。 |
|||
|
|||
## モデルの属性の宣言 |
|||
|
|||
以下のように`Field`をモデルの属性として使用することができます: |
|||
|
|||
```Python hl_lines="11 12 13 14" |
|||
{!../../../docs_src/body_fields/tutorial001.py!} |
|||
``` |
|||
|
|||
`Field`は`Query`や`Path`、`Body`と同じように動作し、全く同様のパラメータなどを持ちます。 |
|||
|
|||
!!! note "技術詳細" |
|||
実際には次に見る`Query`や`Path`などは、共通の`Param`クラスのサブクラスのオブジェクトを作成しますが、それ自体はPydanticの`FieldInfo`クラスのサブクラスです。 |
|||
|
|||
また、Pydanticの`Field`は`FieldInfo`のインスタンスも返します。 |
|||
|
|||
`Body`は`FieldInfo`のサブクラスのオブジェクトを直接返すこともできます。そして、他にも`Body`クラスのサブクラスであるものがあります。 |
|||
|
|||
`fastapi`から`Query`や`Path`などをインポートする場合、これらは実際には特殊なクラスを返す関数であることに注意してください。 |
|||
|
|||
!!! tip "豆知識" |
|||
型、デフォルト値、`Field`を持つ各モデルの属性が、`Path`や`Query`、`Body`の代わりに`Field`を持つ、*path operation 関数の*パラメータと同じ構造になっていることに注目してください。 |
|||
|
|||
## 追加情報の追加 |
|||
|
|||
追加情報は`Field`や`Query`、`Body`などで宣言することができます。そしてそれは生成されたJSONスキーマに含まれます。 |
|||
|
|||
後に例を用いて宣言を学ぶ際に、追加情報を句悪方法を学べます。 |
|||
|
|||
## まとめ |
|||
|
|||
Pydanticの`Field`を使用して、モデルの属性に追加のバリデーションやメタデータを宣言することができます。 |
|||
|
|||
追加のキーワード引数を使用して、追加のJSONスキーマのメタデータを渡すこともできます。 |
@ -0,0 +1,169 @@ |
|||
# ボディ - 複数のパラメータ |
|||
|
|||
これまで`Path`と`Query`をどう使うかを見てきましたが、リクエストボディの宣言のより高度な使い方を見てみましょう。 |
|||
|
|||
## `Path`、`Query`とボディパラメータを混ぜる |
|||
|
|||
まず、もちろん、`Path`と`Query`とリクエストボディのパラメータの宣言は自由に混ぜることができ、 **FastAPI** は何をするべきかを知っています。 |
|||
|
|||
また、デフォルトの`None`を設定することで、ボディパラメータをオプションとして宣言することもできます: |
|||
|
|||
```Python hl_lines="19 20 21" |
|||
{!../../../docs_src/body_multiple_params/tutorial001.py!} |
|||
``` |
|||
|
|||
!!! note "備考" |
|||
この場合、ボディから取得する`item`はオプションであることに注意してください。デフォルト値は`None`です。 |
|||
|
|||
## 複数のボディパラメータ |
|||
|
|||
上述の例では、*path operations*は`item`の属性を持つ以下のようなJSONボディを期待していました: |
|||
|
|||
```JSON |
|||
{ |
|||
"name": "Foo", |
|||
"description": "The pretender", |
|||
"price": 42.0, |
|||
"tax": 3.2 |
|||
} |
|||
``` |
|||
|
|||
しかし、`item`と`user`のように複数のボディパラメータを宣言することもできます: |
|||
|
|||
```Python hl_lines="22" |
|||
{!../../../docs_src/body_multiple_params/tutorial002.py!} |
|||
``` |
|||
|
|||
この場合、**FastAPI**は関数内に複数のボディパラメータ(Pydanticモデルである2つのパラメータ)があることに気付きます。 |
|||
|
|||
そのため、パラメータ名をボディのキー(フィールド名)として使用し、以下のようなボディを期待しています: |
|||
|
|||
```JSON |
|||
{ |
|||
"item": { |
|||
"name": "Foo", |
|||
"description": "The pretender", |
|||
"price": 42.0, |
|||
"tax": 3.2 |
|||
}, |
|||
"user": { |
|||
"username": "dave", |
|||
"full_name": "Dave Grohl" |
|||
} |
|||
} |
|||
``` |
|||
|
|||
!!! note "備考" |
|||
以前と同じように`item`が宣言されていたにもかかわらず、`item`はキー`item`を持つボディの内部にあることが期待されていることに注意してください。 |
|||
|
|||
**FastAPI** はリクエストから自動で変換を行い、パラメータ`item`が特定の内容を受け取り、`user`も同じように特定の内容を受け取ります。 |
|||
|
|||
複合データの検証を行い、OpenAPIスキーマや自動ドキュメントのように文書化してくれます。 |
|||
|
|||
## ボディ内の単数値 |
|||
|
|||
クエリとパスパラメータの追加データを定義するための `Query` と `Path` があるのと同じように、 **FastAPI** は同等の `Body` を提供します。 |
|||
|
|||
例えば、前のモデルを拡張して、同じボディに `item` と `user` の他にもう一つのキー `importance` を入れたいと決めることができます。 |
|||
|
|||
単数値なのでそのまま宣言すると、**FastAPI** はそれがクエリパラメータであるとみなします。 |
|||
|
|||
しかし、`Body`を使用して、**FastAPI** に別のボディキーとして扱うように指示することができます: |
|||
|
|||
|
|||
```Python hl_lines="23" |
|||
{!../../../docs_src/body_multiple_params/tutorial003.py!} |
|||
``` |
|||
|
|||
この場合、**FastAPI** は以下のようなボディを期待します: |
|||
|
|||
|
|||
```JSON |
|||
{ |
|||
"item": { |
|||
"name": "Foo", |
|||
"description": "The pretender", |
|||
"price": 42.0, |
|||
"tax": 3.2 |
|||
}, |
|||
"user": { |
|||
"username": "dave", |
|||
"full_name": "Dave Grohl" |
|||
}, |
|||
"importance": 5 |
|||
} |
|||
``` |
|||
|
|||
繰り返しになりますが、データ型の変換、検証、文書化などを行います。 |
|||
|
|||
## 複数のボディパラメータとクエリ |
|||
|
|||
もちろん、ボディパラメータに加えて、必要に応じて追加のクエリパラメータを宣言することもできます。 |
|||
|
|||
デフォルトでは、単数値はクエリパラメータとして解釈されるので、明示的に `Query` を追加する必要はありません。 |
|||
|
|||
```Python |
|||
q: str = None |
|||
``` |
|||
|
|||
以下において: |
|||
|
|||
```Python hl_lines="27" |
|||
{!../../../docs_src/body_multiple_params/tutorial004.py!} |
|||
``` |
|||
|
|||
!!! info "情報" |
|||
`Body`もまた、後述する `Query` や `Path` などと同様に、すべての検証パラメータとメタデータパラメータを持っています。 |
|||
|
|||
|
|||
## 単一のボディパラメータの埋め込み |
|||
|
|||
Pydanticモデル`Item`のボディパラメータ`item`を1つだけ持っているとしましょう。 |
|||
|
|||
デフォルトでは、**FastAPI**はそのボディを直接期待します。 |
|||
|
|||
しかし、追加のボディパラメータを宣言したときのように、キー `item` を持つ JSON とその中のモデルの内容を期待したい場合は、特別な `Body` パラメータ `embed` を使うことができます: |
|||
|
|||
```Python |
|||
item: Item = Body(..., embed=True) |
|||
``` |
|||
|
|||
以下において: |
|||
|
|||
```Python hl_lines="17" |
|||
{!../../../docs_src/body_multiple_params/tutorial005.py!} |
|||
``` |
|||
|
|||
この場合、**FastAPI** は以下のようなボディを期待します: |
|||
|
|||
```JSON hl_lines="2" |
|||
{ |
|||
"item": { |
|||
"name": "Foo", |
|||
"description": "The pretender", |
|||
"price": 42.0, |
|||
"tax": 3.2 |
|||
} |
|||
} |
|||
``` |
|||
|
|||
以下の代わりに: |
|||
|
|||
```JSON |
|||
{ |
|||
"name": "Foo", |
|||
"description": "The pretender", |
|||
"price": 42.0, |
|||
"tax": 3.2 |
|||
} |
|||
``` |
|||
|
|||
## まとめ |
|||
|
|||
リクエストが単一のボディしか持てない場合でも、*path operation関数*に複数のボディパラメータを追加することができます。 |
|||
|
|||
しかし、**FastAPI** はそれを処理し、関数内の正しいデータを与え、*path operation*内の正しいスキーマを検証し、文書化します。 |
|||
|
|||
また、ボディの一部として受け取る単数値を宣言することもできます。 |
|||
|
|||
また、単一のパラメータしか宣言されていない場合でも、ボディをキーに埋め込むように **FastAPI** に指示することができます。 |
@ -0,0 +1,244 @@ |
|||
# ボディ - ネストされたモデル |
|||
|
|||
**FastAPI** を使用すると、深くネストされた任意のモデルを定義、検証、文書化、使用することができます(Pydanticのおかげです)。 |
|||
|
|||
## リストのフィールド |
|||
|
|||
属性をサブタイプとして定義することができます。例えば、Pythonの`list`は以下のように定義できます: |
|||
|
|||
```Python hl_lines="12" |
|||
{!../../../docs_src/body_nested_models/tutorial001.py!} |
|||
``` |
|||
|
|||
これにより、各項目の型は宣言されていませんが、`tags`はある項目のリストになります。 |
|||
|
|||
## タイプパラメータを持つリストのフィールド |
|||
|
|||
しかし、Pythonには型や「タイプパラメータ」を使ってリストを宣言する方法があります: |
|||
|
|||
### typingの`List`をインポート |
|||
|
|||
まず、Pythonの標準の`typing`モジュールから`List`をインポートします: |
|||
|
|||
```Python hl_lines="1" |
|||
{!../../../docs_src/body_nested_models/tutorial002.py!} |
|||
``` |
|||
|
|||
### タイプパラメータを持つ`List`の宣言 |
|||
|
|||
`list`や`dict`、`tuple`のようなタイプパラメータ(内部の型)を持つ型を宣言するには: |
|||
|
|||
* `typing`モジュールからそれらをインストールします。 |
|||
* 角括弧(`[`と`]`)を使って「タイプパラメータ」として内部の型を渡します: |
|||
|
|||
```Python |
|||
from typing import List |
|||
|
|||
my_list: List[str] |
|||
``` |
|||
|
|||
型宣言の標準的なPythonの構文はこれだけです。 |
|||
|
|||
内部の型を持つモデルの属性にも同じ標準の構文を使用してください。 |
|||
|
|||
そのため、以下の例では`tags`を具体的な「文字列のリスト」にすることができます: |
|||
|
|||
```Python hl_lines="14" |
|||
{!../../../docs_src/body_nested_models/tutorial002.py!} |
|||
``` |
|||
|
|||
## セット型 |
|||
|
|||
しかし、よく考えてみると、タグは繰り返すべきではなく、おそらくユニークな文字列になるのではないかと気付いたとします。 |
|||
|
|||
そして、Pythonにはユニークな項目のセットのための特別なデータ型`set`があります。 |
|||
|
|||
そのため、以下のように、`Set`をインポートして`str`の`set`として`tags`を宣言することができます: |
|||
|
|||
```Python hl_lines="1 14" |
|||
{!../../../docs_src/body_nested_models/tutorial003.py!} |
|||
``` |
|||
|
|||
これを使えば、データが重複しているリクエストを受けた場合でも、ユニークな項目のセットに変換されます。 |
|||
|
|||
そして、そのデータを出力すると、たとえソースに重複があったとしても、固有の項目のセットとして出力されます。 |
|||
|
|||
また、それに応じて注釈をつけたり、文書化したりします。 |
|||
|
|||
## ネストされたモデル |
|||
|
|||
Pydanticモデルの各属性には型があります。 |
|||
|
|||
しかし、その型はそれ自体が別のPydanticモデルである可能性があります。 |
|||
|
|||
そのため、特定の属性名、型、バリデーションを指定して、深くネストしたJSON`object`を宣言することができます。 |
|||
|
|||
すべては、任意のネストにされています。 |
|||
|
|||
### サブモデルの定義 |
|||
|
|||
例えば、`Image`モデルを定義することができます: |
|||
|
|||
```Python hl_lines="9 10 11" |
|||
{!../../../docs_src/body_nested_models/tutorial004.py!} |
|||
``` |
|||
|
|||
### サブモデルを型として使用 |
|||
|
|||
そして、それを属性の型として使用することができます: |
|||
|
|||
```Python hl_lines="20" |
|||
{!../../../docs_src/body_nested_models/tutorial004.py!} |
|||
``` |
|||
|
|||
これは **FastAPI** が以下のようなボディを期待することを意味します: |
|||
|
|||
```JSON |
|||
{ |
|||
"name": "Foo", |
|||
"description": "The pretender", |
|||
"price": 42.0, |
|||
"tax": 3.2, |
|||
"tags": ["rock", "metal", "bar"], |
|||
"image": { |
|||
"url": "http://example.com/baz.jpg", |
|||
"name": "The Foo live" |
|||
} |
|||
} |
|||
``` |
|||
|
|||
繰り返しになりますが、**FastAPI** を使用して、その宣言を行うだけで以下のような恩恵を受けられます: |
|||
|
|||
* ネストされたモデルでも対応可能なエディタのサポート(補完など) |
|||
* データ変換 |
|||
* データの検証 |
|||
* 自動文書化 |
|||
|
|||
## 特殊な型とバリデーション |
|||
|
|||
`str`や`int`、`float`のような通常の単数型の他にも、`str`を継承したより複雑な単数型を使うこともできます。 |
|||
|
|||
すべてのオプションをみるには、<a href="https://pydantic-docs.helpmanual.io/usage/types/" class="external-link" target="_blank">Pydanticのエキゾチック な型</a>のドキュメントを確認してください。次の章でいくつかの例をみることができます。 |
|||
|
|||
例えば、`Image`モデルのように`url`フィールドがある場合、`str`の代わりにPydanticの`HttpUrl`を指定することができます: |
|||
|
|||
```Python hl_lines="4 10" |
|||
{!../../../docs_src/body_nested_models/tutorial005.py!} |
|||
``` |
|||
|
|||
文字列は有効なURLであることが確認され、そのようにJSONスキーマ・OpenAPIで文書化されます。 |
|||
|
|||
## サブモデルのリストを持つ属性 |
|||
|
|||
Pydanticモデルを`list`や`set`などのサブタイプとして使用することもできます: |
|||
|
|||
```Python hl_lines="20" |
|||
{!../../../docs_src/body_nested_models/tutorial006.py!} |
|||
``` |
|||
|
|||
これは、次のようなJSONボディを期待します(変換、検証、ドキュメントなど): |
|||
|
|||
```JSON hl_lines="11" |
|||
{ |
|||
"name": "Foo", |
|||
"description": "The pretender", |
|||
"price": 42.0, |
|||
"tax": 3.2, |
|||
"tags": [ |
|||
"rock", |
|||
"metal", |
|||
"bar" |
|||
], |
|||
"images": [ |
|||
{ |
|||
"url": "http://example.com/baz.jpg", |
|||
"name": "The Foo live" |
|||
}, |
|||
{ |
|||
"url": "http://example.com/dave.jpg", |
|||
"name": "The Baz" |
|||
} |
|||
] |
|||
} |
|||
``` |
|||
|
|||
!!! info "情報" |
|||
`images`キーが画像オブジェクトのリストを持つようになったことに注目してください。 |
|||
|
|||
## 深くネストされたモデル |
|||
|
|||
深くネストされた任意のモデルを定義することができます: |
|||
|
|||
```Python hl_lines="9 14 20 23 27" |
|||
{!../../../docs_src/body_nested_models/tutorial007.py!} |
|||
``` |
|||
|
|||
!!! info "情報" |
|||
`Offer`は`Item`のリストであり、オプションの`Image`のリストを持っていることに注目してください。 |
|||
|
|||
## 純粋なリストのボディ |
|||
|
|||
期待するJSONボディのトップレベルの値がJSON`array`(Pythonの`list`)であれば、Pydanticモデルと同じように、関数のパラメータで型を宣言することができます: |
|||
|
|||
```Python |
|||
images: List[Image] |
|||
``` |
|||
|
|||
以下のように: |
|||
|
|||
```Python hl_lines="15" |
|||
{!../../../docs_src/body_nested_models/tutorial008.py!} |
|||
``` |
|||
|
|||
## あらゆる場所でのエディタサポート |
|||
|
|||
エディタのサポートもどこでも受けることができます。 |
|||
|
|||
以下のようにリストの中の項目でも: |
|||
|
|||
<img src="https://fastapi.tiangolo.com/img/tutorial/body-nested-models/image01.png"> |
|||
|
|||
Pydanticモデルではなく、`dict`を直接使用している場合はこのようなエディタのサポートは得られません。 |
|||
|
|||
しかし、それらについて心配する必要はありません。入力された辞書は自動的に変換され、出力も自動的にJSONに変換されます。 |
|||
|
|||
## 任意の`dict`のボディ |
|||
|
|||
また、ある型のキーと別の型の値を持つ`dict`としてボディを宣言することもできます。 |
|||
|
|||
有効なフィールド・属性名を事前に知る必要がありません(Pydanticモデルの場合のように)。 |
|||
|
|||
これは、まだ知らないキーを受け取りたいときに便利だと思います。 |
|||
|
|||
--- |
|||
|
|||
他にも、`int`のように他の型のキーを持ちたい場合などに便利です。 |
|||
|
|||
それをここで見ていきましょう。 |
|||
|
|||
この場合、`int`のキーと`float`の値を持つものであれば、どんな`dict`でも受け入れることができます: |
|||
|
|||
```Python hl_lines="15" |
|||
{!../../../docs_src/body_nested_models/tutorial009.py!} |
|||
``` |
|||
|
|||
!!! tip "豆知識" |
|||
JSONはキーとして`str`しかサポートしていないことに注意してください。 |
|||
|
|||
しかしPydanticには自動データ変換機能があります。 |
|||
|
|||
これは、APIクライアントがキーとして文字列しか送信できなくても、それらの文字列に純粋な整数が含まれている限り、Pydanticが変換して検証することを意味します。 |
|||
|
|||
そして、`weights`として受け取る`dict`は、実際には`int`のキーと`float`の値を持つことになります。 |
|||
|
|||
## まとめ |
|||
|
|||
**FastAPI** を使用すると、Pydanticモデルが提供する最大限の柔軟性を持ちながら、コードをシンプルに短く、エレガントに保つことができます。 |
|||
|
|||
以下のような利点があります: |
|||
|
|||
* エディタのサポート(どこでも補完!) |
|||
* データ変換(別名:構文解析・シリアライズ) |
|||
* データの検証 |
|||
* スキーマ文書 |
|||
* 自動文書化 |
@ -0,0 +1,191 @@ |
|||
# 依存関係としてのクラス |
|||
|
|||
**依存性注入** システムを深く掘り下げる前に、先ほどの例をアップグレードしてみましょう。 |
|||
|
|||
## 前の例の`dict` |
|||
|
|||
前の例では、依存関係("dependable")から`dict`を返していました: |
|||
|
|||
```Python hl_lines="9" |
|||
{!../../../docs_src/dependencies/tutorial001.py!} |
|||
``` |
|||
|
|||
しかし、*path operation関数*のパラメータ`commons`に`dict`が含まれています。 |
|||
|
|||
また、エディタは`dict`のキーと値の型を知ることができないため、多くのサポート(補完のような)を提供することができません。 |
|||
|
|||
もっとうまくやれるはずです...。 |
|||
|
|||
## 依存関係を作るもの |
|||
|
|||
これまでは、依存関係が関数として宣言されているのを見てきました。 |
|||
|
|||
しかし、依存関係を定義する方法はそれだけではありません(その方が一般的かもしれませんが)。 |
|||
|
|||
重要なのは、依存関係が「呼び出し可能」なものであることです。 |
|||
|
|||
Pythonにおける「**呼び出し可能**」とは、Pythonが関数のように「呼び出す」ことができるものを指します。 |
|||
|
|||
そのため、`something`オブジェクト(関数ではないかもしれませんが)を持っていて、それを次のように「呼び出す」(実行する)ことができるとします: |
|||
|
|||
```Python |
|||
something() |
|||
``` |
|||
|
|||
または |
|||
|
|||
```Python |
|||
something(some_argument, some_keyword_argument="foo") |
|||
``` |
|||
|
|||
これを「呼び出し可能」なものと呼びます。 |
|||
|
|||
## 依存関係としてのクラス |
|||
|
|||
Pythonのクラスのインスタンスを作成する際に、同じ構文を使用していることに気づくかもしれません。 |
|||
|
|||
例えば: |
|||
|
|||
```Python |
|||
class Cat: |
|||
def __init__(self, name: str): |
|||
self.name = name |
|||
|
|||
|
|||
fluffy = Cat(name="Mr Fluffy") |
|||
``` |
|||
|
|||
この場合、`fluffy`は`Cat`クラスのインスタンスです。 |
|||
|
|||
そして`fluffy`を作成するために、`Cat`を「呼び出している」ことになります。 |
|||
|
|||
そのため、Pythonのクラスもまた「呼び出し可能」です。 |
|||
|
|||
そして、**FastAPI** では、Pythonのクラスを依存関係として使用することができます。 |
|||
|
|||
FastAPIが実際にチェックしているのは、それが「呼び出し可能」(関数、クラス、その他なんでも)であり、パラメータが定義されているかどうかということです。 |
|||
|
|||
**FastAPI** の依存関係として「呼び出し可能なもの」を渡すと、その「呼び出し可能なもの」のパラメータを解析し、サブ依存関係も含めて、*path operation関数*のパラメータと同じように処理します。 |
|||
|
|||
それは、パラメータが全くない呼び出し可能なものにも適用されます。パラメータのない*path operation関数*と同じように。 |
|||
|
|||
そこで、上で紹介した依存関係の`common_parameters`を`CommonQueryParams`クラスに変更します: |
|||
|
|||
```Python hl_lines="11 12 13 14 15" |
|||
{!../../../docs_src/dependencies/tutorial002.py!} |
|||
``` |
|||
|
|||
クラスのインスタンスを作成するために使用される`__init__`メソッドに注目してください: |
|||
|
|||
```Python hl_lines="12" |
|||
{!../../../docs_src/dependencies/tutorial002.py!} |
|||
``` |
|||
|
|||
...以前の`common_parameters`と同じパラメータを持っています: |
|||
|
|||
```Python hl_lines="8" |
|||
{!../../../docs_src/dependencies/tutorial001.py!} |
|||
``` |
|||
|
|||
これらのパラメータは **FastAPI** が依存関係を「解決」するために使用するものです。 |
|||
|
|||
どちらの場合も以下を持っています: |
|||
|
|||
* オプショナルの`q`クエリパラメータ。 |
|||
* `skip`クエリパラメータ、デフォルトは`0`。 |
|||
* `limit`クエリパラメータ、デフォルトは`100`。 |
|||
|
|||
どちらの場合も、データは変換され、検証され、OpenAPIスキーマなどで文書化されます。 |
|||
|
|||
## 使用 |
|||
|
|||
これで、このクラスを使用して依存関係を宣言することができます。 |
|||
|
|||
```Python hl_lines="19" |
|||
{!../../../docs_src/dependencies/tutorial002.py!} |
|||
``` |
|||
|
|||
**FastAPI** は`CommonQueryParams`クラスを呼び出します。これにより、そのクラスの「インスタンス」が作成され、インスタンスはパラメータ`commons`として関数に渡されます。 |
|||
|
|||
## 型注釈と`Depends` |
|||
|
|||
上のコードでは`CommonQueryParams`を2回書いていることに注目してください: |
|||
|
|||
```Python |
|||
commons: CommonQueryParams = Depends(CommonQueryParams) |
|||
``` |
|||
|
|||
以下にある最後の`CommonQueryParams`: |
|||
|
|||
```Python |
|||
... = Depends(CommonQueryParams) |
|||
``` |
|||
|
|||
...は、**FastAPI** が依存関係を知るために実際に使用するものです。 |
|||
|
|||
そこからFastAPIが宣言されたパラメータを抽出し、それが実際にFastAPIが呼び出すものです。 |
|||
|
|||
--- |
|||
|
|||
この場合、以下にある最初の`CommonQueryParams`: |
|||
|
|||
```Python |
|||
commons: CommonQueryParams ... |
|||
``` |
|||
|
|||
...は **FastAPI** に対して特別な意味をもちません。FastAPIはデータ変換や検証などには使用しません(それらのためには`= Depends(CommonQueryParams)`を使用しています)。 |
|||
|
|||
実際には以下のように書けばいいだけです: |
|||
|
|||
```Python |
|||
commons = Depends(CommonQueryParams) |
|||
``` |
|||
|
|||
以下にあるように: |
|||
|
|||
```Python hl_lines="19" |
|||
{!../../../docs_src/dependencies/tutorial003.py!} |
|||
``` |
|||
|
|||
しかし、型を宣言することは推奨されています。そうすれば、エディタは`commons`のパラメータとして何が渡されるかを知ることができ、コードの補完や型チェックなどを行うのに役立ちます: |
|||
|
|||
<img src="https://fastapi.tiangolo.com/img/tutorial/dependencies/image02.png"> |
|||
|
|||
## ショートカット |
|||
|
|||
しかし、ここでは`CommonQueryParams`を2回書くというコードの繰り返しが発生していることがわかります: |
|||
|
|||
```Python |
|||
commons: CommonQueryParams = Depends(CommonQueryParams) |
|||
``` |
|||
|
|||
依存関係が、クラス自体のインスタンスを作成するために**FastAPI**が「呼び出す」*特定の*クラスである場合、**FastAPI** はこれらのケースのショートカットを提供しています。 |
|||
|
|||
それらの具体的なケースについては以下のようにします: |
|||
|
|||
以下のように書く代わりに: |
|||
|
|||
```Python |
|||
commons: CommonQueryParams = Depends(CommonQueryParams) |
|||
``` |
|||
|
|||
...以下のように書きます: |
|||
|
|||
```Python |
|||
commons: CommonQueryParams = Depends() |
|||
``` |
|||
|
|||
パラメータの型として依存関係を宣言し、`Depends()`の中でパラメータを指定せず、`Depends()`をその関数のパラメータの「デフォルト」値(`=`のあとの値)として使用することで、`Depends(CommonQueryParams)`の中でクラス全体を*もう一度*書かなくてもよくなります。 |
|||
|
|||
同じ例では以下のようになります: |
|||
|
|||
```Python hl_lines="19" |
|||
{!../../../docs_src/dependencies/tutorial004.py!} |
|||
``` |
|||
|
|||
...そして **FastAPI** は何をすべきか知っています。 |
|||
|
|||
!!! tip "豆知識" |
|||
役に立つというよりも、混乱するようであれば無視してください。それをする*必要*はありません。 |
|||
|
|||
それは単なるショートカットです。なぜなら **FastAPI** はコードの繰り返しを最小限に抑えることに気を使っているからです。 |
@ -0,0 +1,62 @@ |
|||
# path operationデコレータの依存関係 |
|||
|
|||
場合によっては*path operation関数*の中で依存関係の戻り値を本当に必要としないこともあります。 |
|||
|
|||
もしくは、依存関係が値を返さない場合もあります。 |
|||
|
|||
しかし、それでも実行・解決する必要があります。 |
|||
|
|||
このような場合、*path operation関数*のパラメータを`Depends`で宣言する代わりに、*path operation decorator*に`dependencies`の`list`を追加することができます。 |
|||
|
|||
## *path operationデコレータ*への`dependencies`の追加 |
|||
|
|||
*path operationデコレータ*はオプショナルの引数`dependencies`を受け取ります。 |
|||
|
|||
それは`Depends()`の`list`であるべきです: |
|||
|
|||
```Python hl_lines="17" |
|||
{!../../../docs_src/dependencies/tutorial006.py!} |
|||
``` |
|||
|
|||
これらの依存関係は、通常の依存関係と同様に実行・解決されます。しかし、それらの値(何かを返す場合)は*path operation関数*には渡されません。 |
|||
|
|||
!!! tip "豆知識" |
|||
エディタによっては、未使用の関数パラメータをチェックしてエラーとして表示するものもあります。 |
|||
|
|||
`dependencies`を`path operationデコレータ`で使用することで、エディタやツールのエラーを回避しながら確実に実行することができます。 |
|||
|
|||
また、コードの未使用のパラメータがあるのを見て、それが不要だと思ってしまうような新しい開発者の混乱を避けるのにも役立つかもしれません。 |
|||
|
|||
## 依存関係のエラーと戻り値 |
|||
|
|||
通常使用している依存関係の*関数*と同じものを使用することができます。 |
|||
|
|||
### 依存関係の要件 |
|||
|
|||
これらはリクエストの要件(ヘッダのようなもの)やその他のサブ依存関係を宣言することができます: |
|||
|
|||
```Python hl_lines="6 11" |
|||
{!../../../docs_src/dependencies/tutorial006.py!} |
|||
``` |
|||
|
|||
### 例外の発生 |
|||
|
|||
これらの依存関係は通常の依存関係と同じように、例外を`raise`発生させることができます: |
|||
|
|||
```Python hl_lines="8 13" |
|||
{!../../../docs_src/dependencies/tutorial006.py!} |
|||
``` |
|||
|
|||
### 戻り値 |
|||
|
|||
そして、値を返すことも返さないこともできますが、値は使われません。 |
|||
|
|||
つまり、すでにどこかで使っている通常の依存関係(値を返すもの)を再利用することができ、値は使われなくても依存関係は実行されます: |
|||
|
|||
```Python hl_lines="9 14" |
|||
{!../../../docs_src/dependencies/tutorial006.py!} |
|||
``` |
|||
|
|||
## *path operations*のグループに対する依存関係 |
|||
|
|||
後で、より大きなアプリケーションの構造([Bigger Applications - Multiple Files](../../tutorial/bigger-applications.md){.internal-link target=_blank})について読む時に、おそらく複数のファイルを使用して、*path operations*のグループに対して単一の`dependencies`パラメータを宣言する方法を学ぶでしょう。 |
@ -0,0 +1,225 @@ |
|||
# yieldを持つ依存関係 |
|||
|
|||
FastAPIは、いくつかの<abbr title='時々"exit"、"cleanup"、"teardown"、"close"、"context managers"、 ...のように呼ばれる'>終了後の追加のステップ</abbr>を行う依存関係をサポートしています。 |
|||
|
|||
これを行うには、`return`の代わりに`yield`を使い、その後に追加のステップを書きます。 |
|||
|
|||
!!! tip "豆知識" |
|||
`yield`は必ず一度だけ使用するようにしてください。 |
|||
|
|||
!!! info "情報" |
|||
これを動作させるには、**Python 3.7** 以上を使用するか、**Python 3.6** では"backports"をインストールする必要があります: |
|||
|
|||
``` |
|||
pip install async-exit-stack async-generator |
|||
``` |
|||
|
|||
これにより<a href="https://github.com/sorcio/async_exit_stack" class="external-link" target="_blank">async-exit-stack</a>と<a href="https://github.com/python-trio/async_generator" class="external-link" target="_blank">async-generator</a>がインストールされます。 |
|||
|
|||
!!! note "技術詳細" |
|||
以下と一緒に使用できる関数なら何でも有効です: |
|||
|
|||
* <a href="https://docs.python.org/3/library/contextlib.html#contextlib.contextmanager" class="external-link" target="_blank">`@contextlib.contextmanager`</a>または |
|||
* <a href="https://docs.python.org/3/library/contextlib.html#contextlib.asynccontextmanager" class="external-link" target="_blank">`@contextlib.asynccontextmanager`</a> |
|||
|
|||
これらは **FastAPI** の依存関係として使用するのに有効です。 |
|||
|
|||
実際、FastAPIは内部的にこれら2つのデコレータを使用しています。 |
|||
|
|||
## `yield`を持つデータベースの依存関係 |
|||
|
|||
例えば、これを使ってデータベースセッションを作成し、終了後にそれを閉じることができます。 |
|||
|
|||
レスポンスを送信する前に`yield`文を含む前のコードのみが実行されます。 |
|||
|
|||
```Python hl_lines="2 3 4" |
|||
{!../../../docs_src/dependencies/tutorial007.py!} |
|||
``` |
|||
|
|||
生成された値は、*path operations*や他の依存関係に注入されるものです: |
|||
|
|||
```Python hl_lines="4" |
|||
{!../../../docs_src/dependencies/tutorial007.py!} |
|||
``` |
|||
|
|||
`yield`文に続くコードは、レスポンスが送信された後に実行されます: |
|||
|
|||
```Python hl_lines="5 6" |
|||
{!../../../docs_src/dependencies/tutorial007.py!} |
|||
``` |
|||
|
|||
!!! tip "豆知識" |
|||
`async`や通常の関数を使用することができます。 |
|||
|
|||
**FastAPI** は、通常の依存関係と同じように、それぞれで正しいことを行います。 |
|||
|
|||
## `yield`と`try`を持つ依存関係 |
|||
|
|||
`yield`を持つ依存関係で`try`ブロックを使用した場合、その依存関係を使用した際に発生した例外を受け取ることになります。 |
|||
|
|||
例えば、途中のどこかの時点で、別の依存関係や*path operation*の中で、データベーストランザクションを「ロールバック」したり、その他のエラーを作成したりするコードがあった場合、依存関係の中で例外を受け取ることになります。 |
|||
|
|||
そのため、依存関係の中にある特定の例外を`except SomeException`で探すことができます。 |
|||
|
|||
同様に、`finally`を用いて例外があったかどうかにかかわらず、終了ステップを確実に実行することができます。 |
|||
|
|||
```Python hl_lines="3 5" |
|||
{!../../../docs_src/dependencies/tutorial007.py!} |
|||
``` |
|||
|
|||
## `yield`を持つサブ依存関係 |
|||
|
|||
任意の大きさや形のサブ依存関係やサブ依存関係の「ツリー」を持つことができ、その中で`yield`を使用することができます。 |
|||
|
|||
**FastAPI** は、`yield`を持つ各依存関係の「終了コード」が正しい順番で実行されていることを確認します。 |
|||
|
|||
例えば、`dependency_c`は`dependency_b`と`dependency_b`に依存する`dependency_a`に、依存することができます: |
|||
|
|||
```Python hl_lines="4 12 20" |
|||
{!../../../docs_src/dependencies/tutorial008.py!} |
|||
``` |
|||
|
|||
そして、それらはすべて`yield`を使用することができます。 |
|||
|
|||
この場合、`dependency_c`は終了コードを実行するために、`dependency_b`(ここでは`dep_b`という名前)の値がまだ利用可能である必要があります。 |
|||
|
|||
そして、`dependency_b`は`dependency_a`(ここでは`dep_a`という名前)の値を終了コードで利用できるようにする必要があります。 |
|||
|
|||
```Python hl_lines="16 17 24 25" |
|||
{!../../../docs_src/dependencies/tutorial008.py!} |
|||
``` |
|||
|
|||
同様に、`yield`と`return`が混在した依存関係を持つこともできます。 |
|||
|
|||
また、単一の依存関係を持っていて、`yield`などの他の依存関係をいくつか必要とすることもできます。 |
|||
|
|||
依存関係の組み合わせは自由です。 |
|||
|
|||
**FastAPI** は、全てが正しい順序で実行されていることを確認します。 |
|||
|
|||
!!! note "技術詳細" |
|||
これはPythonの<a href="https://docs.python.org/3/library/contextlib.html" class="external-link" target="_blank">Context Managers</a>のおかげで動作します。 |
|||
|
|||
**FastAPI** はこれを実現するために内部的に使用しています。 |
|||
|
|||
## `yield`と`HTTPException`を持つ依存関係 |
|||
|
|||
`yield`と例外をキャッチする`try`ブロックを持つことができる依存関係を使用することができることがわかりました。 |
|||
|
|||
`yield`の後の終了コードで`HTTPException`などを発生させたくなるかもしれません。しかし**それはうまくいきません** |
|||
|
|||
`yield`を持つ依存関係の終了コードは[例外ハンドラ](../handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank}の*後に*実行されます。依存関係によって投げられた例外を終了コード(`yield`の後)でキャッチするものはなにもありません。 |
|||
|
|||
つまり、`yield`の後に`HTTPException`を発生させた場合、`HTTTPException`をキャッチしてHTTP 400のレスポンスを返すデフォルトの(あるいは任意のカスタムの)例外ハンドラは、その例外をキャッチすることができなくなります。 |
|||
|
|||
これは、依存関係に設定されているもの(例えば、DBセッション)を、例えば、バックグラウンドタスクで使用できるようにするものです。 |
|||
|
|||
バックグラウンドタスクはレスポンスが送信された*後*に実行されます。そのため、*すでに送信されている*レスポンスを変更する方法すらないので、`HTTPException`を発生させる方法はありません。 |
|||
|
|||
しかし、バックグラウンドタスクがDBエラーを発生させた場合、少なくとも`yield`で依存関係のセッションをロールバックしたり、きれいに閉じたりすることができ、エラーをログに記録したり、リモートのトラッキングシステムに報告したりすることができます。 |
|||
|
|||
例外が発生する可能性があるコードがある場合は、最も普通の「Python流」なことをして、コードのその部分に`try`ブロックを追加してください。 |
|||
|
|||
レスポンスを返したり、レスポンスを変更したり、`HTTPException`を発生させたりする*前に*処理したいカスタム例外がある場合は、[カスタム例外ハンドラ](../handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank}を作成してください。 |
|||
|
|||
!!! tip "豆知識" |
|||
`HTTPException`を含む例外は、`yield`の*前*でも発生させることができます。ただし、後ではできません。 |
|||
|
|||
実行の順序は多かれ少なかれ以下の図のようになります。時間は上から下へと流れていきます。そして、各列はコードを相互作用させたり、実行したりしている部分の一つです。 |
|||
|
|||
```mermaid |
|||
sequenceDiagram |
|||
|
|||
participant client as Client |
|||
participant handler as Exception handler |
|||
participant dep as Dep with yield |
|||
participant operation as Path Operation |
|||
participant tasks as Background tasks |
|||
|
|||
Note over client,tasks: Can raise exception for dependency, handled after response is sent |
|||
Note over client,operation: Can raise HTTPException and can change the response |
|||
client ->> dep: Start request |
|||
Note over dep: Run code up to yield |
|||
opt raise |
|||
dep -->> handler: Raise HTTPException |
|||
handler -->> client: HTTP error response |
|||
dep -->> dep: Raise other exception |
|||
end |
|||
dep ->> operation: Run dependency, e.g. DB session |
|||
opt raise |
|||
operation -->> handler: Raise HTTPException |
|||
handler -->> client: HTTP error response |
|||
operation -->> dep: Raise other exception |
|||
end |
|||
operation ->> client: Return response to client |
|||
Note over client,operation: Response is already sent, can't change it anymore |
|||
opt Tasks |
|||
operation -->> tasks: Send background tasks |
|||
end |
|||
opt Raise other exception |
|||
tasks -->> dep: Raise other exception |
|||
end |
|||
Note over dep: After yield |
|||
opt Handle other exception |
|||
dep -->> dep: Handle exception, can't change response. E.g. close DB session. |
|||
end |
|||
``` |
|||
|
|||
!!! info "情報" |
|||
**1つのレスポンス** だけがクライアントに送信されます。それはエラーレスポンスの一つかもしれませんし、*path operation*からのレスポンスかもしれません。 |
|||
|
|||
いずれかのレスポンスが送信された後、他のレスポンスを送信することはできません。 |
|||
|
|||
!!! tip "豆知識" |
|||
この図は`HTTPException`を示していますが、[カスタム例外ハンドラ](../handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank}を作成することで、他の例外を発生させることもできます。そして、その例外は依存関係の終了コードではなく、そのカスタム例外ハンドラによって処理されます。 |
|||
|
|||
しかし例外ハンドラで処理されない例外を発生させた場合は、依存関係の終了コードで処理されます。 |
|||
|
|||
## コンテキストマネージャ |
|||
|
|||
### 「コンテキストマネージャ」とは |
|||
|
|||
「コンテキストマネージャ」とは、`with`文の中で使用できるPythonオブジェクトのことです。 |
|||
|
|||
例えば、<a href="https://docs.python.org/3/tutorial/inputoutput.html#reading-and-writing-files" class="external-link" target="_blank">ファイルを読み込むには`with`を使用することができます</a>: |
|||
|
|||
```Python |
|||
with open("./somefile.txt") as f: |
|||
contents = f.read() |
|||
print(contents) |
|||
``` |
|||
|
|||
その後の`open("./somefile.txt")`は「コンテキストマネージャ」と呼ばれるオブジェクトを作成します。 |
|||
|
|||
`with`ブロックが終了すると、例外があったとしてもファイルを確かに閉じます。 |
|||
|
|||
`yield`を依存関係を作成すると、**FastAPI** は内部的にそれをコンテキストマネージャに変換し、他の関連ツールと組み合わせます。 |
|||
|
|||
### `yield`を持つ依存関係でのコンテキストマネージャの使用 |
|||
|
|||
!!! warning "注意" |
|||
これは多かれ少なかれ、「高度な」発想です。 |
|||
|
|||
**FastAPI** を使い始めたばかりの方は、とりあえずスキップした方がよいかもしれません。 |
|||
|
|||
Pythonでは、<a href="https://docs.python.org/3/reference/datamodel.html#context-managers" class="external-link" target="_blank">以下の2つのメソッドを持つクラスを作成する: `__enter__()`と`__exit__()`</a>ことでコンテキストマネージャを作成することができます。 |
|||
|
|||
また、依存関数の中で`with`や`async with`文を使用することによって`yield`を持つ **FastAPI** の依存関係の中でそれらを使用することができます: |
|||
|
|||
```Python hl_lines="1 2 3 4 5 6 7 8 9 13" |
|||
{!../../../docs_src/dependencies/tutorial010.py!} |
|||
``` |
|||
|
|||
!!! tip "豆知識" |
|||
コンテキストマネージャを作成するもう一つの方法はwithです: |
|||
|
|||
* <a href="https://docs.python.org/3/library/contextlib.html#contextlib.contextmanager" class="external-link" target="_blank">`@contextlib.contextmanager`</a> または |
|||
* <a href="https://docs.python.org/3/library/contextlib.html#contextlib.asynccontextmanager" class="external-link" target="_blank">`@contextlib.asynccontextmanager`</a> |
|||
|
|||
これらを使って、関数を単一の`yield`でデコレートすることができます。 |
|||
|
|||
これは **FastAPI** が内部的に`yield`を持つ依存関係のために使用しているものです。 |
|||
|
|||
しかし、FastAPIの依存関係にデコレータを使う必要はありません(そして使うべきではありません)。 |
|||
|
|||
FastAPIが内部的にやってくれます。 |
@ -0,0 +1,209 @@ |
|||
# 依存関係 - 最初のステップ |
|||
|
|||
** FastAPI** は非常に強力でありながら直感的な **<abbr title="コンポーネント、リソース、プロバイダ、サービス、インジェクタブルとしても知られている">依存性注入</abbr>** システムを持っています。 |
|||
|
|||
それは非常にシンプルに使用できるように設計されており、開発者が他のコンポーネント **FastAPI** と統合するのが非常に簡単になるように設計されています。 |
|||
|
|||
## 「依存性注入」とは |
|||
|
|||
**「依存性注入」** とは、プログラミングにおいて、コード(この場合は、*path operation関数*)が動作したり使用したりするために必要なもの(「依存関係」)を宣言する方法があることを意味します: |
|||
|
|||
そして、そのシステム(この場合は、**FastAPI**)は、必要な依存関係をコードに提供するために必要なことは何でも行います(依存関係を「注入」します)。 |
|||
|
|||
これは以下のようなことが必要な時にとても便利です: |
|||
|
|||
* ロジックを共有している。(同じコードロジックを何度も繰り返している)。 |
|||
* データベース接続を共有する。 |
|||
* セキュリティ、認証、ロール要件などを強制する。 |
|||
* そのほかにも多くのこと... |
|||
|
|||
これらすべてを、コードの繰り返しを最小限に抑えながら行います。 |
|||
|
|||
## 最初のステップ |
|||
|
|||
非常にシンプルな例を見てみましょう。あまりにもシンプルなので、今のところはあまり参考にならないでしょう。 |
|||
|
|||
しかし、この方法では **依存性注入** システムがどのように機能するかに焦点を当てることができます。 |
|||
|
|||
### 依存関係の作成 |
|||
|
|||
まずは依存関係に注目してみましょう。 |
|||
|
|||
以下のように、*path operation関数*と同じパラメータを全て取ることができる関数にすぎません: |
|||
|
|||
```Python hl_lines="8 9" |
|||
{!../../../docs_src/dependencies/tutorial001.py!} |
|||
``` |
|||
|
|||
これだけです。 |
|||
|
|||
**2行**。 |
|||
|
|||
そして、それはすべての*path operation関数*が持っているのと同じ形と構造を持っています。 |
|||
|
|||
「デコレータ」を含まない(`@app.get("/some-path")`を含まない)*path operation関数*と考えることもできます。 |
|||
|
|||
そして何でも返すことができます。 |
|||
|
|||
この場合、この依存関係は以下を期待しています: |
|||
|
|||
* オプショナルのクエリパラメータ`q`は`str`です。 |
|||
* オプショナルのクエリパラメータ`skip`は`int`で、デフォルトは`0`です。 |
|||
* オプショナルのクエリパラメータ`limit`は`int`で、デフォルトは`100`です。 |
|||
|
|||
そして、これらの値を含む`dict`を返します。 |
|||
|
|||
### `Depends`のインポート |
|||
|
|||
```Python hl_lines="3" |
|||
{!../../../docs_src/dependencies/tutorial001.py!} |
|||
``` |
|||
|
|||
### "dependant"での依存関係の宣言 |
|||
|
|||
*path operation関数*のパラメータに`Body`や`Query`などを使用するのと同じように、新しいパラメータに`Depends`を使用することができます: |
|||
|
|||
```Python hl_lines="13 18" |
|||
{!../../../docs_src/dependencies/tutorial001.py!} |
|||
``` |
|||
|
|||
関数のパラメータに`Depends`を使用するのは`Body`や`Query`などと同じですが、`Depends`の動作は少し異なります。 |
|||
|
|||
`Depends`は1つのパラメータしか与えられません。 |
|||
|
|||
このパラメータは関数のようなものである必要があります。 |
|||
|
|||
そして、その関数は、*path operation関数*が行うのと同じ方法でパラメータを取ります。 |
|||
|
|||
!!! tip "豆知識" |
|||
次の章では、関数以外の「もの」が依存関係として使用できるものを見ていきます。 |
|||
|
|||
新しいリクエストが到着するたびに、**FastAPI** が以下のような処理を行います: |
|||
|
|||
* 依存関係("dependable")関数を正しいパラメータで呼び出します。 |
|||
* 関数の結果を取得します。 |
|||
* *path operation関数*のパラメータにその結果を代入してください。 |
|||
|
|||
```mermaid |
|||
graph TB |
|||
|
|||
common_parameters(["common_parameters"]) |
|||
read_items["/items/"] |
|||
read_users["/users/"] |
|||
|
|||
common_parameters --> read_items |
|||
common_parameters --> read_users |
|||
``` |
|||
|
|||
この方法では、共有されるコードを一度書き、**FastAPI** が*path operations*のための呼び出しを行います。 |
|||
|
|||
!!! check "確認" |
|||
特別なクラスを作成してどこかで **FastAPI** に渡して「登録」する必要はないことに注意してください。 |
|||
|
|||
`Depends`を渡すだけで、**FastAPI** が残りの処理をしてくれます。 |
|||
|
|||
## `async`にするかどうか |
|||
|
|||
依存関係は **FastAPI**(*path operation関数*と同じ)からも呼び出されるため、関数を定義する際にも同じルールが適用されます。 |
|||
|
|||
`async def`や通常の`def`を使用することができます。 |
|||
|
|||
また、通常の`def`*path operation関数*の中に`async def`を入れて依存関係を宣言したり、`async def`*path operation関数*の中に`def`を入れて依存関係を宣言したりすることなどができます。 |
|||
|
|||
それは重要ではありません。**FastAPI** は何をすべきかを知っています。 |
|||
|
|||
!!! note "備考" |
|||
わからない場合は、ドキュメントの[Async: *"In a hurry?"*](../../async.md){.internal-link target=_blank}の中の`async`と`await`についてのセクションを確認してください。 |
|||
|
|||
## OpenAPIとの統合 |
|||
|
|||
依存関係(およびサブ依存関係)のすべてのリクエスト宣言、検証、および要件は、同じOpenAPIスキーマに統合されます。 |
|||
|
|||
つまり、対話型ドキュメントにはこれらの依存関係から得られる全ての情報も含まれているということです: |
|||
|
|||
<img src="https://fastapi.tiangolo.com/img/tutorial/dependencies/image01.png"> |
|||
|
|||
## 簡単な使い方 |
|||
|
|||
見てみると、*path*と*operation*が一致した時に*path operation関数*が宣言されていて、**FastAPI** が正しいパラメータで関数を呼び出してリクエストからデータを抽出する処理をしています。 |
|||
|
|||
実は、すべての(あるいはほとんどの)Webフレームワークは、このように動作します。 |
|||
|
|||
これらの関数を直接呼び出すことはありません。これらの関数はフレームワーク(この場合は、**FastAPI**)によって呼び出されます。 |
|||
|
|||
依存性注入システムでは、**FastAPI** に*path operation*もまた、*path operation関数*の前に実行されるべき他の何かに「依存」していることを伝えることができ、**FastAPI** がそれを実行し、結果を「注入」することを引き受けます。 |
|||
|
|||
他にも、「依存性注入」と同じような考えの一般的な用語があります: |
|||
|
|||
* リソース |
|||
* プロバイダ |
|||
* サービス |
|||
* インジェクタブル |
|||
* コンポーネント |
|||
|
|||
## **FastAPI** プラグイン |
|||
|
|||
統合や「プラグイン」は **依存性注入** システムを使って構築することができます。しかし、実際には、**「プラグイン」を作成する必要はありません**。依存関係を使用することで、無限の数の統合やインタラクションを宣言することができ、それが**path operation関数*で利用可能になるからです。 |
|||
|
|||
依存関係は非常にシンプルで直感的な方法で作成することができ、必要なPythonパッケージをインポートするだけで、*文字通り*数行のコードでAPI関数と統合することができます。 |
|||
|
|||
次の章では、リレーショナルデータベースやNoSQLデータベース、セキュリティなどについて、その例を見ていきます。 |
|||
|
|||
## **FastAPI** 互換性 |
|||
|
|||
依存性注入システムがシンプルなので、**FastAPI** は以下のようなものと互換性があります: |
|||
|
|||
* すべてのリレーショナルデータベース |
|||
* NoSQLデータベース |
|||
* 外部パッケージ |
|||
* 外部API |
|||
* 認証・認可システム |
|||
* API利用状況監視システム |
|||
* レスポンスデータ注入システム |
|||
* など。 |
|||
|
|||
## シンプルでパワフル |
|||
|
|||
階層依存性注入システムは、定義や使用方法が非常にシンプルであるにもかかわらず、非常に強力なものとなっています。 |
|||
|
|||
依存関係事態を定義する依存関係を定義することができます。 |
|||
|
|||
最終的には、依存関係の階層ツリーが構築され、**依存性注入**システムが、これらの依存関係(およびそのサブ依存関係)をすべて解決し、各ステップで結果を提供(注入)します。 |
|||
|
|||
例えば、4つのAPIエンドポイント(*path operations*)があるとします: |
|||
|
|||
* `/items/public/` |
|||
* `/items/private/` |
|||
* `/users/{user_id}/activate` |
|||
* `/items/pro/` |
|||
|
|||
そして、依存関係とサブ依存関係だけで、それぞれに異なるパーミッション要件を追加することができます: |
|||
|
|||
```mermaid |
|||
graph TB |
|||
|
|||
current_user(["current_user"]) |
|||
active_user(["active_user"]) |
|||
admin_user(["admin_user"]) |
|||
paying_user(["paying_user"]) |
|||
|
|||
public["/items/public/"] |
|||
private["/items/private/"] |
|||
activate_user["/users/{user_id}/activate"] |
|||
pro_items["/items/pro/"] |
|||
|
|||
current_user --> active_user |
|||
active_user --> admin_user |
|||
active_user --> paying_user |
|||
|
|||
current_user --> public |
|||
active_user --> private |
|||
admin_user --> activate_user |
|||
paying_user --> pro_items |
|||
``` |
|||
|
|||
## **OpenAPI** との統合 |
|||
|
|||
これら全ての依存関係は、要件を宣言すると同時に、*path operations*にパラメータやバリデーションを追加します。 |
|||
|
|||
**FastAPI** はそれをすべてOpenAPIスキーマに追加して、対話型のドキュメントシステムに表示されるようにします。 |
@ -0,0 +1,86 @@ |
|||
# サブ依存関係 |
|||
|
|||
**サブ依存関係** を持つ依存関係を作成することができます。 |
|||
|
|||
それらは必要なだけ **深く** することができます。 |
|||
|
|||
**FastAPI** はそれらを解決してくれます。 |
|||
|
|||
### 最初の依存関係「依存可能なもの」 |
|||
|
|||
以下のような最初の依存関係(「依存可能なもの」)を作成することができます: |
|||
|
|||
```Python hl_lines="8 9" |
|||
{!../../../docs_src/dependencies/tutorial005.py!} |
|||
``` |
|||
|
|||
これはオプショナルのクエリパラメータ`q`を`str`として宣言し、それを返すだけです。 |
|||
|
|||
これは非常にシンプルです(あまり便利ではありません)が、サブ依存関係がどのように機能するかに焦点を当てるのに役立ちます。 |
|||
|
|||
### 第二の依存関係 「依存可能なもの」と「依存」 |
|||
|
|||
そして、別の依存関数(「依存可能なもの」)を作成して、同時にそれ自身の依存関係を宣言することができます(つまりそれ自身も「依存」です): |
|||
|
|||
```Python hl_lines="13" |
|||
{!../../../docs_src/dependencies/tutorial005.py!} |
|||
``` |
|||
|
|||
宣言されたパラメータに注目してみましょう: |
|||
|
|||
* この関数は依存関係(「依存可能なもの」)そのものであるにもかかわらず、別の依存関係を宣言しています(何か他のものに「依存」しています)。 |
|||
* これは`query_extractor`に依存しており、それが返す値をパラメータ`q`に代入します。 |
|||
* また、オプショナルの`last_query`クッキーを`str`として宣言します。 |
|||
* ユーザーがクエリ`q`を提供しなかった場合、クッキーに保存していた最後に使用したクエリを使用します。 |
|||
|
|||
### 依存関係の使用 |
|||
|
|||
以下のように依存関係を使用することができます: |
|||
|
|||
```Python hl_lines="21" |
|||
{!../../../docs_src/dependencies/tutorial005.py!} |
|||
``` |
|||
|
|||
!!! info "情報" |
|||
*path operation関数*の中で宣言している依存関係は`query_or_cookie_extractor`の1つだけであることに注意してください。 |
|||
|
|||
しかし、**FastAPI** は`query_extractor`を最初に解決し、その結果を`query_or_cookie_extractor`を呼び出す時に渡す必要があることを知っています。 |
|||
|
|||
```mermaid |
|||
graph TB |
|||
|
|||
query_extractor(["query_extractor"]) |
|||
query_or_cookie_extractor(["query_or_cookie_extractor"]) |
|||
|
|||
read_query["/items/"] |
|||
|
|||
query_extractor --> query_or_cookie_extractor --> read_query |
|||
``` |
|||
|
|||
## 同じ依存関係の複数回の使用 |
|||
|
|||
依存関係の1つが同じ*path operation*に対して複数回宣言されている場合、例えば、複数の依存関係が共通のサブ依存関係を持っている場合、**FastAPI** はリクエストごとに1回だけそのサブ依存関係を呼び出します。 |
|||
|
|||
そして、返された値を<abbr title="計算された値・生成された値を保存するユーティリティまたはシステム、再計算する代わりに再利用するためのもの">「キャッシュ」</abbr>に保存し、同じリクエストに対して依存関係を何度も呼び出す代わりに、特定のリクエストでそれを必要とする全ての「依存関係」に渡すことになります。 |
|||
|
|||
高度なシナリオでは、「キャッシュされた」値を使うのではなく、同じリクエストの各ステップ(おそらく複数回)で依存関係を呼び出す必要があることがわかっている場合、`Depens`を使用する際に、`use_cache=False`というパラメータを設定することができます。 |
|||
|
|||
```Python hl_lines="1" |
|||
async def needy_dependency(fresh_value: str = Depends(get_value, use_cache=False)): |
|||
return {"fresh_value": fresh_value} |
|||
``` |
|||
|
|||
## まとめ |
|||
|
|||
ここで使われている派手な言葉は別にして、**依存性注入** システムは非常にシンプルです。 |
|||
|
|||
*path operation関数*と同じように見えるただの関数です。 |
|||
|
|||
しかし、それでも非常に強力で、任意の深くネストされた依存関係「グラフ」(ツリー)を宣言することができます。 |
|||
|
|||
!!! tip "豆知識" |
|||
これらの単純な例では、全てが役に立つとは言えないかもしれません。 |
|||
|
|||
しかし、**security** についての章で、それがどれほど有用であるかがわかるでしょう。 |
|||
|
|||
そして、あなたを救うコードの量もみることになるでしょう。 |
@ -0,0 +1,195 @@ |
|||
# モデル - より詳しく |
|||
|
|||
先ほどの例に続き、複数の関連モデルを持つことが一般的です。 |
|||
|
|||
これはユーザーモデルの場合は特にそうです。なぜなら: |
|||
|
|||
* **入力モデル** にはパスワードが必要です。 |
|||
* **出力モデル**はパスワードをもつべきではありません。 |
|||
* **データベースモデル**はおそらくハッシュ化されたパスワードが必要になるでしょう。 |
|||
|
|||
!!! danger "危険" |
|||
ユーザーの平文のパスワードは絶対に保存しないでください。常に認証に利用可能な「安全なハッシュ」を保存してください。 |
|||
|
|||
知らない方は、[セキュリティの章](security/simple-oauth2.md#password-hashing){.internal-link target=_blank}で「パスワードハッシュ」とは何かを学ぶことができます。 |
|||
|
|||
## 複数のモデル |
|||
|
|||
ここでは、パスワードフィールドをもつモデルがどのように見えるのか、また、どこで使われるのか、大まかなイメージを紹介します: |
|||
|
|||
```Python hl_lines="9 11 16 22 24 29-30 33-35 40-41" |
|||
{!../../../docs_src/extra_models/tutorial001.py!} |
|||
``` |
|||
|
|||
### `**user_in.dict()`について |
|||
|
|||
#### Pydanticの`.dict()` |
|||
|
|||
`user_in`は`UserIn`クラスのPydanticモデルです。 |
|||
|
|||
Pydanticモデルには、モデルのデータを含む`dict`を返す`.dict()`メソッドがあります。 |
|||
|
|||
そこで、以下のようなPydanticオブジェクト`user_in`を作成すると: |
|||
|
|||
```Python |
|||
user_in = UserIn(username="john", password="secret", email="[email protected]") |
|||
``` |
|||
|
|||
そして呼び出すと: |
|||
|
|||
```Python |
|||
user_dict = user_in.dict() |
|||
``` |
|||
|
|||
これで変数`user_dict`のデータを持つ`dict`ができました。(これはPydanticモデルのオブジェクトの代わりに`dict`です)。 |
|||
|
|||
そして呼び出すと: |
|||
|
|||
```Python |
|||
print(user_dict) |
|||
``` |
|||
|
|||
以下のようなPythonの`dict`を得ることができます: |
|||
|
|||
```Python |
|||
{ |
|||
'username': 'john', |
|||
'password': 'secret', |
|||
'email': '[email protected]', |
|||
'full_name': None, |
|||
} |
|||
``` |
|||
|
|||
#### `dict`の展開 |
|||
|
|||
`user_dict`のような`dict`を受け取り、それを`**user_dict`を持つ関数(またはクラス)に渡すと、Pythonはそれを「展開」します。これは`user_dict`のキーと値を直接キー・バリューの引数として渡します。 |
|||
|
|||
そこで上述の`user_dict`の続きを以下のように書くと: |
|||
|
|||
```Python |
|||
UserInDB(**user_dict) |
|||
``` |
|||
|
|||
以下と同等の結果になります: |
|||
|
|||
```Python |
|||
UserInDB( |
|||
username="john", |
|||
password="secret", |
|||
email="[email protected]", |
|||
full_name=None, |
|||
) |
|||
``` |
|||
|
|||
もっと正確に言えば、`user_dict`を将来的にどんな内容であっても直接使用することになります: |
|||
|
|||
```Python |
|||
UserInDB( |
|||
username = user_dict["username"], |
|||
password = user_dict["password"], |
|||
email = user_dict["email"], |
|||
full_name = user_dict["full_name"], |
|||
) |
|||
``` |
|||
|
|||
#### 別のモデルからつくるPydanticモデル |
|||
|
|||
上述の例では`user_in.dict()`から`user_dict`をこのコードのように取得していますが: |
|||
|
|||
```Python |
|||
user_dict = user_in.dict() |
|||
UserInDB(**user_dict) |
|||
``` |
|||
|
|||
これは以下と同等です: |
|||
|
|||
```Python |
|||
UserInDB(**user_in.dict()) |
|||
``` |
|||
|
|||
...なぜなら`user_in.dict()`は`dict`であり、`**`を付与して`UserInDB`を渡してPythonに「展開」させているからです。 |
|||
|
|||
そこで、別のPydanticモデルのデータからPydanticモデルを取得します。 |
|||
|
|||
#### `dict`の展開と追加引数 |
|||
|
|||
そして、追加のキーワード引数`hashed_password=hashed_password`を以下のように追加すると: |
|||
|
|||
```Python |
|||
UserInDB(**user_in.dict(), hashed_password=hashed_password) |
|||
``` |
|||
|
|||
...以下のようになります: |
|||
|
|||
```Python |
|||
UserInDB( |
|||
username = user_dict["username"], |
|||
password = user_dict["password"], |
|||
email = user_dict["email"], |
|||
full_name = user_dict["full_name"], |
|||
hashed_password = hashed_password, |
|||
) |
|||
``` |
|||
|
|||
!!! warning "注意" |
|||
サポートしている追加機能は、データの可能な流れをデモするだけであり、もちろん本当のセキュリティを提供しているわけではありません。 |
|||
|
|||
## 重複の削減 |
|||
|
|||
コードの重複を減らすことは、**FastAPI**の中核的なアイデアの1つです。 |
|||
|
|||
コードの重複が増えると、バグやセキュリティの問題、コードの非同期化問題(ある場所では更新しても他の場所では更新されない場合)などが発生する可能性が高くなります。 |
|||
|
|||
そして、これらのモデルは全てのデータを共有し、属性名や型を重複させています。 |
|||
|
|||
もっと良い方法があります。 |
|||
|
|||
他のモデルのベースとなる`UserBase`モデルを宣言することができます。そして、そのモデルの属性(型宣言、検証など)を継承するサブクラスを作ることができます。 |
|||
|
|||
データの変換、検証、文書化などはすべて通常通りに動作します。 |
|||
|
|||
このようにして、モデル間の違いだけを宣言することができます: |
|||
|
|||
```Python hl_lines="9 15 16 19 20 23 24" |
|||
{!../../../docs_src/extra_models/tutorial002.py!} |
|||
``` |
|||
|
|||
## `Union`または`anyOf` |
|||
|
|||
レスポンスを2つの型の`Union`として宣言することができます。 |
|||
|
|||
OpenAPIでは`anyOf`で定義されます。 |
|||
|
|||
そのためには、標準的なPythonの型ヒント<a href="https://docs.python.org/3/library/typing.html#typing.Union" class="external-link" target="_blank">`typing.Union`</a>を使用します: |
|||
|
|||
```Python hl_lines="1 14 15 18 19 20 33" |
|||
{!../../../docs_src/extra_models/tutorial003.py!} |
|||
``` |
|||
|
|||
## モデルのリスト |
|||
|
|||
同じように、オブジェクトのリストのレスポンスを宣言することができます。 |
|||
|
|||
そのためには、標準のPythonの`typing.List`を使用する: |
|||
|
|||
```Python hl_lines="1 20" |
|||
{!../../../docs_src/extra_models/tutorial004.py!} |
|||
``` |
|||
|
|||
## 任意の`dict`を持つレスポンス |
|||
|
|||
また、Pydanticモデルを使用せずに、キーと値の型だけを定義した任意の`dict`を使ってレスポンスを宣言することもできます。 |
|||
|
|||
これは、有効なフィールド・属性名(Pydanticモデルに必要なもの)を事前に知らない場合に便利です。 |
|||
|
|||
この場合、`typing.Dict`を使用することができます: |
|||
|
|||
```Python hl_lines="1 8" |
|||
{!../../../docs_src/extra_models/tutorial005.py!} |
|||
``` |
|||
|
|||
## まとめ |
|||
|
|||
複数のPydanticモデルを使用し、ケースごとに自由に継承します。 |
|||
|
|||
エンティティが異なる「状態」を持たなければならない場合は、エンティティごとに単一のデータモデルを持つ必要はありません。`password` や `password_hash` やパスワードなしなどのいくつかの「状態」をもつユーザー「エンティティ」の場合の様にすれば良いです。 |
@ -0,0 +1,265 @@ |
|||
# エラーハンドリング |
|||
|
|||
APIを使用しているクライアントにエラーを通知する必要がある状況はたくさんあります。 |
|||
|
|||
このクライアントは、フロントエンドを持つブラウザ、誰かのコード、IoTデバイスなどが考えられます。 |
|||
|
|||
クライアントに以下のようなことを伝える必要があるかもしれません: |
|||
|
|||
* クライアントにはその操作のための十分な権限がありません。 |
|||
* クライアントはそのリソースにアクセスできません。 |
|||
* クライアントがアクセスしようとしていた項目が存在しません。 |
|||
* など |
|||
|
|||
これらの場合、通常は **400**(400から499)の範囲内の **HTTPステータスコード** を返すことになります。 |
|||
|
|||
これは200のHTTPステータスコード(200から299)に似ています。これらの「200」ステータスコードは、何らかの形でリクエスト「成功」であったことを意味します。 |
|||
|
|||
400の範囲にあるステータスコードは、クライアントからのエラーがあったことを意味します。 |
|||
|
|||
**"404 Not Found"** のエラー(およびジョーク)を覚えていますか? |
|||
|
|||
## `HTTPException`の使用 |
|||
|
|||
HTTPレスポンスをエラーでクライアントに返すには、`HTTPException`を使用します。 |
|||
|
|||
### `HTTPException`のインポート |
|||
|
|||
```Python hl_lines="1" |
|||
{!../../../docs_src/handling_errors/tutorial001.py!} |
|||
``` |
|||
|
|||
### コード内での`HTTPException`の発生 |
|||
|
|||
`HTTPException`は通常のPythonの例外であり、APIに関連するデータを追加したものです。 |
|||
|
|||
Pythonの例外なので、`return`ではなく、`raise`です。 |
|||
|
|||
これはまた、*path operation関数*の内部で呼び出しているユーティリティ関数の内部から`HTTPException`を発生させた場合、*path operation関数*の残りのコードは実行されず、そのリクエストを直ちに終了させ、`HTTPException`からのHTTPエラーをクライアントに送信することを意味します。 |
|||
|
|||
値を返す`return`よりも例外を発生させることの利点は、「依存関係とセキュリティ」のセクションでより明確になります。 |
|||
|
|||
この例では、クライアントが存在しないIDでアイテムを要求した場合、`404`のステータスコードを持つ例外を発生させます: |
|||
|
|||
```Python hl_lines="11" |
|||
{!../../../docs_src/handling_errors/tutorial001.py!} |
|||
``` |
|||
|
|||
### レスポンス結果 |
|||
|
|||
クライアントが`http://example.com/items/foo`(`item_id` `"foo"`)をリクエストすると、HTTPステータスコードが200で、以下のJSONレスポンスが返されます: |
|||
|
|||
```JSON |
|||
{ |
|||
"item": "The Foo Wrestlers" |
|||
} |
|||
``` |
|||
|
|||
しかし、クライアントが`http://example.com/items/bar`(存在しない`item_id` `"bar"`)をリクエストした場合、HTTPステータスコード404("not found"エラー)と以下のJSONレスポンスが返されます: |
|||
|
|||
```JSON |
|||
{ |
|||
"detail": "Item not found" |
|||
} |
|||
``` |
|||
|
|||
!!! tip "豆知識" |
|||
`HTTPException`を発生させる際には、`str`だけでなく、JSONに変換できる任意の値を`detail`パラメータとして渡すことができます。 |
|||
|
|||
`dist`や`list`などを渡すことができます。 |
|||
|
|||
これらは **FastAPI** によって自動的に処理され、JSONに変換されます。 |
|||
|
|||
## カスタムヘッダーの追加 |
|||
|
|||
例えば、いくつかのタイプのセキュリティのために、HTTPエラーにカスタムヘッダを追加できると便利な状況がいくつかあります。 |
|||
|
|||
おそらくコードの中で直接使用する必要はないでしょう。 |
|||
|
|||
しかし、高度なシナリオのために必要な場合には、カスタムヘッダーを追加することができます: |
|||
|
|||
```Python hl_lines="14" |
|||
{!../../../docs_src/handling_errors/tutorial002.py!} |
|||
``` |
|||
|
|||
## カスタム例外ハンドラのインストール |
|||
|
|||
カスタム例外ハンドラは<a href="https://www.starlette.io/exceptions/" class="external-link" target="_blank">Starletteと同じ例外ユーティリティ</a>を使用して追加することができます。 |
|||
|
|||
あなた(または使用しているライブラリ)が`raise`するかもしれないカスタム例外`UnicornException`があるとしましょう。 |
|||
|
|||
そして、この例外をFastAPIでグローバルに処理したいと思います。 |
|||
|
|||
カスタム例外ハンドラを`@app.exception_handler()`で追加することができます: |
|||
|
|||
```Python hl_lines="5 6 7 13 14 15 16 17 18 24" |
|||
{!../../../docs_src/handling_errors/tutorial003.py!} |
|||
``` |
|||
|
|||
ここで、`/unicorns/yolo`をリクエストすると、*path operation*は`UnicornException`を`raise`します。 |
|||
|
|||
しかし、これは`unicorn_exception_handler`で処理されます。 |
|||
|
|||
そのため、HTTPステータスコードが`418`で、JSONの内容が以下のような明確なエラーを受け取ることになります: |
|||
|
|||
```JSON |
|||
{"message": "Oops! yolo did something. There goes a rainbow..."} |
|||
``` |
|||
|
|||
!!! note "技術詳細" |
|||
また、`from starlette.requests import Request`と`from starlette.responses import JSONResponse`を使用することもできます。 |
|||
|
|||
**FastAPI** は開発者の利便性を考慮して、`fastapi.responses`と同じ`starlette.responses`を提供しています。しかし、利用可能なレスポンスのほとんどはStarletteから直接提供されます。これは`Request`と同じです。 |
|||
|
|||
## デフォルトの例外ハンドラのオーバーライド |
|||
|
|||
**FastAPI** にはいくつかのデフォルトの例外ハンドラがあります。 |
|||
|
|||
これらのハンドラは、`HTTPException`を`raise`させた場合や、リクエストに無効なデータが含まれている場合にデフォルトのJSONレスポンスを返す役割を担っています。 |
|||
|
|||
これらの例外ハンドラを独自のものでオーバーライドすることができます。 |
|||
|
|||
### リクエスト検証の例外のオーバーライド |
|||
|
|||
リクエストに無効なデータが含まれている場合、**FastAPI** は内部的に`RequestValidationError`を発生させます。 |
|||
|
|||
また、そのためのデフォルトの例外ハンドラも含まれています。 |
|||
|
|||
これをオーバーライドするには`RequestValidationError`をインポートして`@app.exception_handler(RequestValidationError)`と一緒に使用して例外ハンドラをデコレートします。 |
|||
|
|||
この例外ハンドラは`Requset`と例外を受け取ります。 |
|||
|
|||
```Python hl_lines="2 14 15 16" |
|||
{!../../../docs_src/handling_errors/tutorial004.py!} |
|||
``` |
|||
|
|||
これで、`/items/foo`にアクセスすると、デフォルトのJSONエラーの代わりに以下が返されます: |
|||
|
|||
```JSON |
|||
{ |
|||
"detail": [ |
|||
{ |
|||
"loc": [ |
|||
"path", |
|||
"item_id" |
|||
], |
|||
"msg": "value is not a valid integer", |
|||
"type": "type_error.integer" |
|||
} |
|||
] |
|||
} |
|||
``` |
|||
|
|||
以下のようなテキスト版を取得します: |
|||
|
|||
``` |
|||
1 validation error |
|||
path -> item_id |
|||
value is not a valid integer (type=type_error.integer) |
|||
``` |
|||
|
|||
#### `RequestValidationError`と`ValidationError` |
|||
|
|||
!!! warning "注意" |
|||
これらは今のあなたにとって重要でない場合は省略しても良い技術的な詳細です。 |
|||
|
|||
`RequestValidationError`はPydanticの<a href="https://pydantic-docs.helpmanual.io/#error-handling" class="external-link" target="_blank">`ValidationError`</a>のサブクラスです。 |
|||
|
|||
**FastAPI** は`response_model`でPydanticモデルを使用していて、データにエラーがあった場合、ログにエラーが表示されるようにこれを使用しています。 |
|||
|
|||
しかし、クライアントやユーザーはそれを見ることはありません。その代わりに、クライアントはHTTPステータスコード`500`の「Internal Server Error」を受け取ります。 |
|||
|
|||
*レスポンス*やコードのどこか(クライアントの*リクエスト*ではなく)にPydanticの`ValidationError`がある場合、それは実際にはコードのバグなのでこのようにすべきです。 |
|||
|
|||
また、あなたがそれを修正している間は、セキュリティの脆弱性が露呈する場合があるため、クライアントやユーザーがエラーに関する内部情報にアクセスできないようにしてください。 |
|||
|
|||
### エラーハンドラ`HTTPException`のオーバーライド |
|||
|
|||
同様に、`HTTPException`ハンドラをオーバーライドすることもできます。 |
|||
|
|||
例えば、これらのエラーに対しては、JSONではなくプレーンテキストを返すようにすることができます: |
|||
|
|||
```Python hl_lines="3 4 9 10 11 22" |
|||
{!../../../docs_src/handling_errors/tutorial004.py!} |
|||
``` |
|||
|
|||
!!! note "技術詳細" |
|||
また、`from starlette.responses import PlainTextResponse`を使用することもできます。 |
|||
|
|||
**FastAPI** は開発者の利便性を考慮して、`fastapi.responses`と同じ`starlette.responses`を提供しています。しかし、利用可能なレスポンスのほとんどはStarletteから直接提供されます。 |
|||
|
|||
### `RequestValidationError`のボディの使用 |
|||
|
|||
`RequestValidationError`には無効なデータを含む`body`が含まれています。 |
|||
|
|||
アプリ開発中に本体のログを取ってデバッグしたり、ユーザーに返したりなどに使用することができます。 |
|||
|
|||
```Python hl_lines="14" |
|||
{!../../../docs_src/handling_errors/tutorial005.py!} |
|||
``` |
|||
|
|||
ここで、以下のような無効な項目を送信してみてください: |
|||
|
|||
```JSON |
|||
{ |
|||
"title": "towel", |
|||
"size": "XL" |
|||
} |
|||
``` |
|||
|
|||
受信したボディを含むデータが無効であることを示すレスポンスが表示されます: |
|||
|
|||
```JSON hl_lines="12 13 14 15" |
|||
{ |
|||
"detail": [ |
|||
{ |
|||
"loc": [ |
|||
"body", |
|||
"size" |
|||
], |
|||
"msg": "value is not a valid integer", |
|||
"type": "type_error.integer" |
|||
} |
|||
], |
|||
"body": { |
|||
"title": "towel", |
|||
"size": "XL" |
|||
} |
|||
} |
|||
``` |
|||
|
|||
#### FastAPIの`HTTPException`とStarletteの`HTTPException` |
|||
|
|||
**FastAPI**は独自の`HTTPException`を持っています。 |
|||
|
|||
また、 **FastAPI**のエラークラス`HTTPException`はStarletteのエラークラス`HTTPException`を継承しています。 |
|||
|
|||
唯一の違いは、**FastAPI** の`HTTPException`はレスポンスに含まれるヘッダを追加できることです。 |
|||
|
|||
これはOAuth 2.0といくつかのセキュリティユーティリティのために内部的に必要とされ、使用されています。 |
|||
|
|||
そのため、コード内では通常通り **FastAPI** の`HTTPException`を発生させ続けることができます。 |
|||
|
|||
しかし、例外ハンドラを登録する際には、Starletteの`HTTPException`を登録しておく必要があります。 |
|||
|
|||
これにより、Starletteの内部コードやStarletteの拡張機能やプラグインの一部が`HTTPException`を発生させた場合、ハンドラがそれをキャッチして処理することができるようになります。 |
|||
|
|||
以下の例では、同じコード内で両方の`HTTPException`を使用できるようにするために、Starletteの例外の名前を`StarletteHTTPException`に変更しています: |
|||
|
|||
```Python |
|||
from starlette.exceptions import HTTPException as StarletteHTTPException |
|||
``` |
|||
|
|||
### **FastAPI** の例外ハンドラの再利用 |
|||
|
|||
また、何らかの方法で例外を使用することもできますが、**FastAPI** から同じデフォルトの例外ハンドラを使用することもできます。 |
|||
|
|||
デフォルトの例外ハンドラを`fastapi.exception_handlers`からインポートして再利用することができます: |
|||
|
|||
```Python hl_lines="2 3 4 5 15 21" |
|||
{!../../../docs_src/handling_errors/tutorial006.py!} |
|||
``` |
|||
|
|||
この例では、非常に表現力のあるメッセージでエラーを`print`しています。 |
|||
|
|||
しかし、例外を使用して、デフォルトの例外ハンドラを再利用することができるということが理解できます。 |
@ -0,0 +1,122 @@ |
|||
# パスパラメータと数値の検証 |
|||
|
|||
クエリパラメータに対して`Query`でより多くのバリデーションとメタデータを宣言できるのと同じように、パスパラメータに対しても`Path`で同じ種類のバリデーションとメタデータを宣言することができます。 |
|||
|
|||
## Pathのインポート |
|||
|
|||
まず初めに、`fastapi`から`Path`をインポートします: |
|||
|
|||
```Python hl_lines="1" |
|||
{!../../../docs_src/path_params_numeric_validations/tutorial001.py!} |
|||
``` |
|||
|
|||
## メタデータの宣言 |
|||
|
|||
パラメータは`Query`と同じものを宣言することができます。 |
|||
|
|||
例えば、パスパラメータ`item_id`に対して`title`のメタデータを宣言するには以下のようにします: |
|||
|
|||
```Python hl_lines="8" |
|||
{!../../../docs_src/path_params_numeric_validations/tutorial001.py!} |
|||
``` |
|||
|
|||
!!! note "備考" |
|||
パスの一部でなければならないので、パスパラメータは常に必須です。 |
|||
|
|||
そのため、`...`を使用して必須と示す必要があります。 |
|||
|
|||
それでも、`None`で宣言しても、デフォルト値を設定しても、何の影響もなく、常に必要とされていることに変わりはありません。 |
|||
|
|||
## 必要に応じてパラメータを並び替える |
|||
|
|||
クエリパラメータ`q`を必須の`str`として宣言したいとしましょう。 |
|||
|
|||
また、このパラメータには何も宣言する必要がないので、`Query`を使う必要はありません。 |
|||
|
|||
しかし、パスパラメータ`item_id`のために`Path`を使用する必要があります。 |
|||
|
|||
Pythonは「デフォルト」を持たない値の前に「デフォルト」を持つ値を置くことができません。 |
|||
|
|||
しかし、それらを並び替えることができ、デフォルト値を持たない値(クエリパラメータ`q`)を最初に持つことができます。 |
|||
|
|||
**FastAPI**では関係ありません。パラメータは名前、型、デフォルトの宣言(`Query`、`Path`など)で検出され、順番は気にしません。 |
|||
|
|||
そのため、以下のように関数を宣言することができます: |
|||
|
|||
```Python hl_lines="8" |
|||
{!../../../docs_src/path_params_numeric_validations/tutorial002.py!} |
|||
``` |
|||
|
|||
## 必要に応じてパラメータを並び替えるトリック |
|||
|
|||
クエリパラメータ`q`を`Query`やデフォルト値なしで宣言し、パスパラメータ`item_id`を`Path`を用いて宣言し、それらを別の順番に並びたい場合、Pythonには少し特殊な構文が用意されています。 |
|||
|
|||
関数の最初のパラメータとして`*`を渡します。 |
|||
|
|||
Pythonはその`*`で何かをすることはありませんが、それ以降のすべてのパラメータがキーワード引数(キーと値のペア)として呼ばれるべきものであると知っているでしょう。それは<abbr title="From: K-ey W-ord Arg-uments"><code>kwargs</code></abbr>としても知られています。たとえデフォルト値がなくても。 |
|||
|
|||
```Python hl_lines="8" |
|||
{!../../../docs_src/path_params_numeric_validations/tutorial003.py!} |
|||
``` |
|||
|
|||
## 数値の検証: 以上 |
|||
|
|||
`Query`と`Path`(、そして後述する他のもの)を用いて、文字列の制約を宣言することができますが、数値の制約も同様に宣言できます。 |
|||
|
|||
ここで、`ge=1`の場合、`item_id`は`1`「より大きい`g`か、同じ`e`」整数でなれけばなりません。 |
|||
|
|||
```Python hl_lines="8" |
|||
{!../../../docs_src/path_params_numeric_validations/tutorial004.py!} |
|||
``` |
|||
|
|||
## 数値の検証: より大きいと小なりイコール |
|||
|
|||
以下も同様です: |
|||
|
|||
* `gt`: より大きい(`g`reater `t`han) |
|||
* `le`: 小なりイコール(`l`ess than or `e`qual) |
|||
|
|||
```Python hl_lines="9" |
|||
{!../../../docs_src/path_params_numeric_validations/tutorial005.py!} |
|||
``` |
|||
|
|||
## 数値の検証: 浮動小数点、 大なり小なり |
|||
|
|||
数値のバリデーションは`float`の値に対しても有効です。 |
|||
|
|||
ここで重要になってくるのは<abbr title="より大きい"><code>gt</code></abbr>だけでなく<abbr title="以下"><code>ge</code></abbr>も宣言できることです。これと同様に、例えば、値が`1`より小さくても`0`より大きくなければならないことを要求することができます。 |
|||
|
|||
したがって、`0.5`は有効な値ですが、`0.0`や`0`はそうではありません。 |
|||
|
|||
これは<abbr title="未満"><code>lt</code></abbr>も同じです。 |
|||
|
|||
```Python hl_lines="11" |
|||
{!../../../docs_src/path_params_numeric_validations/tutorial006.py!} |
|||
``` |
|||
|
|||
## まとめ |
|||
|
|||
`Query`と`Path`(そしてまだ見たことない他のもの)では、[クエリパラメータと文字列の検証](query-params-str-validations.md){.internal-link target=_blank}と同じようにメタデータと文字列の検証を宣言することができます。 |
|||
|
|||
また、数値のバリデーションを宣言することもできます: |
|||
|
|||
* `gt`: より大きい(`g`reater `t`han) |
|||
* `ge`: 以上(`g`reater than or `e`qual) |
|||
* `lt`: より小さい(`l`ess `t`han) |
|||
* `le`: 以下(`l`ess than or `e`qual) |
|||
|
|||
!!! info "情報" |
|||
`Query`、`Path`などは後に共通の`Param`クラスのサブクラスを見ることになります。(使う必要はありません) |
|||
|
|||
そして、それらすべては、これまで見てきた追加のバリデーションとメタデータと同じパラメータを共有しています。 |
|||
|
|||
!!! note "技術詳細" |
|||
`fastapi`から`Query`、`Path`などをインポートすると、これらは実際には関数です。 |
|||
|
|||
呼び出されると、同じ名前のクラスのインスタンスを返します。 |
|||
|
|||
そのため、関数である`Query`をインポートし、それを呼び出すと、`Query`という名前のクラスのインスタンスが返されます。 |
|||
|
|||
これらの関数は(クラスを直接使うのではなく)エディタが型についてエラーとしないようにするために存在します。 |
|||
|
|||
この方法によって、これらのエラーを無視するための設定を追加することなく、通常のエディタやコーディングツールを使用することができます。 |
@ -0,0 +1,208 @@ |
|||
# レスポンスモデル |
|||
|
|||
*path operations* のいずれにおいても、`response_model`パラメータを使用して、レスポンスのモデルを宣言することができます: |
|||
|
|||
* `@app.get()` |
|||
* `@app.post()` |
|||
* `@app.put()` |
|||
* `@app.delete()` |
|||
* など。 |
|||
|
|||
```Python hl_lines="17" |
|||
{!../../../docs_src/response_model/tutorial001.py!} |
|||
``` |
|||
|
|||
!!! note "備考" |
|||
`response_model`は「デコレータ」メソッド(`get`、`post`など)のパラメータであることに注意してください。すべてのパラメータやボディのように、*path operation関数* のパラメータではありません。 |
|||
|
|||
Pydanticモデルの属性に対して宣言するのと同じ型を受け取るので、Pydanticモデルになることもできますが、例えば、`List[Item]`のようなPydanticモデルの`list`になることもできます。 |
|||
|
|||
FastAPIは`response_model`を使って以下のことをします: |
|||
|
|||
* 出力データを型宣言に変換します。 |
|||
* データを検証します。 |
|||
* OpenAPIの *path operation* で、レスポンス用のJSON Schemaを追加します。 |
|||
* 自動ドキュメントシステムで使用されます。 |
|||
|
|||
しかし、最も重要なのは: |
|||
|
|||
* 出力データをモデルのデータに限定します。これがどのように重要なのか以下で見ていきましょう。 |
|||
|
|||
!!! note "技術詳細" |
|||
レスポンスモデルは、関数の戻り値のアノテーションではなく、このパラメータで宣言されています。なぜなら、パス関数は実際にはそのレスポンスモデルを返すのではなく、`dict`やデータベースオブジェクト、あるいは他のモデルを返し、`response_model`を使用してフィールドの制限やシリアライズを行うからです。 |
|||
|
|||
## 同じ入力データの返却 |
|||
|
|||
ここでは`UserIn`モデルを宣言しています。それには平文のパスワードが含まれています: |
|||
|
|||
```Python hl_lines="9 11" |
|||
{!../../../docs_src/response_model/tutorial002.py!} |
|||
``` |
|||
|
|||
そして、このモデルを使用して入力を宣言し、同じモデルを使って出力を宣言しています: |
|||
|
|||
```Python hl_lines="17 18" |
|||
{!../../../docs_src/response_model/tutorial002.py!} |
|||
``` |
|||
|
|||
これで、ブラウザがパスワードを使ってユーザーを作成する際に、APIがレスポンスで同じパスワードを返すようになりました。 |
|||
|
|||
この場合、ユーザー自身がパスワードを送信しているので問題ないかもしれません。 |
|||
|
|||
しかし、同じモデルを別の*path operation*に使用すると、すべてのクライアントにユーザーのパスワードを送信してしまうことになります。 |
|||
|
|||
!!! danger "危険" |
|||
ユーザーの平文のパスワードを保存したり、レスポンスで送信したりすることは絶対にしないでください。 |
|||
|
|||
## 出力モデルの追加 |
|||
|
|||
代わりに、平文のパスワードを持つ入力モデルと、パスワードを持たない出力モデルを作成することができます: |
|||
|
|||
```Python hl_lines="9 11 16" |
|||
{!../../../docs_src/response_model/tutorial003.py!} |
|||
``` |
|||
|
|||
ここでは、*path operation関数*がパスワードを含む同じ入力ユーザーを返しているにもかかわらず: |
|||
|
|||
```Python hl_lines="24" |
|||
{!../../../docs_src/response_model/tutorial003.py!} |
|||
``` |
|||
|
|||
...`response_model`を`UserOut`と宣言したことで、パスワードが含まれていません: |
|||
|
|||
```Python hl_lines="22" |
|||
{!../../../docs_src/response_model/tutorial003.py!} |
|||
``` |
|||
|
|||
そのため、**FastAPI** は出力モデルで宣言されていない全てのデータをフィルタリングしてくれます(Pydanticを使用)。 |
|||
|
|||
## ドキュメントを見る |
|||
|
|||
自動ドキュメントを見ると、入力モデルと出力モデルがそれぞれ独自のJSON Schemaを持っていることが確認できます。 |
|||
|
|||
<img src="https://fastapi.tiangolo.com/img/tutorial/response-model/image01.png"> |
|||
|
|||
そして、両方のモデルは、対話型のAPIドキュメントに使用されます: |
|||
|
|||
<img src="https://fastapi.tiangolo.com/img/tutorial/response-model/image02.png"> |
|||
|
|||
## レスポンスモデルのエンコーディングパラメータ |
|||
|
|||
レスポンスモデルにはデフォルト値を設定することができます: |
|||
|
|||
```Python hl_lines="11 13 14" |
|||
{!../../../docs_src/response_model/tutorial004.py!} |
|||
``` |
|||
|
|||
* `description: str = None`は`None`がデフォルト値です。 |
|||
* `tax: float = 10.5`は`10.5`がデフォルト値です。 |
|||
* `tags: List[str] = []` は空のリスト(`[]`)がデフォルト値です。 |
|||
|
|||
しかし、実際に保存されていない場合には結果からそれらを省略した方が良いかもしれません。 |
|||
|
|||
例えば、NoSQLデータベースに多くのオプション属性を持つモデルがあるが、デフォルト値でいっぱいの非常に長いJSONレスポンスを送信したくない場合です。 |
|||
|
|||
### `response_model_exclude_unset`パラメータの使用 |
|||
|
|||
*path operation デコレータ*に`response_model_exclude_unset=True`パラメータを設定することができます: |
|||
|
|||
```Python hl_lines="24" |
|||
{!../../../docs_src/response_model/tutorial004.py!} |
|||
``` |
|||
|
|||
そして、これらのデフォルト値はレスポンスに含まれず、実際に設定された値のみが含まれます。 |
|||
|
|||
そのため、*path operation*にID`foo`が設定されたitemのリクエストを送ると、レスポンスは以下のようになります(デフォルト値を含まない): |
|||
|
|||
```JSON |
|||
{ |
|||
"name": "Foo", |
|||
"price": 50.2 |
|||
} |
|||
``` |
|||
|
|||
!!! info "情報" |
|||
FastAPIはこれをするために、Pydanticモデルの`.dict()`を<a href="https://pydantic-docs.helpmanual.io/usage/exporting_models/#modeldict" class="external-link" target="_blank">その`exclude_unset`パラメータ</a>で使用しています。 |
|||
|
|||
!!! info "情報" |
|||
以下も使用することができます: |
|||
|
|||
* `response_model_exclude_defaults=True` |
|||
* `response_model_exclude_none=True` |
|||
|
|||
`exclude_defaults`と`exclude_none`については、<a href="https://pydantic-docs.helpmanual.io/usage/exporting_models/#modeldict" class="external-link" target="_blank">Pydanticのドキュメント</a>で説明されている通りです。 |
|||
|
|||
#### デフォルト値を持つフィールドの値を持つデータ |
|||
|
|||
しかし、ID`bar`のitemのように、デフォルト値が設定されているモデルのフィールドに値が設定されている場合: |
|||
|
|||
```Python hl_lines="3 5" |
|||
{ |
|||
"name": "Bar", |
|||
"description": "The bartenders", |
|||
"price": 62, |
|||
"tax": 20.2 |
|||
} |
|||
``` |
|||
|
|||
それらはレスポンスに含まれます。 |
|||
|
|||
#### デフォルト値と同じ値を持つデータ |
|||
|
|||
ID`baz`のitemのようにデフォルト値と同じ値を持つデータの場合: |
|||
|
|||
```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`だけでなく、なんでも良いことに注意してください。 |
|||
例えば、リスト(`[]`)や`10.5`の`float`などです。 |
|||
|
|||
### `response_model_include`と`response_model_exclude` |
|||
|
|||
*path operationデコレータ*として`response_model_include`と`response_model_exclude`も使用することができます。 |
|||
|
|||
属性名を持つ`str`の`set`を受け取り、含める(残りを省略する)か、除外(残りを含む)します。 |
|||
|
|||
これは、Pydanticモデルが1つしかなく、出力からいくつかのデータを削除したい場合のクイックショートカットとして使用することができます。 |
|||
|
|||
!!! tip "豆知識" |
|||
それでも、これらのパラメータではなく、複数のクラスを使用して、上記のようなアイデアを使うことをおすすめします。 |
|||
|
|||
これは`response_model_include`や`response_mode_exclude`を使用していくつかの属性を省略しても、アプリケーションのOpenAPI(とドキュメント)で生成されたJSON Schemaが完全なモデルになるからです。 |
|||
|
|||
同様に動作する`response_model_by_alias`にも当てはまります。 |
|||
|
|||
```Python hl_lines="31 37" |
|||
{!../../../docs_src/response_model/tutorial005.py!} |
|||
``` |
|||
|
|||
!!! tip "豆知識" |
|||
`{"name", "description"}`の構文はこれら2つの値をもつ`set`を作成します。 |
|||
|
|||
これは`set(["name", "description"])`と同等です。 |
|||
|
|||
#### `set`の代わりに`list`を使用する |
|||
|
|||
もし`set`を使用することを忘れて、代わりに`list`や`tuple`を使用しても、FastAPIはそれを`set`に変換して正しく動作します: |
|||
|
|||
```Python hl_lines="31 37" |
|||
{!../../../docs_src/response_model/tutorial006.py!} |
|||
``` |
|||
|
|||
## まとめ |
|||
|
|||
*path operationデコレータの*`response_model`パラメータを使用して、レスポンスモデルを定義し、特にプライベートデータがフィルタリングされていることを保証します。 |
|||
|
|||
明示的に設定された値のみを返すには、`response_model_exclude_unset`を使用します。 |
@ -0,0 +1,89 @@ |
|||
# レスポンスステータスコード |
|||
|
|||
レスポンスモデルを指定するのと同じ方法で、レスポンスに使用されるHTTPステータスコードを以下の*path operations*のいずれかの`status_code`パラメータで宣言することもできます。 |
|||
|
|||
* `@app.get()` |
|||
* `@app.post()` |
|||
* `@app.put()` |
|||
* `@app.delete()` |
|||
* など。 |
|||
|
|||
```Python hl_lines="6" |
|||
{!../../../docs_src/response_status_code/tutorial001.py!} |
|||
``` |
|||
|
|||
!!! note "備考" |
|||
`status_code`は「デコレータ」メソッド(`get`、`post`など)のパラメータであることに注意してください。すべてのパラメータやボディのように、*path operation関数*のものではありません。 |
|||
|
|||
`status_code`パラメータはHTTPステータスコードを含む数値を受け取ります。 |
|||
|
|||
!!! info "情報" |
|||
`status_code`は代わりに、Pythonの<a href="https://docs.python.org/3/library/http.html#http.HTTPStatus" class="external-link" target="_blank">`http.HTTPStatus`</a>のように、`IntEnum`を受け取ることもできます。 |
|||
|
|||
これは: |
|||
|
|||
* レスポンスでステータスコードを返します。 |
|||
* OpenAPIスキーマ(およびユーザーインターフェース)に以下のように文書化します: |
|||
|
|||
<img src="https://fastapi.tiangolo.com/img/tutorial/response-status-code/image01.png"> |
|||
|
|||
!!! note "備考" |
|||
いくつかのレスポンスコード(次のセクションを参照)は、レスポンスにボディがないことを示しています。 |
|||
|
|||
FastAPIはこれを知っていて、レスポンスボディがないというOpenAPIドキュメントを生成します。 |
|||
|
|||
## HTTPステータスコードについて |
|||
|
|||
!!! note "備考" |
|||
すでにHTTPステータスコードが何であるかを知っている場合は、次のセクションにスキップしてください。 |
|||
|
|||
HTTPでは、レスポンスの一部として3桁の数字のステータスコードを送信します。 |
|||
|
|||
これらのステータスコードは、それらを認識するために関連付けられた名前を持っていますが、重要な部分は番号です。 |
|||
|
|||
つまり: |
|||
|
|||
* `100`以上は「情報」のためのものです。。直接使うことはほとんどありません。これらのステータスコードを持つレスポンスはボディを持つことができません。 |
|||
* **`200`** 以上は「成功」のレスポンスのためのものです。これらは最も利用するであろうものです。 |
|||
* `200`はデフォルトのステータスコードで、すべてが「OK」であったことを意味します。 |
|||
* 別の例としては、`201`(Created)があります。これはデータベースに新しいレコードを作成した後によく使用されます。 |
|||
* 特殊なケースとして、`204`(No Content)があります。このレスポンスはクライアントに返すコンテンツがない場合に使用されます。そしてこのレスポンスはボディを持つことはできません。 |
|||
* **`300`** 以上は「リダイレクト」のためのものです。これらのステータスコードを持つレスポンスは`304`(Not Modified)を除き、ボディを持つことも持たないこともできます。 |
|||
* **`400`** 以上は「クライアントエラー」のレスポンスのためのものです。これらは、おそらく最も多用するであろう2番目のタイプです。 |
|||
* 例えば、`404`は「Not Found」レスポンスです。 |
|||
* クライアントからの一般的なエラーについては、`400`を使用することができます。 |
|||
* `500`以上はサーバーエラーのためのものです。これらを直接使うことはほとんどありません。アプリケーションコードやサーバーのどこかで何か問題が発生した場合、これらのステータスコードのいずれかが自動的に返されます。 |
|||
|
|||
!!! tip "豆知識" |
|||
それぞれのステータスコードとどのコードが何のためのコードなのかについて詳細は<a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Status" class="external-link" target="_blank"><abbr title="Mozilla Developer Network">MDN</abbr> HTTP レスポンスステータスコードについてのドキュメント</a>を参照してください。 |
|||
|
|||
## 名前を覚えるための近道 |
|||
|
|||
先ほどの例をもう一度見てみましょう: |
|||
|
|||
```Python hl_lines="6" |
|||
{!../../../docs_src/response_status_code/tutorial001.py!} |
|||
``` |
|||
|
|||
`201`は「作成完了」のためのステータスコードです。 |
|||
|
|||
しかし、それぞれのコードの意味を暗記する必要はありません。 |
|||
|
|||
`fastapi.status`の便利な変数を利用することができます。 |
|||
|
|||
```Python hl_lines="1 6" |
|||
{!../../../docs_src/response_status_code/tutorial002.py!} |
|||
``` |
|||
|
|||
それらは便利です。それらは同じ番号を保持しており、その方法ではエディタの自動補完を使用してそれらを見つけることができます。 |
|||
|
|||
<img src="https://fastapi.tiangolo.com/img/tutorial/response-status-code/image02.png"> |
|||
|
|||
!!! note "技術詳細" |
|||
また、`from starlette import status`を使うこともできます。 |
|||
|
|||
**FastAPI** は、`開発者の利便性を考慮して、fastapi.status`と同じ`starlette.status`を提供しています。しかし、これはStarletteから直接提供されています。 |
|||
|
|||
## デフォルトの変更 |
|||
|
|||
後に、[高度なユーザーガイド](../advanced/response-change-status-code.md){.internal-link target=_blank}で、ここで宣言しているデフォルトとは異なるステータスコードを返す方法を見ていきます。 |
@ -0,0 +1,58 @@ |
|||
# スキーマの追加 - 例 |
|||
|
|||
JSON Schemaに追加する情報を定義することができます。 |
|||
|
|||
一般的なユースケースはこのドキュメントで示されているように`example`を追加することです。 |
|||
|
|||
JSON Schemaの追加情報を宣言する方法はいくつかあります。 |
|||
|
|||
## Pydanticの`schema_extra` |
|||
|
|||
<a href="https://pydantic-docs.helpmanual.io/usage/schema/#schema-customization" class="external-link" target="_blank">Pydanticのドキュメント: スキーマのカスタマイズ</a>で説明されているように、`Config`と`schema_extra`を使ってPydanticモデルの例を宣言することができます: |
|||
|
|||
```Python hl_lines="15 16 17 18 19 20 21 22 23" |
|||
{!../../../docs_src/schema_extra_example/tutorial001.py!} |
|||
``` |
|||
|
|||
その追加情報はそのまま出力され、JSON Schemaに追加されます。 |
|||
|
|||
## `Field`の追加引数 |
|||
|
|||
後述する`Field`、`Path`、`Query`、`Body`などでは、任意の引数を関数に渡すことでJSON Schemaの追加情報を宣言することもできます: |
|||
|
|||
```Python hl_lines="4 10 11 12 13" |
|||
{!../../../docs_src/schema_extra_example/tutorial002.py!} |
|||
``` |
|||
|
|||
!!! warning "注意" |
|||
これらの追加引数が渡されても、文書化のためのバリデーションは追加されず、注釈だけが追加されることを覚えておいてください。 |
|||
|
|||
## `Body`の追加引数 |
|||
|
|||
追加情報を`Field`に渡すのと同じように、`Path`、`Query`、`Body`などでも同じことができます。 |
|||
|
|||
例えば、`Body`にボディリクエストの`example`を渡すことができます: |
|||
|
|||
```Python hl_lines="21 22 23 24 25 26" |
|||
{!../../../docs_src/schema_extra_example/tutorial003.py!} |
|||
``` |
|||
|
|||
## ドキュメントのUIの例 |
|||
|
|||
上記のいずれの方法でも、`/docs`の中では以下のようになります: |
|||
|
|||
<img src="https://fastapi.tiangolo.com/img/tutorial/body-fields/image01.png"> |
|||
|
|||
## 技術詳細 |
|||
|
|||
`example` と `examples`について... |
|||
|
|||
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は`examples`を持たない古いバージョンのJSON Schemaをベースにしています。 |
|||
|
|||
そのため、OpenAPIでは同じ目的のために<a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#fixed-fields-20" class="external-link" target="_blank">`example`</a>を独自に定義しており(`examples`ではなく`example`として)、それがdocs UI(Swagger UIを使用)で使用されています。 |
|||
|
|||
つまり、`example`はJSON Schemaの一部ではありませんが、OpenAPIの一部であり、それがdocs UIで使用されることになります。 |
|||
|
|||
## その他の情報 |
|||
|
|||
同じように、フロントエンドのユーザーインターフェースなどをカスタマイズするために、各モデルのJSON Schemaに追加される独自の追加情報を追加することができます。 |
@ -0,0 +1,478 @@ |
|||
# Классы как зависимости |
|||
|
|||
Прежде чем углубиться в систему **Внедрения Зависимостей**, давайте обновим предыдущий пример. |
|||
|
|||
## `Словарь` из предыдущего примера |
|||
|
|||
В предыдущем примере мы возвращали `словарь` из нашей зависимости: |
|||
|
|||
=== "Python 3.10+" |
|||
|
|||
```Python hl_lines="9" |
|||
{!> ../../../docs_src/dependencies/tutorial001_an_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.9+" |
|||
|
|||
```Python hl_lines="11" |
|||
{!> ../../../docs_src/dependencies/tutorial001_an_py39.py!} |
|||
``` |
|||
|
|||
=== "Python 3.6+" |
|||
|
|||
```Python hl_lines="12" |
|||
{!> ../../../docs_src/dependencies/tutorial001_an.py!} |
|||
``` |
|||
|
|||
=== "Python 3.10+ без Annotated" |
|||
|
|||
!!! tip "Подсказка" |
|||
Рекомендуется использовать версию с `Annotated` если возможно. |
|||
|
|||
```Python hl_lines="7" |
|||
{!> ../../../docs_src/dependencies/tutorial001_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.6+ без Annotated" |
|||
|
|||
!!! tip "Подсказка" |
|||
Рекомендуется использовать версию с `Annotated` если возможно. |
|||
|
|||
```Python hl_lines="11" |
|||
{!> ../../../docs_src/dependencies/tutorial001.py!} |
|||
``` |
|||
|
|||
Но затем мы получаем `словарь` в параметре `commons` *функции операции пути*. И мы знаем, что редакторы не могут обеспечить достаточную поддержку для `словаря`, поскольку они не могут знать их ключи и типы значений. |
|||
|
|||
Мы можем сделать лучше... |
|||
|
|||
## Что делает зависимость |
|||
|
|||
До сих пор вы видели зависимости, объявленные как функции. |
|||
|
|||
Но это не единственный способ объявления зависимостей (хотя, вероятно, более распространенный). |
|||
|
|||
Ключевым фактором является то, что зависимость должна быть "вызываемой". |
|||
|
|||
В Python "**вызываемый**" - это все, что Python может "вызвать", как функцию. |
|||
|
|||
Так, если у вас есть объект `something` (который может _не_ быть функцией) и вы можете "вызвать" его (выполнить) как: |
|||
|
|||
```Python |
|||
something() |
|||
``` |
|||
|
|||
или |
|||
|
|||
```Python |
|||
something(some_argument, some_keyword_argument="foo") |
|||
``` |
|||
|
|||
в таком случае он является "вызываемым". |
|||
|
|||
## Классы как зависимости |
|||
|
|||
Вы можете заметить, что для создания экземпляра класса в Python используется тот же синтаксис. |
|||
|
|||
Например: |
|||
|
|||
```Python |
|||
class Cat: |
|||
def __init__(self, name: str): |
|||
self.name = name |
|||
|
|||
|
|||
fluffy = Cat(name="Mr Fluffy") |
|||
``` |
|||
|
|||
В данном случае `fluffy` является экземпляром класса `Cat`. |
|||
|
|||
А чтобы создать `fluffy`, вы "вызываете" `Cat`. |
|||
|
|||
Таким образом, класс в Python также является **вызываемым**. |
|||
|
|||
Тогда в **FastAPI** в качестве зависимости можно использовать класс Python. |
|||
|
|||
На самом деле FastAPI проверяет, что переданный объект является "вызываемым" (функция, класс или что-либо еще) и указаны необходимые для его вызова параметры. |
|||
|
|||
Если вы передаёте что-то, что можно "вызывать" в качестве зависимости в **FastAPI**, то он будет анализировать параметры, необходимые для "вызова" этого объекта и обрабатывать их так же, как параметры *функции операции пути*. Включая подзависимости. |
|||
|
|||
Это относится и к вызываемым объектам без параметров. Работа с ними происходит точно так же, как и для *функций операции пути* без параметров. |
|||
|
|||
Теперь мы можем изменить зависимость `common_parameters`, указанную выше, на класс `CommonQueryParams`: |
|||
|
|||
=== "Python 3.10+" |
|||
|
|||
```Python hl_lines="11-15" |
|||
{!> ../../../docs_src/dependencies/tutorial002_an_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.9+" |
|||
|
|||
```Python hl_lines="11-15" |
|||
{!> ../../../docs_src/dependencies/tutorial002_an_py39.py!} |
|||
``` |
|||
|
|||
=== "Python 3.6+" |
|||
|
|||
```Python hl_lines="12-16" |
|||
{!> ../../../docs_src/dependencies/tutorial002_an.py!} |
|||
``` |
|||
|
|||
=== "Python 3.10+ без Annotated" |
|||
|
|||
!!! tip "Подсказка" |
|||
Рекомендуется использовать версию с `Annotated` если возможно. |
|||
|
|||
```Python hl_lines="9-13" |
|||
{!> ../../../docs_src/dependencies/tutorial002_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.6+ без Annotated" |
|||
|
|||
!!! tip "Подсказка" |
|||
Рекомендуется использовать версию с `Annotated` если возможно. |
|||
|
|||
```Python hl_lines="11-15" |
|||
{!> ../../../docs_src/dependencies/tutorial002.py!} |
|||
``` |
|||
|
|||
Обратите внимание на метод `__init__`, используемый для создания экземпляра класса: |
|||
|
|||
=== "Python 3.10+" |
|||
|
|||
```Python hl_lines="12" |
|||
{!> ../../../docs_src/dependencies/tutorial002_an_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.9+" |
|||
|
|||
```Python hl_lines="12" |
|||
{!> ../../../docs_src/dependencies/tutorial002_an_py39.py!} |
|||
``` |
|||
|
|||
=== "Python 3.6+" |
|||
|
|||
```Python hl_lines="13" |
|||
{!> ../../../docs_src/dependencies/tutorial002_an.py!} |
|||
``` |
|||
|
|||
=== "Python 3.10+ без Annotated" |
|||
|
|||
!!! tip "Подсказка" |
|||
Рекомендуется использовать версию с `Annotated` если возможно. |
|||
|
|||
```Python hl_lines="10" |
|||
{!> ../../../docs_src/dependencies/tutorial002_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.6+ без Annotated" |
|||
|
|||
!!! tip "Подсказка" |
|||
Рекомендуется использовать версию с `Annotated` если возможно. |
|||
|
|||
```Python hl_lines="12" |
|||
{!> ../../../docs_src/dependencies/tutorial002.py!} |
|||
``` |
|||
|
|||
...имеет те же параметры, что и ранее используемая функция `common_parameters`: |
|||
|
|||
=== "Python 3.10+" |
|||
|
|||
```Python hl_lines="8" |
|||
{!> ../../../docs_src/dependencies/tutorial001_an_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.9+" |
|||
|
|||
```Python hl_lines="9" |
|||
{!> ../../../docs_src/dependencies/tutorial001_an_py39.py!} |
|||
``` |
|||
|
|||
=== "Python 3.6+" |
|||
|
|||
```Python hl_lines="10" |
|||
{!> ../../../docs_src/dependencies/tutorial001_an.py!} |
|||
``` |
|||
|
|||
=== "Python 3.10+ без Annotated" |
|||
|
|||
!!! tip "Подсказка" |
|||
Рекомендуется использовать версию с `Annotated` если возможно. |
|||
|
|||
```Python hl_lines="6" |
|||
{!> ../../../docs_src/dependencies/tutorial001_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.6+ без Annotated" |
|||
|
|||
!!! tip "Подсказка" |
|||
Рекомендуется использовать версию с `Annotated` если возможно. |
|||
|
|||
```Python hl_lines="9" |
|||
{!> ../../../docs_src/dependencies/tutorial001.py!} |
|||
``` |
|||
|
|||
Эти параметры и будут использоваться **FastAPI** для "решения" зависимости. |
|||
|
|||
В обоих случаях она будет иметь: |
|||
|
|||
* Необязательный параметр запроса `q`, представляющий собой `str`. |
|||
* Параметр запроса `skip`, представляющий собой `int`, по умолчанию `0`. |
|||
* Параметр запроса `limit`, представляющий собой `int`, по умолчанию равный `100`. |
|||
|
|||
В обоих случаях данные будут конвертированы, валидированы, документированы по схеме OpenAPI и т.д. |
|||
|
|||
## Как это использовать |
|||
|
|||
Теперь вы можете объявить свою зависимость, используя этот класс. |
|||
|
|||
=== "Python 3.10+" |
|||
|
|||
```Python hl_lines="19" |
|||
{!> ../../../docs_src/dependencies/tutorial002_an_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.9+" |
|||
|
|||
```Python hl_lines="19" |
|||
{!> ../../../docs_src/dependencies/tutorial002_an_py39.py!} |
|||
``` |
|||
|
|||
=== "Python 3.6+" |
|||
|
|||
```Python hl_lines="20" |
|||
{!> ../../../docs_src/dependencies/tutorial002_an.py!} |
|||
``` |
|||
|
|||
=== "Python 3.10+ без Annotated" |
|||
|
|||
!!! tip "Подсказка" |
|||
Рекомендуется использовать версию с `Annotated` если возможно. |
|||
|
|||
```Python hl_lines="17" |
|||
{!> ../../../docs_src/dependencies/tutorial002_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.6+ без Annotated" |
|||
|
|||
!!! tip "Подсказка" |
|||
Рекомендуется использовать версию с `Annotated` если возможно. |
|||
|
|||
```Python hl_lines="19" |
|||
{!> ../../../docs_src/dependencies/tutorial002.py!} |
|||
``` |
|||
|
|||
**FastAPI** вызывает класс `CommonQueryParams`. При этом создается "экземпляр" этого класса, который будет передан в качестве параметра `commons` в вашу функцию. |
|||
|
|||
## Аннотация типа или `Depends` |
|||
|
|||
Обратите внимание, что в приведенном выше коде мы два раза пишем `CommonQueryParams`: |
|||
|
|||
=== "Python 3.6+ без Annotated" |
|||
|
|||
!!! tip "Подсказка" |
|||
Рекомендуется использовать версию с `Annotated` если возможно. |
|||
|
|||
```Python |
|||
commons: CommonQueryParams = Depends(CommonQueryParams) |
|||
``` |
|||
|
|||
=== "Python 3.6+" |
|||
|
|||
```Python |
|||
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)] |
|||
``` |
|||
|
|||
Последний параметр `CommonQueryParams`, в: |
|||
|
|||
```Python |
|||
... Depends(CommonQueryParams) |
|||
``` |
|||
|
|||
...это то, что **FastAPI** будет использовать, чтобы узнать, что является зависимостью. |
|||
|
|||
Из него FastAPI извлечёт объявленные параметры и именно их будет вызывать. |
|||
|
|||
--- |
|||
|
|||
В этом случае первый `CommonQueryParams`, в: |
|||
|
|||
=== "Python 3.6+" |
|||
|
|||
```Python |
|||
commons: Annotated[CommonQueryParams, ... |
|||
``` |
|||
|
|||
=== "Python 3.6+ без Annotated" |
|||
|
|||
!!! tip "Подсказка" |
|||
Рекомендуется использовать версию с `Annotated` если возможно. |
|||
|
|||
```Python |
|||
commons: CommonQueryParams ... |
|||
``` |
|||
|
|||
...не имеет никакого специального значения для **FastAPI**. FastAPI не будет использовать его для преобразования данных, валидации и т.д. (поскольку для этого используется `Depends(CommonQueryParams)`). |
|||
|
|||
На самом деле можно написать просто: |
|||
|
|||
=== "Python 3.6+" |
|||
|
|||
```Python |
|||
commons: Annotated[Any, Depends(CommonQueryParams)] |
|||
``` |
|||
|
|||
=== "Python 3.6+ без Annotated" |
|||
|
|||
!!! tip "Подсказка" |
|||
Рекомендуется использовать версию с `Annotated` если возможно. |
|||
|
|||
```Python |
|||
commons = Depends(CommonQueryParams) |
|||
``` |
|||
|
|||
...как тут: |
|||
|
|||
=== "Python 3.10+" |
|||
|
|||
```Python hl_lines="19" |
|||
{!> ../../../docs_src/dependencies/tutorial003_an_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.9+" |
|||
|
|||
```Python hl_lines="19" |
|||
{!> ../../../docs_src/dependencies/tutorial003_an_py39.py!} |
|||
``` |
|||
|
|||
=== "Python 3.6+" |
|||
|
|||
```Python hl_lines="20" |
|||
{!> ../../../docs_src/dependencies/tutorial003_an.py!} |
|||
``` |
|||
|
|||
=== "Python 3.10+ без Annotated" |
|||
|
|||
!!! tip "Подсказка" |
|||
Рекомендуется использовать версию с `Annotated` если возможно. |
|||
|
|||
```Python hl_lines="17" |
|||
{!> ../../../docs_src/dependencies/tutorial003_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.6+ без Annotated" |
|||
|
|||
!!! tip "Подсказка" |
|||
Рекомендуется использовать версию с `Annotated` если возможно. |
|||
|
|||
```Python hl_lines="19" |
|||
{!> ../../../docs_src/dependencies/tutorial003.py!} |
|||
``` |
|||
|
|||
Но объявление типа приветствуется, так как в этом случае ваш редактор будет знать, что будет передано в качестве параметра `commons`, и тогда он сможет помочь вам с автодополнением, проверкой типов и т.д: |
|||
|
|||
<img src="/img/tutorial/dependencies/image02.png"> |
|||
|
|||
## Сокращение |
|||
|
|||
Но вы видите, что здесь мы имеем некоторое повторение кода, дважды написав `CommonQueryParams`: |
|||
|
|||
=== "Python 3.6+ без Annotated" |
|||
|
|||
!!! tip "Подсказка" |
|||
Рекомендуется использовать версию с `Annotated` если возможно. |
|||
|
|||
```Python |
|||
commons: CommonQueryParams = Depends(CommonQueryParams) |
|||
``` |
|||
|
|||
=== "Python 3.6+" |
|||
|
|||
```Python |
|||
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)] |
|||
``` |
|||
|
|||
Для случаев, когда зависимостью является *конкретный* класс, который **FastAPI** "вызовет" для создания экземпляра этого класса, можно использовать укороченную запись. |
|||
|
|||
|
|||
Вместо того чтобы писать: |
|||
|
|||
=== "Python 3.6+" |
|||
|
|||
```Python |
|||
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)] |
|||
``` |
|||
|
|||
=== "Python 3.6+ без Annotated" |
|||
|
|||
!!! tip "Подсказка" |
|||
Рекомендуется использовать версию с `Annotated` если возможно. |
|||
|
|||
```Python |
|||
commons: CommonQueryParams = Depends(CommonQueryParams) |
|||
``` |
|||
|
|||
...следует написать: |
|||
|
|||
=== "Python 3.6+" |
|||
|
|||
```Python |
|||
commons: Annotated[CommonQueryParams, Depends()] |
|||
``` |
|||
|
|||
=== "Python 3.6 без Annotated" |
|||
|
|||
!!! tip "Подсказка" |
|||
Рекомендуется использовать версию с `Annotated` если возможно. |
|||
|
|||
```Python |
|||
commons: CommonQueryParams = Depends() |
|||
``` |
|||
|
|||
Вы объявляете зависимость как тип параметра и используете `Depends()` без какого-либо параметра, вместо того чтобы *снова* писать полный класс внутри `Depends(CommonQueryParams)`. |
|||
|
|||
Аналогичный пример будет выглядеть следующим образом: |
|||
|
|||
=== "Python 3.10+" |
|||
|
|||
```Python hl_lines="19" |
|||
{!> ../../../docs_src/dependencies/tutorial004_an_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.9+" |
|||
|
|||
```Python hl_lines="19" |
|||
{!> ../../../docs_src/dependencies/tutorial004_an_py39.py!} |
|||
``` |
|||
|
|||
=== "Python 3.6+" |
|||
|
|||
```Python hl_lines="20" |
|||
{!> ../../../docs_src/dependencies/tutorial004_an.py!} |
|||
``` |
|||
|
|||
=== "Python 3.10+ без Annotated" |
|||
|
|||
!!! tip "Подсказка" |
|||
Рекомендуется использовать версию с `Annotated` если возможно. |
|||
|
|||
```Python hl_lines="17" |
|||
{!> ../../../docs_src/dependencies/tutorial004_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.6+ без Annotated" |
|||
|
|||
!!! tip "Подсказка" |
|||
Рекомендуется использовать версию с `Annotated` если возможно. |
|||
|
|||
```Python hl_lines="19" |
|||
{!> ../../../docs_src/dependencies/tutorial004.py!} |
|||
``` |
|||
|
|||
...и **FastAPI** будет знать, что делать. |
|||
|
|||
!!! tip "Подсказка" |
|||
Если это покажется вам более запутанным, чем полезным, не обращайте внимания, это вам не *нужно*. |
|||
|
|||
Это просто сокращение. Потому что **FastAPI** заботится о том, чтобы помочь вам свести к минимуму повторение кода. |
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue