Browse Source

Merge branch 'master' into fix-duplicate-special-dependency-handling

pull/12406/head
Peter Volf 6 months ago
committed by GitHub
parent
commit
4c374d2d34
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 4
      .github/workflows/build-docs.yml
  2. 2
      .github/workflows/deploy-docs.yml
  3. 2
      .github/workflows/label-approved.yml
  4. 2
      .github/workflows/notify-translations.yml
  5. 2
      .github/workflows/smokeshow.yml
  6. 6
      .github/workflows/test.yml
  7. 4
      docs/en/docs/contributing.md
  8. 13
      docs/en/docs/release-notes.md
  9. 3
      docs/en/docs/tutorial/sql-databases.md
  10. 258
      docs/id/docs/tutorial/path-params.md
  11. 360
      docs/ko/docs/tutorial/sql-databases.md
  12. 358
      docs/ru/docs/tutorial/sql-databases.md
  13. 83
      docs/uk/docs/fastapi-cli.md
  14. 4
      docs/zh-hant/docs/tutorial/index.md
  15. 99
      docs/zh/docs/advanced/async-tests.md
  16. 132
      docs/zh/docs/advanced/events.md
  17. 55
      docs/zh/docs/advanced/openapi-webhooks.md
  18. 2
      docs/zh/docs/advanced/testing-dependencies.md
  19. 34
      docs/zh/docs/async.md

4
.github/workflows/build-docs.yml

@ -53,7 +53,7 @@ jobs:
with:
python-version: "3.11"
- name: Setup uv
uses: astral-sh/setup-uv@v4
uses: astral-sh/setup-uv@v5
with:
version: "0.4.15"
enable-cache: true
@ -95,7 +95,7 @@ jobs:
with:
python-version: "3.11"
- name: Setup uv
uses: astral-sh/setup-uv@v4
uses: astral-sh/setup-uv@v5
with:
version: "0.4.15"
enable-cache: true

2
.github/workflows/deploy-docs.yml

@ -29,7 +29,7 @@ jobs:
with:
python-version: "3.11"
- name: Setup uv
uses: astral-sh/setup-uv@v4
uses: astral-sh/setup-uv@v5
with:
version: "0.4.15"
enable-cache: true

2
.github/workflows/label-approved.yml

@ -26,7 +26,7 @@ jobs:
with:
python-version: "3.11"
- name: Setup uv
uses: astral-sh/setup-uv@v4
uses: astral-sh/setup-uv@v5
with:
version: "0.4.15"
enable-cache: true

2
.github/workflows/notify-translations.yml

@ -35,7 +35,7 @@ jobs:
with:
python-version: "3.11"
- name: Setup uv
uses: astral-sh/setup-uv@v4
uses: astral-sh/setup-uv@v5
with:
version: "0.4.15"
enable-cache: true

2
.github/workflows/smokeshow.yml

@ -26,7 +26,7 @@ jobs:
with:
python-version: '3.9'
- name: Setup uv
uses: astral-sh/setup-uv@v4
uses: astral-sh/setup-uv@v5
with:
version: "0.4.15"
enable-cache: true

6
.github/workflows/test.yml

@ -29,7 +29,7 @@ jobs:
with:
python-version: "3.11"
- name: Setup uv
uses: astral-sh/setup-uv@v4
uses: astral-sh/setup-uv@v5
with:
version: "0.4.15"
enable-cache: true
@ -66,7 +66,7 @@ jobs:
with:
python-version: ${{ matrix.python-version }}
- name: Setup uv
uses: astral-sh/setup-uv@v4
uses: astral-sh/setup-uv@v5
with:
version: "0.4.15"
enable-cache: true
@ -107,7 +107,7 @@ jobs:
with:
python-version: '3.8'
- name: Setup uv
uses: astral-sh/setup-uv@v4
uses: astral-sh/setup-uv@v5
with:
version: "0.4.15"
enable-cache: true

4
docs/en/docs/contributing.md

@ -107,7 +107,7 @@ $ cd docs/en/
Then run `mkdocs` in that directory:
```console
$ mkdocs serve --dev-addr 8008
$ mkdocs serve --dev-addr 127.0.0.1:8008
```
///
@ -245,7 +245,7 @@ $ cd docs/es/
Then run `mkdocs` in that directory:
```console
$ mkdocs serve --dev-addr 8008
$ mkdocs serve --dev-addr 127.0.0.1:8008
```
///

13
docs/en/docs/release-notes.md

@ -9,12 +9,24 @@ hide:
### Docs
* ✏️ Fix error in `docs/en/docs/contributing.md`. PR [#12899](https://github.com/fastapi/fastapi/pull/12899) by [@kingsubin](https://github.com/kingsubin).
* 📝 Minor corrections in `docs/en/docs/tutorial/sql-databases.md`. PR [#13081](https://github.com/fastapi/fastapi/pull/13081) by [@alv2017](https://github.com/alv2017).
* 📝 Update includes in `docs/ru/docs/tutorial/query-param-models.md`. PR [#12994](https://github.com/fastapi/fastapi/pull/12994) by [@alejsdev](https://github.com/alejsdev).
* ✏️ Fix typo in README installation instructions. PR [#13011](https://github.com/fastapi/fastapi/pull/13011) by [@dave-hay](https://github.com/dave-hay).
* 📝 Update docs for `fastapi-cli`. PR [#13031](https://github.com/fastapi/fastapi/pull/13031) by [@tiangolo](https://github.com/tiangolo).
### Translations
* 🌐 Add Indonesian translation for `docs/id/docs/tutorial/path-params.md`. PR [#13086](https://github.com/fastapi/fastapi/pull/13086) by [@gerry-sabar](https://github.com/gerry-sabar).
* 🌐 Add Korean translation for `docs/ko/docs/tutorial/sql-databases.md`. PR [#13093](https://github.com/fastapi/fastapi/pull/13093) by [@GeumBinLee](https://github.com/GeumBinLee).
* 🌐 Update Chinese translation for `docs/zh/docs/async.md`. PR [#13095](https://github.com/fastapi/fastapi/pull/13095) by [@Zhongheng-Cheng](https://github.com/Zhongheng-Cheng).
* 🌐 Add Chinese translation for `docs/zh/docs/advanced/openapi-webhooks.md`. PR [#13091](https://github.com/fastapi/fastapi/pull/13091) by [@Zhongheng-Cheng](https://github.com/Zhongheng-Cheng).
* 🌐 Add Chinese translation for `docs/zh/docs/advanced/async-tests.md`. PR [#13074](https://github.com/fastapi/fastapi/pull/13074) by [@Zhongheng-Cheng](https://github.com/Zhongheng-Cheng).
* 🌐 Add Ukrainian translation for `docs/uk/docs/fastapi-cli.md`. PR [#13020](https://github.com/fastapi/fastapi/pull/13020) by [@ykertytsky](https://github.com/ykertytsky).
* 🌐 Add Chinese translation for `docs/zh/docs/advanced/events.md`. PR [#12512](https://github.com/fastapi/fastapi/pull/12512) by [@ZhibangYue](https://github.com/ZhibangYue).
* 🌐 Add Russian translation for `/docs/ru/docs/tutorial/sql-databases.md`. PR [#13079](https://github.com/fastapi/fastapi/pull/13079) by [@alv2017](https://github.com/alv2017).
* 🌐 Update Chinese translation for `docs/zh/docs/advanced/testing-dependencies.md`. PR [#13066](https://github.com/fastapi/fastapi/pull/13066) by [@Zhongheng-Cheng](https://github.com/Zhongheng-Cheng).
* 🌐 Update Traditional Chinese translation for `docs/zh-hant/docs/tutorial/index.md`. PR [#13075](https://github.com/fastapi/fastapi/pull/13075) by [@codingjenny](https://github.com/codingjenny).
* 🌐 Add Chinese translation for `docs/zh/docs/tutorial/sql-databases.md`. PR [#13051](https://github.com/fastapi/fastapi/pull/13051) by [@Zhongheng-Cheng](https://github.com/Zhongheng-Cheng).
* 🌐 Update Chinese translation for `docs/zh/docs/tutorial/query-params-str-validations.md`. PR [#12928](https://github.com/fastapi/fastapi/pull/12928) by [@Vincy1230](https://github.com/Vincy1230).
* 🌐 Add Chinese translation for `docs/zh/docs/tutorial/header-param-models.md`. PR [#13040](https://github.com/fastapi/fastapi/pull/13040) by [@Zhongheng-Cheng](https://github.com/Zhongheng-Cheng).
@ -46,6 +58,7 @@ hide:
### Internal
* ⬆ Bump astral-sh/setup-uv from 4 to 5. PR [#13096](https://github.com/fastapi/fastapi/pull/13096) by [@dependabot[bot]](https://github.com/apps/dependabot).
* 🔧 Update sponsors: rename CryptAPI to BlockBee. PR [#13078](https://github.com/fastapi/fastapi/pull/13078) by [@tiangolo](https://github.com/tiangolo).
* ⬆ Bump pypa/gh-action-pypi-publish from 1.12.2 to 1.12.3. PR [#13055](https://github.com/fastapi/fastapi/pull/13055) by [@dependabot[bot]](https://github.com/apps/dependabot).
* ⬆ Bump types-ujson from 5.7.0.1 to 5.10.0.20240515. PR [#13018](https://github.com/fastapi/fastapi/pull/13018) by [@dependabot[bot]](https://github.com/apps/dependabot).

3
docs/en/docs/tutorial/sql-databases.md

@ -125,8 +125,6 @@ The same way, you can declare it as the function's **return type**, and then the
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[40:45] hl[40:45] *}
</details>
Here we use the `SessionDep` dependency (a `Session`) to add the new `Hero` to the `Session` instance, commit the changes to the database, refresh the data in the `hero`, and then return it.
### Read Heroes
@ -235,7 +233,6 @@ All the fields in `HeroPublic` are the same as in `HeroBase`, with `id` declared
* `id`
* `name`
* `age`
* `secret_name`
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:18] hl[17:18] *}

258
docs/id/docs/tutorial/path-params.md

@ -0,0 +1,258 @@
# Parameter Path
"parameter" atau "variabel" path didefinisikan dengan sintaksis Python format string:
{* ../../docs_src/path_params/tutorial001.py hl[6:7] *}
Nilai parameter path `item_id` akan dikirim ke fungsi sebagai argument `item_id`:
Jika anda menjalankan contoh berikut dan kunjungi <a href="http://127.0.0.1:8000/items/foo" class="external-link" target="_blank">http://127.0.0.1:8000/items/foo</a>, anda akan melihat respon:
```JSON
{"item_id":"foo"}
```
## Parameter path dengan tipe data
Tipe data parameter path bisa didefinisikan di dalam fungsi, menggunakan anotasi tipe data standar Python:
{* ../../docs_src/path_params/tutorial002.py hl[7] *}
Dalam hal ini `item_id` didefinisikan sebagai `int`.
/// check | Periksa
Penyunting kode anda bisa membantu periksa di dalam fungsi seperti pemeriksaan kesalahan, kelengkapan kode, dll.
///
## <abbr title="juga disebut: serialization, parsing, marshalling">Konversi</abbr> data
Jika contoh berikut dijalankan dan diakses browser melalui <a href="http://127.0.0.1:8000/items/3" class="external-link" target="_blank">http://127.0.0.1:8000/items/3</a>, anda akan melihat respon:
```JSON
{"item_id":3}
```
/// check | Periksa
Perhatikan nilai fungsi yang diterima (dan dihasilkan) adalah `3`, sebagai `int` di Python, dan bukan string `"3"`.
Sehingga dengan deklarasi tipe data **FastAPI** memberikan request otomatis <abbr title="konversi string dari request HTTP menjadi data Python">"parsing"</abbr>.
///
## Validasi Data
Tetapi jika di browser anda akses <a href="http://127.0.0.1:8000/items/foo" class="external-link" target="_blank">http://127.0.0.1:8000/items/foo</a>, anda akan melihat pesan kesalahan HTTP:
```JSON
{
"detail": [
{
"type": "int_parsing",
"loc": [
"path",
"item_id"
],
"msg": "Input should be a valid integer, unable to parse string as an integer",
"input": "foo",
"url": "https://errors.pydantic.dev/2.1/v/int_parsing"
}
]
}
```
Karena parameter path `item_id` bernilai `"foo"` yang bukan tipe data `int`.
Kesalahan yang sama akan muncul jika menggunakan `float` daripada `int`, seperti di: <a href="http://127.0.0.1:8000/items/4.2" class="external-link" target="_blank">http://127.0.0.1:8000/items/4.2</a>
/// check | Periksa
Dengan deklarasi tipe data Python, **FastAPI** melakukan validasi data.
Perhatikan kesalahan tersebut juga menjelaskan validasi apa yang tidak sesuai.
Validasi ini sangat membantu ketika mengembangkan dan men-*debug* kode yang berhubungan dengan API anda.
///
## Dokumentasi
Ketika anda membuka browser di <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>, anda melihat dokumentasi API interaktif otomatis berikut:
<img src="/img/tutorial/path-params/image01.png">
/// check | Periksa
Dengan deklarasi tipe data Python yang sama, **FastAPI** membuat dokumentasi interaktif otomatis (terintegrasi Swagger UI).
Perhatikan parameter path dideklarasikan sebagai integer.
///
## Keuntungan basis-standar, dokumentasi alternatif
Karena skema yang dibuat berasal dari standar <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md" class="external-link" target="_blank">OpenAPI</a>, maka banyak alat lain yang kompatibel.
Sehingga **FastAPI** menyediakan dokumentasi alternatif (menggunakan ReDoc), yang bisa diakses di <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>:
<img src="/img/tutorial/path-params/image02.png">
Cara yang sama untuk menggunakan tools kompatibel lainnya. Termasuk alat membuat kode otomatis untuk banyak bahasa.
## Pydantic
Semua validasi data dikerjakan di belakang layar oleh <a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic</a>, sehingga anda mendapatkan banyak kemudahan. Anda juga tahu proses ini akan ditangani dengan baik.
Anda bisa mendeklarasikan tipe data dengan `str`, `float`, `bool` dan banyak tipe data kompleks lainnya.
Beberapa tipe di atas akan dibahas pada bab berikutnya tutorial ini.
## Urutan berpengaruh
Ketika membuat *operasi path*, anda bisa menghadapi kondisi dimana *path* nya sudah tetap.
Seperti `/users/me`, untuk mendapatkan data user yang sedang aktif.
Kemudian anda bisa memiliki path `/users/{user_id}` untuk mendapatkan data user tertentu melalui user ID.
karena *operasi path* dievaluasi melalui urutan, anda harus memastikan path untuk `/users/me` dideklarasikan sebelum `/user/{user_id}`:
{* ../../docs_src/path_params/tutorial003.py hl[6,11] *}
Sebaliknya, path `/users/{user_id}` juga akan sesuai dengan `/users/me`, "menganggap" menerima parameter `user_id` dengan nilai `"me"`.
Serupa, anda juga tidak bisa mendefinisikan operasi path:
{* ../../docs_src/path_params/tutorial003b.py hl[6,11] *}
Path pertama akan selalu digunakan karena path sesuai dengan yang pertama.
## Nilai terdefinisi
Jika ada *operasi path* yang menerima *parameter path*, tetapi anda ingin nilai valid *parameter path* sudah terdefinisi, anda bisa menggunakan standar Python <abbr title="Enumeration">`Enum`</abbr>.
### Membuat class `Enum`
Import `Enum` dan buat *sub-class* warisan dari `str` dan `Enum`.
Dengan warisan dari `str` dokumen API mengetahui nilai nya harus berjenis `string` supaya bisa digunakan dengan benar.
Kemudian buat atribut *class* dengan nilai tetap *string* yang benar:
{* ../../docs_src/path_params/tutorial005.py hl[1,6:9] *}
/// info
<a href="https://docs.python.org/3/library/enum.html" class="external-link" target="_blank">Enumerasi (atau enum) tersedia di Python</a> sejak versi 3.4.
///
/// tip | Tips
"AlxexNet", "ResNet", dan "LeNet" adalah nama <abbr title="Secara teknis, arsitektur model Deep Learning">model</abbr> *Machine Learning*.
///
### Mendeklarasikan *parameter path*
Kemudian buat *parameter path* dengan tipe anotasi menggunakan *class* enum dari (`ModelName`)
{* ../../docs_src/path_params/tutorial005.py hl[16] *}
### Periksa dokumentasi
Karena nilai yang tersedia untuk *parameter path* telah terdefinisi, dokumen interatik bisa memunculkan:
<img src="/img/tutorial/path-params/image03.png">
### Bekerja dengan *enumarasi* Python
Nilai *parameter path* akan menjadi *anggota enumerasi*.
#### Membandingkan *anggota enumerasi*
Anda bisa membandingkan parameter *path* dengan *anggota enumerasi* di enum `ModelName` yang anda buat:
{* ../../docs_src/path_params/tutorial005.py hl[17] *}
#### Mendapatkan *nilai enumerasi*
Anda bisa mendapatkan nilai (`str` dalam kasus ini) menggunakan `model_name.value`, atau secara umum `anggota_enum_anda.value`:
{* ../../docs_src/path_params/tutorial005.py hl[20] *}
/// tip | Tips
Anda bisa mengakses nilai `"lenet"` dnegan `ModelName.lenet.value`.
///
#### Menghasilkan *anggota enumerasi*
Anda bisa menghasilkan *anggota enumerasi* dari *operasi path* bahkan di body JSON bersarang (contoh `dict`).
They will be converted to their corresponding values (strings in this case) before returning them to the client:
{* ../../docs_src/path_params/tutorial005.py hl[18,21,23] *}
Klien akan mendapatkan respon JSON seperti berikut:
```JSON
{
"model_name": "alexnet",
"message": "Deep Learning FTW!"
}
```
## Parameter path berisi path
Misalkan terdapat *operasi path* dengan path `/files/{file_path}`.
Tetapi anda memerlukan `file_path` itu berisi *path*, seperti like `home/johndoe/myfile.txt`.
Sehingga URL untuk file tersebut akan seperti: `/files/home/johndoe/myfile.txt`.
### Dukungan OpenAPI
OpenAPI tidak bisa mendeklarasikan *parameter path* berisi *path* di dalamnya, karena menyebabkan kondisi yang sulit di*test* dan didefinisikan.
Tetapi, di **FastAPI** anda tetap bisa melakukannya dengan menggunakan *tools* internal dari Starlette.
Dan dokumentasi tetap berfungsi walaupun tidak menambahkan keterangan bahwa parameter harus berisi *path*.
### Konverter path
Melalui Starlette anda bisa mendeklarasikan *parameter path* berisi *path* dengan URL seperti:
```
/files/{file_path:path}
```
Dikondisi ini nama parameter adalah `file_path` dan bagian terakhir `:path` menginformasikan parameter harus sesuai dengan setiap *path*.
Sehingga anda bisa menggunakan:
{* ../../docs_src/path_params/tutorial004.py hl[6] *}
/// tip | Tips
Anda mungkin perlu parameter berisi `/home/johndoe/myfile.txt` di awali garis belakang (`/`).
Di kondisi ini, URL nya menjadi: `/files//home/johndoe/myfile.txt`, dengan dua garis belakang (`//`) di antara `files` dan `home`.
///
## Ringkasan
Di **FastAPI** dengan menggunakan deklarasi tipe Python standar, pendek, intuitif, anda mendapatkan:
* Dukungan editor: pemeriksaan kesalahan, autocompletion, dll.
* "<abbr title="konversi string dari request HTTP menjadi data Python">Parsing</abbr>" data.
* Validasi data.
* Annotasi API dan dokumentasi otomatis.
Semua itu anda hanya perlu mendeklarasikan sekali saja.
Ini adalah salah satu keunggulan **FastAPI** dibandingkan dengan *framework* lainnya (selain dari performa Python *native*c)

360
docs/ko/docs/tutorial/sql-databases.md

@ -0,0 +1,360 @@
# SQL (관계형) 데이터베이스
**FastAPI**에서 SQL(관계형) 데이터베이스 사용은 필수가 아닙니다. 여러분이 원하는 **어떤 데이터베이스든** 사용할 수 있습니다.
여기서는 <a href="https://sqlmodel.tiangolo.com/" class="external-link" target="_blank">SQLModel</a>을 사용하는 예제를 살펴보겠습니다.
**SQLModel**은 <a href="https://www.sqlalchemy.org/" class="external-link" target="_blank">SQLAlchemy</a>와 Pydantic을 기반으로 구축되었습니다.SQLModel은 **SQL 데이터베이스**를 사용하는 FastAPI 애플리케이션에 완벽히 어울리도록 **FastAPI**의 제작자가 설계한 도구입니다.
/// tip | 팁
다른 SQL 또는 NoSQL 데이터베이스 라이브러리를 사용할 수도 있습니다 (일부는 <abbr title="객체 관계 매퍼(Object Relational Mapper), SQL 테이블을 나타내는 클래스를 제공하고 테이블의 행을 인스턴스로 표현하는 라이브러리를 지칭하는 용어">"ORM"</abbr>이라고도 불립니다), FastAPI는 특정 라이브러리의 사용을 강요하지 않습니다. 😎
///
SQLModel은 SQLAlchemy를 기반으로 하므로, SQLAlchemy에서 **지원하는 모든 데이터베이스**를 손쉽게 사용할 수 있습니다(SQLModel에서도 동일하게 지원됩니다). 예를 들면:
* PostgreSQL
* MySQL
* SQLite
* Oracle
* Microsoft SQL Server 등.
이 예제에서는 **SQLite**를 사용합니다. SQLite는 단일 파일을 사용하고 파이썬에서 기본적으로 지원하기 때문입니다. 따라서 이 예제를 그대로 복사하여 실행할 수 있습니다.
나중에 실제 프로덕션 애플리케이션에서는 **PostgreSQL**과 같은 데이터베이스 서버를 사용하는 것이 좋습니다.
/// tip | 팁
**FastAPI**와 **PostgreSQL**를 포함하여 프론트엔드와 다양한 도구를 제공하는 공식 프로젝트 생성기가 있습니다: <a href="https://github.com/fastapi/full-stack-fastapi-template" class="external-link" target="_blank">https://github.com/fastapi/full-stack-fastapi-template</a>
///
이 튜토리얼은 매우 간단하고 짧습니다. 데이터베이스 기본 개념, SQL, 또는 더 복잡한 기능에 대해 배우고 싶다면, <a href="https://sqlmodel.tiangolo.com/" class="external-link" target="_blank">SQLModel 문서</a>를 참고하세요.
## `SQLModel` 설치하기
먼저, [가상 환경](../virtual-environments.md){.internal-link target=_blank}을 생성하고 활성화한 다음, `sqlmodel`을 설치하세요:
<div class="termy">
```console
$ pip install sqlmodel
---> 100%
```
</div>
## 단일 모델로 애플리케이션 생성하기
우선 단일 **SQLModel** 모델을 사용하여 애플리케이션의 가장 간단한 첫 번째 버전을 생성해보겠습니다.
이후 **다중 모델**을 추가하여 보안과 유연성을 강화할 것입니다. 🤓
### 모델 생성하기
`SQLModel`을 가져오고 데이터베이스 모델을 생성합니다:
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[1:11] hl[7:11] *}
`Hero` 클래스는 Pydantic 모델과 매우 유사합니다 (실제로 내부적으로 *Pydantic 모델이기도 합니다*).
몇 가지 차이점이 있습니다:
* `table=True`는 SQLModel에 이 모델이 *테이블 모델*이며, 단순한 데이터 모델이 아니라 SQL 데이터베이스의 **테이블**을 나타낸다는 것을 알려줍니다. (다른 일반적인 Pydantic 클래스처럼) 단순한 *데이터 모델*이 아닙니다.
* `Field(primary_key=True)`는 SQLModel에 `id`가 SQL 데이터베이스의 **기본 키**임을 알려줍니다 (SQL 기본 키에 대한 자세한 내용은 SQLModel 문서를 참고하세요).
`int | None` 유형으로 설정하면, SQLModel은 해당 열이 SQL 데이터베이스에서 `INTEGER` 유형이며 `NULLABLE` 값이어야 한다는 것을 알 수 있습니다.
* `Field(index=True)`는 SQLModel에 해당 열에 대해 **SQL 인덱스**를 생성하도록 지시합니다. 이를 통해 데이터베이스에서 이 열으로 필터링된 데이터를 읽을 때 더 빠르게 조회할 수 있습니다.
SQLModel은 `str`으로 선언된 항목이 SQL 데이터베이스에서 `TEXT` (또는 데이터베이스에 따라 `VARCHAR`) 유형의 열로 저장된다는 것을 인식합니다.
### 엔진 생성하기
SQLModel의 `engine` (내부적으로는 SQLAlchemy `engine`)은 데이터베이스에 대한 **연결을 유지**하는 역할을 합니다.
**하나의 단일 engine 객체**를 통해 코드 전체에서 동일한 데이터베이스에 연결할 수 있습니다.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[14:18] hl[14:15,17:18] *}
`check_same_thread=False`를 사용하면 FastAPI에서 여러 스레드에서 동일한 SQLite 데이터베이스를 사용할 수 있습니다. 이는 **하나의 단일 요청**이 **여러 스레드**를 사용할 수 있기 때문에 필요합니다(예: 의존성에서 사용되는 경우).
걱정하지 마세요. 코드가 구조화된 방식으로 인해, 이후에 **각 요청마다 단일 SQLModel *세션*을 사용**하도록 보장할 것입니다. 실제로 그것이 `check_same_thread`가 하려는 것입니다.
### 테이블 생성하기
그 다음 `SQLModel.metadata.create_all(engine)`을 사용하여 모든 *테이블 모델*의 **테이블을 생성**하는 함수를 추가합니다.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[21:22] hl[21:22] *}
### 세션 의존성 생성하기
**`Session`**은 **메모리에 객체**를 저장하고 데이터에 필요한 모든 변경 사항을 추적한 후, **`engine`을 통해** 데이터베이스와 통신합니다.
`yield`를 사용해 FastAPI의 **의존성**을 생성하여 각 요청마다 새로운 `Session`을 제공합니다. 이는 요청당 하나의 세션만 사용되도록 보장합니다. 🤓
그런 다음 이 의존성을 사용하는 코드를 간소화하기 위해 `Annotated` 의존성 `SessionDep`을 생성합니다.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[25:30] hl[25:27,30] *}
### 시작 시 데이터베이스 테이블 생성하기
애플리케이션 시작 시 데이터베이스 테이블을 생성합니다.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[32:37] hl[35:37] *}
여기서는 애플리케이션 시작 이벤트 시 테이블을 생성합니다.
프로덕션 환경에서는 애플리케이션을 시작하기 전에 실행되는 마이그레이션 스크립트를 사용할 가능성이 높습니다. 🤓
/// tip | 팁
SQLModel은 Alembic을 감싸는 마이그레이션 유틸리티를 제공할 예정입니다. 하지만 현재 <a href="https://alembic.sqlalchemy.org/en/latest/" class="external-link" target="_blank">Alembic</a>을 직접 사용할 수 있습니다.
///
### Hero 생성하기
각 SQLModel 모델은 Pydantic 모델이기도 하므로, Pydantic 모델을 사용할 수 있는 **타입 어노테이**션에서 동일하게 사용할 수 있습니다.
예를 들어, 파라미터를 `Hero` 타입으로 선언하면 **JSON 본문**에서 값을 읽어옵니다.
마찬가지로, 함수의 **반환 타입**으로 선언하면 해당 데이터의 구조가 자동으로 생성되는 API 문서의 UI에 나타납니다.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[40:45] hl[40:45] *}
</details>
여기서 `SessionDep` 의존성 (즉, `Session`)을 사용하여 새로운 `Hero``Session` 인스턴스에 추가하고, 데이터베이스에 변경 사항을 커밋하고, `hero` 데이터의 최신 상태를 갱신한 다음 이를 반환합니다.
### Heroes 조회하기
`select()`를 사용하여 데이터베이스에서 `Hero`를 **조회**할 수 있습니다. 결과에 페이지네이션을 적용하기 위해 `limit``offset`을 포함할 수 있습니다.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[48:55] hl[51:52,54] *}
### 단일 Hero 조회하기
단일 `Hero`를 **조회**할 수도 있습니다.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[58:63] hl[60] *}
### Hero 삭제하기
`Hero`를 **삭제**하는 것도 가능합니다.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[66:73] hl[71] *}
### 애플리케이션 실행하기
애플리케이션을 실행하려면 다음 명령을 사용합니다:
<div class="termy">
```console
$ fastapi dev main.py
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
</div>
그런 다음 `/docs` UI로 이동하면, **FastAPI**가 해당 **model들**을 사용하여 API **문서를 생성**하는 것으르 확인할 수 있습니다. 또한 이 모델들은 데이터를 직렬화하고 검증하는 데에도 사용됩니다.
<div class="screenshot">
<img src="/img/tutorial/sql-databases/image01.png">
</div>
## 여러 모델로 애플리케이션 업데이트
이제 애플리케이션을 약간 **리팩토링**하여 **보안**과 **유연성**을 개선해 보겠습니다.
이전 애플리케이션의 UI를 보면, 지금까지는 클라이언트가 생성할 `Hero``id`를 직접 지정할 수 있다는 것을 알 수 있습니다. 😱
이는 허용되어선 안 됩니다. 클라이언트가 이미 데이터베이스에 저장된 `id`를 덮어쓸 위험이 있기 때문입니다. `id`**백엔드** 또는 **데이터베이스**가 결정해야 하며, **클라이언트**가 결정해서는 안 됩니다.
또한 hero의 `secret_name`을 생성하긴 했지만, 지금까지는 이 값을 어디에서나 반환하고 있습니다. 이는 그다지 **비밀스럽지** 않습니다... 😅
이러한 문제를 해결하기 위해 몇 가지 **추가 모델**을 추가할 것입니다. 바로 여기서 SQLModel이 빛을 발하게 됩니다. ✨
### 여러 모델 생성하기
**SQLModel**에서 `table=True`가 설정된 모델 클래스는 **테이블 모델**입니다.
`table=True`가 없는 모델 클래스는 **데이터 모델**로, 이는 실제로 몇 가지 추가 기능이 포함된 Pydantic 모델에 불과합니다. 🤓
SQLModel을 사용하면 **상속**을 통해 모든 경우에 필드를 **중복 선언하지 않아도** 됩니다.
#### `HeroBase` - 기본 클래스
모든 모델에서 **공유되는 필드**를 가진 `HeroBase` 모델을 시작해 봅시다:
* `name`
* `age`
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:9] hl[7:9] *}
#### `Hero` - *테이블 모델*
다음으로 실제 *테이블 모델*인 `Hero`를 생성합니다. 이 모델은 다른 모델에는 항상 포함되는 건 아닌 **추가 필드**를 포함합니다:
* `id`
* `secret_name`
`Hero``HeroBase`를 상속하므로 `HeroBase`에 선언된 필드도 포함합니다. 따라서 `Hero`는 다음 **필드들도** 가지게 됩니다:
* `id`
* `name`
* `age`
* `secret_name`
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:14] hl[12:14] *}
#### `HeroPublic` - 공개 *데이터 모델*
다음으로 `HeroPublic` 모델을 생성합니다. 이 모델은 API 클라이언트에 **반환**되는 모델입니다.
`HeroPublic``HeroBase`와 동일한 필드를 가지며, `secret_name`은 포함하지 않습니다.
마침내 우리의 heroes의 정체가 보호됩니다! 🥷
또한 `id: int`를 다시 선언합니다. 이를 통해, API 클라이언트와 **계약**을 맺어 `id`가 항상 존재하며 항상 `int` 타입이라는 것을 보장합니다(`None`이 될 수 없습니다).
/// tip | 팁
반환 모델이 값이 항상 존재하고 항상 `int`(`None`이 아님)를 보장하는 것은 API 클라이언트에게 매우 유용합니다. 이를 통해 API와 통신하는 개발자가 훨씬 더 간단한 코드를 작성할 수 있습니다.
또한 **자동으로 생성된 클라이언트**는 더 단순한 인터페이스를 제공하므로, API와 소통하는 개발자들이 훨씬 수월하게 작업할 수 있습니다. 😎
///
`HeroPublic`의 모든 필드는 `HeroBase`와 동일하며, `id``int`로 선언됩니다(`None`이 아님):
* `id`
* `name`
* `age`
* `secret_name`
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:18] hl[17:18] *}
#### `HeroCreate` - hero 생성용 *데이터 모델*
이제 `HeroCreate` 모델을 생성합니다. 이 모델은 클라이언트로부터 받은 데이터를 **검증**하는 역할을 합니다.
`HeroCreate``HeroBase와` 동일한 필드를 가지며, 추가로 `secret_name을` 포함합니다.
클라이언트가 **새 hero을 생성**할 때 `secret_name`을 보내고, 이는 데이터베이스에 저장되지만, 해당 비밀 이름은 API를 통해 클라이언트에게 반환되지 않습니다.
/// tip | 팁
이 방식은 **비밀번호**를 처리하는 방법과 동일합니다. 비밀번호를 받지만, 이를 API에서 반환하지는 않습니다.
비밀번호 값을 저장하기 전에 **해싱**하여 저장하고, **평문으로 저장하지 마십시오**.
///
`HeroCreate`의 필드는 다음과 같습니다:
* `name`
* `age`
* `secret_name`
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:22] hl[21:22] *}
#### `HeroUpdate` - hero 수정용 *데이터 모델*
이전 애플리케이션에서는 **hero를 수정**할 방법이 없었지만, 이제 **다중 모델**을 통해 수정 기능을 추가할 수 있습니다. 🎉
`HeroUpdate` *데이터 모델*은 약간 특별한데, 새 hero을 생성할 때 필요한 **모든 동일한 필드**를 가지지만, 모든 필드가 **선택적**(기본값이 있음)입니다. 이렇게 하면 hero을 수정할 때 수정하려는 필드만 보낼 수 있습니다.
모든 **필드가 변경되기** 때문에(타입이 `None`을 포함하고, 기본값이 `None`으로 설정됨), 모든 필드를 **다시 선언**해야 합니다.
엄밀히 말하면 `HeroBase`를 상속할 필요는 없습니다. 모든 필드를 다시 선언하기 때문입니다. 일관성을 위해 상속을 유지하긴 했지만, 필수는 아닙니다. 이는 개인적인 취향의 문제입니다. 🤷
`HeroUpdate`의 필드는 다음과 같습니다:
* `name`
* `age`
* `secret_name`
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:28] hl[25:28] *}
### `HeroCreate`로 생성하고 `HeroPublic` 반환하기
이제 **다중 모델**을 사용하므로 애플리케이션의 관련 부분을 업데이트할 수 있습니다.
요청에서 `HeroCreate` *데이터 모델*을 받아 이를 기반으로 `Hero` *테이블 모델*을 생성합니다.
이 새 *테이블 모델* `Hero`는 클라이언트에서 보낸 필드를 가지며, 데이터베이스에서 생성된 `id`도 포함합니다.
그런 다음 함수를 통해 동일한 *테이블 모델* `Hero`를 반환합니다. 하지만 `response_model``HeroPublic` *데이터 모델*을 선언했기 때문에, **FastAPI**는 `HeroPublic`을 사용하여 데이터를 검증하고 직렬화합니다.
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[56:62] hl[56:58] *}
/// tip | 팁
이제 **반환 타입 주석** `-> HeroPublic` 대신 `response_model=HeroPublic`을 사용합니다. 반환하는 값이 실제로 `HeroPublic`*아니기* 때문입니다.
만약 `-> HeroPublic`으로 선언했다면, 에디터와 린터에서 반환값이 `HeroPublic`이 아니라 `Hero`라고 경고했을 것입니다. 이는 적절한 경고입니다.
`response_model`에 선언함으로써 **FastAPI**가 이를 처리하도록 하고, 타입 어노테이션과 에디터 및 다른 도구의 도움에는 영향을 미치지 않도록 설정합니다.
///
### `HeroPublic`으로 Heroes 조회하기
이전과 동일하게 `Hero`를 **조회**할 수 있습니다. 이번에도 `response_model=list[HeroPublic]`을 사용하여 데이터가 올바르게 검증되고 직렬화되도록 보장합니다.
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[65:72] hl[65] *}
### `HeroPublic`으로 단일 Hero 조회하기
단일 hero을 **조회**할 수도 있습니다:
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[75:80] hl[77] *}
### `HeroUpdate`로 Hero 수정하기
**hero를 수정**할 수도 있습니다. 이를 위해 HTTP `PATCH` 작업을 사용합니다.
코드에서는 클라이언트가 보낸 데이터를 딕셔너리 형태(`dict`)로 가져옵니다. 이는 **클라이언트가 보낸 데이터만 포함**하며, 기본값으로 들어가는 값은 제외합니다. 이를 위해 `exclude_unset=True`를 사용합니다. 이것이 주요 핵심입니다. 🪄
그런 다음, `hero_db.sqlmodel_update(hero_data)`를 사용하여 `hero_data`의 데이터를 `hero_db`에 업데이트합니다.
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[83:93] hl[83:84,88:89] *}
### Hero 다시 삭제하기
hero **삭제**는 이전과 거의 동일합니다.
이번에는 모든 것을 리팩토링하고 싶은 욕구를 만족시키지 못할 것 같습니다. 😅
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[96:103] hl[101] *}
### 애플리케이션 다시 실행하기
다음 명령을 사용해 애플리케이션을 다시 실행할 수 있습니다:
<div class="termy">
```console
$ fastapi dev main.py
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
</div>
`/docs` API UI로 이동하면 업데이트된 것을 확인할 수 있습니다. 클라이언트가 영웅을 생성할 때 `id`를 제공할 필요가 없게 되는 등의 변화도 보입니다.
<div class="screenshot">
<img src="/img/tutorial/sql-databases/image02.png">
</div>
## 요약
<a href="https://sqlmodel.tiangolo.com/" class="external-link" target="_blank">**SQLModel**</a>을 사용하여 SQL 데이터베이스와 상호작용하고, *데이터 모델* 및 *테이블 모델*로 코드를 간소화할 수 있습니다.
더 많은 내용을 배우고 싶다면, **SQLModel** 문서를 참고하세요. <a href="https://sqlmodel.tiangolo.com/tutorial/fastapi/" class="external-link" target="_blank">SQLModel을 **FastAPI**와 함께 사용하는 것에 대한 더 긴 미니 튜토리얼</a>도 제공합니다. 🚀

358
docs/ru/docs/tutorial/sql-databases.md

@ -0,0 +1,358 @@
# SQL (реляционные) базы данных
**FastAPI** не требует использования реляционной базы данных. Вы можете воспользоваться любой базой данных, которой хотите.
В этом разделе мы продемонстрируем, как работать с <a href="https://sqlmodel.tiangolo.com/" class="external-link" target="_blank">SQLModel</a>.
Библиотека **SQLModel** построена на основе <a href="https://www.sqlalchemy.org/" class="external-link" target="_blank">SQLAlchemy</a> и Pydantic. Она была разработана автором **FastAPI** специально для приложений на основе FastAPI, которые используют **реляционные базы данных**.
/// tip | Подсказка
Вы можете воспользоваться любой библиотекой для работы с реляционными (SQL) или нереляционными (NoSQL) базами данных. (Их ещё называют <abbr title="ORM = Object Relational Mapper, этот термин для библиотеки, в которой классы представляют SQL-таблицы, а экземпляры классов представляют строки в этих таблицах.">**ORM**</abbr> библиотеками). FastAPI не принуждает вас к использованию чего-либо конкретного. 😎
///
В основе SQLModel лежит SQLAlchemy, поэтому вы спокойно можете использовать любую базу данных, поддерживаемую SQLAlchemy (и, соответственно, поддерживаемую SQLModel), например:
* PostgreSQL
* MySQL
* SQLite
* Oracle
* Microsoft SQL Server, и т.д.
В данном примере мы будем использовать базу данных **SQLite**, т.к. она состоит из единственного файла и поддерживается встроенными библиотеками Python. Таким образом, вы сможете скопировать данный пример и запустить его как он есть.
В дальнейшем, для продакшн-версии вашего приложения, возможно, вам стоит использовать серверную базу данных, например, **PostgreSQL**.
/// tip | Подсказка
Существует официальный генератор проектов на **FastAPI** и **PostgreSQL**, который также включает frontend и дополнительные инструменты <a href="https://github.com/fastapi/full-stack-fastapi-template" class="external-link" target="_blank">https://github.com/fastapi/full-stack-fastapi-template</a>
///
Это очень простое и короткое руководство, поэтому, если вы хотите узнать о базах данных в целом, об SQL, разобраться с более продвинутым функционалом, то воспользуйтесь <a href="https://sqlmodel.tiangolo.com/" class="external-link" target="_blank">документацией SQLModel</a>.
## Установка `SQLModel`
Создайте виртуальное окружение [virtual environment](../virtual-environments.md){.internal-link target=_blank}, активируйте его и установите `sqlmodel`:
<div class="termy">
```console
$ pip install sqlmodel
---> 100%
```
</div>
## Создание приложения с единственной моделью
Мы начнем с создания наиболее простой первой версии нашего приложения с одной единственной моделью **SQLModel**.
В дальнейшем с помощью **дополнительных моделей** мы его улучшим и сделаем более безопасным и универсальным. 🤓
### Создание моделей
Импортируйте `SQLModel` и создайте модель базы данных:
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[1:11] hl[7:11] *}
Класс `Hero` очень напоминает модель Pydantic (фактически, под капотом, *это и есть модель Pydantic*).
Но есть и некоторые различия
* `table=True` для SQLModel означает, что это *модель-таблица*, которая должна представлять **таблицу** в реляционной базе данных. Это не просто *модель данных* (в отличие от обычного класса в Pydantic).
* `Field(primary_key=True)` для SQLModel означает, что поле `id` является первичным ключом в таблице базы данных (вы можете подробнее узнать о первичных ключах баз данных в документации по SQLModel).
Тип `int | None` сигнализирует для SQLModel, что столбец таблицы базы данных должен иметь тип `INTEGER`, или иметь пустое значение `NULL`.
* `Field(index=True)` для SQLModel означает, что нужно создать **SQL индекс** для данного столбца. Это обеспечит более быстрый поиск при чтении данных, отфильтрованных по данному столбцу.
SQLModel будет знать, что данные типа `str`, будут представлены в базе данных как `TEXT` (или `VARCHAR`, в зависимости от типа базы данных).
### Создание соединения с базой данных (Engine)
В SQLModel объект соединения `engine` (по сути это `Engine` из SQLAlchemy) **содержит пул соединений** к базе данных.
Для обеспечения всех подключений приложения к одной базе данных нужен только один объект соединения `engine`.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[14:18] hl[14:15,17:18] *}
Использование настройки `check_same_thread=False` позволяет FastAPI использовать одну и ту же SQLite базу данных в различных потоках (threads). Это необходимо, когда **один запрос** использует **более одного потока** (например, в зависимостях).
Не беспокойтесь, учитывая структуру кода, мы позже позаботимся о том, чтобы использовать **отдельную SQLModel-сессию на каждый отдельный запрос**, это как раз то, что пытается обеспечить `check_same_thread`.
### Создание таблиц
Далее мы добавляем функцию, использующую `SQLModel.metadata.create_all(engine)`, для того, чтобы создать **таблицы** для каждой из **моделей таблицы**.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[21:22] hl[21:22] *}
### Создание зависимости Session
Сессия базы данных (**`Session`**) хранит **объекты в памяти** и отслеживает любые необходимые изменения в данных, а затем **использует `engine`** для коммуникации с базой данных.
Мы создадим FastAPI-**зависимость** с помощью `yield`, которая будет создавать новую сессию (Session) для каждого запроса. Это как раз и обеспечит использование отдельной сессии на каждый отдельный запрос. 🤓
Затем мы создадим объявленную (`Annotated`) зависимость `SessionDep`. Мы сделаем это для того, чтобы упростить остальной код, который будет использовать эту зависимость.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[25:30] hl[25:27,30] *}
### Создание таблиц базы данных при запуске приложения
Мы будем создавать таблицы базы данных при запуске приложения.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[32:37] hl[35:37] *}
В данном примере мы создаем таблицы при наступлении события запуска приложения.
В продуктовом приложении вы, скорее всего, будете использовать скрипт для миграции базы данных, который выполняется перед запуском приложения. 🤓
/// tip | Подсказка
В SQLModel будут включены утилиты миграции, входящие в состав Alembic, но на данный момент вы просто можете использовать
<a href="https://alembic.sqlalchemy.org/en/latest/" class="external-link" target="_blank">Alembic</a> напрямую.
///
### Создание героя (Hero)
Каждая модель в SQLModel является также моделью Pydantic, поэтому вы можете использовать её при **объявлении типов**, точно также, как и модели Pydantic.
Например, при объявлении параметра типа `Hero`, она будет считана из **тела JSON**.
Точно также, вы можете использовать её при объявлении типа значения, возвращаемого функцией, и тогда структурированные данные будут отображены через пользовательский интерфейс автоматически сгенерированной документации FastAPI.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[40:45] hl[40:45] *}
Мы используем зависимость `SessionDep` (сессию базы данных) для того, чтобы добавить нового героя `Hero` в объект сессии (`Session`), сохранить изменения в базе данных, обновить данные героя и затем вернуть их.
### Чтение данных о героях
Мы можем **читать** данные героев из базы данных с помощью `select()`. Мы можем включить `limit` и `offset` для постраничного считывания результатов.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[48:55] hl[51:52,54] *}
### Чтение данных отдельного героя
Мы можем прочитать данные отдельного героя (`Hero`).
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[58:63] hl[60] *}
### Удаление данных героя
Мы также можем удалить героя `Hero` из базы данных.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[66:73] hl[71] *}
### Запуск приложения
Вы можете запустить приложение следующим образом:
<div class="termy">
```console
$ fastapi dev main.py
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
</div>
Далее перейдите в пользовательский интерфейс API `/docs`. Вы увидите, что **FastAPI** использует модели для создания документации API. Эти же модели используются для сериализации и проверки данных.
<div class="screenshot">
<img src="/img/tutorial/sql-databases/image01.png">
</div>
## Добавление в приложение дополнительных (вспомогательных) моделей
Теперь давайте проведём **рефакторинг** нашего приложения, чтобы сделать его более безопасным и более универсальным.
Обратите внимание, что на данном этапе наше приложение позволяет на уровне клиента определять `id` создаваемого героя (`Hero`). 😱
Мы не можем этого допустить, т.к. существует риск переписать уже присвоенные `id` в базе данных. Присвоение `id` должно происходить **на уровне бэкэнда (backend)** или **на уровне базы данных**, но никак **не на уровне клиента**.
Кроме того, мы создаем секретное имя `secret_name` для героя, но пока что, мы возвращаем его повсеместно, и это слабо напоминает **секретность**... 😅
Мы поправим это с помощью нескольких дополнительных (вспомогательных) моделей. Вот где SQLModel по-настоящему покажет себя. ✨
### Создание дополнительных моделей
В **SQLModel**, любая модель с параметром `table=True` является **моделью таблицы**.
Любая модель, не содержащая `table=True` является **моделью данных**, это по сути обычные модели Pydantic (с несколько расширенным функционалом). 🤓
С помощью SQLModel мы можем использовать **наследование**, что поможет нам **избежать дублирования** всех полей.
#### Базовый класс `HeroBase`
Давайте начнём с модели `HeroBase`, которая содержит поля, общие для всех моделей:
* `name`
* `age`
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:9] hl[7:9] *}
#### Модель таблицы `Hero`
Далее давайте создадим **модель таблицы** `Hero` с дополнительными полями, которых может не быть в других моделях:
* `id`
* `secret_name`
Модель `Hero` наследует от `HeroBase`, и поэтому включает также поля из `HeroBase`. Таким образом, все поля, содержащиеся в `Hero`, будут следующими:
* `id`
* `name`
* `age`
* `secret_name`
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:14] hl[12:14] *}
#### Публичная модель данных `HeroPublic`
Далее мы создадим модель `HeroPublic`. Мы будем возвращать её клиентам API.
Она включает в себя те же поля, что и `HeroBase`, и, соответственно, поле `secret_name` в ней отсутствует.
Наконец-то личность наших героев защищена! 🥷
В модели `HeroPublic` также объявляется поле `id: int`. Мы как бы заключаем договоренность с API клиентом, на то, что передаваемые данные всегда должны содержать поле `id`, и это поле должно содержать целое число (и никогда не содержать `None`).
/// tip | Подсказка
Модель ответа, гарантирующая наличие поля со значением типа `int` (не `None`), очень полезна при разработке API клиентов. Определенность в передаваемых данных может обеспечить написание более простого кода.
Также **автоматически генерируемые клиенты** будут иметь более простой интерфейс. И в результате жизнь разработчиков, использующих ваш API, станет значительно легче. 😎
///
`HeroPublic` содержит все поля `HeroBase`, а также поле `id`, объявленное как `int` (не `None`):
* `id`
* `name`
* `age`
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:18] hl[17:18] *}
#### Модель для создания героя `HeroCreate`
Сейчас мы создадим модель `HeroCreate`. Эта модель будет использоваться для проверки данных, переданных клиентом.
Она содержит те же поля, что и `HeroBase`, а также поле `secret_name`.
Теперь, при создании нового героя, клиенты будут передавать секретное имя `secret_name`, которое будет сохранено в базе данных, но не будет возвращено в ответе API клиентам.
/// tip | Подсказка
Вот как нужно работать с **паролями**: получайте их, но не возвращайте их через API.
Также хэшируйте значения паролей перед тем, как их сохранить. Ни в коем случае не храните пароли в открытом виде, как обычный текст.
///
Поля модели `HeroCreate`:
* `name`
* `age`
* `secret_name`
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:22] hl[21:22] *}
#### Модель для обновления данных героя `HeroUpdate`
В предыдущих версиях нашей программы мы не могли обновить данные героя, теперь, воспользовавшись дополнительными моделями, мы сможем это сделать. 🎉
Модель данных `HeroUpdate` в некотором смысле особенная. Она содержит все те же поля, что и модель создания героя, но все поля модели являются **необязательными**. (Все они имеют значение по умолчанию.) Таким образом, при обновлении данных героя, вам достаточно передать только те поля, которые требуют изменения.
Поскольку **все поля по сути меняются** (теперь тип каждого поля допускает значение `None` и значение по умолчанию `None`), мы должны их **объявить заново**.
Фактически, нам не нужно наследоваться от `HeroBase`, потому что мы будем заново объявлять все поля. Я оставлю наследование просто для поддержания общего стиля, но оно (наследование) здесь необязательно. 🤷
Поля `HeroUpdate`:
* `name`
* `age`
* `secret_name`
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:28] hl[25:28] *}
### Создание героя с помощью `HeroCreate` и возвращение результатов с помощью `HeroPublic`
Теперь, когда у нас есть дополнительные модели, мы можем обновить те части приложения, которые их используют.
Вместе c запросом на создание героя мы получаем объект данных `HeroCreate`, и создаем на его основе объект модели таблицы `Hero`.
Созданный объект *модели таблицы* `Hero` будет иметь все поля, переданные клиентом, а также поле `id`, сгенерированное базой данных.
Далее функция вернёт объект *модели таблицы* `Hero`. Но поскольку, мы объявили `HeroPublic` как модель ответа, то **FastAPI** будет использовать именно её для проверки и сериализации данных.
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[56:62] hl[56:58] *}
/// tip | Подсказка
Теперь мы используем модель ответа `response_model=HeroPublic`, вместо того, чтобы объявить тип возвращаемого значения как `-> HeroPublic`. Мы это делаем потому, что тип возвращаемого значения не относится к `HeroPublic`.
Если бы мы объявили тип возвращаемого значения как `-> HeroPublic`, то редактор и линтер начали бы ругаться (и вполне справедливо), что возвращаемое значение принадлежит типу `Hero`, а совсем не `HeroPublic`.
Объявляя модель ответа в `response_model`, мы как бы говорим **FastAPI**: делай свое дело, не вмешиваясь в аннотацию типов и не полагаясь на помощь редактора или других инструментов.
///
### Чтение данных героев с помощью `HeroPublic`
Мы можем проделать то же самое **для чтения данных** героев. Мы применим модель ответа `response_model=list[HeroPublic]`, и тем самым обеспечим правильную проверку и сериализацию данных.
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[65:72] hl[65] *}
### Чтение данных отдельного героя с помощью `HeroPublic`
Мы можем **прочитать** данные отдельного героя:
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[75:80] hl[77] *}
### Обновление данных героя с помощью `HeroUpdate`
Мы можем **обновить данные героя**. Для этого мы воспользуемся HTTP методом `PATCH`.
В коде мы получаем объект словаря `dict` с данными, переданными клиентом (т.е. **только c данными, переданными клиентом**, исключая любые значения, которые могли бы быть там только потому, что они являются значениями по умолчанию). Для того чтобы сделать это, мы воспользуемся опцией `exclude_unset=True`. В этом главная хитрость. 🪄
Затем мы применим `hero_db.sqlmodel_update(hero_data)`, и обновим `hero_db`, использовав данные `hero_data`.
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[83:93] hl[83:84,88:89] *}
### Удалим героя ещё раз
Операция **удаления** героя практически не меняется.
В данном случае желание *`отрефакторить всё`* остаётся неудовлетворенным. 😅
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[96:103] hl[101] *}
### Снова запустим приложение
Вы можете снова запустить приложение:
<div class="termy">
```console
$ fastapi dev main.py
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
</div>
Если вы перейдете в пользовательский интерфейс API `/docs`, то вы увидите, что он был обновлен, и больше не принимает параметра `id` от клиента при создании нового героя, и т.д.
<div class="screenshot">
<img src="/img/tutorial/sql-databases/image02.png">
</div>
## Резюме
Вы можете использовать <a href="https://sqlmodel.tiangolo.com/" class="external-link" target="_blank">**SQLModel**</a> для взаимодействия с реляционными базами данных, а также для упрощения работы с **моделями данных** и **моделями таблиц**.
Вы можете узнать гораздо больше информации в документации по **SQLModel**. Там вы найдете более подробное <a href="https://sqlmodel.tiangolo.com/tutorial/fastapi/" class="external-link" target="_blank">мини-руководство по использованию SQLModel с **FastAPI**</a>. 🚀

83
docs/uk/docs/fastapi-cli.md

@ -0,0 +1,83 @@
# FastAPI CLI
**FastAPI CLI** це програма командного рядка, яку Ви можете використовувати, щоб обслуговувати Ваш додаток FastAPI, керувати Вашими FastApi проектами, тощо.
Коли Ви встановлюєте FastApi (тобто виконуєте `pip install "fastapi[standard]"`), Ви також встановлюєте пакунок `fastapi-cli`, цей пакунок надає команду `fastapi` в терміналі.
Для запуску Вашого FastAPI проекту для розробки, Ви можете скористатись командою `fastapi dev`:
<div class="termy">
```console
$ <font color="#4E9A06">fastapi</font> dev <u style="text-decoration-style:single">main.py</u>
<font color="#3465A4">INFO </font> Using path <font color="#3465A4">main.py</font>
<font color="#3465A4">INFO </font> Resolved absolute path <font color="#75507B">/home/user/code/awesomeapp/</font><font color="#AD7FA8">main.py</font>
<font color="#3465A4">INFO </font> Searching for package file structure from directories with <font color="#3465A4">__init__.py</font> files
<font color="#3465A4">INFO </font> Importing from <font color="#75507B">/home/user/code/</font><font color="#AD7FA8">awesomeapp</font>
╭─ <font color="#8AE234"><b>Python module file</b></font> ─╮
│ │
│ 🐍 main.py │
│ │
╰──────────────────────╯
<font color="#3465A4">INFO </font> Importing module <font color="#4E9A06">main</font>
<font color="#3465A4">INFO </font> Found importable FastAPI app
╭─ <font color="#8AE234"><b>Importable FastAPI app</b></font> ─╮
│ │
<span style="background-color:#272822"><font color="#FF4689">from</font></span><span style="background-color:#272822"><font color="#F8F8F2"> main </font></span><span style="background-color:#272822"><font color="#FF4689">import</font></span><span style="background-color:#272822"><font color="#F8F8F2"> app</font></span><span style="background-color:#272822"> </span>
│ │
╰──────────────────────────╯
<font color="#3465A4">INFO </font> Using import string <font color="#8AE234"><b>main:app</b></font>
<span style="background-color:#C4A000"><font color="#2E3436">╭────────── FastAPI CLI - Development mode ───────────╮</font></span>
<span style="background-color:#C4A000"><font color="#2E3436">│ │</font></span>
<span style="background-color:#C4A000"><font color="#2E3436">│ Serving at: http://127.0.0.1:8000 │</font></span>
<span style="background-color:#C4A000"><font color="#2E3436">│ │</font></span>
<span style="background-color:#C4A000"><font color="#2E3436">│ API docs: http://127.0.0.1:8000/docs │</font></span>
<span style="background-color:#C4A000"><font color="#2E3436">│ │</font></span>
<span style="background-color:#C4A000"><font color="#2E3436">│ Running in development mode, for production use: │</font></span>
<span style="background-color:#C4A000"><font color="#2E3436">│ │</font></span>
<span style="background-color:#C4A000"><font color="#2E3436"></font></span><span style="background-color:#C4A000"><font color="#555753"><b>fastapi run</b></font></span><span style="background-color:#C4A000"><font color="#2E3436"></font></span>
<span style="background-color:#C4A000"><font color="#2E3436">│ │</font></span>
<span style="background-color:#C4A000"><font color="#2E3436">╰─────────────────────────────────────────────────────╯</font></span>
<font color="#4E9A06">INFO</font>: Will watch for changes in these directories: [&apos;/home/user/code/awesomeapp&apos;]
<font color="#4E9A06">INFO</font>: Uvicorn running on <b>http://127.0.0.1:8000</b> (Press CTRL+C to quit)
<font color="#4E9A06">INFO</font>: Started reloader process [<font color="#34E2E2"><b>2265862</b></font>] using <font color="#34E2E2"><b>WatchFiles</b></font>
<font color="#4E9A06">INFO</font>: Started server process [<font color="#06989A">2265873</font>]
<font color="#4E9A06">INFO</font>: Waiting for application startup.
<font color="#4E9A06">INFO</font>: Application startup complete.
```
</div>
Програма командного рядка `fastapi` це **FastAPI CLI**.
FastAPI CLI приймає шлях до Вашої Python програми (напр. `main.py`) і автоматично виявляє екземпляр `FastAPI` (зазвичай названий `app`), обирає коректний процес імпорту, а потім обслуговує його.
Натомість, для запуску у продакшн використовуйте `fastapi run`. 🚀
Всередині **FastAPI CLI** використовує <a href="https://www.uvicorn.org" class="external-link" target="_blank">Uvicorn</a>, високопродуктивний, production-ready, ASGI cервер. 😎
## `fastapi dev`
Використання `fastapi dev` ініціює режим розробки.
За замовчуванням, **автоматичне перезавантаження** увімкнене, автоматично перезавантажуючи сервер кожного разу, коли Ви змінюєте Ваш код. Це ресурсо-затратно, та може бути менш стабільним, ніж коли воно вимкнене. Ви повинні використовувати його тільки під час розробки. Воно також слухає IP-адресу `127.0.0.1`, що є IP Вашого девайсу для самостійної комунікації з самим собою (`localhost`).
## `fastapi run`
Виконання `fastapi run` запустить FastAPI у продакшн-режимі за замовчуванням.
За замовчуванням, **автоматичне перезавантаження** вимкнене. Воно також прослуховує IP-адресу `0.0.0.0`, що означає всі доступні IP адреси, тим самим даючи змогу будь-кому комунікувати з девайсом. Так Ви зазвичай будете запускати його у продакшн, наприклад у контейнері.
В більшості випадків Ви можете (і маєте) мати "termination proxy", який обробляє HTTPS для Вас, це залежить від способу розгортання вашого додатку, Ваш провайдер може зробити це для Вас, або Вам потрібно налаштувати його самостійно.
/// tip
Ви можете дізнатись більше про це у [документації про розгортування](deployment/index.md){.internal-link target=_blank}.
///

4
docs/zh-hant/docs/tutorial/index.md

@ -85,9 +85,9 @@ $ pip install "fastapi[standard]"
/// note
當你使用 `pip install "fastapi[standard]"` 安裝時,會包含一些預設的可選標準依項。
當你使用 `pip install "fastapi[standard]"` 安裝時,會包含一些預設的可選標準依項。
如果你不想包含那些可選的依項,你可以使用 `pip install fastapi` 來安裝。
如果你不想包含那些可選的依項,你可以使用 `pip install fastapi` 來安裝。
///

99
docs/zh/docs/advanced/async-tests.md

@ -0,0 +1,99 @@
# 异步测试
您已经了解了如何使用 `TestClient` 测试 **FastAPI** 应用程序。但是到目前为止,您只了解了如何编写同步测试,而没有使用 `async` 异步函数。
在测试中能够使用异步函数可能会很有用,比如当您需要异步查询数据库的时候。想象一下,您想要测试向 FastAPI 应用程序发送请求,然后验证您的后端是否成功在数据库中写入了正确的数据,与此同时您使用了异步的数据库的库。
让我们看看如何才能实现这一点。
## pytest.mark.anyio
如果我们想在测试中调用异步函数,那么我们的测试函数必须是异步的。 AnyIO 为此提供了一个简洁的插件,它允许我们指定一些测试函数要异步调用。
## HTTPX
即使您的 **FastAPI** 应用程序使用普通的 `def` 函数而不是 `async def` ,它本质上仍是一个 `async` 异步应用程序。
`TestClient` 在内部通过一些“魔法”操作,使得您可以在普通的 `def` 测试函数中调用异步的 FastAPI 应用程序,并使用标准的 pytest。但当我们在异步函数中使用它时,这种“魔法”就不再生效了。由于测试以异步方式运行,我们无法在测试函数中继续使用 `TestClient`
`TestClient` 是基于 <a href="https://www.python-httpx.org" class="external-link" target="_blank">HTTPX</a> 的。幸运的是,我们可以直接使用它来测试API。
## 示例
举个简单的例子,让我们来看一个[更大的应用](../tutorial/bigger-applications.md){.internal-link target=_blank}和[测试](../tutorial/testing.md){.internal-link target=_blank}中描述的类似文件结构:
```
.
├── app
│   ├── __init__.py
│   ├── main.py
│   └── test_main.py
```
文件 `main.py` 将包含:
{* ../../docs_src/async_tests/main.py *}
文件 `test_main.py` 将包含针对 `main.py` 的测试,现在它可能看起来如下:
{* ../../docs_src/async_tests/test_main.py *}
## 运行测试
您可以通过以下方式照常运行测试:
<div class="termy">
```console
$ pytest
---> 100%
```
</div>
## 详细说明
这个标记 `@pytest.mark.anyio` 会告诉 pytest 该测试函数应该被异步调用:
{* ../../docs_src/async_tests/test_main.py hl[7] *}
/// tip
请注意,测试函数现在用的是 `async def`,而不是像以前使用 `TestClient` 时那样只是 `def`
///
我们现在可以使用应用程序创建一个 `AsyncClient` ,并使用 `await` 向其发送异步请求。
{* ../../docs_src/async_tests/test_main.py hl[9:12] *}
这相当于:
```Python
response = client.get('/')
```
我们曾经通过它向 `TestClient` 发出请求。
/// tip
请注意,我们正在将 async/await 与新的 `AsyncClient` 一起使用——请求是异步的。
///
/// warning
如果您的应用程序依赖于生命周期事件, `AsyncClient` 将不会触发这些事件。为了确保它们被触发,请使用 <a href="https://github.com/florimondmanca/asgi-lifespan#usage" class="external-link" target="_blank">florimondmanca/asgi-lifespan</a> 中的 `LifespanManager`
///
## 其他异步函数调用
由于测试函数现在是异步的,因此除了在测试中向 FastAPI 应用程序发送请求之外,您现在还可以调用(和使用 `await` 等待)其他 `async` 异步函数,就和您在代码中的其他任何地方调用它们的方法一样。
/// tip
如果您在测试程序中集成异步函数调用的时候遇到一个 `RuntimeError: Task attached to a different loop` 的报错(例如,使用 <a href="https://stackoverflow.com/questions/41584243/runtimeerror-task-attached-to-a-different-loop" class="external-link" target="_blank">MongoDB 的 MotorClient</a> 时),请记住,只能在异步函数中实例化需要事件循环的对象,例如通过 `'@app.on_event("startup")` 回调函数进行初始化。
///

132
docs/zh/docs/advanced/events.md

@ -1,16 +1,114 @@
# 事件:启动 - 关闭
# 生命周期事件
**FastAPI** 支持定义在应用启动前,或应用关闭后执行的事件处理器(函数)
你可以定义在应用**启动**前执行的逻辑(代码)。这意味着在应用**开始接收请求**之前,这些代码只会被执行**一次**
事件函数既可以声明为异步函数(`async def`),也可以声明为普通函数(`def`)。
同样地,你可以定义在应用**关闭**时应执行的逻辑。在这种情况下,这段代码将在**处理可能的多次请求后**执行**一次**。
因为这段代码在应用开始接收请求**之前**执行,也会在处理可能的若干请求**之后**执行,它覆盖了整个应用程序的**生命周期**("生命周期"这个词很重要😉)。
这对于设置你需要在整个应用中使用的**资源**非常有用,这些资源在请求之间**共享**,你可能需要在之后进行**释放**。例如,数据库连接池,或加载一个共享的机器学习模型。
## 用例
让我们从一个示例用例开始,看看如何解决它。
假设你有几个**机器学习的模型**,你想要用它们来处理请求。
相同的模型在请求之间是共享的,因此并非每个请求或每个用户各自拥有一个模型。
假设加载模型可能**需要相当长的时间**,因为它必须从**磁盘**读取大量数据。因此你不希望每个请求都加载它。
你可以在模块/文件的顶部加载它,但这也意味着即使你只是在运行一个简单的自动化测试,它也会**加载模型**,这样测试将**变慢**,因为它必须在能够独立运行代码的其他部分之前等待模型加载完成。
这就是我们要解决的问题——在处理请求前加载模型,但只是在应用开始接收请求前,而不是代码执行时。
## 生命周期 lifespan
你可以使用`FastAPI()`应用的`lifespan`参数和一个上下文管理器(稍后我将为你展示)来定义**启动**和**关闭**的逻辑。
让我们从一个例子开始,然后详细介绍。
我们使用`yield`创建了一个异步函数`lifespan()`像这样:
```Python hl_lines="16 19"
{!../../docs_src/events/tutorial003.py!}
```
在这里,我们在 `yield` 之前将(虚拟的)模型函数放入机器学习模型的字典中,以此模拟加载模型的耗时**启动**操作。这段代码将在应用程序**开始处理请求之前**执行,即**启动**期间。
然后,在 `yield` 之后,我们卸载模型。这段代码将会在应用程序**完成处理请求后**执行,即在**关闭**之前。这可以释放诸如内存或 GPU 之类的资源。
/// tip | 提示
**关闭**事件只会在你停止应用时触发。
可能你需要启动一个新版本,或者你只是你厌倦了运行它。 🤷
///
## 生命周期函数
首先要注意的是,我们定义了一个带有 `yield` 的异步函数。这与带有 `yield` 的依赖项非常相似。
```Python hl_lines="14-19"
{!../../docs_src/events/tutorial003.py!}
```
这个函数在 `yield`之前的部分,会在应用启动前执行。
剩下的部分在 `yield` 之后,会在应用完成后执行。
## 异步上下文管理器
如你所见,这个函数有一个装饰器 `@asynccontextmanager`
它将函数转化为所谓的“**异步上下文管理器**”。
```Python hl_lines="1 13"
{!../../docs_src/events/tutorial003.py!}
```
在 Python 中, **上下文管理器**是一个你可以在 `with` 语句中使用的东西,例如,`open()` 可以作为上下文管理器使用。
```Python
with open("file.txt") as file:
file.read()
```
Python 的最近几个版本也有了一个**异步上下文管理器**,你可以通过 `async with` 来使用:
```Python
async with lifespan(app):
await do_stuff()
```
你可以像上面一样创建了一个上下文管理器或者异步上下文管理器,它的作用是在进入 `with` 块时,执行 `yield` 之前的代码,并且在离开 `with` 块时,执行 `yield` 后面的代码。
但在我们上面的例子里,我们并不是直接使用,而是传递给 FastAPI 来供其使用。
`FastAPI()``lifespan` 参数接受一个**异步上下文管理器**,所以我们可以把我们新定义的上下文管理器 `lifespan` 传给它。
```Python hl_lines="22"
{!../../docs_src/events/tutorial003.py!}
```
## 替代事件(弃用)
/// warning | 警告
**FastAPI** 只执行主应用中的事件处理器,不执行[子应用 - 挂载](sub-applications.md){.internal-link target=_blank}中的事件处理器。
配置**启动**和**关闭**事件的推荐方法是使用 `FastAPI()` 应用的 `lifespan` 参数,如前所示。如果你提供了一个 `lifespan` 参数,启动(`startup`)和关闭(`shutdown`)事件处理器将不再生效。要么使用 `lifespan`,要么配置所有事件,两者不能共用。
你可以跳过这一部分。
///
## `startup` 事件
有一种替代方法可以定义在**启动**和**关闭**期间执行的逻辑。
**FastAPI** 支持定义在应用启动前,或应用关闭时执行的事件处理器(函数)。
事件函数既可以声明为异步函数(`async def`),也可以声明为普通函数(`def`)。
### `startup` 事件
使用 `startup` 事件声明 `app` 启动前运行的函数:
@ -22,7 +120,7 @@
只有所有 `startup` 事件处理器运行完毕,**FastAPI** 应用才开始接收请求。
## `shutdown` 事件
### `shutdown` 事件
使用 `shutdown` 事件声明 `app` 关闭时运行的函数:
@ -48,8 +146,28 @@
///
### `startup``shutdown` 一起使用
启动和关闭的逻辑很可能是连接在一起的,你可能希望启动某个东西然后结束它,获取一个资源然后释放它等等。
在不共享逻辑或变量的不同函数中处理这些逻辑比较困难,因为你需要在全局变量中存储值或使用类似的方式。
因此,推荐使用 `lifespan`
## 技术细节
只是为好奇者提供的技术细节。🤓
在底层,这部分是<a href="https://asgi.readthedocs.io/en/latest/specs/lifespan.html" class="external-link" target="_blank">生命周期协议</a>的一部分,参见 ASGI 技术规范,定义了称为启动(`startup`)和关闭(`shutdown`)的事件。
/// info | 说明
有关事件处理器的详情,请参阅 <a href="https://www.starlette.io/events/" class="external-link" target="_blank">Starlette 官档 - 事件</a>
有关事件处理器的详情,请参阅 <a href="https://www.starlette.io/lifespan/" class="external-link" target="_blank">Starlette 官档 - 事件</a>
包括如何处理生命周期状态,这可以用于程序的其他部分。
///
## 子应用
🚨 **FastAPI** 只会触发主应用中的生命周期事件,不包括[子应用 - 挂载](sub-applications.md){.internal-link target=_blank}中的。

55
docs/zh/docs/advanced/openapi-webhooks.md

@ -0,0 +1,55 @@
# OpenAPI 网络钩子
有些情况下,您可能想告诉您的 API **用户**,您的应用程序可以携带一些数据调用*他们的*应用程序(给它们发送请求),通常是为了**通知**某种**事件**。
这意味着,除了您的用户向您的 API 发送请求的一般情况,**您的 API**(或您的应用)也可以向**他们的系统**(他们的 API、他们的应用)**发送请求**。
这通常被称为**网络钩子**(Webhook)。
## 使用网络钩子的步骤
通常的过程是**您**在代码中**定义**要发送的消息,即**请求的主体**。
您还需要以某种方式定义您的应用程序将在**何时**发送这些请求或事件。
**用户**会以某种方式(例如在某个网页仪表板上)定义您的应用程序发送这些请求应该使用的 **URL**
所有关于注册网络钩子的 URL 的**逻辑**以及发送这些请求的实际代码都由您决定。您可以在**自己的代码**中以任何想要的方式来编写它。
## 使用 `FastAPI` 和 OpenAPI 文档化网络钩子
使用 **FastAPI**,您可以利用 OpenAPI 来自定义这些网络钩子的名称、您的应用可以发送的 HTTP 操作类型(例如 `POST`、`PUT` 等)以及您的应用将发送的**请求体**。
这能让您的用户更轻松地**实现他们的 API** 来接收您的**网络钩子**请求,他们甚至可能能够自动生成一些自己的 API 代码。
/// info
网络钩子在 OpenAPI 3.1.0 及以上版本中可用,FastAPI `0.99.0` 及以上版本支持。
///
## 带有网络钩子的应用程序
当您创建一个 **FastAPI** 应用程序时,有一个 `webhooks` 属性可以用来定义网络钩子,方式与您定义*路径操作*的时候相同,例如使用 `@app.webhooks.post()`
{* ../../docs_src/openapi_webhooks/tutorial001.py hl[9:13,36:53] *}
您定义的网络钩子将被包含在 `OpenAPI` 的架构中,并出现在自动生成的**文档 UI** 中。
/// info
`app.webhooks` 对象实际上只是一个 `APIRouter` ,与您在使用多个文件来构建应用程序时所使用的类型相同。
///
请注意,使用网络钩子时,您实际上并没有声明一个*路径*(比如 `/items/` ),您传递的文本只是这个网络钩子的**标识符**(事件的名称)。例如在 `@app.webhooks.post("new-subscription")` 中,网络钩子的名称是 `new-subscription`
这是因为我们预计**您的用户**会以其他方式(例如通过网页仪表板)来定义他们希望接收网络钩子的请求的实际 **URL 路径**
### 查看文档
现在您可以启动您的应用程序并访问 <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
您会看到您的文档不仅有正常的*路径操作*显示,现在还多了一些**网络钩子**:
<img src="/img/tutorial/openapi-webhooks/image01.png">

2
docs/zh/docs/advanced/testing-dependencies.md

@ -28,7 +28,7 @@
这样一来,**FastAPI** 就会调用覆盖依赖项,不再调用原依赖项。
{* ../../docs_src/dependency_testing/tutorial001.py hl[26:27,30] *}
{* ../../docs_src/dependency_testing/tutorial001_an_py310.py hl[26:27,30] *}
/// tip | 提示

34
docs/zh/docs/async.md

@ -251,7 +251,7 @@ Python 的现代版本支持通过一种叫**"协程"**——使用 `async` 和
这与 **FastAPI** 的性能水平相同。
您可以同时拥有并行性和异步性,您可以获得比大多数经过测试的 NodeJS 框架更高的性能,并且与 Go 不相上下, Go 是一种更接近于 C 的编译语言(<a href="https://www.techempower.com/benchmarks/#section=data-r17&hw=ph&test=query&l=zijmkf-1" class="external-link" target="_blank">全部归功于 Starlette</a>)。
你可以同时拥有并行性和异步性,你可以获得比大多数经过测试的 NodeJS 框架更高的性能,并且与 Go 不相上下, Go 是一种更接近于 C 的编译语言(<a href="https://www.techempower.com/benchmarks/#section=data-r17&hw=ph&test=query&l=zijmkf-1" class="external-link" target="_blank">全部归功于 Starlette</a>)。
### 并发比并行好吗?
@ -275,7 +275,7 @@ Python 的现代版本支持通过一种叫**"协程"**——使用 `async` 和
但在这种情况下,如果你能带上 8 名前收银员/厨师,现在是清洁工一起清扫,他们中的每一个人(加上你)都能占据房子的一个区域来清扫,你就可以在额外的帮助下并行的更快地完成所有工作。
在这个场景中,每个清洁工(包括)都将是一个处理器,完成这个工作的一部分。
在这个场景中,每个清洁工(包括)都将是一个处理器,完成这个工作的一部分。
由于大多数执行时间是由实际工作(而不是等待)占用的,并且计算机中的工作是由 <abbr title="Central Processing Unit">CPU</abbr> 完成的,所以他们称这些问题为"CPU 密集型"。
@ -292,9 +292,9 @@ CPU 密集型操作的常见示例是需要复杂的数学处理。
### 并发 + 并行: Web + 机器学习
使用 **FastAPI**可以利用 Web 开发中常见的并发机制的优势(NodeJS 的主要吸引力)。
使用 **FastAPI**可以利用 Web 开发中常见的并发机制的优势(NodeJS 的主要吸引力)。
并且,也可以利用并行和多进程(让多个进程并行运行)的优点来处理与机器学习系统中类似的 **CPU 密集型** 工作。
并且,也可以利用并行和多进程(让多个进程并行运行)的优点来处理与机器学习系统中类似的 **CPU 密集型** 工作。
这一点,再加上 Python 是**数据科学**、机器学习(尤其是深度学习)的主要语言这一简单事实,使得 **FastAPI** 与数据科学/机器学习 Web API 和应用程序(以及其他许多应用程序)非常匹配。
@ -304,7 +304,7 @@ CPU 密集型操作的常见示例是需要复杂的数学处理。
现代版本的 Python 有一种非常直观的方式来定义异步代码。这使它看起来就像正常的"顺序"代码,并在适当的时候"等待"。
当有一个操作需要等待才能给出结果,且支持这个新的 Python 特性时,可以编写如下代码:
当有一个操作需要等待才能给出结果,且支持这个新的 Python 特性时,可以编写如下代码:
```Python
burgers = await get_burgers(2)
@ -340,7 +340,7 @@ burgers = get_burgers(2)
---
因此,如果您使用的库告诉您可以使用 `await` 调用它,则需要使用 `async def` 创建路径操作函数 ,如:
因此,如果你使用的库告诉你可以使用 `await` 调用它,则需要使用 `async def` 创建路径操作函数 ,如:
```Python hl_lines="2-3"
@app.get('/burgers')
@ -351,15 +351,15 @@ async def read_burgers():
### 更多技术细节
可能已经注意到,`await` 只能在 `async def` 定义的函数内部使用。
可能已经注意到,`await` 只能在 `async def` 定义的函数内部使用。
但与此同时,必须"等待"通过 `async def` 定义的函数。因此,带 `async def` 的函数也只能在 `async def` 定义的函数内部调用。
那么,这关于先有鸡还是先有蛋的问题,如何调用第一个 `async` 函数?
如果使用 **FastAPI**,你不必担心这一点,因为"第一个"函数将是你的路径操作函数,FastAPI 将知道如何做正确的事情。
如果使用 **FastAPI**,你不必担心这一点,因为"第一个"函数将是你的路径操作函数,FastAPI 将知道如何做正确的事情。
但如果想在没有 FastAPI 的情况下使用 `async` / `await`,则可以这样做。
但如果想在没有 FastAPI 的情况下使用 `async` / `await`,则可以这样做。
### 编写自己的异步代码
@ -367,7 +367,9 @@ Starlette (和 **FastAPI**) 是基于 <a href="https://anyio.readthedocs.io/
特别是,你可以直接使用 <a href="https://anyio.readthedocs.io/en/stable/" class="external-link" target="_blank">AnyIO</a> 来处理高级的并发用例,这些用例需要在自己的代码中使用更高级的模式。
即使您没有使用 **FastAPI**,您也可以使用 <a href="https://anyio.readthedocs.io/en/stable/" class="external-link" target="_blank">AnyIO</a> 编写自己的异步程序,使其拥有较高的兼容性并获得一些好处(例如, 结构化并发)。
即使你没有使用 **FastAPI**,你也可以使用 <a href="https://anyio.readthedocs.io/en/stable/" class="external-link" target="_blank">AnyIO</a> 编写自己的异步程序,使其拥有较高的兼容性并获得一些好处(例如, 结构化并发)。
我(指原作者 —— 译者注)基于 AnyIO 新建了一个库,作为一个轻量级的封装层,用来优化类型注解,同时提供了更好的**自动补全**、**内联错误提示**等功能。这个库还附带了一个友好的入门指南和教程,能帮助你**理解**并编写**自己的异步代码**:<a href="https://asyncer.tiangolo.com/" class="external-link" target="_blank">Asyncer</a>。如果你有**结合使用异步代码和常规**(阻塞/同步)代码的需求,这个库会特别有用。
### 其他形式的异步代码
@ -407,7 +409,7 @@ Starlette (和 **FastAPI**) 是基于 <a href="https://anyio.readthedocs.io/
这些都是 FastAPI 如何在内部工作的技术细节。
如果有相当多的技术知识(协程、线程、阻塞等),并且对 FastAPI 如何处理 `async def` 与常规 `def` 感到好奇,请继续。
如果有相当多的技术知识(协程、线程、阻塞等),并且对 FastAPI 如何处理 `async def` 与常规 `def` 感到好奇,请继续。
///
@ -415,9 +417,9 @@ Starlette (和 **FastAPI**) 是基于 <a href="https://anyio.readthedocs.io/
当你使用 `def` 而不是 `async def` 来声明一个*路径操作函数*时,它运行在外部的线程池中并等待其结果,而不是直接调用(因为它会阻塞服务器)。
如果您使用过另一个不以上述方式工作的异步框架,并且您习惯于用普通的 `def` 定义普通的仅计算路径操作函数,以获得微小的性能增益(大约100纳秒),请注意,在 FastAPI 中,效果将完全相反。在这些情况下,最好使用 `async def`,除非路径操作函数内使用执行阻塞 <abbr title="输入/输出:磁盘读写,网络通讯.">I/O</abbr> 的代码。
如果你使用过另一个不以上述方式工作的异步框架,并且你习惯于用普通的 `def` 定义普通的仅计算路径操作函数,以获得微小的性能增益(大约100纳秒),请注意,在 FastAPI 中,效果将完全相反。在这些情况下,最好使用 `async def`,除非路径操作函数内使用执行阻塞 <abbr title="输入/输出:磁盘读写,网络通讯.">I/O</abbr> 的代码。
在这两种情况下,与之前的框架相比,**FastAPI** 可能[仍然很快](index.md#_11){.internal-link target=_blank}。
在这两种情况下,与之前的框架相比,**FastAPI** 可能[仍然很快](index.md#_11){.internal-link target=_blank}。
### 依赖
@ -429,9 +431,9 @@ Starlette (和 **FastAPI**) 是基于 <a href="https://anyio.readthedocs.io/
### 其他函数
可直接调用通过 `def``async def` 创建的任何其他函数,FastAPI 不会影响调用它们的方式。
可直接调用通过 `def``async def` 创建的任何其他函数,FastAPI 不会影响调用它们的方式。
这与 FastAPI 为调用*路径操作函数*和依赖项的逻辑相反。
这与 FastAPI 为调用*路径操作函数*和依赖项的逻辑相反。
如果你的函数是通过 `def` 声明的,它将被直接调用(在代码中编写的地方),而不会在线程池中,如果这个函数通过 `async def` 声明,当在代码中调用时,你就应该使用 `await` 等待函数的结果。
@ -439,4 +441,4 @@ Starlette (和 **FastAPI**) 是基于 <a href="https://anyio.readthedocs.io/
再次提醒,这些是非常技术性的细节,如果你来搜索它可能对你有用。
否则,最好应该遵守的指导原则<a href="#_1">赶时间吗?</a>.
否则,最好应该遵守的指导原则<a href="#_1">赶时间吗?</a>.

Loading…
Cancel
Save