From 633ed1d8aff74a2575512d95c644ba9f2084b801 Mon Sep 17 00:00:00 2001 From: Rishat-F <66554797+Rishat-F@users.noreply.github.com> Date: Mon, 3 Feb 2025 16:33:39 +0300 Subject: [PATCH 001/157] =?UTF-8?q?=F0=9F=8C=90=20Add=20Russian=20translat?= =?UTF-8?q?ion=20for=20`docs/ru/docs/advanced/websockets.md`=20(#13279)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/ru/docs/advanced/websockets.md | 186 ++++++++++++++++++++++++++++ 1 file changed, 186 insertions(+) create mode 100644 docs/ru/docs/advanced/websockets.md diff --git a/docs/ru/docs/advanced/websockets.md b/docs/ru/docs/advanced/websockets.md new file mode 100644 index 000000000..bc9dfcbff --- /dev/null +++ b/docs/ru/docs/advanced/websockets.md @@ -0,0 +1,186 @@ +# Веб-сокеты + +Вы можете использовать веб-сокеты в **FastAPI**. + +## Установка `WebSockets` + +Убедитесь, что [виртуальная среда](../virtual-environments.md){.internal-link target=_blank} создана, активируйте её и установите `websockets`: + +
+ +```console +$ pip install websockets + +---> 100% +``` + +
+ +## Клиент WebSockets + +### Рабочее приложение + +Скорее всего, в вашей реальной продуктовой системе есть фронтенд, реализованный при помощи современных фреймворков React, Vue.js или Angular. + +И наверняка для взаимодействия с бекендом через веб-сокеты вы будете использовать средства фронтенда. + +Также у вас может быть нативное мобильное приложение, коммуницирующее непосредственно с веб-сокетами на бекенд-сервере. + +Либо вы можете сделать какой-либо другой способ взаимодействия с веб-сокетами. + +--- + +Но для этого примера мы воспользуемся очень простым HTML документом с небольшими вставками JavaScript кода. + +Конечно же это неоптимально, и на практике так делать не стоит. + +В реальных приложениях стоит воспользоваться одним из вышеупомянутых способов. + +Для примера нам нужен наиболее простой способ, который позволит сосредоточиться на серверной части веб-сокетов и получить рабочий код: + +{* ../../docs_src/websockets/tutorial001.py hl[2,6:38,41:43] *} + +## Создание `websocket` + +Создайте `websocket` в своем **FastAPI** приложении: + +{* ../../docs_src/websockets/tutorial001.py hl[1,46:47] *} + +/// note | Технические детали + +Вы также можете использовать `from starlette.websockets import WebSocket`. + +**FastAPI** напрямую предоставляет тот же самый `WebSocket` просто для удобства. На самом деле это `WebSocket` из Starlette. + +/// + +## Ожидание и отправка сообщений + +Через эндпоинт веб-сокета вы можете получать и отправлять сообщения. + +{* ../../docs_src/websockets/tutorial001.py hl[48:52] *} + +Вы можете получать и отправлять двоичные, текстовые и JSON данные. + +## Проверка в действии + +Если ваш файл называется `main.py`, то запустите приложение командой: + +
+ +```console +$ fastapi dev main.py + +INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) +``` + +
+ +Откройте браузер по адресу http://127.0.0.1:8000. + +Вы увидите следующую простенькую страницу: + + + +Вы можете набирать сообщения в поле ввода и отправлять их: + + + +И ваше **FastAPI** приложение с веб-сокетами ответит: + + + +Вы можете отправлять и получать множество сообщений: + + + +И все они будут использовать одно и то же веб-сокет соединение. + +## Использование `Depends` и не только + +Вы можете импортировать из `fastapi` и использовать в эндпоинте вебсокета: + +* `Depends` +* `Security` +* `Cookie` +* `Header` +* `Path` +* `Query` + +Они работают так же, как и в других FastAPI эндпоинтах/*операциях пути*: + +{* ../../docs_src/websockets/tutorial002_an_py310.py hl[68:69,82] *} + +/// info | Примечание + +В веб-сокете вызывать `HTTPException` не имеет смысла. Вместо этого нужно использовать `WebSocketException`. + +Закрывающий статус код можно использовать из valid codes defined in the specification. + +/// + +### Веб-сокеты с зависимостями: проверка в действии + +Если ваш файл называется `main.py`, то запустите приложение командой: + +
+ +```console +$ fastapi dev main.py + +INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) +``` + +
+ +Откройте браузер по адресу http://127.0.0.1:8000. + +Там вы можете задать: + +* "Item ID", используемый в пути. +* "Token", используемый как query-параметр. + +/// tip | Подсказка + +Обратите внимание, что query-параметр `token` будет обработан в зависимости. + +/// + +Теперь вы можете подключиться к веб-сокету и начинать отправку и получение сообщений: + + + +## Обработка отключений и работа с несколькими клиентами + +Если веб-сокет соединение закрыто, то `await websocket.receive_text()` вызовет исключение `WebSocketDisconnect`, которое можно поймать и обработать как в этом примере: + +{* ../../docs_src/websockets/tutorial003_py39.py hl[79:81] *} + +Чтобы воспроизвести пример: + +* Откройте приложение в нескольких вкладках браузера. +* Отправьте из них сообщения. +* Затем закройте одну из вкладок. + +Это вызовет исключение `WebSocketDisconnect`, и все остальные клиенты получат следующее сообщение: + +``` +Client #1596980209979 left the chat +``` + +/// tip | Примечание + +Приложение выше - это всего лишь простой минимальный пример, демонстрирующий обработку и передачу сообщений нескольким веб-сокет соединениям. + +Но имейте в виду, что это будет работать только в одном процессе и только пока он активен, так как всё обрабатывается в простом списке в оперативной памяти. + +Если нужно что-то легко интегрируемое с FastAPI, но более надежное и с поддержкой Redis, PostgreSQL или другого, то можно воспользоваться encode/broadcaster. + +/// + +## Дополнительная информация + +Для более глубокого изучения темы воспользуйтесь документацией Starlette: + +* The `WebSocket` class. +* Class-based WebSocket handling. From 0310af3557ef1fba1d2487f929c5080180642e4b Mon Sep 17 00:00:00 2001 From: github-actions Date: Mon, 3 Feb 2025 13:34:01 +0000 Subject: [PATCH 002/157] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [skip ci] --- docs/en/docs/release-notes.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index 12d6487aa..a9d86d854 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -7,6 +7,10 @@ hide: ## Latest Changes +### Translations + +* 🌐 Add Russian translation for `docs/ru/docs/advanced/websockets.md`. PR [#13279](https://github.com/fastapi/fastapi/pull/13279) by [@Rishat-F](https://github.com/Rishat-F). + ### Internal * 🔧 Update sponsors, add Permit. PR [#13288](https://github.com/fastapi/fastapi/pull/13288) by [@tiangolo](https://github.com/tiangolo). From c73e895b8639b6d69e3ff1849825c03b9a8e7b60 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Feb 2025 13:51:01 +0000 Subject: [PATCH 003/157] =?UTF-8?q?=E2=AC=86=20Bump=20inline-snapshot=20fr?= =?UTF-8?q?om=200.18.1=20to=200.19.3=20(#13298)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [inline-snapshot](https://github.com/15r10nk/inline-snapshot) from 0.18.1 to 0.19.3. - [Release notes](https://github.com/15r10nk/inline-snapshot/releases) - [Changelog](https://github.com/15r10nk/inline-snapshot/blob/main/CHANGELOG.md) - [Commits](https://github.com/15r10nk/inline-snapshot/compare/0.18.1...0.19.3) --- updated-dependencies: - dependency-name: inline-snapshot dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements-tests.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-tests.txt b/requirements-tests.txt index 91e7fb7aa..4a15844e4 100644 --- a/requirements-tests.txt +++ b/requirements-tests.txt @@ -10,7 +10,7 @@ anyio[trio] >=3.2.1,<5.0.0 PyJWT==2.8.0 pyyaml >=5.3.1,<7.0.0 passlib[bcrypt] >=1.7.2,<2.0.0 -inline-snapshot==0.18.1 +inline-snapshot==0.19.3 # types types-ujson ==5.10.0.20240515 types-orjson ==3.6.2 From ae724b05ceef1a7d62a8aa549fc9dc34c9bd3ad9 Mon Sep 17 00:00:00 2001 From: github-actions Date: Mon, 3 Feb 2025 13:51:28 +0000 Subject: [PATCH 004/157] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [skip ci] --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index a9d86d854..d3e652d19 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -13,6 +13,7 @@ hide: ### Internal +* ⬆ Bump inline-snapshot from 0.18.1 to 0.19.3. PR [#13298](https://github.com/fastapi/fastapi/pull/13298) by [@dependabot[bot]](https://github.com/apps/dependabot). * 🔧 Update sponsors, add Permit. PR [#13288](https://github.com/fastapi/fastapi/pull/13288) by [@tiangolo](https://github.com/tiangolo). ## 0.115.8 From fb19d9895d1b4ca2baf1695204c0f9753ed737fe Mon Sep 17 00:00:00 2001 From: Rafael de Oliveira Marques Date: Fri, 7 Feb 2025 19:01:55 -0300 Subject: [PATCH 005/157] =?UTF-8?q?=F0=9F=8C=90=20Update=20Portuguese=20Tr?= =?UTF-8?q?anslation=20for=20`docs/pt/docs/index.md`=20(#13328)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/pt/docs/index.md | 139 +++++++++++++++++++++++++++--------------- 1 file changed, 89 insertions(+), 50 deletions(-) diff --git a/docs/pt/docs/index.md b/docs/pt/docs/index.md index bc23114dc..138048f06 100644 --- a/docs/pt/docs/index.md +++ b/docs/pt/docs/index.md @@ -11,15 +11,18 @@ Framework FastAPI, alta performance, fácil de aprender, fácil de codar, pronto para produção

- - Test + + Test - - Coverage + + Coverage Package version + + Supported Python versions +

--- @@ -60,7 +63,7 @@ Os recursos chave são: -Outros patrocinadores +Outros patrocinadores ## Opiniões @@ -70,6 +73,18 @@ Os recursos chave são: --- +"_Nós adotamos a biblioteca **FastAPI** para iniciar um servidor **REST** que pode ser consultado para obter **previsões**. [para o Ludwig]_" + +
Piero Molino, Yaroslav Dudin, e Sai Sumanth Miryala - Uber (ref)
+ +--- + +"_A **Netflix** tem o prazer de anunciar o lançamento open-source do nosso framework de orquestração de **gerenciamento de crises**: **Dispatch**! [criado com **FastAPI**]_" + +
Kevin Glisson, Marc Vilanova, Forest Monsen - Netflix (ref)
+ +--- + "*Estou extremamente entusiasmado com o **FastAPI**. É tão divertido!*"
Brian Okken - Python Bytes podcaster (ref)
@@ -90,9 +105,9 @@ Os recursos chave são: --- -"*Nós adotamos a biblioteca **FastAPI** para criar um servidor **REST** que possa ser chamado para obter **predições**. [para o Ludwig]*" +"_Se alguém estiver procurando construir uma API Python para produção, eu recomendaria fortemente o **FastAPI**. Ele é **lindamente projetado**, **simples de usar** e **altamente escalável**. Ele se tornou um **componente chave** para a nossa estratégia API first de desenvolvimento e está impulsionando diversas automações e serviços, como o nosso Virtual TAC Engineer._" -
Piero Molino, Yaroslav Dudin e Sai Sumanth Miryala - Uber (ref)
+
Deon Pillsbury - Cisco (ref)
--- @@ -113,28 +128,20 @@ FastAPI está nos ombros de gigantes: ## Instalação -
- -```console -$ pip install fastapi - ----> 100% -``` - -
- -Você também precisará de um servidor ASGI para produção, tal como Uvicorn ou Hypercorn. +Crie e ative um ambiente virtual, e então instale o FastAPI:
```console -$ pip install "uvicorn[standard]" +$ pip install "fastapi[standard]" ---> 100% ```
+**Nota**: Certifique-se de que você colocou `"fastapi[standard]"` com aspas, para garantir que funcione em todos os terminais. + ## Exemplo ### Crie @@ -184,7 +191,7 @@ async def read_item(item_id: int, q: Union[str, None] = None): **Nota**: -Se você não sabe, verifique a seção _"In a hurry?"_ sobre `async` e `await` nas docs. +Se você não sabe, verifique a seção _"Com pressa?"_ sobre `async` e `await` nas docs. @@ -195,11 +202,24 @@ Rode o servidor com:
```console -$ uvicorn main:app --reload - +$ fastapi dev main.py + + ╭────────── FastAPI CLI - Development mode ───────────╮ + │ │ + │ Serving at: http://127.0.0.1:8000 │ + │ │ + │ API docs: http://127.0.0.1:8000/docs │ + │ │ + │ Running in development mode, for production use: │ + │ │ + │ fastapi run │ + │ │ + ╰─────────────────────────────────────────────────────╯ + +INFO: Will watch for changes in these directories: ['/home/user/code/awesomeapp'] INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) -INFO: Started reloader process [28720] -INFO: Started server process [28722] +INFO: Started reloader process [2248755] using WatchFiles +INFO: Started server process [2248757] INFO: Waiting for application startup. INFO: Application startup complete. ``` @@ -207,13 +227,13 @@ INFO: Application startup complete.
-Sobre o comando uvicorn main:app --reload... +Sobre o comando fastapi dev main.py... -O comando `uvicorn main:app` se refere a: +O comando `fastapi dev` lê o seu arquivo `main.py`, identifica o aplicativo **FastAPI** nele, e inicia um servidor usando o Uvicorn. -* `main`: o arquivo `main.py` (o "módulo" Python). -* `app`: o objeto criado dentro de `main.py` com a linha `app = FastAPI()`. -* `--reload`: faz o servidor recarregar após mudanças de código. Somente faça isso para desenvolvimento. +Por padrão, o `fastapi dev` iniciará com *auto-reload* habilitado para desenvolvimento local. + +Você pode ler mais sobre isso na documentação do FastAPI CLI.
@@ -268,7 +288,7 @@ app = FastAPI() class Item(BaseModel): name: str price: float - is_offer: Union[bool] = None + is_offer: Union[bool, None] = None @app.get("/") @@ -286,7 +306,7 @@ def update_item(item_id: int, item: Item): return {"item_name": item.name, "item_id": item_id} ``` -O servidor deverá recarregar automaticamente (porquê você adicionou `--reload` ao comando `uvicorn` acima). +O servidor `fastapi dev` deverá recarregar automaticamente. ### Evoluindo a Documentação Interativa da API @@ -316,7 +336,7 @@ E agora, vá para Tutorial - Guia do Usuário. +Para um exemplo mais completo incluindo mais recursos, veja Tutorial - Guia do Usuário. **Alerta de Spoiler**: o tutorial - guia do usuário inclui: @@ -416,9 +436,9 @@ Para um exemplo mais completo incluindo mais recursos, veja Injeção de Dependência**. * Segurança e autenticação, incluindo suporte para **OAuth2** com autenticação **JWT tokens** e **HTTP Basic**. * Técnicas mais avançadas (mas igualmente fáceis) para declaração de **modelos JSON profundamente aninhados** (graças ao Pydantic). +* Integrações **GraphQL** com o Strawberry e outras bibliotecas. * Muitos recursos extras (graças ao Starlette) como: * **WebSockets** - * **GraphQL** * testes extrememamente fáceis baseados em HTTPX e `pytest` * **CORS** * **Cookie Sessions** @@ -428,30 +448,49 @@ Para um exemplo mais completo incluindo mais recursos, veja um dos _frameworks_ Python mais rápidos disponíveis, somente atrás de Starlette e Uvicorn (utilizados internamente pelo FastAPI). (*) -Para entender mais sobre performance, veja a seção Benchmarks. +Para entender mais sobre performance, veja a seção Comparações. + +## Dependências + +O FastAPI depende do Pydantic e do Starlette. + -## Dependências opcionais +### Dependências `standard` -Usados por Pydantic: +Quando você instala o FastAPI com `pip install "fastapi[standard]"`, ele vêm com o grupo `standard` (padrão) de dependências opcionais: + +Utilizado pelo Pydantic: * email-validator - para validação de email. -Usados por Starlette: +Utilizado pelo Starlette: + +* httpx - Obrigatório caso você queira utilizar o `TestClient`. +* jinja2 - Obrigatório se você quer utilizar a configuração padrão de templates. +* python-multipart - Obrigatório se você deseja suporte a "parsing" de formulário, com `request.form()`. + +Utilizado pelo FastAPI / Starlette: + +* uvicorn - para o servidor que carrega e serve a sua aplicação. Isto inclui `uvicorn[standard]`, que inclui algumas dependências (e.g. `uvloop`) necessárias para servir em alta performance. +* `fastapi-cli` - que disponibiliza o comando `fastapi`. + +### Sem as dependências `standard` + +Se você não deseja incluir as dependências opcionais `standard`, você pode instalar utilizando `pip install fastapi` ao invés de `pip install "fastapi[standard]"`. + +### Dpendências opcionais adicionais + +Existem algumas dependências adicionais que você pode querer instalar. -* httpx - Necessário se você quiser utilizar o `TestClient`. -* jinja2 - Necessário se você quiser utilizar a configuração padrão de templates. -* python-multipart - Necessário se você quiser suporte com "parsing" de formulário, com `request.form()`. -* itsdangerous - Necessário para suporte a `SessionMiddleware`. -* pyyaml - Necessário para suporte a `SchemaGenerator` da Starlette (você provavelmente não precisará disso com o FastAPI). -* graphene - Necessário para suporte a `GraphQLApp`. +Dependências opcionais adicionais do Pydantic: -Usados por FastAPI / Starlette: +* pydantic-settings - para gerenciamento de configurações. +* pydantic-extra-types - tipos extras para serem utilizados com o Pydantic. -* uvicorn - para o servidor que carrega e serve sua aplicação. -* orjson - Necessário se você quer utilizar `ORJSONResponse`. -* ujson - Necessário se você quer utilizar `UJSONResponse`. +Dependências opcionais adicionais do FastAPI: -Você pode instalar todas essas dependências com `pip install fastapi[all]`. +* orjson - Obrigatório se você deseja utilizar o `ORJSONResponse`. +* ujson - Obrigatório se você deseja utilizar o `UJSONResponse`. ## Licença From 3958e5a113fbe9b80e24b46b6d91334ec842beb7 Mon Sep 17 00:00:00 2001 From: github-actions Date: Fri, 7 Feb 2025 22:02:19 +0000 Subject: [PATCH 006/157] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [skip ci] --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index d3e652d19..07d620043 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -9,6 +9,7 @@ hide: ### Translations +* 🌐 Update Portuguese Translation for `docs/pt/docs/index.md`. PR [#13328](https://github.com/fastapi/fastapi/pull/13328) by [@ceb10n](https://github.com/ceb10n). * 🌐 Add Russian translation for `docs/ru/docs/advanced/websockets.md`. PR [#13279](https://github.com/fastapi/fastapi/pull/13279) by [@Rishat-F](https://github.com/Rishat-F). ### Internal From 52c1488a372f1c5808429aebc1fbbfd7ca2ba6be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Pedro?= Date: Fri, 7 Feb 2025 19:02:59 -0300 Subject: [PATCH 007/157] =?UTF-8?q?=F0=9F=8C=90=20Update=20Portuguese=20Tr?= =?UTF-8?q?anslation=20for=20`docs/pt/docs/deployment/https.md`=20(#13317)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/pt/docs/deployment/https.md | 194 +++++++++++++++++++++++++++---- 1 file changed, 171 insertions(+), 23 deletions(-) diff --git a/docs/pt/docs/deployment/https.md b/docs/pt/docs/deployment/https.md index 9a13977ec..9ad419934 100644 --- a/docs/pt/docs/deployment/https.md +++ b/docs/pt/docs/deployment/https.md @@ -14,38 +14,186 @@ Para aprender o básico de HTTPS de uma perspectiva do usuário, verifique SNI. - * Esta extensão SNI permite que um único servidor (com um único endereço IP) tenha vários certificados HTTPS e atenda a vários domínios / aplicativos HTTPS. - * Para que isso funcione, um único componente (programa) em execução no servidor, ouvindo no endereço IP público, deve ter todos os certificados HTTPS no servidor. -* Depois de obter uma conexão segura, o protocolo de comunicação ainda é HTTP. - * Os conteúdos são criptografados, embora sejam enviados com o protocolo HTTP. + * No entanto, existe uma **solução** para isso. +* Há uma **extensão** para o protocolo **TLS** (aquele que lida com a criptografia no nível TCP, antes do HTTP) chamado **SNI**. + * Esta extensão SNI permite que um único servidor (com um **único endereço IP**) tenha **vários certificados HTTPS** e atenda a **vários domínios / aplicativos HTTPS**. + * Para que isso funcione, um **único** componente (programa) em execução no servidor, ouvindo no **endereço IP público**, deve ter **todos os certificados HTTPS** no servidor. +* **Depois** de obter uma conexão segura, o protocolo de comunicação **ainda é HTTP**. + * Os conteúdos são **criptografados**, embora sejam enviados com o **protocolo HTTP**. -É uma prática comum ter um programa/servidor HTTP em execução no servidor (máquina, host, etc.) e gerenciar todas as partes HTTPS: enviando as solicitações HTTP descriptografadas para o aplicativo HTTP real em execução no mesmo servidor (a aplicação **FastAPI**, neste caso), pegue a resposta HTTP do aplicativo, criptografe-a usando o certificado apropriado e envie-a de volta ao cliente usando HTTPS. Este servidor é frequentemente chamado de TLS Termination Proxy. +É uma prática comum ter um **programa/servidor HTTP** em execução no servidor (máquina, host, etc.) e **gerenciar todas as partes HTTPS**: **recebendo as requisições encriptadas**, enviando as **solicitações HTTP descriptografadas** para o aplicativo HTTP real em execução no mesmo servidor (a aplicação **FastAPI**, neste caso), pegue a **resposta HTTP** do aplicativo, **criptografe-a** usando o **certificado HTTPS** apropriado e envie-a de volta ao cliente usando **HTTPS**. Este servidor é frequentemente chamado de **Proxy de Terminação TLS**. + +Algumas das opções que você pode usar como Proxy de Terminação TLS são: + +* Traefik (que também pode gerenciar a renovação de certificados) +* Caddy (que também pode gerenciar a renovação de certificados) +* Nginx +* HAProxy ## Let's Encrypt -Antes de Let's Encrypt, esses certificados HTTPS eram vendidos por terceiros confiáveis. +Antes de Let's Encrypt, esses **certificados HTTPS** eram vendidos por terceiros confiáveis. O processo de aquisição de um desses certificados costumava ser complicado, exigia bastante papelada e os certificados eram bastante caros. -Mas então Let's Encrypt foi criado. +Mas então o **Let's Encrypt** foi criado. -Ele é um projeto da Linux Foundation que fornece certificados HTTPS gratuitamente. De forma automatizada. Esses certificados usam toda a segurança criptográfica padrão e têm vida curta (cerca de 3 meses), então a segurança é realmente melhor por causa de sua vida útil reduzida. +Ele é um projeto da Linux Foundation que fornece **certificados HTTPS gratuitamente** . De forma automatizada. Esses certificados usam toda a segurança criptográfica padrão e têm vida curta (cerca de 3 meses), então a **segurança é, na verdade, melhor** por causa de sua vida útil reduzida. Os domínios são verificados com segurança e os certificados são gerados automaticamente. Isso também permite automatizar a renovação desses certificados. -A ideia é automatizar a aquisição e renovação desses certificados, para que você tenha HTTPS seguro, de graça e para sempre. +A ideia é automatizar a aquisição e renovação desses certificados, para que você tenha **HTTPS seguro, de graça e para sempre**. + +## HTTPS para Desenvolvedores + +Aqui está um exemplo de como uma API HTTPS poderia ser estruturada, passo a passo, com foco principal nas ideias relevantes para desenvolvedores. + +### Nome do domínio + +A etapa inicial provavelmente seria **adquirir** algum **nome de domínio**. Então, você iria configurá-lo em um servidor DNS (possivelmente no mesmo provedor em nuvem). + +Você provavelmente usaria um servidor em nuvem (máquina virtual) ou algo parecido, e ele teria fixed **Endereço IP público**. + +No(s) servidor(es) DNS, você configuraria um registro (`registro A`) para apontar **seu domínio** para o **endereço IP público do seu servidor**. + +Você provavelmente fará isso apenas uma vez, na primeira vez em que tudo estiver sendo configurado. + +/// tip | Dica + +Essa parte do Nome do Domínio se dá muito antes do HTTPS, mas como tudo depende do domínio e endereço IP público, vale a pena mencioná-la aqui. + +/// + +### DNS + +Agora vamos focar em todas as partes que realmente fazem parte do HTTPS. + +Primeiro, o navegador iria verificar com os **servidores DNS** qual o **IP do domínio**, nesse caso, `someapp.example.com`. + +Os servidores DNS iriam informar o navegador para utilizar algum **endereço IP** específico. Esse seria o endereço IP público em uso no seu servidor, que você configurou nos servidores DNS. + + + +### Início do Handshake TLS + +O navegador então irá comunicar-se com esse endereço IP na **porta 443** (a porta HTTPS). + +A primeira parte dessa comunicação é apenas para estabelecer a conexão entre o cliente e o servidor e para decidir as chaves criptográficas a serem utilizadas, etc. + + + +Esse interação entre o cliente e o servidor para estabelecer uma conexão TLS é chamada de **Handshake TLS**. + +### TLS com a Extensão SNI + +**Apenas um processo** no servidor pode se conectar a uma **porta** em um **endereço IP**. Poderiam existir outros processos conectados em outras portas desse mesmo endereço IP, mas apenas um para cada combinação de endereço IP e porta. + +TLS (HTTPS) usa a porta `443` por padrão. Então essa é a porta que precisamos. + +Como apenas um único processo pode se comunicar com essa porta, o processo que faria isso seria o **Proxy de Terminação TLS**. + +O Proxy de Terminação TLS teria acesso a um ou mais **certificados TLS** (certificados HTTPS). + +Utilizando a **extensão SNI** discutida acima, o Proxy de Terminação TLS iria checar qual dos certificados TLS (HTTPS) disponíveis deve ser usado para essa conexão, utilizando o que corresponda ao domínio esperado pelo cliente. + +Nesse caso, ele usaria o certificado para `someapp.example.com`. + + + +O cliente já **confia** na entidade que gerou o certificado TLS (nesse caso, o Let's Encrypt, mas veremos sobre isso mais tarde), então ele pode **verificar** que o certificado é válido. + +Então, utilizando o certificado, o cliente e o Proxy de Terminação TLS **decidem como encriptar** o resto da **comunicação TCP**. Isso completa a parte do **Handshake TLS**. + +Após isso, o cliente e o servidor possuem uma **conexão TCP encriptada**, que é provida pelo TLS. E então eles podem usar essa conexão para começar a **comunicação HTTP** propriamente dita. + +E isso resume o que é **HTTPS**, apenas **HTTP** simples dentro de uma **conexão TLS segura** em vez de uma conexão TCP pura (não encriptada). + +/// tip | Dica + +Percebe que a encriptação da comunicação acontece no **nível do TCP**, não no nível do HTTP. + +/// + +### Solicitação HTTPS + +Agora que o cliente e servidor (especialmente o navegador e o Proxy de Terminação TLS) possuem uma **conexão TCP encriptada**, eles podem iniciar a **comunicação HTTP**. + +Então, o cliente envia uma **solicitação HTTPS**. Que é apenas uma solicitação HTTP sobre uma conexão TLS encriptada. + + + +### Desencriptando a Solicitação + +O Proxy de Terminação TLS então usaria a encriptação combinada para **desencriptar a solicitação**, e transmitiria a **solicitação básica (desencriptada)** para o processo executando a aplicação (por exemplo, um processo com Uvicorn executando a aplicação FastAPI). + + + +### Resposta HTTP + +A aplicação processaria a solicitação e retornaria uma **resposta HTTP básica (não encriptada)** para o Proxy de Terminação TLS. + + + +### Resposta HTTPS + +O Proxy de Terminação TLS iria **encriptar a resposta** utilizando a criptografia combinada anteriormente (que foi definida com o certificado para `someapp.example.com`), e devolveria para o navegador. + +No próximo passo, o navegador verifica que a resposta é válida e encriptada com a chave criptográfica correta, etc. E depois **desencripta a resposta** e a processa. + + + +O cliente (navegador) saberá que a resposta vem do servidor correto por que ela usa a criptografia que foi combinada entre eles usando o **certificado HTTPS** anterior. + +### Múltiplas Aplicações + +Podem existir **múltiplas aplicações** em execução no mesmo servidor (ou servidores), por exemplo: outras APIs ou um banco de dados. + +Apenas um processo pode estar vinculado a um IP e porta (o Proxy de Terminação TLS, por exemplo), mas outras aplicações/processos também podem estar em execução no(s) servidor(es), desde que não tentem usar a mesma **combinação de IP público e porta**. + + + +Dessa forma, o Proxy de Terminação TLS pode gerenciar o HTTPS e os certificados de **múltiplos domínios**, para múltiplas aplicações, e então transmitir as requisições para a aplicação correta em cada caso. + +### Renovação de Certificados + +Em algum momento futuro, cada certificado irá **expirar** (aproximadamente 3 meses após a aquisição). + +E então, haverá outro programa (em alguns casos pode ser o próprio Proxy de Terminação TLS) que irá interagir com o Let's Encrypt e renovar o(s) certificado(s). + + + +Os **certificados TLS** são **associados com um nome de domínio**, e não a um endereço IP. + +Então para renovar os certificados, o programa de renovação precisa **provar** para a autoridade (Let's Encrypt) que ele realmente **possui e controla esse domínio**> + +Para fazer isso, e acomodar as necessidades de diferentes aplicações, existem diferentes opções para esse programa. Algumas escolhas populares são: + +* **Modificar alguns registros DNS** + * Para isso, o programa de renovação precisa ter suporte as APIs do provedor DNS, então, dependendo do provedor DNS que você utilize, isso pode ou não ser uma opção viável. +* **Executar como um servidor** (ao menos durante o processo de aquisição do certificado) no endereço IP público associado com o domínio. + * Como dito anteriormente, apenas um processo pode estar ligado a uma porta e IP específicos. + * Essa é uma dos motivos que fazem utilizar o mesmo Proxy de Terminação TLS para gerenciar a renovação de certificados ser tão útil. + * Caso contrário, você pode ter que parar a execução do Proxy de Terminação TLS momentaneamente, inicializar o programa de renovação para renovar os certificados, e então reiniciar o Proxy de Terminação TLS. Isso não é o ideal, já que sua(s) aplicação(ões) não vão estar disponíveis enquanto o Proxy de Terminação TLS estiver desligado. + +Todo esse processo de renovação, enquanto o aplicativo ainda funciona, é uma das principais razões para preferir um **sistema separado para gerenciar HTTPS** com um Proxy de Terminação TLS em vez de usar os certificados TLS no servidor da aplicação diretamente (e.g. com o Uvicorn). + +## Recapitulando + +Possuir **HTTPS** habilitado na sua aplicação é bastante importante, e até **crítico** na maioria dos casos. A maior parte do esforço que você tem que colocar sobre o HTTPS como desenvolvedor está em **entender esses conceitos** e como eles funcionam. + +Mas uma vez que você saiba o básico de **HTTPS para desenvolvedores**, você pode combinar e configurar diferentes ferramentas facilmente para gerenciar tudo de uma forma simples. + +Em alguns dos próximos capítulos, eu mostrarei para você vários exemplos concretos de como configurar o **HTTPS** para aplicações **FastAPI**. 🔒 From f97c8de41a401ce04ef1c5e8f7891f01b54b0261 Mon Sep 17 00:00:00 2001 From: github-actions Date: Fri, 7 Feb 2025 22:04:25 +0000 Subject: [PATCH 008/157] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [skip ci] --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index 07d620043..6a8b0f5d1 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -9,6 +9,7 @@ hide: ### Translations +* 🌐 Update Portuguese Translation for `docs/pt/docs/deployment/https.md`. PR [#13317](https://github.com/fastapi/fastapi/pull/13317) by [@Joao-Pedro-P-Holanda](https://github.com/Joao-Pedro-P-Holanda). * 🌐 Update Portuguese Translation for `docs/pt/docs/index.md`. PR [#13328](https://github.com/fastapi/fastapi/pull/13328) by [@ceb10n](https://github.com/ceb10n). * 🌐 Add Russian translation for `docs/ru/docs/advanced/websockets.md`. PR [#13279](https://github.com/fastapi/fastapi/pull/13279) by [@Rishat-F](https://github.com/Rishat-F). From 0c24d0607b843027b827b556beef5ead7eb61695 Mon Sep 17 00:00:00 2001 From: Valentyn Date: Sat, 8 Feb 2025 00:06:37 +0200 Subject: [PATCH 009/157] =?UTF-8?q?=F0=9F=8C=90=20Add=20Ukrainian=20transl?= =?UTF-8?q?ation=20for=20`docs/uk/docs/learn/index.md`=20(#13306)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/uk/docs/learn/index.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 docs/uk/docs/learn/index.md diff --git a/docs/uk/docs/learn/index.md b/docs/uk/docs/learn/index.md new file mode 100644 index 000000000..7f9f21e57 --- /dev/null +++ b/docs/uk/docs/learn/index.md @@ -0,0 +1,5 @@ +# Навчання + +У цьому розділі надані вступні та навчальні матеріали для вивчення FastAPI. + +Це можна розглядати як **книгу**, **курс**, або **офіційний** та рекомендований спосіб освоїти FastAPI. 😎 From eee8d4c58a6620f240564655d11b355d3945f802 Mon Sep 17 00:00:00 2001 From: github-actions Date: Fri, 7 Feb 2025 22:07:12 +0000 Subject: [PATCH 010/157] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [skip ci] --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index 6a8b0f5d1..b6145c9bb 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -9,6 +9,7 @@ hide: ### Translations +* 🌐 Add Ukrainian translation for `docs/uk/docs/learn/index.md`. PR [#13306](https://github.com/fastapi/fastapi/pull/13306) by [@valentinDruzhinin](https://github.com/valentinDruzhinin). * 🌐 Update Portuguese Translation for `docs/pt/docs/deployment/https.md`. PR [#13317](https://github.com/fastapi/fastapi/pull/13317) by [@Joao-Pedro-P-Holanda](https://github.com/Joao-Pedro-P-Holanda). * 🌐 Update Portuguese Translation for `docs/pt/docs/index.md`. PR [#13328](https://github.com/fastapi/fastapi/pull/13328) by [@ceb10n](https://github.com/ceb10n). * 🌐 Add Russian translation for `docs/ru/docs/advanced/websockets.md`. PR [#13279](https://github.com/fastapi/fastapi/pull/13279) by [@Rishat-F](https://github.com/Rishat-F). From 701f5791d373ec82f3660b9d94b97574d4fbddbe Mon Sep 17 00:00:00 2001 From: Valentyn Date: Sat, 8 Feb 2025 00:08:49 +0200 Subject: [PATCH 011/157] =?UTF-8?q?=F0=9F=8C=90=20Add=20Ukrainian=20transl?= =?UTF-8?q?ation=20for=20`docs/uk/docs/features.md`=20(#13308)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/uk/docs/features.md | 189 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 189 insertions(+) create mode 100644 docs/uk/docs/features.md diff --git a/docs/uk/docs/features.md b/docs/uk/docs/features.md new file mode 100644 index 000000000..7d679d8ee --- /dev/null +++ b/docs/uk/docs/features.md @@ -0,0 +1,189 @@ +# Функціональні можливості + +## Функціональні можливості FastAPI + +**FastAPI** надає вам такі можливості: + +### Використання відкритих стандартів + +* OpenAPI для створення API, включаючи оголошення шляхів, операцій, параметрів, тіл запитів, безпеки тощо. +* Автоматична документація моделей даних за допомогою JSON Schema (оскільки OpenAPI базується саме на JSON Schema). +* Розроблено на основі цих стандартів після ретельного аналізу, а не як додатковий рівень поверх основної архітектури. +* Це також дає змогу автоматично **генерувати код клієнта** багатьма мовами. + +### Автоматична генерація документації + +Інтерактивна документація API та вебінтерфейс для його дослідження. Оскільки фреймворк базується на OpenAPI, є кілька варіантів, два з яких включені за замовчуванням. + +* Swagger UI — дозволяє інтерактивно переглядати API, викликати та тестувати його прямо у браузері. + +![Swagger UI interaction](https://fastapi.tiangolo.com/img/index/index-03-swagger-02.png) + +* Альтернативна документація API за допомогою ReDoc. + +![ReDoc](https://fastapi.tiangolo.com/img/index/index-06-redoc-02.png) + +### Тільки сучасний Python + +FastAPI використовує стандартні **типи Python** (завдяки Pydantic). Вам не потрібно вивчати новий синтаксис — лише стандартний сучасний Python. + +Якщо вам потрібне коротке нагадування про використання типів у Python (навіть якщо ви не використовуєте FastAPI), перегляньте короткий підручник: [Вступ до типів Python](python-types.md){.internal-link target=_blank}. + +Ось приклад стандартного Python-коду з типами: + +```Python +from datetime import date +from pydantic import BaseModel + +# Оголошення змінної як str +# з підтримкою автодоповнення у редакторі +def main(user_id: str): + return user_id + +# Модель Pydantic +class User(BaseModel): + id: int + name: str + joined: date +``` + +Приклад використання цієї моделі: + +```Python +my_user: User = User(id=3, name="John Doe", joined="2018-07-19") + +second_user_data = { + "id": 4, + "name": "Mary", + "joined": "2018-11-30", +} + +my_second_user: User = User(**second_user_data) +``` + +/// info | Інформація + +`**second_user_data` означає: + +Передати ключі та значення словника `second_user_data` як аргументи у вигляді "ключ-значення", еквівалентно `User(id=4, name="Mary", joined="2018-11-30")`. + +/// + +### Підтримка редакторів (IDE) + +Фреймворк спроєктований так, щоб бути легким і інтуїтивно зрозумілим. Усі рішення тестувалися у різних редакторах ще до початку розробки, щоб забезпечити найкращий досвід програмування. + +За результатами опитувань розробників Python однією з найпопулярніших функцій є "автодоповнення". + +**FastAPI** повністю підтримує автодоповнення у всіх місцях, тому вам рідко доведеться повертатися до документації. + +Приклад автодоповнення у редакторах: + +* у Visual Studio Code: + +![editor support](https://fastapi.tiangolo.com/img/vscode-completion.png) + +* у PyCharm: + +![editor support](https://fastapi.tiangolo.com/img/pycharm-completion.png) + +### Короткий код +FastAPI має розумні налаштування **за замовчуванням**, але всі параметри можна налаштовувати відповідно до ваших потреб. Однак за замовчуванням все "просто працює". + +### Валідація +* Підтримка валідації для більшості (або всіх?) **типів даних Python**, зокрема: + * JSON-об'єктів (`dict`). + * JSON-списків (`list`) з визначенням типів елементів. + * Рядків (`str`) із мінімальною та максимальною довжиною. + * Чисел (`int`, `float`) з обмеженнями мінімальних та максимальних значень тощо. + +* Валідація складніших типів, таких як: + * URL. + * Email. + * UUID. + * ...та інші. + +Уся валідація виконується через надійний та перевірений **Pydantic**. + +### Безпека та автентифікація + +**FastAPI** підтримує вбудовану автентифікацію та авторизацію, без прив’язки до конкретних баз даних чи моделей даних. + +Підтримуються всі схеми безпеки OpenAPI, включаючи: + +* HTTP Basic. +* **OAuth2** (також із підтримкою **JWT-токенів**). Див. підручник: [OAuth2 із JWT](tutorial/security/oauth2-jwt.md){.internal-link target=_blank}. +* Ключі API в: + * Заголовках. + * Параметрах запиту. + * Cookies тощо. + +А також усі можливості безпеки від Starlette (зокрема **сесійні cookies**). + +Усі вони створені як багаторазові інструменти та компоненти, які легко інтегруються з вашими системами, сховищами даних, реляційними та NoSQL базами даних тощо. + +### Впровадження залежностей + +**FastAPI** містить надзвичайно просту у використанні, але потужну систему впровадження залежностей. + +* Залежності можуть мати власні залежності, утворюючи ієрархію або **"граф залежностей"**. +* Усі залежності автоматично керуються фреймворком. +* Усі залежності можуть отримувати дані з запитів і розширювати **обмеження операції за шляхом** та автоматичну документацію. +* **Автоматична валідація** навіть для параметрів *операцій шляху*, визначених у залежностях. +* Підтримка складних систем автентифікації користувачів, **з'єднань із базами даних** тощо. +* **Жодних обмежень** щодо використання баз даних, фронтендів тощо, але водночас проста інтеграція з усіма ними. + +### Немає обмежень на "плагіни" + +Або іншими словами, вони не потрібні – просто імпортуйте та використовуйте необхідний код. + +Будь-яка інтеграція спроєктована настільки просто (з використанням залежностей), що ви можете створити "плагін" для свого застосунку всього у 2 рядках коду, використовуючи ту саму структуру та синтаксис, що й для ваших *операцій шляху*. + +### Протестовано + +* 100% покриття тестами. +* 100% анотована типами кодова база. +* Використовується у робочих середовищах. + +## Можливості Starlette + +**FastAPI** повністю сумісний із (та побудований на основі) Starlette. Тому будь-який додатковий код Starlette, який ви маєте, також працюватиме. + +**FastAPI** фактично є підкласом **Starlette**. Тому, якщо ви вже знайомі зі Starlette або використовуєте його, більшість функціональності працюватиме так само. + +З **FastAPI** ви отримуєте всі можливості **Starlette** (адже FastAPI — це, по суті, Starlette на стероїдах): + +* Разюча продуктивність. Це один із найшвидших фреймворків на Python, на рівні з **NodeJS** і **Go**. +* Підтримка **WebSocket**. +* Фонові задачі у процесі. +* Події запуску та завершення роботи. +* Клієнт для тестування, побудований на HTTPX. +* Підтримка **CORS**, **GZip**, статичних файлів, потокових відповідей. +* Підтримка **сесій** і **cookie**. +* 100% покриття тестами. +* 100% анотована типами кодова база. + +## Можливості Pydantic + +**FastAPI** повністю сумісний із (та побудований на основі) Pydantic. Тому будь-який додатковий код Pydantic, який ви маєте, також працюватиме. + +Включаючи зовнішні бібліотеки, побудовані також на Pydantic, такі як ORM, ODM для баз даних. + +Це також означає, що в багатьох випадках ви можете передати той самий об'єкт, який отримуєте з запиту, **безпосередньо в базу даних**, оскільки все автоматично перевіряється. + +Те ж саме відбувається й у зворотному напрямку — у багатьох випадках ви можете просто передати об'єкт, який отримуєте з бази даних, **безпосередньо клієнту**. + +З **FastAPI** ви отримуєте всі можливості **Pydantic** (адже FastAPI базується на Pydantic для обробки всіх даних): + +* **Ніякої плутанини** : + * Не потрібно вчити нову мову для визначення схем. + * Якщо ви знаєте типи Python, ви знаєте, як використовувати Pydantic. +* Легко працює з вашим **IDE/лінтером/мозком**: + * Оскільки структури даних Pydantic є просто екземплярами класів, які ви визначаєте; автодоповнення, лінтинг, mypy і ваша інтуїція повинні добре працювати з вашими перевіреними даними. +* Валідація **складних структур**: + * Використання ієрархічних моделей Pydantic. Python `typing`, `List` і `Dict` тощо. + * Валідатори дозволяють чітко і просто визначати, перевіряти й документувати складні схеми даних у вигляді JSON-схеми. + * Ви можете мати глибоко **вкладені JSON об'єкти** та перевірити та анотувати їх всі. +* **Розширюваність**: + * Pydantic дозволяє визначати користувацькі типи даних або розширювати валідацію методами в моделі декоратором `validator`. +* 100% покриття тестами. From 2d7d5dafb0ad59c0a19f996a85ec1210f80a141e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ph=C6=B0=C6=A1ng=20T=E1=BA=A5n=20Th=C3=A0nh?= <51350651+ptt3199@users.noreply.github.com> Date: Sat, 8 Feb 2025 05:09:16 +0700 Subject: [PATCH 012/157] =?UTF-8?q?=F0=9F=8C=90=20Add=20Vietnamese=20trans?= =?UTF-8?q?lation=20for=20`docs/vi/docs/fastapi-cli.md`=20(#13294)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/vi/docs/fastapi-cli.md | 75 +++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 docs/vi/docs/fastapi-cli.md diff --git a/docs/vi/docs/fastapi-cli.md b/docs/vi/docs/fastapi-cli.md new file mode 100644 index 000000000..d9e315ae4 --- /dev/null +++ b/docs/vi/docs/fastapi-cli.md @@ -0,0 +1,75 @@ +# FastAPI CLI + +**FastAPI CLI** là một chương trình dòng lệnh có thể được sử dụng để phục vụ ứng dụng FastAPI của bạn, quản lý dự án FastAPI của bạn và nhiều hoạt động khác. + +Khi bạn cài đặt FastAPI (vd với `pip install "fastapi[standard]"`), nó sẽ bao gồm một gói được gọi là `fastapi-cli`, gói này cung cấp lệnh `fastapi` trong terminal. + +Để chạy ứng dụng FastAPI của bạn cho quá trình phát triển (development), bạn có thể sử dụng lệnh `fastapi dev`: + +
+ +```console +$ fastapi dev main.py + + FastAPI Starting development server 🚀 + + Searching for package file structure from directories with + __init__.py files + Importing from /home/user/code/awesomeapp + + module 🐍 main.py + + code Importing the FastAPI app object from the module with the + following code: + + from main import app + + app Using import string: main:app + + server Server started at http://127.0.0.1:8000 + server Documentation at http://127.0.0.1:8000/docs + + tip Running in development mode, for production use: + fastapi run + + Logs: + + INFO Will watch for changes in these directories: + ['/home/user/code/awesomeapp'] + INFO Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to + quit) + INFO Started reloader process [383138] using WatchFiles + INFO Started server process [383153] + INFO Waiting for application startup. + INFO Application startup complete. +``` + +
+ +Chương trình dòng lệnh `fastapi` là **FastAPI CLI**. + +FastAPI CLI nhận đường dẫn đến chương trình Python của bạn (vd `main.py`) và tự động phát hiện đối tượng `FastAPI` (thường được gọi là `app`), xác định quá trình nhập đúng, và sau đó chạy nó (serve). + +Đối với vận hành thực tế (production), bạn sẽ sử dụng `fastapi run` thay thế. 🚀 + +Ở bên trong, **FastAPI CLI** sử dụng Uvicorn, một server ASGI có hiệu suất cao, sẵn sàng cho vận hành thực tế (production). 😎 + +## `fastapi dev` + +Chạy `fastapi dev` sẽ khởi động quá trình phát triển. + +Mặc định, **auto-reload** được bật, tự động tải lại server khi bạn thay đổi code của bạn. Điều này tốn nhiều tài nguyên và có thể kém ổn định hơn khi nó bị tắt. Bạn nên sử dụng nó cho quá trình phát triển. Nó cũng lắng nghe địa chỉ IP `127.0.0.1`, đó là địa chỉ IP của máy tính để tự giao tiếp với chính nó (`localhost`). + +## `fastapi run` + +Chạy `fastapi run` mặc định sẽ khởi động FastAPI cho quá trình vận hành thực tế. + +Mặc định, **auto-reload** bị tắt. Nó cũng lắng nghe địa chỉ IP `0.0.0.0`, đó là tất cả các địa chỉ IP có sẵn, như vậy nó sẽ được truy cập công khai bởi bất kỳ ai có thể giao tiếp với máy tính. Đây là cách bạn thường chạy nó trong sản phẩm hoàn thiện, ví dụ trong một container. + +Trong hầu hết các trường hợp, bạn sẽ (và nên) có một "proxy điểm cuối (termination proxy)" xử lý HTTPS cho bạn, điều này sẽ phụ thuộc vào cách bạn triển khai ứng dụng của bạn, nhà cung cấp có thể làm điều này cho bạn, hoặc bạn có thể cần thiết lập nó. + +/// tip + +Bạn có thể tìm hiểu thêm về FastAPI CLI trong [tài liệu triển khai](deployment/index.md){.internal-link target=_blank}. + +/// From 2bb94fb90bccd4a0545b477b67f6076ba2e2945c Mon Sep 17 00:00:00 2001 From: github-actions Date: Fri, 7 Feb 2025 22:09:51 +0000 Subject: [PATCH 013/157] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [skip ci] --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index b6145c9bb..75a024a0b 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -9,6 +9,7 @@ hide: ### Translations +* 🌐 Add Ukrainian translation for `docs/uk/docs/features.md`. PR [#13308](https://github.com/fastapi/fastapi/pull/13308) by [@valentinDruzhinin](https://github.com/valentinDruzhinin). * 🌐 Add Ukrainian translation for `docs/uk/docs/learn/index.md`. PR [#13306](https://github.com/fastapi/fastapi/pull/13306) by [@valentinDruzhinin](https://github.com/valentinDruzhinin). * 🌐 Update Portuguese Translation for `docs/pt/docs/deployment/https.md`. PR [#13317](https://github.com/fastapi/fastapi/pull/13317) by [@Joao-Pedro-P-Holanda](https://github.com/Joao-Pedro-P-Holanda). * 🌐 Update Portuguese Translation for `docs/pt/docs/index.md`. PR [#13328](https://github.com/fastapi/fastapi/pull/13328) by [@ceb10n](https://github.com/ceb10n). From 50b307c9f68ad9a966e86d7f62c82d476abd0cc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Fri, 7 Feb 2025 22:10:25 +0000 Subject: [PATCH 014/157] =?UTF-8?q?=F0=9F=91=A5=20Update=20FastAPI=20Peopl?= =?UTF-8?q?e=20-=20Contributors=20and=20Translators=20(#13293)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: github-actions Co-authored-by: Alejandra <90076947+alejsdev@users.noreply.github.com> --- docs/en/data/contributors.yml | 41 +++++--- docs/en/data/translation_reviewers.yml | 125 +++++++++++++++---------- docs/en/data/translators.yml | 45 +++++---- 3 files changed, 128 insertions(+), 83 deletions(-) diff --git a/docs/en/data/contributors.yml b/docs/en/data/contributors.yml index f679d7286..0e1a6505b 100644 --- a/docs/en/data/contributors.yml +++ b/docs/en/data/contributors.yml @@ -1,13 +1,18 @@ tiangolo: login: tiangolo - count: 697 + count: 713 avatarUrl: https://avatars.githubusercontent.com/u/1326112?u=cb5d06e73a9e1998141b1641aa88e443c6717651&v=4 url: https://github.com/tiangolo dependabot: login: dependabot - count: 89 + count: 90 avatarUrl: https://avatars.githubusercontent.com/in/29110?v=4 url: https://github.com/apps/dependabot +alejsdev: + login: alejsdev + count: 47 + avatarUrl: https://avatars.githubusercontent.com/u/90076947?u=356f39ff3f0211c720b06d3dbb060e98884085e3&v=4 + url: https://github.com/alejsdev github-actions: login: github-actions count: 26 @@ -15,7 +20,7 @@ github-actions: url: https://github.com/apps/github-actions Kludex: login: Kludex - count: 22 + count: 23 avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=df8a3f06ba8f55ae1967a3e2d5ed882903a4e330&v=4 url: https://github.com/Kludex pre-commit-ci: @@ -23,11 +28,6 @@ pre-commit-ci: count: 22 avatarUrl: https://avatars.githubusercontent.com/in/68672?v=4 url: https://github.com/apps/pre-commit-ci -alejsdev: - login: alejsdev - count: 21 - avatarUrl: https://avatars.githubusercontent.com/u/90076947?u=356f39ff3f0211c720b06d3dbb060e98884085e3&v=4 - url: https://github.com/alejsdev dmontagu: login: dmontagu count: 17 @@ -108,6 +108,11 @@ hitrust: count: 4 avatarUrl: https://avatars.githubusercontent.com/u/3360631?u=5fa1f475ad784d64eb9666bdd43cc4d285dcc773&v=4 url: https://github.com/hitrust +ShahriyarR: + login: ShahriyarR + count: 4 + avatarUrl: https://avatars.githubusercontent.com/u/3852029?u=c9a1691e5ebdc94cbf543086099a6ed705cdb873&v=4 + url: https://github.com/ShahriyarR adriangb: login: adriangb count: 4 @@ -208,11 +213,6 @@ graingert: count: 3 avatarUrl: https://avatars.githubusercontent.com/u/413772?u=64b77b6aa405c68a9c6bcf45f84257c66eea5f32&v=4 url: https://github.com/graingert -ShahriyarR: - login: ShahriyarR - count: 3 - avatarUrl: https://avatars.githubusercontent.com/u/3852029?u=c9a1691e5ebdc94cbf543086099a6ed705cdb873&v=4 - url: https://github.com/ShahriyarR jaystone776: login: jaystone776 count: 3 @@ -433,6 +433,11 @@ imba-tjd: count: 2 avatarUrl: https://avatars.githubusercontent.com/u/24759802?u=01e901a4fe004b4b126549d3ff1c4000fe3720b5&v=4 url: https://github.com/imba-tjd +johnthagen: + login: johnthagen + count: 2 + avatarUrl: https://avatars.githubusercontent.com/u/10340167?u=47147fc4e4db1f573bee3fe428deeacb3197bc5f&v=4 + url: https://github.com/johnthagen paxcodes: login: paxcodes count: 2 @@ -443,6 +448,11 @@ kaustubhgupta: count: 2 avatarUrl: https://avatars.githubusercontent.com/u/43691873?u=8dd738718ac7ffad4ef31e86b5d780a1141c695d&v=4 url: https://github.com/kaustubhgupta +kinuax: + login: kinuax + count: 2 + avatarUrl: https://avatars.githubusercontent.com/u/13321374?u=22dc9873d6d9f2c7e4fc44c6480c3505efb1531f&v=4 + url: https://github.com/kinuax wakabame: login: wakabame count: 2 @@ -503,3 +513,8 @@ AyushSinghal1794: count: 2 avatarUrl: https://avatars.githubusercontent.com/u/89984761?v=4 url: https://github.com/AyushSinghal1794 +DanielKusyDev: + login: DanielKusyDev + count: 2 + avatarUrl: https://avatars.githubusercontent.com/u/36250676?u=2ea6114ff751fc48b55f231987a0e2582c6b1bd2&v=4 + url: https://github.com/DanielKusyDev diff --git a/docs/en/data/translation_reviewers.yml b/docs/en/data/translation_reviewers.yml index 6cc09a7c1..6f16893ba 100644 --- a/docs/en/data/translation_reviewers.yml +++ b/docs/en/data/translation_reviewers.yml @@ -5,12 +5,12 @@ s111d: url: https://github.com/s111d Xewus: login: Xewus - count: 139 + count: 140 avatarUrl: https://avatars.githubusercontent.com/u/85196001?u=f8e2dc7e5104f109cef944af79050ea8d1b8f914&v=4 url: https://github.com/Xewus ceb10n: login: ceb10n - count: 108 + count: 110 avatarUrl: https://avatars.githubusercontent.com/u/235213?u=edcce471814a1eba9f0cdaa4cd0de18921a940a6&v=4 url: https://github.com/ceb10n tokusumi: @@ -33,21 +33,26 @@ AlertRED: count: 81 avatarUrl: https://avatars.githubusercontent.com/u/15695000?u=f5a4944c6df443030409c88da7d7fa0b7ead985c&v=4 url: https://github.com/AlertRED +nazarepiedady: + login: nazarepiedady + count: 81 + avatarUrl: https://avatars.githubusercontent.com/u/31008635?u=8dc25777dc9cb51fb0dbba2f137988953d330b78&v=4 + url: https://github.com/nazarepiedady sodaMelon: login: sodaMelon count: 81 avatarUrl: https://avatars.githubusercontent.com/u/66295123?u=be939db90f1119efee9e6110cc05066ff1f40f00&v=4 url: https://github.com/sodaMelon -nazarepiedady: - login: nazarepiedady - count: 78 - avatarUrl: https://avatars.githubusercontent.com/u/31008635?u=8dc25777dc9cb51fb0dbba2f137988953d330b78&v=4 - url: https://github.com/nazarepiedady Alexandrhub: login: Alexandrhub count: 68 avatarUrl: https://avatars.githubusercontent.com/u/119126536?u=9fc0d48f3307817bafecc5861eb2168401a6cb04&v=4 url: https://github.com/Alexandrhub +alv2017: + login: alv2017 + count: 64 + avatarUrl: https://avatars.githubusercontent.com/u/31544722?v=4 + url: https://github.com/alv2017 waynerv: login: waynerv count: 63 @@ -55,7 +60,7 @@ waynerv: url: https://github.com/waynerv cassiobotaro: login: cassiobotaro - count: 61 + count: 62 avatarUrl: https://avatars.githubusercontent.com/u/3127847?u=a08022b191ddbd0a6159b2981d9d878b6d5bb71f&v=4 url: https://github.com/cassiobotaro mattwang44: @@ -138,26 +143,21 @@ romashevchenko: count: 32 avatarUrl: https://avatars.githubusercontent.com/u/132477732?v=4 url: https://github.com/romashevchenko +alejsdev: + login: alejsdev + count: 32 + avatarUrl: https://avatars.githubusercontent.com/u/90076947?u=356f39ff3f0211c720b06d3dbb060e98884085e3&v=4 + url: https://github.com/alejsdev wdh99: login: wdh99 count: 31 avatarUrl: https://avatars.githubusercontent.com/u/108172295?u=8a8fb95d5afe3e0fa33257b2aecae88d436249eb&v=4 url: https://github.com/wdh99 -alv2017: - login: alv2017 - count: 31 - avatarUrl: https://avatars.githubusercontent.com/u/31544722?v=4 - url: https://github.com/alv2017 LorhanSohaky: login: LorhanSohaky count: 30 avatarUrl: https://avatars.githubusercontent.com/u/16273730?u=095b66f243a2cd6a0aadba9a095009f8aaf18393&v=4 url: https://github.com/LorhanSohaky -alejsdev: - login: alejsdev - count: 30 - avatarUrl: https://avatars.githubusercontent.com/u/90076947?u=356f39ff3f0211c720b06d3dbb060e98884085e3&v=4 - url: https://github.com/alejsdev black-redoc: login: black-redoc count: 29 @@ -246,7 +246,7 @@ axel584: wisderfin: login: wisderfin count: 23 - avatarUrl: https://avatars.githubusercontent.com/u/77553770?u=94478d3e1ef7d36d70479c5bd35d8de28b071c10&v=4 + avatarUrl: https://avatars.githubusercontent.com/u/77553770?u=f3b00a26736ba664e9927a1116c6e8088295e073&v=4 url: https://github.com/wisderfin rostik1410: login: rostik1410 @@ -353,6 +353,11 @@ mastizada: count: 16 avatarUrl: https://avatars.githubusercontent.com/u/1975818?u=0751a06d7271c8bf17cb73b1b845644ab4d2c6dc&v=4 url: https://github.com/mastizada +Joao-Pedro-P-Holanda: + login: Joao-Pedro-P-Holanda + count: 16 + avatarUrl: https://avatars.githubusercontent.com/u/110267046?u=331bd016326dac4cf3df4848f6db2dbbf8b5f978&v=4 + url: https://github.com/Joao-Pedro-P-Holanda JaeHyuckSa: login: JaeHyuckSa count: 16 @@ -363,11 +368,6 @@ Jedore: count: 15 avatarUrl: https://avatars.githubusercontent.com/u/17944025?u=81d503e1c800eb666b3861ca47a3a773bbc3f539&v=4 url: https://github.com/Jedore -Joao-Pedro-P-Holanda: - login: Joao-Pedro-P-Holanda - count: 15 - avatarUrl: https://avatars.githubusercontent.com/u/110267046?u=331bd016326dac4cf3df4848f6db2dbbf8b5f978&v=4 - url: https://github.com/Joao-Pedro-P-Holanda kim-sangah: login: kim-sangah count: 15 @@ -386,7 +386,7 @@ dukkee: mkdir700: login: mkdir700 count: 14 - avatarUrl: https://avatars.githubusercontent.com/u/56359329?u=0ba13427420e7f6e4c83947736de247326f2c292&v=4 + avatarUrl: https://avatars.githubusercontent.com/u/56359329?u=3d6ea8714f5000829b60dcf7b13a75b1e73aaf47&v=4 url: https://github.com/mkdir700 BORA040126: login: BORA040126 @@ -473,6 +473,11 @@ kwang1215: count: 12 avatarUrl: https://avatars.githubusercontent.com/u/74170199?u=2a63ff6692119dde3f5e5693365b9fcd6f977b08&v=4 url: https://github.com/kwang1215 +Rishat-F: + login: Rishat-F + count: 12 + avatarUrl: https://avatars.githubusercontent.com/u/66554797?v=4 + url: https://github.com/Rishat-F AdrianDeAnda: login: AdrianDeAnda count: 11 @@ -513,6 +518,11 @@ KNChiu: count: 11 avatarUrl: https://avatars.githubusercontent.com/u/36751646?v=4 url: https://github.com/KNChiu +gitgernit: + login: gitgernit + count: 11 + avatarUrl: https://avatars.githubusercontent.com/u/129539613?u=d04f10143ab32c93f563ea14bf242d1d2bc991b0&v=4 + url: https://github.com/gitgernit mariacamilagl: login: mariacamilagl count: 10 @@ -538,6 +548,11 @@ RobotToI: count: 10 avatarUrl: https://avatars.githubusercontent.com/u/44951382?u=e41dbc19191ce7abed86694b1a44ea0523e1c60e&v=4 url: https://github.com/RobotToI +vitumenezes: + login: vitumenezes + count: 10 + avatarUrl: https://avatars.githubusercontent.com/u/9680878?u=05fd25cfafdc09382bf8907c37293a696c205754&v=4 + url: https://github.com/vitumenezes fcrozetta: login: fcrozetta count: 10 @@ -626,7 +641,7 @@ marcelomarkus: JoaoGustavoRogel: login: JoaoGustavoRogel count: 9 - avatarUrl: https://avatars.githubusercontent.com/u/29525510?u=1dd3096c6c2be2576fd5e818b1be15b2c9768aa5&v=4 + avatarUrl: https://avatars.githubusercontent.com/u/29525510?u=a0a91251f5e43e132608d55d28ccb8645c5ea405&v=4 url: https://github.com/JoaoGustavoRogel Zhongheng-Cheng: login: Zhongheng-Cheng @@ -673,11 +688,6 @@ camigomezdev: count: 8 avatarUrl: https://avatars.githubusercontent.com/u/16061815?u=25b5ebc042fff53fa03dc107ded10e36b1b7a5b9&v=4 url: https://github.com/camigomezdev -gitgernit: - login: gitgernit - count: 8 - avatarUrl: https://avatars.githubusercontent.com/u/129539613?u=d04f10143ab32c93f563ea14bf242d1d2bc991b0&v=4 - url: https://github.com/gitgernit Serrones: login: Serrones count: 7 @@ -698,11 +708,6 @@ anthonycepeda: count: 7 avatarUrl: https://avatars.githubusercontent.com/u/72019805?u=60bdf46240cff8fca482ff0fc07d963fd5e1a27c&v=4 url: https://github.com/anthonycepeda -vitumenezes: - login: vitumenezes - count: 7 - avatarUrl: https://avatars.githubusercontent.com/u/9680878?u=e7c6865aec49c3c94b8c8edc1198d1eac3e50b26&v=4 - url: https://github.com/vitumenezes fabioueno: login: fabioueno count: 7 @@ -956,7 +961,7 @@ devluisrodrigues: timothy-jeong: login: timothy-jeong count: 5 - avatarUrl: https://avatars.githubusercontent.com/u/53824764?u=659311b6f6aeb0fbb8b527723fd4c83642f04327&v=4 + avatarUrl: https://avatars.githubusercontent.com/u/53824764?u=db3d0cea2f5fab64d810113c5039a369699a2774&v=4 url: https://github.com/timothy-jeong lpdswing: login: lpdswing @@ -1053,6 +1058,11 @@ matiasbertani: count: 4 avatarUrl: https://avatars.githubusercontent.com/u/65260383?u=d5edd86a6e2ab4fb1aab7751931fe045a963afd7&v=4 url: https://github.com/matiasbertani +k94-ishi: + login: k94-ishi + count: 4 + avatarUrl: https://avatars.githubusercontent.com/u/32672580?u=bc7c5c07af0656be9fe4f1784a444af8d81ded89&v=4 + url: https://github.com/k94-ishi javillegasna: login: javillegasna count: 4 @@ -1063,6 +1073,16 @@ javillegasna: count: 4 avatarUrl: https://avatars.githubusercontent.com/u/174453744?v=4 url: https://github.com/9zimin9 +ilhamfadillah: + login: ilhamfadillah + count: 4 + avatarUrl: https://avatars.githubusercontent.com/u/20577838?u=c56192cf99b55affcaad408b240259c62e633450&v=4 + url: https://github.com/ilhamfadillah +Yarous: + login: Yarous + count: 4 + avatarUrl: https://avatars.githubusercontent.com/u/61277193?u=5b462347458a373b2d599c6f416d2b75eddbffad&v=4 + url: https://github.com/Yarous tyronedamasceno: login: tyronedamasceno count: 3 @@ -1151,7 +1171,7 @@ rafsaf: frnsimoes: login: frnsimoes count: 3 - avatarUrl: https://avatars.githubusercontent.com/u/66239468?u=771c4b0c403a42ccf2676ac987ac4999e5ad09bc&v=4 + avatarUrl: https://avatars.githubusercontent.com/u/66239468?u=a405e8f10654251e239a4a1d9dd5bda59216727d&v=4 url: https://github.com/frnsimoes lieryan: login: lieryan @@ -1283,11 +1303,16 @@ celestywang: count: 3 avatarUrl: https://avatars.githubusercontent.com/u/184830753?v=4 url: https://github.com/celestywang -ilhamfadillah: - login: ilhamfadillah +RyaWcksn: + login: RyaWcksn count: 3 - avatarUrl: https://avatars.githubusercontent.com/u/20577838?u=c56192cf99b55affcaad408b240259c62e633450&v=4 - url: https://github.com/ilhamfadillah + avatarUrl: https://avatars.githubusercontent.com/u/42831964?u=0cb4265faf3e3425a89e59b6fddd3eb2de180af0&v=4 + url: https://github.com/RyaWcksn +gerry-sabar: + login: gerry-sabar + count: 3 + avatarUrl: https://avatars.githubusercontent.com/u/1120123?v=4 + url: https://github.com/gerry-sabar blaisep: login: blaisep count: 2 @@ -1633,13 +1658,13 @@ logan2d5: count: 2 avatarUrl: https://avatars.githubusercontent.com/u/146642263?u=dbd6621f8b0330d6919f6a7131277b92e26fbe87&v=4 url: https://github.com/logan2d5 -RyaWcksn: - login: RyaWcksn +tiaggo16: + login: tiaggo16 count: 2 - avatarUrl: https://avatars.githubusercontent.com/u/42831964?u=0cb4265faf3e3425a89e59b6fddd3eb2de180af0&v=4 - url: https://github.com/RyaWcksn -gerry-sabar: - login: gerry-sabar + avatarUrl: https://avatars.githubusercontent.com/u/62227573?u=359f4e2c51a4b13c8553ac5af405d635b07bb61f&v=4 + url: https://github.com/tiaggo16 +kiharito: + login: kiharito count: 2 - avatarUrl: https://avatars.githubusercontent.com/u/1120123?v=4 - url: https://github.com/gerry-sabar + avatarUrl: https://avatars.githubusercontent.com/u/38311245?v=4 + url: https://github.com/kiharito diff --git a/docs/en/data/translators.yml b/docs/en/data/translators.yml index 7b199dc08..13859044d 100644 --- a/docs/en/data/translators.yml +++ b/docs/en/data/translators.yml @@ -8,6 +8,11 @@ jaystone776: count: 46 avatarUrl: https://avatars.githubusercontent.com/u/11191137?u=299205a95e9b6817a43144a48b643346a5aac5cc&v=4 url: https://github.com/jaystone776 +ceb10n: + login: ceb10n + count: 26 + avatarUrl: https://avatars.githubusercontent.com/u/235213?u=edcce471814a1eba9f0cdaa4cd0de18921a940a6&v=4 + url: https://github.com/ceb10n tokusumi: login: tokusumi count: 23 @@ -23,11 +28,6 @@ hasansezertasan: count: 22 avatarUrl: https://avatars.githubusercontent.com/u/13135006?u=99f0b0f0fc47e88e8abb337b4447357939ef93e7&v=4 url: https://github.com/hasansezertasan -ceb10n: - login: ceb10n - count: 22 - avatarUrl: https://avatars.githubusercontent.com/u/235213?u=edcce471814a1eba9f0cdaa4cd0de18921a940a6&v=4 - url: https://github.com/ceb10n waynerv: login: waynerv count: 20 @@ -55,7 +55,7 @@ Xewus: url: https://github.com/Xewus Joao-Pedro-P-Holanda: login: Joao-Pedro-P-Holanda - count: 12 + count: 13 avatarUrl: https://avatars.githubusercontent.com/u/110267046?u=331bd016326dac4cf3df4848f6db2dbbf8b5f978&v=4 url: https://github.com/Joao-Pedro-P-Holanda Smlep: @@ -78,6 +78,11 @@ Vincy1230: count: 9 avatarUrl: https://avatars.githubusercontent.com/u/81342412?u=ab5e256a4077a4a91f3f9cd2115ba80780454cbe&v=4 url: https://github.com/Vincy1230 +Zhongheng-Cheng: + login: Zhongheng-Cheng + count: 9 + avatarUrl: https://avatars.githubusercontent.com/u/95612344?u=a0f7730a3cc7486827965e01a119ad610bda4b0a&v=4 + url: https://github.com/Zhongheng-Cheng rjNemo: login: rjNemo count: 8 @@ -93,11 +98,6 @@ pablocm83: count: 8 avatarUrl: https://avatars.githubusercontent.com/u/28315068?u=3310fbb05bb8bfc50d2c48b6cb64ac9ee4a14549&v=4 url: https://github.com/pablocm83 -Zhongheng-Cheng: - login: Zhongheng-Cheng - count: 8 - avatarUrl: https://avatars.githubusercontent.com/u/95612344?u=a0f7730a3cc7486827965e01a119ad610bda4b0a&v=4 - url: https://github.com/Zhongheng-Cheng batlopes: login: batlopes count: 6 @@ -188,6 +188,11 @@ kwang1215: count: 4 avatarUrl: https://avatars.githubusercontent.com/u/74170199?u=2a63ff6692119dde3f5e5693365b9fcd6f977b08&v=4 url: https://github.com/kwang1215 +alv2017: + login: alv2017 + count: 4 + avatarUrl: https://avatars.githubusercontent.com/u/31544722?v=4 + url: https://github.com/alv2017 jfunez: login: jfunez count: 3 @@ -313,11 +318,11 @@ nahyunkeem: count: 3 avatarUrl: https://avatars.githubusercontent.com/u/174440096?u=e12401d492eee58570f8914d0872b52e421a776e&v=4 url: https://github.com/nahyunkeem -alv2017: - login: alv2017 +gerry-sabar: + login: gerry-sabar count: 3 - avatarUrl: https://avatars.githubusercontent.com/u/31544722?v=4 - url: https://github.com/alv2017 + avatarUrl: https://avatars.githubusercontent.com/u/1120123?v=4 + url: https://github.com/gerry-sabar izaguerreiro: login: izaguerreiro count: 2 @@ -481,10 +486,10 @@ saeye: timothy-jeong: login: timothy-jeong count: 2 - avatarUrl: https://avatars.githubusercontent.com/u/53824764?u=659311b6f6aeb0fbb8b527723fd4c83642f04327&v=4 + avatarUrl: https://avatars.githubusercontent.com/u/53824764?u=db3d0cea2f5fab64d810113c5039a369699a2774&v=4 url: https://github.com/timothy-jeong -gerry-sabar: - login: gerry-sabar +Rishat-F: + login: Rishat-F count: 2 - avatarUrl: https://avatars.githubusercontent.com/u/1120123?v=4 - url: https://github.com/gerry-sabar + avatarUrl: https://avatars.githubusercontent.com/u/66554797?v=4 + url: https://github.com/Rishat-F From 495ff5baa9694c773015abfaaf233a5b74ceaec5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Fri, 7 Feb 2025 22:10:37 +0000 Subject: [PATCH 015/157] =?UTF-8?q?=F0=9F=91=A5=20Update=20FastAPI=20GitHu?= =?UTF-8?q?b=20topic=20repositories=20(#13302)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: github-actions --- docs/en/data/topic_repos.yml | 334 +++++++++++++++++------------------ 1 file changed, 167 insertions(+), 167 deletions(-) diff --git a/docs/en/data/topic_repos.yml b/docs/en/data/topic_repos.yml index c1176e55c..302dc3bb5 100644 --- a/docs/en/data/topic_repos.yml +++ b/docs/en/data/topic_repos.yml @@ -1,495 +1,495 @@ - name: full-stack-fastapi-template html_url: https://github.com/fastapi/full-stack-fastapi-template - stars: 28796 + stars: 29409 owner_login: fastapi owner_html_url: https://github.com/fastapi - name: Hello-Python html_url: https://github.com/mouredev/Hello-Python - stars: 27554 + stars: 28113 owner_login: mouredev owner_html_url: https://github.com/mouredev - name: serve html_url: https://github.com/jina-ai/serve - stars: 21225 + stars: 21264 owner_login: jina-ai owner_html_url: https://github.com/jina-ai - name: sqlmodel html_url: https://github.com/fastapi/sqlmodel - stars: 14921 + stars: 15109 owner_login: fastapi owner_html_url: https://github.com/fastapi - name: HivisionIDPhotos html_url: https://github.com/Zeyi-Lin/HivisionIDPhotos - stars: 14025 + stars: 14564 owner_login: Zeyi-Lin owner_html_url: https://github.com/Zeyi-Lin - name: Douyin_TikTok_Download_API html_url: https://github.com/Evil0ctal/Douyin_TikTok_Download_API - stars: 10001 + stars: 10701 owner_login: Evil0ctal owner_html_url: https://github.com/Evil0ctal - name: fastapi-best-practices html_url: https://github.com/zhanymkanov/fastapi-best-practices - stars: 9820 + stars: 10180 owner_login: zhanymkanov owner_html_url: https://github.com/zhanymkanov - name: awesome-fastapi html_url: https://github.com/mjhea0/awesome-fastapi - stars: 8899 + stars: 9061 owner_login: mjhea0 owner_html_url: https://github.com/mjhea0 - name: FastUI html_url: https://github.com/pydantic/FastUI - stars: 8400 + stars: 8644 owner_login: pydantic owner_html_url: https://github.com/pydantic - name: nonebot2 html_url: https://github.com/nonebot/nonebot2 - stars: 6235 + stars: 6312 owner_login: nonebot owner_html_url: https://github.com/nonebot - name: serge html_url: https://github.com/serge-chat/serge - stars: 5685 + stars: 5686 owner_login: serge-chat owner_html_url: https://github.com/serge-chat -- name: fastapi-users - html_url: https://github.com/fastapi-users/fastapi-users - stars: 4787 - owner_login: fastapi-users - owner_html_url: https://github.com/fastapi-users - name: FileCodeBox html_url: https://github.com/vastsa/FileCodeBox - stars: 4479 + stars: 4933 owner_login: vastsa owner_html_url: https://github.com/vastsa +- name: fastapi-users + html_url: https://github.com/fastapi-users/fastapi-users + stars: 4849 + owner_login: fastapi-users + owner_html_url: https://github.com/fastapi-users - name: hatchet html_url: https://github.com/hatchet-dev/hatchet - stars: 4413 + stars: 4514 owner_login: hatchet-dev owner_html_url: https://github.com/hatchet-dev - name: chatgpt-web-share html_url: https://github.com/chatpire/chatgpt-web-share - stars: 4322 + stars: 4319 owner_login: chatpire owner_html_url: https://github.com/chatpire -- name: atrilabs-engine - html_url: https://github.com/Atri-Labs/atrilabs-engine - stars: 4115 - owner_login: Atri-Labs - owner_html_url: https://github.com/Atri-Labs +- name: polar + html_url: https://github.com/polarsource/polar + stars: 4216 + owner_login: polarsource + owner_html_url: https://github.com/polarsource - name: strawberry html_url: https://github.com/strawberry-graphql/strawberry - stars: 4084 + stars: 4126 owner_login: strawberry-graphql owner_html_url: https://github.com/strawberry-graphql +- name: atrilabs-engine + html_url: https://github.com/Atri-Labs/atrilabs-engine + stars: 4114 + owner_login: Atri-Labs + owner_html_url: https://github.com/Atri-Labs - name: dynaconf html_url: https://github.com/dynaconf/dynaconf - stars: 3844 + stars: 3874 owner_login: dynaconf owner_html_url: https://github.com/dynaconf - name: poem html_url: https://github.com/poem-web/poem - stars: 3698 + stars: 3746 owner_login: poem-web owner_html_url: https://github.com/poem-web -- name: polar - html_url: https://github.com/polarsource/polar - stars: 3355 - owner_login: polarsource - owner_html_url: https://github.com/polarsource - name: opyrator html_url: https://github.com/ml-tooling/opyrator - stars: 3114 + stars: 3117 owner_login: ml-tooling owner_html_url: https://github.com/ml-tooling - name: farfalle html_url: https://github.com/rashadphz/farfalle - stars: 3022 + stars: 3094 owner_login: rashadphz owner_html_url: https://github.com/rashadphz - name: fastapi-admin html_url: https://github.com/fastapi-admin/fastapi-admin - stars: 3002 + stars: 3040 owner_login: fastapi-admin owner_html_url: https://github.com/fastapi-admin - name: docarray html_url: https://github.com/docarray/docarray - stars: 2998 + stars: 3007 owner_login: docarray owner_html_url: https://github.com/docarray - name: datamodel-code-generator html_url: https://github.com/koxudaxi/datamodel-code-generator - stars: 2845 + stars: 2914 owner_login: koxudaxi owner_html_url: https://github.com/koxudaxi - name: fastapi-realworld-example-app html_url: https://github.com/nsidnev/fastapi-realworld-example-app - stars: 2832 + stars: 2840 owner_login: nsidnev owner_html_url: https://github.com/nsidnev -- name: uvicorn-gunicorn-fastapi-docker - html_url: https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker - stars: 2727 - owner_login: tiangolo - owner_html_url: https://github.com/tiangolo -- name: WrenAI - html_url: https://github.com/Canner/WrenAI - stars: 2699 - owner_login: Canner - owner_html_url: https://github.com/Canner - name: LitServe html_url: https://github.com/Lightning-AI/LitServe - stars: 2664 + stars: 2804 owner_login: Lightning-AI owner_html_url: https://github.com/Lightning-AI +- name: uvicorn-gunicorn-fastapi-docker + html_url: https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker + stars: 2730 + owner_login: tiangolo + owner_html_url: https://github.com/tiangolo - name: logfire html_url: https://github.com/pydantic/logfire - stars: 2495 + stars: 2620 owner_login: pydantic owner_html_url: https://github.com/pydantic - name: huma html_url: https://github.com/danielgtaylor/huma - stars: 2479 + stars: 2567 owner_login: danielgtaylor owner_html_url: https://github.com/danielgtaylor - name: tracecat html_url: https://github.com/TracecatHQ/tracecat - stars: 2446 + stars: 2494 owner_login: TracecatHQ owner_html_url: https://github.com/TracecatHQ -- name: RasaGPT - html_url: https://github.com/paulpierre/RasaGPT - stars: 2378 - owner_login: paulpierre - owner_html_url: https://github.com/paulpierre - name: best-of-web-python html_url: https://github.com/ml-tooling/best-of-web-python - stars: 2374 + stars: 2433 owner_login: ml-tooling owner_html_url: https://github.com/ml-tooling +- name: RasaGPT + html_url: https://github.com/paulpierre/RasaGPT + stars: 2386 + owner_login: paulpierre + owner_html_url: https://github.com/paulpierre - name: fastapi-react html_url: https://github.com/Buuntu/fastapi-react - stars: 2274 + stars: 2293 owner_login: Buuntu owner_html_url: https://github.com/Buuntu - name: nextpy html_url: https://github.com/dot-agent/nextpy - stars: 2244 + stars: 2256 owner_login: dot-agent owner_html_url: https://github.com/dot-agent - name: 30-Days-of-Python html_url: https://github.com/codingforentrepreneurs/30-Days-of-Python - stars: 2154 + stars: 2155 owner_login: codingforentrepreneurs owner_html_url: https://github.com/codingforentrepreneurs - name: FastAPI-template html_url: https://github.com/s3rius/FastAPI-template - stars: 2067 + stars: 2121 owner_login: s3rius owner_html_url: https://github.com/s3rius -- name: langserve - html_url: https://github.com/langchain-ai/langserve - stars: 1980 - owner_login: langchain-ai - owner_html_url: https://github.com/langchain-ai - name: sqladmin html_url: https://github.com/aminalaee/sqladmin - stars: 1980 + stars: 2021 owner_login: aminalaee owner_html_url: https://github.com/aminalaee +- name: langserve + html_url: https://github.com/langchain-ai/langserve + stars: 2006 + owner_login: langchain-ai + owner_html_url: https://github.com/langchain-ai - name: fastapi-utils html_url: https://github.com/fastapiutils/fastapi-utils - stars: 1970 + stars: 2002 owner_login: fastapiutils owner_html_url: https://github.com/fastapiutils - name: solara html_url: https://github.com/widgetti/solara - stars: 1950 + stars: 1967 owner_login: widgetti owner_html_url: https://github.com/widgetti -- name: python-week-2022 - html_url: https://github.com/rochacbruno/python-week-2022 - stars: 1836 - owner_login: rochacbruno - owner_html_url: https://github.com/rochacbruno - name: supabase-py html_url: https://github.com/supabase/supabase-py - stars: 1803 + stars: 1848 owner_login: supabase owner_html_url: https://github.com/supabase +- name: python-week-2022 + html_url: https://github.com/rochacbruno/python-week-2022 + stars: 1832 + owner_login: rochacbruno + owner_html_url: https://github.com/rochacbruno - name: mangum html_url: https://github.com/Kludex/mangum - stars: 1760 + stars: 1789 owner_login: Kludex owner_html_url: https://github.com/Kludex - name: manage-fastapi html_url: https://github.com/ycd/manage-fastapi - stars: 1704 + stars: 1711 owner_login: ycd owner_html_url: https://github.com/ycd - name: ormar html_url: https://github.com/collerek/ormar - stars: 1688 + stars: 1701 owner_login: collerek owner_html_url: https://github.com/collerek - name: agentkit html_url: https://github.com/BCG-X-Official/agentkit - stars: 1615 + stars: 1630 owner_login: BCG-X-Official owner_html_url: https://github.com/BCG-X-Official - name: langchain-serve html_url: https://github.com/jina-ai/langchain-serve - stars: 1615 + stars: 1617 owner_login: jina-ai owner_html_url: https://github.com/jina-ai - name: termpair html_url: https://github.com/cs01/termpair - stars: 1613 + stars: 1612 owner_login: cs01 owner_html_url: https://github.com/cs01 - name: coronavirus-tracker-api html_url: https://github.com/ExpDev07/coronavirus-tracker-api - stars: 1591 + stars: 1590 owner_login: ExpDev07 owner_html_url: https://github.com/ExpDev07 - name: piccolo html_url: https://github.com/piccolo-orm/piccolo - stars: 1477 + stars: 1519 owner_login: piccolo-orm owner_html_url: https://github.com/piccolo-orm - name: fastapi-crudrouter html_url: https://github.com/awtkns/fastapi-crudrouter - stars: 1435 + stars: 1449 owner_login: awtkns owner_html_url: https://github.com/awtkns - name: fastapi-cache html_url: https://github.com/long2ice/fastapi-cache - stars: 1412 + stars: 1447 owner_login: long2ice owner_html_url: https://github.com/long2ice - name: openapi-python-client html_url: https://github.com/openapi-generators/openapi-python-client - stars: 1398 + stars: 1434 owner_login: openapi-generators owner_html_url: https://github.com/openapi-generators - name: awesome-fastapi-projects html_url: https://github.com/Kludex/awesome-fastapi-projects - stars: 1386 + stars: 1398 owner_login: Kludex owner_html_url: https://github.com/Kludex - name: awesome-python-resources html_url: https://github.com/DjangoEx/awesome-python-resources - stars: 1371 + stars: 1380 owner_login: DjangoEx owner_html_url: https://github.com/DjangoEx - name: budgetml html_url: https://github.com/ebhy/budgetml - stars: 1342 + stars: 1344 owner_login: ebhy owner_html_url: https://github.com/ebhy - name: slowapi html_url: https://github.com/laurentS/slowapi - stars: 1289 + stars: 1339 owner_login: laurentS owner_html_url: https://github.com/laurentS - name: fastapi-pagination html_url: https://github.com/uriyyo/fastapi-pagination - stars: 1240 + stars: 1263 owner_login: uriyyo owner_html_url: https://github.com/uriyyo - name: fastapi-boilerplate html_url: https://github.com/teamhide/fastapi-boilerplate - stars: 1173 + stars: 1206 owner_login: teamhide owner_html_url: https://github.com/teamhide - name: fastapi-tutorial html_url: https://github.com/liaogx/fastapi-tutorial - stars: 1162 + stars: 1178 owner_login: liaogx owner_html_url: https://github.com/liaogx - name: fastapi-amis-admin html_url: https://github.com/amisadmin/fastapi-amis-admin - stars: 1118 + stars: 1142 owner_login: amisadmin owner_html_url: https://github.com/amisadmin - name: fastapi-code-generator html_url: https://github.com/koxudaxi/fastapi-code-generator - stars: 1095 + stars: 1119 owner_login: koxudaxi owner_html_url: https://github.com/koxudaxi - name: bolt-python html_url: https://github.com/slackapi/bolt-python - stars: 1086 + stars: 1116 owner_login: slackapi owner_html_url: https://github.com/slackapi - name: odmantic html_url: https://github.com/art049/odmantic - stars: 1085 + stars: 1096 owner_login: art049 owner_html_url: https://github.com/art049 - name: langchain-extract html_url: https://github.com/langchain-ai/langchain-extract - stars: 1068 + stars: 1093 owner_login: langchain-ai owner_html_url: https://github.com/langchain-ai - name: fastapi_production_template html_url: https://github.com/zhanymkanov/fastapi_production_template - stars: 1059 + stars: 1078 owner_login: zhanymkanov owner_html_url: https://github.com/zhanymkanov - name: fastapi-alembic-sqlmodel-async html_url: https://github.com/jonra1993/fastapi-alembic-sqlmodel-async - stars: 1031 + stars: 1055 owner_login: jonra1993 owner_html_url: https://github.com/jonra1993 +- name: Kokoro-FastAPI + html_url: https://github.com/remsky/Kokoro-FastAPI + stars: 1047 + owner_login: remsky + owner_html_url: https://github.com/remsky - name: prometheus-fastapi-instrumentator html_url: https://github.com/trallnag/prometheus-fastapi-instrumentator - stars: 1013 + stars: 1036 owner_login: trallnag owner_html_url: https://github.com/trallnag +- name: SurfSense + html_url: https://github.com/MODSetter/SurfSense + stars: 1018 + owner_login: MODSetter + owner_html_url: https://github.com/MODSetter +- name: bedrock-claude-chat + html_url: https://github.com/aws-samples/bedrock-claude-chat + stars: 1010 + owner_login: aws-samples + owner_html_url: https://github.com/aws-samples - name: runhouse html_url: https://github.com/run-house/runhouse - stars: 988 + stars: 1000 owner_login: run-house owner_html_url: https://github.com/run-house - name: lanarky html_url: https://github.com/ajndkr/lanarky - stars: 982 + stars: 986 owner_login: ajndkr owner_html_url: https://github.com/ajndkr - name: autollm html_url: https://github.com/viddexa/autollm - stars: 981 + stars: 982 owner_login: viddexa owner_html_url: https://github.com/viddexa -- name: bedrock-claude-chat - html_url: https://github.com/aws-samples/bedrock-claude-chat - stars: 977 - owner_login: aws-samples - owner_html_url: https://github.com/aws-samples -- name: SurfSense - html_url: https://github.com/MODSetter/SurfSense - stars: 971 - owner_login: MODSetter - owner_html_url: https://github.com/MODSetter - name: restish html_url: https://github.com/danielgtaylor/restish - stars: 954 + stars: 970 owner_login: danielgtaylor owner_html_url: https://github.com/danielgtaylor +- name: fastcrud + html_url: https://github.com/igorbenav/fastcrud + stars: 929 + owner_login: igorbenav + owner_html_url: https://github.com/igorbenav - name: secure html_url: https://github.com/TypeError/secure - stars: 911 + stars: 921 owner_login: TypeError owner_html_url: https://github.com/TypeError - name: langcorn html_url: https://github.com/msoedov/langcorn - stars: 909 + stars: 915 owner_login: msoedov owner_html_url: https://github.com/msoedov -- name: energy-forecasting - html_url: https://github.com/iusztinpaul/energy-forecasting - stars: 884 - owner_login: iusztinpaul - owner_html_url: https://github.com/iusztinpaul - name: vue-fastapi-admin html_url: https://github.com/mizhexiaoxiao/vue-fastapi-admin - stars: 863 + stars: 915 owner_login: mizhexiaoxiao owner_html_url: https://github.com/mizhexiaoxiao +- name: energy-forecasting + html_url: https://github.com/iusztinpaul/energy-forecasting + stars: 891 + owner_login: iusztinpaul + owner_html_url: https://github.com/iusztinpaul - name: authx html_url: https://github.com/yezz123/authx - stars: 850 + stars: 862 owner_login: yezz123 owner_html_url: https://github.com/yezz123 - name: titiler html_url: https://github.com/developmentseed/titiler - stars: 809 + stars: 823 owner_login: developmentseed owner_html_url: https://github.com/developmentseed - name: marker-api html_url: https://github.com/adithya-s-k/marker-api - stars: 792 + stars: 798 owner_login: adithya-s-k owner_html_url: https://github.com/adithya-s-k +- name: FastAPI-boilerplate + html_url: https://github.com/igorbenav/FastAPI-boilerplate + stars: 774 + owner_login: igorbenav + owner_html_url: https://github.com/igorbenav - name: fastapi_best_architecture html_url: https://github.com/fastapi-practices/fastapi_best_architecture - stars: 742 + stars: 766 owner_login: fastapi-practices owner_html_url: https://github.com/fastapi-practices - name: fastapi-mail html_url: https://github.com/sabuhish/fastapi-mail - stars: 728 + stars: 735 owner_login: sabuhish owner_html_url: https://github.com/sabuhish -- name: fastcrud - html_url: https://github.com/igorbenav/fastcrud - stars: 727 - owner_login: igorbenav - owner_html_url: https://github.com/igorbenav - name: annotated-py-projects html_url: https://github.com/hhstore/annotated-py-projects - stars: 722 + stars: 725 owner_login: hhstore owner_html_url: https://github.com/hhstore -- name: FastAPI-boilerplate - html_url: https://github.com/igorbenav/FastAPI-boilerplate - stars: 716 - owner_login: igorbenav - owner_html_url: https://github.com/igorbenav +- name: fastapi-do-zero + html_url: https://github.com/dunossauro/fastapi-do-zero + stars: 723 + owner_login: dunossauro + owner_html_url: https://github.com/dunossauro - name: lccn_predictor html_url: https://github.com/baoliay2008/lccn_predictor - stars: 707 + stars: 718 owner_login: baoliay2008 owner_html_url: https://github.com/baoliay2008 +- name: fastapi-observability + html_url: https://github.com/blueswen/fastapi-observability + stars: 718 + owner_login: blueswen + owner_html_url: https://github.com/blueswen - name: chatGPT-web html_url: https://github.com/mic1on/chatGPT-web - stars: 706 + stars: 708 owner_login: mic1on owner_html_url: https://github.com/mic1on -- name: fastapi-do-zero - html_url: https://github.com/dunossauro/fastapi-do-zero - stars: 702 - owner_login: dunossauro - owner_html_url: https://github.com/dunossauro +- name: learn-generative-ai + html_url: https://github.com/panaverse/learn-generative-ai + stars: 701 + owner_login: panaverse + owner_html_url: https://github.com/panaverse - name: linbing html_url: https://github.com/taomujian/linbing - stars: 699 + stars: 700 owner_login: taomujian owner_html_url: https://github.com/taomujian -- name: fastapi-observability - html_url: https://github.com/blueswen/fastapi-observability - stars: 698 - owner_login: blueswen - owner_html_url: https://github.com/blueswen - name: FastAPI-Backend-Template html_url: https://github.com/Aeternalis-Ingenium/FastAPI-Backend-Template - stars: 682 + stars: 692 owner_login: Aeternalis-Ingenium owner_html_url: https://github.com/Aeternalis-Ingenium -- name: learn-generative-ai - html_url: https://github.com/panaverse/learn-generative-ai - stars: 673 - owner_login: panaverse - owner_html_url: https://github.com/panaverse +- name: starlette-admin + html_url: https://github.com/jowilf/starlette-admin + stars: 692 + owner_login: jowilf + owner_html_url: https://github.com/jowilf - name: fastapi-jwt-auth html_url: https://github.com/IndominusByte/fastapi-jwt-auth - stars: 668 + stars: 674 owner_login: IndominusByte owner_html_url: https://github.com/IndominusByte - name: pity html_url: https://github.com/wuranxu/pity - stars: 660 + stars: 663 owner_login: wuranxu owner_html_url: https://github.com/wuranxu -- name: starlette-admin - html_url: https://github.com/jowilf/starlette-admin - stars: 653 - owner_login: jowilf - owner_html_url: https://github.com/jowilf - name: fastapi_login html_url: https://github.com/MushroomMaula/fastapi_login - stars: 650 + stars: 656 owner_login: MushroomMaula owner_html_url: https://github.com/MushroomMaula From b6b031b456d825fd8378d280d5d7d7c167784bb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Fri, 7 Feb 2025 22:10:51 +0000 Subject: [PATCH 016/157] =?UTF-8?q?=F0=9F=91=A5=20Update=20FastAPI=20Peopl?= =?UTF-8?q?e=20-=20Experts=20(#13303)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: github-actions Co-authored-by: Alejandra <90076947+alejsdev@users.noreply.github.com> --- docs/en/data/people.yml | 130 +++++++++++++++++++++------------------- 1 file changed, 67 insertions(+), 63 deletions(-) diff --git a/docs/en/data/people.yml b/docs/en/data/people.yml index 112567778..7f910ab34 100644 --- a/docs/en/data/people.yml +++ b/docs/en/data/people.yml @@ -13,7 +13,7 @@ experts: avatarUrl: https://avatars.githubusercontent.com/in/15368?v=4 url: https://github.com/apps/github-actions - login: Kludex - count: 644 + count: 645 avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=df8a3f06ba8f55ae1967a3e2d5ed882903a4e330&v=4 url: https://github.com/Kludex - login: jgould22 @@ -116,6 +116,10 @@ experts: count: 39 avatarUrl: https://avatars.githubusercontent.com/u/30960668?v=4 url: https://github.com/sinisaos +- login: luzzodev + count: 37 + avatarUrl: https://avatars.githubusercontent.com/u/27291415?v=4 + url: https://github.com/luzzodev - login: chbndrhnns count: 37 avatarUrl: https://avatars.githubusercontent.com/u/7534547?v=4 @@ -124,10 +128,6 @@ experts: count: 37 avatarUrl: https://avatars.githubusercontent.com/u/5167622?u=de8f597c81d6336fcebc37b32dfd61a3f877160c&v=4 url: https://github.com/STeveShary -- login: luzzodev - count: 36 - avatarUrl: https://avatars.githubusercontent.com/u/27291415?v=4 - url: https://github.com/luzzodev - login: krishnardt count: 35 avatarUrl: https://avatars.githubusercontent.com/u/31960541?u=47f4829c77f4962ab437ffb7995951e41eeebe9b&v=4 @@ -188,6 +188,10 @@ experts: count: 19 avatarUrl: https://avatars.githubusercontent.com/u/10840422?u=45f015f95e1c0f06df602be4ab688d4b854cc8a8&v=4 url: https://github.com/estebanx64 +- login: sehraramiz + count: 18 + avatarUrl: https://avatars.githubusercontent.com/u/14166324?u=8fac65e84dfff24245d304a5b5b09f7b5bd69dc9&v=4 + url: https://github.com/sehraramiz - login: zoliknemet count: 18 avatarUrl: https://avatars.githubusercontent.com/u/22326718?u=31ba446ac290e23e56eea8e4f0c558aaf0b40779&v=4 @@ -196,10 +200,6 @@ experts: count: 18 avatarUrl: https://avatars.githubusercontent.com/u/24581770?v=4 url: https://github.com/retnikt -- login: sehraramiz - count: 17 - avatarUrl: https://avatars.githubusercontent.com/u/14166324?u=8fac65e84dfff24245d304a5b5b09f7b5bd69dc9&v=4 - url: https://github.com/sehraramiz - login: caeser1996 count: 17 avatarUrl: https://avatars.githubusercontent.com/u/16540232?u=05d2beb8e034d584d0a374b99d8826327bd7f614&v=4 @@ -224,6 +224,10 @@ experts: count: 16 avatarUrl: https://avatars.githubusercontent.com/u/41964673?u=9f2174f9d61c15c6e3a4c9e3aeee66f711ce311f&v=4 url: https://github.com/dstlny +- login: ceb10n + count: 15 + avatarUrl: https://avatars.githubusercontent.com/u/235213?u=edcce471814a1eba9f0cdaa4cd0de18921a940a6&v=4 + url: https://github.com/ceb10n - login: jorgerpo count: 15 avatarUrl: https://avatars.githubusercontent.com/u/12537771?u=7444d20019198e34911082780cc7ad73f2b97cb3&v=4 @@ -240,13 +244,9 @@ experts: count: 15 avatarUrl: https://avatars.githubusercontent.com/u/25699289?u=b5d219277b4d001ac26fb8be357fddd88c29d51b&v=4 url: https://github.com/abhint -- login: pythonweb2 - count: 14 - avatarUrl: https://avatars.githubusercontent.com/u/32141163?v=4 - url: https://github.com/pythonweb2 last_month_experts: - login: Kludex - count: 15 + count: 14 avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=df8a3f06ba8f55ae1967a3e2d5ed882903a4e330&v=4 url: https://github.com/Kludex - login: YuriiMotov @@ -254,11 +254,11 @@ last_month_experts: avatarUrl: https://avatars.githubusercontent.com/u/109919500?u=e83a39697a2d33ab2ec9bfbced794ee48bc29cec&v=4 url: https://github.com/YuriiMotov - login: sehraramiz - count: 8 + count: 7 avatarUrl: https://avatars.githubusercontent.com/u/14166324?u=8fac65e84dfff24245d304a5b5b09f7b5bd69dc9&v=4 url: https://github.com/sehraramiz - login: luzzodev - count: 4 + count: 5 avatarUrl: https://avatars.githubusercontent.com/u/27291415?v=4 url: https://github.com/luzzodev - login: yokwejuste @@ -269,6 +269,10 @@ last_month_experts: count: 3 avatarUrl: https://avatars.githubusercontent.com/u/31544722?v=4 url: https://github.com/alv2017 +- login: Trinkes + count: 2 + avatarUrl: https://avatars.githubusercontent.com/u/9466879?v=4 + url: https://github.com/Trinkes - login: PREPONDERANCE count: 2 avatarUrl: https://avatars.githubusercontent.com/u/112809059?u=30ab12dc9ddba2f94ab90e6ad4ad8bc5cfa7fccd&v=4 @@ -287,19 +291,19 @@ last_month_experts: url: https://github.com/iloveitaly three_months_experts: - login: luzzodev - count: 34 + count: 33 avatarUrl: https://avatars.githubusercontent.com/u/27291415?v=4 url: https://github.com/luzzodev - login: YuriiMotov - count: 33 + count: 31 avatarUrl: https://avatars.githubusercontent.com/u/109919500?u=e83a39697a2d33ab2ec9bfbced794ee48bc29cec&v=4 url: https://github.com/YuriiMotov - login: Kludex - count: 23 + count: 24 avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=df8a3f06ba8f55ae1967a3e2d5ed882903a4e330&v=4 url: https://github.com/Kludex - login: sehraramiz - count: 10 + count: 11 avatarUrl: https://avatars.githubusercontent.com/u/14166324?u=8fac65e84dfff24245d304a5b5b09f7b5bd69dc9&v=4 url: https://github.com/sehraramiz - login: estebanx64 @@ -326,6 +330,10 @@ three_months_experts: count: 3 avatarUrl: https://avatars.githubusercontent.com/u/108818737?u=3d7ffe5808843ee4372f9cc5a559ff1674cf1792&v=4 url: https://github.com/viniciusCalcantara +- login: Trinkes + count: 2 + avatarUrl: https://avatars.githubusercontent.com/u/9466879?v=4 + url: https://github.com/Trinkes - login: PREPONDERANCE count: 2 avatarUrl: https://avatars.githubusercontent.com/u/112809059?u=30ab12dc9ddba2f94ab90e6ad4ad8bc5cfa7fccd&v=4 @@ -358,10 +366,6 @@ three_months_experts: count: 2 avatarUrl: https://avatars.githubusercontent.com/u/40732698?u=611f39d3c1d2f4207a590937a78c1f10eed6232c&v=4 url: https://github.com/gelezo43 -- login: dbfreem - count: 2 - avatarUrl: https://avatars.githubusercontent.com/u/9778569?u=f2f1e9135b5e4f1b0c6821a548b17f97572720fc&v=4 - url: https://github.com/dbfreem - login: AliYmn count: 2 avatarUrl: https://avatars.githubusercontent.com/u/18416653?u=98c1fca46c7e4dabe8c39d17b5e55d1511d41cf9&v=4 @@ -388,47 +392,47 @@ six_months_experts: avatarUrl: https://avatars.githubusercontent.com/u/109919500?u=e83a39697a2d33ab2ec9bfbced794ee48bc29cec&v=4 url: https://github.com/YuriiMotov - login: Kludex - count: 39 + count: 40 avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=df8a3f06ba8f55ae1967a3e2d5ed882903a4e330&v=4 url: https://github.com/Kludex +- login: luzzodev + count: 37 + avatarUrl: https://avatars.githubusercontent.com/u/27291415?v=4 + url: https://github.com/luzzodev - login: sinisaos count: 37 avatarUrl: https://avatars.githubusercontent.com/u/30960668?v=4 url: https://github.com/sinisaos -- login: luzzodev - count: 36 - avatarUrl: https://avatars.githubusercontent.com/u/27291415?v=4 - url: https://github.com/luzzodev - login: JavierSanchezCastro count: 16 avatarUrl: https://avatars.githubusercontent.com/u/72013291?u=ae5679e6bd971d9d98cd5e76e8683f83642ba950&v=4 url: https://github.com/JavierSanchezCastro -- login: tiangolo - count: 13 - avatarUrl: https://avatars.githubusercontent.com/u/1326112?u=cb5d06e73a9e1998141b1641aa88e443c6717651&v=4 - url: https://github.com/tiangolo - login: Kfir-G count: 13 avatarUrl: https://avatars.githubusercontent.com/u/57500876?u=0cd29db046a17f12f382d398141319fca7ff230a&v=4 url: https://github.com/Kfir-G +- login: tiangolo + count: 12 + avatarUrl: https://avatars.githubusercontent.com/u/1326112?u=cb5d06e73a9e1998141b1641aa88e443c6717651&v=4 + url: https://github.com/tiangolo - login: sehraramiz - count: 10 + count: 11 avatarUrl: https://avatars.githubusercontent.com/u/14166324?u=8fac65e84dfff24245d304a5b5b09f7b5bd69dc9&v=4 url: https://github.com/sehraramiz -- login: estebanx64 - count: 10 - avatarUrl: https://avatars.githubusercontent.com/u/10840422?u=45f015f95e1c0f06df602be4ab688d4b854cc8a8&v=4 - url: https://github.com/estebanx64 - login: ceb10n - count: 9 + count: 10 avatarUrl: https://avatars.githubusercontent.com/u/235213?u=edcce471814a1eba9f0cdaa4cd0de18921a940a6&v=4 url: https://github.com/ceb10n +- login: estebanx64 + count: 7 + avatarUrl: https://avatars.githubusercontent.com/u/10840422?u=45f015f95e1c0f06df602be4ab688d4b854cc8a8&v=4 + url: https://github.com/estebanx64 - login: yvallois count: 6 avatarUrl: https://avatars.githubusercontent.com/u/36999744?v=4 url: https://github.com/yvallois - login: n8sty - count: 6 + count: 5 avatarUrl: https://avatars.githubusercontent.com/u/2964996?v=4 url: https://github.com/n8sty - login: TomFaulkner @@ -483,6 +487,10 @@ six_months_experts: count: 3 avatarUrl: https://avatars.githubusercontent.com/u/8796347?u=556c97650c27021911b0b9447ec55e75987b0e8a&v=4 url: https://github.com/svlandeg +- login: Trinkes + count: 2 + avatarUrl: https://avatars.githubusercontent.com/u/9466879?v=4 + url: https://github.com/Trinkes - login: PREPONDERANCE count: 2 avatarUrl: https://avatars.githubusercontent.com/u/112809059?u=30ab12dc9ddba2f94ab90e6ad4ad8bc5cfa7fccd&v=4 @@ -619,17 +627,13 @@ six_months_experts: count: 2 avatarUrl: https://avatars.githubusercontent.com/u/3409962?u=723662989f2027755e67d200137c13c53ae154ac&v=4 url: https://github.com/mattmess1221 -- login: meower1 - count: 2 - avatarUrl: https://avatars.githubusercontent.com/u/109747197?u=0a5cc2a6ae74e558f0afc2874da85132e5953d8b&v=4 - url: https://github.com/meower1 one_year_experts: - login: YuriiMotov count: 223 avatarUrl: https://avatars.githubusercontent.com/u/109919500?u=e83a39697a2d33ab2ec9bfbced794ee48bc29cec&v=4 url: https://github.com/YuriiMotov - login: Kludex - count: 83 + count: 81 avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=df8a3f06ba8f55ae1967a3e2d5ed882903a4e330&v=4 url: https://github.com/Kludex - login: JavierSanchezCastro @@ -645,7 +649,7 @@ one_year_experts: avatarUrl: https://avatars.githubusercontent.com/u/30960668?v=4 url: https://github.com/sinisaos - login: luzzodev - count: 36 + count: 37 avatarUrl: https://avatars.githubusercontent.com/u/27291415?v=4 url: https://github.com/luzzodev - login: tiangolo @@ -660,18 +664,18 @@ one_year_experts: count: 19 avatarUrl: https://avatars.githubusercontent.com/u/10840422?u=45f015f95e1c0f06df602be4ab688d4b854cc8a8&v=4 url: https://github.com/estebanx64 +- login: ceb10n + count: 15 + avatarUrl: https://avatars.githubusercontent.com/u/235213?u=edcce471814a1eba9f0cdaa4cd0de18921a940a6&v=4 + url: https://github.com/ceb10n - login: sehraramiz - count: 14 + count: 15 avatarUrl: https://avatars.githubusercontent.com/u/14166324?u=8fac65e84dfff24245d304a5b5b09f7b5bd69dc9&v=4 url: https://github.com/sehraramiz - login: PhysicallyActive count: 14 avatarUrl: https://avatars.githubusercontent.com/u/160476156?u=7a8e44f4a43d3bba636f795bb7d9476c9233b4d8&v=4 url: https://github.com/PhysicallyActive -- login: ceb10n - count: 14 - avatarUrl: https://avatars.githubusercontent.com/u/235213?u=edcce471814a1eba9f0cdaa4cd0de18921a940a6&v=4 - url: https://github.com/ceb10n - login: Kfir-G count: 13 avatarUrl: https://avatars.githubusercontent.com/u/57500876?u=0cd29db046a17f12f382d398141319fca7ff230a&v=4 @@ -812,6 +816,18 @@ one_year_experts: count: 3 avatarUrl: https://avatars.githubusercontent.com/u/104530599?u=d87b866e7c1db970d6f8e8031643818349b046d5&v=4 url: https://github.com/ahmedabdou14 +- login: Trinkes + count: 2 + avatarUrl: https://avatars.githubusercontent.com/u/9466879?v=4 + url: https://github.com/Trinkes +- login: Leon0824 + count: 2 + avatarUrl: https://avatars.githubusercontent.com/u/1922026?v=4 + url: https://github.com/Leon0824 +- login: CarlosOliveira-23 + count: 2 + avatarUrl: https://avatars.githubusercontent.com/u/102637302?u=cf350a4db956f30cbb2c27d3be0d15c282e32b14&v=4 + url: https://github.com/CarlosOliveira-23 - login: nbx3 count: 2 avatarUrl: https://avatars.githubusercontent.com/u/34649527?u=943812f69e0d40adbd3fa1c9b8ef50dd971a2a45&v=4 @@ -832,10 +848,6 @@ one_year_experts: count: 2 avatarUrl: https://avatars.githubusercontent.com/u/210173?v=4 url: https://github.com/slafs -- login: CarlosOliveira-23 - count: 2 - avatarUrl: https://avatars.githubusercontent.com/u/102637302?u=cf350a4db956f30cbb2c27d3be0d15c282e32b14&v=4 - url: https://github.com/CarlosOliveira-23 - login: monchin count: 2 avatarUrl: https://avatars.githubusercontent.com/u/18521800?v=4 @@ -844,10 +856,6 @@ one_year_experts: count: 2 avatarUrl: https://avatars.githubusercontent.com/u/38752106?u=07f80e451bda00a9492bbc764e49d24ad3ada8cc&v=4 url: https://github.com/AmirHmZz -- login: Leon0824 - count: 2 - avatarUrl: https://avatars.githubusercontent.com/u/1922026?v=4 - url: https://github.com/Leon0824 - login: iloveitaly count: 2 avatarUrl: https://avatars.githubusercontent.com/u/150855?v=4 @@ -860,7 +868,3 @@ one_year_experts: count: 2 avatarUrl: https://avatars.githubusercontent.com/u/11828278?u=6bcadc5ce4f2f56a514331c9f68eb987d4afe29a&v=4 url: https://github.com/shurshilov -- login: LincolnPuzey - count: 2 - avatarUrl: https://avatars.githubusercontent.com/u/18750802?v=4 - url: https://github.com/LincolnPuzey From 27d0ccc11cd3621890d24cac0d9236cb4b040081 Mon Sep 17 00:00:00 2001 From: github-actions Date: Fri, 7 Feb 2025 22:11:28 +0000 Subject: [PATCH 017/157] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [skip ci] --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index 75a024a0b..ac7111cf2 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -9,6 +9,7 @@ hide: ### Translations +* 🌐 Add Vietnamese translation for `docs/vi/docs/fastapi-cli.md`. PR [#13294](https://github.com/fastapi/fastapi/pull/13294) by [@ptt3199](https://github.com/ptt3199). * 🌐 Add Ukrainian translation for `docs/uk/docs/features.md`. PR [#13308](https://github.com/fastapi/fastapi/pull/13308) by [@valentinDruzhinin](https://github.com/valentinDruzhinin). * 🌐 Add Ukrainian translation for `docs/uk/docs/learn/index.md`. PR [#13306](https://github.com/fastapi/fastapi/pull/13306) by [@valentinDruzhinin](https://github.com/valentinDruzhinin). * 🌐 Update Portuguese Translation for `docs/pt/docs/deployment/https.md`. PR [#13317](https://github.com/fastapi/fastapi/pull/13317) by [@Joao-Pedro-P-Holanda](https://github.com/Joao-Pedro-P-Holanda). From 25ee2357d767eaaf46f334c4aecb2fd008703cdc Mon Sep 17 00:00:00 2001 From: github-actions Date: Fri, 7 Feb 2025 22:12:00 +0000 Subject: [PATCH 018/157] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [skip ci] --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index ac7111cf2..7d063b168 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -18,6 +18,7 @@ hide: ### Internal +* 👥 Update FastAPI People - Contributors and Translators. PR [#13293](https://github.com/fastapi/fastapi/pull/13293) by [@tiangolo](https://github.com/tiangolo). * ⬆ Bump inline-snapshot from 0.18.1 to 0.19.3. PR [#13298](https://github.com/fastapi/fastapi/pull/13298) by [@dependabot[bot]](https://github.com/apps/dependabot). * 🔧 Update sponsors, add Permit. PR [#13288](https://github.com/fastapi/fastapi/pull/13288) by [@tiangolo](https://github.com/tiangolo). From 6e8da9d00a6df82edb1c0179d2c14f07818b378b Mon Sep 17 00:00:00 2001 From: github-actions Date: Fri, 7 Feb 2025 22:13:23 +0000 Subject: [PATCH 019/157] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [skip ci] --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index 7d063b168..485454f6a 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -18,6 +18,7 @@ hide: ### Internal +* 👥 Update FastAPI GitHub topic repositories. PR [#13302](https://github.com/fastapi/fastapi/pull/13302) by [@tiangolo](https://github.com/tiangolo). * 👥 Update FastAPI People - Contributors and Translators. PR [#13293](https://github.com/fastapi/fastapi/pull/13293) by [@tiangolo](https://github.com/tiangolo). * ⬆ Bump inline-snapshot from 0.18.1 to 0.19.3. PR [#13298](https://github.com/fastapi/fastapi/pull/13298) by [@dependabot[bot]](https://github.com/apps/dependabot). * 🔧 Update sponsors, add Permit. PR [#13288](https://github.com/fastapi/fastapi/pull/13288) by [@tiangolo](https://github.com/tiangolo). From 640a5b6fc3204e953ae82bb99c66d7c0995c5e44 Mon Sep 17 00:00:00 2001 From: github-actions Date: Fri, 7 Feb 2025 22:14:11 +0000 Subject: [PATCH 020/157] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [skip ci] --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index 485454f6a..18ef5911c 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -18,6 +18,7 @@ hide: ### Internal +* 👥 Update FastAPI People - Experts. PR [#13303](https://github.com/fastapi/fastapi/pull/13303) by [@tiangolo](https://github.com/tiangolo). * 👥 Update FastAPI GitHub topic repositories. PR [#13302](https://github.com/fastapi/fastapi/pull/13302) by [@tiangolo](https://github.com/tiangolo). * 👥 Update FastAPI People - Contributors and Translators. PR [#13293](https://github.com/fastapi/fastapi/pull/13293) by [@tiangolo](https://github.com/tiangolo). * ⬆ Bump inline-snapshot from 0.18.1 to 0.19.3. PR [#13298](https://github.com/fastapi/fastapi/pull/13298) by [@dependabot[bot]](https://github.com/apps/dependabot). From e814707cd1daebcdd765e80a35316f8316496b30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Fri, 7 Feb 2025 22:15:49 +0000 Subject: [PATCH 021/157] =?UTF-8?q?=F0=9F=91=A5=20Update=20FastAPI=20Peopl?= =?UTF-8?q?e=20-=20Sponsors=20(#13295)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: github-actions --- docs/en/data/github_sponsors.yml | 63 ++++++++++++++++++++------------ 1 file changed, 39 insertions(+), 24 deletions(-) diff --git a/docs/en/data/github_sponsors.yml b/docs/en/data/github_sponsors.yml index 55fe3dda9..feb4e727f 100644 --- a/docs/en/data/github_sponsors.yml +++ b/docs/en/data/github_sponsors.yml @@ -2,6 +2,9 @@ sponsors: - - login: bump-sh avatarUrl: https://avatars.githubusercontent.com/u/33217836?v=4 url: https://github.com/bump-sh + - login: renderinc + avatarUrl: https://avatars.githubusercontent.com/u/36424661?v=4 + url: https://github.com/renderinc - login: Nixtla avatarUrl: https://avatars.githubusercontent.com/u/79945230?v=4 url: https://github.com/Nixtla @@ -20,9 +23,6 @@ sponsors: - login: zuplo avatarUrl: https://avatars.githubusercontent.com/u/85497839?v=4 url: https://github.com/zuplo - - login: render-sponsorships - avatarUrl: https://avatars.githubusercontent.com/u/189296666?v=4 - url: https://github.com/render-sponsorships - login: porter-dev avatarUrl: https://avatars.githubusercontent.com/u/62078005?v=4 url: https://github.com/porter-dev @@ -44,6 +44,9 @@ sponsors: - login: databento avatarUrl: https://avatars.githubusercontent.com/u/64141749?v=4 url: https://github.com/databento + - login: permitio + avatarUrl: https://avatars.githubusercontent.com/u/71775833?v=4 + url: https://github.com/permitio - - login: mercedes-benz avatarUrl: https://avatars.githubusercontent.com/u/34240465?v=4 url: https://github.com/mercedes-benz @@ -95,9 +98,6 @@ sponsors: - - login: samuelcolvin avatarUrl: https://avatars.githubusercontent.com/u/4039449?u=42eb3b833047c8c4b4f647a031eaef148c16d93f&v=4 url: https://github.com/samuelcolvin - - login: vincentkoc - avatarUrl: https://avatars.githubusercontent.com/u/25068?u=cbf098fc04c0473523d373b0dd2145b4ec99ef93&v=4 - url: https://github.com/vincentkoc - login: ProteinQure avatarUrl: https://avatars.githubusercontent.com/u/33707203?v=4 url: https://github.com/ProteinQure @@ -107,6 +107,9 @@ sponsors: - login: otosky avatarUrl: https://avatars.githubusercontent.com/u/42260747?u=69d089387c743d89427aa4ad8740cfb34045a9e0&v=4 url: https://github.com/otosky + - login: khadrawy + avatarUrl: https://avatars.githubusercontent.com/u/13686061?u=59f25ef42ecf04c22657aac4238ce0e2d3d30304&v=4 + url: https://github.com/khadrawy - login: mjohnsey avatarUrl: https://avatars.githubusercontent.com/u/16784016?u=38fad2e6b411244560b3af99c5f5a4751bc81865&v=4 url: https://github.com/mjohnsey @@ -215,6 +218,9 @@ sponsors: - login: anomaly avatarUrl: https://avatars.githubusercontent.com/u/3654837?v=4 url: https://github.com/anomaly + - login: vincentkoc + avatarUrl: https://avatars.githubusercontent.com/u/25068?u=fbd5b2d51142daa4bdbc21e21953a3b8b8188a4a&v=4 + url: https://github.com/vincentkoc - login: jstanden avatarUrl: https://avatars.githubusercontent.com/u/63288?u=c3658d57d2862c607a0e19c2101c3c51876e36ad&v=4 url: https://github.com/jstanden @@ -248,6 +254,9 @@ sponsors: - login: TrevorBenson avatarUrl: https://avatars.githubusercontent.com/u/9167887?u=dccbea3327a57750923333d8ebf1a0b3f1948949&v=4 url: https://github.com/TrevorBenson + - login: kaangiray26 + avatarUrl: https://avatars.githubusercontent.com/u/11297495?u=e85327a77db45906d44f3ff06dd7f3303c644096&v=4 + url: https://github.com/kaangiray26 - login: wdwinslow avatarUrl: https://avatars.githubusercontent.com/u/11562137?u=dc01daafb354135603a263729e3d26d939c0c452&v=4 url: https://github.com/wdwinslow @@ -263,9 +272,9 @@ sponsors: - login: dannywade avatarUrl: https://avatars.githubusercontent.com/u/13680237?u=418ee985bd41577b20fde81417fb2d901e875e8a&v=4 url: https://github.com/dannywade - - login: khadrawy - avatarUrl: https://avatars.githubusercontent.com/u/13686061?u=59f25ef42ecf04c22657aac4238ce0e2d3d30304&v=4 - url: https://github.com/khadrawy + - login: gorhack + avatarUrl: https://avatars.githubusercontent.com/u/4141690?u=ec119ebc4bdf00a7bc84657a71aa17834f4f27f3&v=4 + url: https://github.com/gorhack - login: Ryandaydev avatarUrl: https://avatars.githubusercontent.com/u/4292423?u=48f68868db8886fce31a1d802c1003914c6cd7c6&v=4 url: https://github.com/Ryandaydev @@ -314,9 +323,9 @@ sponsors: - login: mobyw avatarUrl: https://avatars.githubusercontent.com/u/44370805?v=4 url: https://github.com/mobyw - - login: ArtyomVancyan - avatarUrl: https://avatars.githubusercontent.com/u/44609997?v=4 - url: https://github.com/ArtyomVancyan + - login: PelicanQ + avatarUrl: https://avatars.githubusercontent.com/u/77930606?v=4 + url: https://github.com/PelicanQ - login: TheR1D avatarUrl: https://avatars.githubusercontent.com/u/16740832?u=b0dfdbdb27b79729430c71c6128962f77b7b53f7&v=4 url: https://github.com/TheR1D @@ -341,6 +350,9 @@ sponsors: - login: dvlpjrs avatarUrl: https://avatars.githubusercontent.com/u/32254642?u=fbd6ad0324d4f1eb6231cf775be1c7bd4404e961&v=4 url: https://github.com/dvlpjrs + - login: ArtyomVancyan + avatarUrl: https://avatars.githubusercontent.com/u/44609997?v=4 + url: https://github.com/ArtyomVancyan - login: caviri avatarUrl: https://avatars.githubusercontent.com/u/45425937?u=4e14bd64282bad8f385eafbdb004b5a279366d6e&v=4 url: https://github.com/caviri @@ -356,9 +368,6 @@ sponsors: - login: PunRabbit avatarUrl: https://avatars.githubusercontent.com/u/70463212?u=1a835cfbc99295a60c8282f6aa6199d1b42241a5&v=4 url: https://github.com/PunRabbit - - login: PelicanQ - avatarUrl: https://avatars.githubusercontent.com/u/77930606?v=4 - url: https://github.com/PelicanQ - login: tochikuji avatarUrl: https://avatars.githubusercontent.com/u/851759?v=4 url: https://github.com/tochikuji @@ -380,9 +389,9 @@ sponsors: - login: Alisa-lisa avatarUrl: https://avatars.githubusercontent.com/u/4137964?u=e7e393504f554f4ff15863a1e01a5746863ef9ce&v=4 url: https://github.com/Alisa-lisa - - login: Graeme22 - avatarUrl: https://avatars.githubusercontent.com/u/4185684?u=498182a42300d7bcd4de1215190cb17eb501136c&v=4 - url: https://github.com/Graeme22 + - login: hcristea + avatarUrl: https://avatars.githubusercontent.com/u/7814406?u=61d7a4fcf846983a4606788eac25e1c6c1209ba8&v=4 + url: https://github.com/hcristea - login: ddanier avatarUrl: https://avatars.githubusercontent.com/u/113563?u=ed1dc79de72f93bd78581f88ebc6952b62f472da&v=4 url: https://github.com/ddanier @@ -434,6 +443,9 @@ sponsors: - login: artempronevskiy avatarUrl: https://avatars.githubusercontent.com/u/12235104?u=03df6e1e55c9c6fe5d230adabb8dd7d43d8bbe8f&v=4 url: https://github.com/artempronevskiy + - login: Graeme22 + avatarUrl: https://avatars.githubusercontent.com/u/4185684?u=498182a42300d7bcd4de1215190cb17eb501136c&v=4 + url: https://github.com/Graeme22 - login: danielunderwood avatarUrl: https://avatars.githubusercontent.com/u/4472301?v=4 url: https://github.com/danielunderwood @@ -458,9 +470,6 @@ sponsors: - login: harsh183 avatarUrl: https://avatars.githubusercontent.com/u/7780198?v=4 url: https://github.com/harsh183 - - login: hcristea - avatarUrl: https://avatars.githubusercontent.com/u/7814406?u=61d7a4fcf846983a4606788eac25e1c6c1209ba8&v=4 - url: https://github.com/hcristea - - login: larsyngvelundin avatarUrl: https://avatars.githubusercontent.com/u/34173819?u=74958599695bf83ac9f1addd935a51548a10c6b0&v=4 url: https://github.com/larsyngvelundin @@ -479,9 +488,15 @@ sponsors: - login: FabulousCodingFox avatarUrl: https://avatars.githubusercontent.com/u/78906517?u=924a27cbee3db7e0ece5cc1509921402e1445e74&v=4 url: https://github.com/FabulousCodingFox - - login: anqorithm - avatarUrl: https://avatars.githubusercontent.com/u/61029571?u=468256fa4e2d9ce2870b608299724bebb7a33f18&v=4 - url: https://github.com/anqorithm + - login: gateremark + avatarUrl: https://avatars.githubusercontent.com/u/91592218?u=969314eb2cfb035196f4d19499ec6f5050d7583a&v=4 + url: https://github.com/gateremark + - login: morzan1001 + avatarUrl: https://avatars.githubusercontent.com/u/47593005?u=c30ab7230f82a12a9b938dcb54f84a996931409a&v=4 + url: https://github.com/morzan1001 + - login: Toothwitch + avatarUrl: https://avatars.githubusercontent.com/u/1710406?u=5eebb23b46cd26e48643b9e5179536cad491c17a&v=4 + url: https://github.com/Toothwitch - login: ssbarnea avatarUrl: https://avatars.githubusercontent.com/u/102495?u=c7bd9ddf127785286fc939dd18cb02db0a453bce&v=4 url: https://github.com/ssbarnea From 38d409dd67513de0983814f7b5e1918c6ed1521e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ph=C6=B0=C6=A1ng=20T=E1=BA=A5n=20Th=C3=A0nh?= <51350651+ptt3199@users.noreply.github.com> Date: Sat, 8 Feb 2025 05:17:13 +0700 Subject: [PATCH 022/157] =?UTF-8?q?=F0=9F=8C=90=20Add=20Vietnamese=20trans?= =?UTF-8?q?lation=20for=20`docs/vi/docs/environment-variables.md`=20(#1328?= =?UTF-8?q?7)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/vi/docs/environment-variables.md | 300 ++++++++++++++++++++++++++ 1 file changed, 300 insertions(+) create mode 100644 docs/vi/docs/environment-variables.md diff --git a/docs/vi/docs/environment-variables.md b/docs/vi/docs/environment-variables.md new file mode 100644 index 000000000..dd06f8959 --- /dev/null +++ b/docs/vi/docs/environment-variables.md @@ -0,0 +1,300 @@ +# Biến môi trường (Environment Variables) + +/// tip + +Nếu bạn đã biết về "biến môi trường" và cách sử dụng chúng, bạn có thể bỏ qua phần này. + +/// + +Một biến môi trường (còn được gọi là "**env var**") là một biến mà tồn tại **bên ngoài** đoạn mã Python, ở trong **hệ điều hành**, và có thể được đọc bởi đoạn mã Python của bạn (hoặc bởi các chương trình khác). + +Các biến môi trường có thể được sử dụng để xử lí **các thiết lập** của ứng dụng, như một phần của **các quá trình cài đặt** Python, v.v. + +## Tạo và Sử dụng các Biến Môi Trường + +Bạn có thể **tạo** và sử dụng các biến môi trường trong **shell (terminal)**, mà không cần sử dụng Python: + +//// tab | Linux, macOS, Windows Bash + +
+ +```console +// Bạn có thể tạo một biến môi trường MY_NAME với +$ export MY_NAME="Wade Wilson" + +// Sau đó bạn có thể sử dụng nó với các chương trình khác, như +$ echo "Hello $MY_NAME" + +Hello Wade Wilson +``` + +
+ +//// + +//// tab | Windows PowerShell + +
+ +```console +// Tạo một biến môi trường MY_NAME +$ $Env:MY_NAME = "Wade Wilson" + +// Sử dụng nó với các chương trình khác, như là +$ echo "Hello $Env:MY_NAME" + +Hello Wade Wilson +``` + +
+ +//// + +## Đọc các Biến Môi Trường trong Python + +Bạn cũng có thể tạo các biến môi trường **bên ngoài** đoạn mã Python, trong terminal (hoặc bằng bất kỳ phương pháp nào khác), và sau đó **đọc chúng trong Python**. + +Ví dụ, bạn có một file `main.py` với: + +```Python hl_lines="3" +import os + +name = os.getenv("MY_NAME", "World") +print(f"Hello {name} from Python") +``` + +/// tip + +Tham số thứ hai cho `os.getenv()` là giá trị mặc định để trả về. + +Nếu không được cung cấp, nó mặc định là `None`, ở đây chúng ta cung cấp `"World"` là giá trị mặc định để sử dụng. + +/// + +Sau đó bạn có thể gọi chương trình Python: + +//// tab | Linux, macOS, Windows Bash + +
+ +```console +// Ở đây chúng ta chưa cài đặt biến môi trường +$ python main.py + +// Vì chúng ta chưa cài đặt biến môi trường, chúng ta nhận được giá trị mặc định + +Hello World from Python + +// Nhưng nếu chúng ta tạo một biến môi trường trước đó +$ export MY_NAME="Wade Wilson" + +// Và sau đó gọi chương trình lại +$ python main.py + +// Bây giờ nó có thể đọc biến môi trường + +Hello Wade Wilson from Python +``` + +
+ +//// + +//// tab | Windows PowerShell + +
+ +```console +// Ở đây chúng ta chưa cài đặt biến môi trường +$ python main.py + +// Vì chúng ta chưa cài đặt biến môi trường, chúng ta nhận được giá trị mặc định + +Hello World from Python + +// Nhưng nếu chúng ta tạo một biến môi trường trước đó +$ $Env:MY_NAME = "Wade Wilson" + +// Và sau đó gọi chương trình lại +$ python main.py + +// Bây giờ nó có thể đọc biến môi trường + +Hello Wade Wilson from Python +``` + +
+ +//// + +Vì các biến môi trường có thể được tạo bên ngoài đoạn mã Python, nhưng có thể được đọc bởi đoạn mã Python, và không cần được lưu trữ (commit vào `git`) cùng với các file khác, nên chúng thường được sử dụng để lưu các thiết lập hoặc **cấu hình**. + +Bạn cũng có thể tạo ra một biến môi trường dành riêng cho một **lần gọi chương trình**, chỉ có thể được sử dụng bởi chương trình đó, và chỉ trong thời gian chạy của chương trình. + +Để làm điều này, tạo nó ngay trước chương trình đó, trên cùng một dòng: + +
+ +```console +// Tạo một biến môi trường MY_NAME cho lần gọi chương trình này +$ MY_NAME="Wade Wilson" python main.py + +// Bây giờ nó có thể đọc biến môi trường + +Hello Wade Wilson from Python + +// Biến môi trường không còn tồn tại sau đó +$ python main.py + +Hello World from Python +``` + +
+ +/// tip + +Bạn có thể đọc thêm về điều này tại The Twelve-Factor App: Config. + +/// + +## Các Kiểu (Types) và Kiểm tra (Validation) + +Các biến môi trường có thể chỉ xử lí **chuỗi ký tự**, vì chúng nằm bên ngoài đoạn mã Python và phải tương thích với các chương trình khác và phần còn lại của hệ thống (và thậm chí với các hệ điều hành khác, như Linux, Windows, macOS). + +Điều này có nghĩa là **bất kỳ giá trị nào** được đọc trong Python từ một biến môi trường **sẽ là một `str`**, và bất kỳ hành động chuyển đổi sang kiểu dữ liệu khác hoặc hành động kiểm tra nào cũng phải được thực hiện trong đoạn mã. + +Bạn sẽ học thêm về việc sử dụng biến môi trường để xử lí **các thiết lập ứng dụng** trong [Hướng dẫn nâng cao - Các thiết lập và biến môi trường](./advanced/settings.md){.internal-link target=_blank}. + +## Biến môi trường `PATH` + +Có một biến môi trường **đặc biệt** được gọi là **`PATH`** được sử dụng bởi các hệ điều hành (Linux, macOS, Windows) nhằm tìm các chương trình để thực thi. + +Giá trị của biến môi trường `PATH` là một chuỗi dài được tạo bởi các thư mục được phân tách bởi dấu hai chấm `:` trên Linux và macOS, và bởi dấu chấm phẩy `;` trên Windows. + +Ví dụ, biến môi trường `PATH` có thể có dạng như sau: + +//// tab | Linux, macOS + +```plaintext +/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin +``` + +Điều này có nghĩa là hệ thống sẽ tìm kiếm các chương trình trong các thư mục: + +* `/usr/local/bin` +* `/usr/bin` +* `/bin` +* `/usr/sbin` +* `/sbin` + +//// + +//// tab | Windows + +```plaintext +C:\Program Files\Python312\Scripts;C:\Program Files\Python312;C:\Windows\System32 +``` + +Điều này có nghĩa là hệ thống sẽ tìm kiếm các chương trình trong các thư mục: + +* `C:\Program Files\Python312\Scripts` +* `C:\Program Files\Python312` +* `C:\Windows\System32` + +//// + +Khi bạn gõ một **lệnh** trong terminal, hệ điều hành **tìm kiếm** chương trình trong **mỗi thư mục** được liệt kê trong biến môi trường `PATH`. + +Ví dụ, khi bạn gõ `python` trong terminal, hệ điều hành tìm kiếm một chương trình được gọi `python` trong **thư mục đầu tiên** trong danh sách đó. + +Nếu tìm thấy, nó sẽ **sử dụng** nó. Nếu không tìm thấy, nó sẽ tiếp tục tìm kiếm trong **các thư mục khác**. + +### Cài đặt Python và cập nhật biến môi trường `PATH` + +Khi bạn cài đặt Python, bạn có thể được hỏi nếu bạn muốn cập nhật biến môi trường `PATH`. + +//// tab | Linux, macOS + +Giả sử bạn cài đặt Python vào thư mục `/opt/custompython/bin`. + +Nếu bạn chọn cập nhật biến môi trường `PATH`, thì cài đặt sẽ thêm `/opt/custompython/bin` vào biến môi trường `PATH`. + +Nó có thể có dạng như sau: + +```plaintext +/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/custompython/bin +``` + +Như vậy, khi bạn gõ `python` trong terminal, hệ thống sẽ tìm thấy chương trình Python trong `/opt/custompython/bin` (thư mục cuối) và sử dụng nó. + +//// + +//// tab | Windows + +Giả sử bạn cài đặt Python vào thư mục `C:\opt\custompython\bin`. + +Nếu bạn chọn cập nhật biến môi trường `PATH`, thì cài đặt sẽ thêm `C:\opt\custompython\bin` vào biến môi trường `PATH`. + +Nó có thể có dạng như sau: + +```plaintext +C:\Program Files\Python312\Scripts;C:\Program Files\Python312;C:\Windows\System32;C:\opt\custompython\bin +``` + +Như vậy, khi bạn gõ `python` trong terminal, hệ thống sẽ tìm thấy chương trình Python trong `C:\opt\custompython\bin` (thư mục cuối) và sử dụng nó. + +//// + +Vậy, nếu bạn gõ: + +
+ +```console +$ python +``` + +
+ +//// tab | Linux, macOS + +Hệ thống sẽ **tìm kiếm** chương trình `python` trong `/opt/custompython/bin` và thực thi nó. + +Nó tương đương với việc bạn gõ: + +
+ +```console +$ /opt/custompython/bin/python +``` + +
+ +//// + +//// tab | Windows + +Hệ thống sẽ **tìm kiếm** chương trình `python` trong `C:\opt\custompython\bin\python` và thực thi nó. + +Nó tương đương với việc bạn gõ: + +
+ +```console +$ C:\opt\custompython\bin\python +``` + +
+ +//// + +Thông tin này sẽ hữu ích khi bạn học về [Môi trường ảo](virtual-environments.md){.internal-link target=_blank}. + +## Kết luận + +Với những thông tin này, bạn có thể hiểu được **các biến môi trường là gì** và **cách sử dụng chúng trong Python**. + +Bạn có thể đọc thêm về chúng tại Wikipedia cho Biến môi trường. + +Trong nhiều trường hợp, cách các biến môi trường trở nên hữu ích và có thể áp dụng không thực sự rõ ràng ngay từ đầu, nhưng chúng sẽ liên tục xuất hiện trong rất nhiều tình huống khi bạn phát triển ứng dụng, vì vậy việc hiểu biết về chúng là hữu ích. + +Chẳng hạn, bạn sẽ cần những thông tin này khi bạn học về [Môi trường ảo](virtual-environments.md). From f9352c18de97dc8867e69c3a0695092495c2ff1f Mon Sep 17 00:00:00 2001 From: Valentyn Date: Sat, 8 Feb 2025 00:17:53 +0200 Subject: [PATCH 023/157] =?UTF-8?q?=F0=9F=8C=90=20Add=20Ukrainian=20transl?= =?UTF-8?q?ation=20for=20`docs/uk/docs/tutorial/static-files.md`=20(#13285?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/uk/docs/tutorial/static-files.md | 40 +++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 docs/uk/docs/tutorial/static-files.md diff --git a/docs/uk/docs/tutorial/static-files.md b/docs/uk/docs/tutorial/static-files.md new file mode 100644 index 000000000..a84782d8f --- /dev/null +++ b/docs/uk/docs/tutorial/static-files.md @@ -0,0 +1,40 @@ +# Статичні файли + +Ви можете автоматично надавати статичні файли з каталогу, використовуючи `StaticFiles`. + +## Використання `StaticFiles` + +* Імпортуйте `StaticFiles`. +* "Під'єднати" екземпляр `StaticFiles()` з вказанням необхідного шляху. + +{* ../../docs_src/static_files/tutorial001.py hl[2,6] *} + +/// note | Технічні деталі + +Ви також можете використовувати `from starlette.staticfiles import StaticFiles`. + +**FastAPI** надає той самий `starlette.staticfiles`, що й `fastapi.staticfiles` для зручності розробників. Але фактично він безпосередньо походить із Starlette. + +/// + +### Що таке "Під'єднання" + +"Під'єднання" означає додавання повноцінного "незалежного" застосунку за певним шляхом, який потім обробляє всі під шляхи. + +Це відрізняється від використання `APIRouter`, оскільки під'єднаний застосунок є повністю незалежним. OpenAPI та документація вашого основного застосунку не будуть знати нічого про ваш під'єднаний застосунок. + +Ви можете дізнатися більше про це в [Посібнику для просунутих користувачів](../advanced/index.md){.internal-link target=_blank}. + +## Деталі + +Перше `"/static"` вказує на під шлях, за яким буде "під'єднано" цей новий "застосунок". Тому будь-який шлях, який починається з `"/static"`, буде оброблятися ним. + +`directory="static"` визначає каталог, що містить ваші статичні файли. + +`name="static"` це ім'я, яке можна використовувати всередині **FastAPI**. + +Усі ці параметри можуть бути змінені відповідно до потреб і особливостей вашого застосунку. + +## Додаткова інформація + +Детальніше про налаштування та можливості можна дізнатися в документації Starlette про статичні файли. From 8a6d81afad05ad57efbce7d72bf5d803029b93bc Mon Sep 17 00:00:00 2001 From: github-actions Date: Fri, 7 Feb 2025 22:17:59 +0000 Subject: [PATCH 024/157] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [skip ci] --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index 18ef5911c..c52fb0f63 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -18,6 +18,7 @@ hide: ### Internal +* 👥 Update FastAPI People - Sponsors. PR [#13295](https://github.com/fastapi/fastapi/pull/13295) by [@tiangolo](https://github.com/tiangolo). * 👥 Update FastAPI People - Experts. PR [#13303](https://github.com/fastapi/fastapi/pull/13303) by [@tiangolo](https://github.com/tiangolo). * 👥 Update FastAPI GitHub topic repositories. PR [#13302](https://github.com/fastapi/fastapi/pull/13302) by [@tiangolo](https://github.com/tiangolo). * 👥 Update FastAPI People - Contributors and Translators. PR [#13293](https://github.com/fastapi/fastapi/pull/13293) by [@tiangolo](https://github.com/tiangolo). From e86ef5e57d17be4e868b495102961b59844bd879 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ph=C6=B0=C6=A1ng=20T=E1=BA=A5n=20Th=C3=A0nh?= <51350651+ptt3199@users.noreply.github.com> Date: Sat, 8 Feb 2025 05:19:18 +0700 Subject: [PATCH 025/157] =?UTF-8?q?=F0=9F=8C=90=20Add=20Vietnamese=20trans?= =?UTF-8?q?lation=20for=20`docs/vi/docs/virtual-environments.md`=20(#13282?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/vi/docs/virtual-environments.md | 842 +++++++++++++++++++++++++++ 1 file changed, 842 insertions(+) create mode 100644 docs/vi/docs/virtual-environments.md diff --git a/docs/vi/docs/virtual-environments.md b/docs/vi/docs/virtual-environments.md new file mode 100644 index 000000000..22d8e153e --- /dev/null +++ b/docs/vi/docs/virtual-environments.md @@ -0,0 +1,842 @@ +# Môi trường ảo (Virtual Environments) + +Khi bạn làm việc trong các dự án Python, bạn có thể sử dụng một **môi trường ảo** (hoặc một cơ chế tương tự) để cách ly các gói bạn cài đặt cho mỗi dự án. + +/// info +Nếu bạn đã biết về các môi trường ảo, cách tạo chúng và sử dụng chúng, bạn có thể bỏ qua phần này. 🤓 + +/// + +/// tip + +Một **môi trường ảo** khác với một **biến môi trường (environment variable)**. + +Một **biến môi trường** là một biến trong hệ thống có thể được sử dụng bởi các chương trình. + +Một **môi trường ảo** là một thư mục với một số tệp trong đó. + +/// + +/// info + +Trang này sẽ hướng dẫn bạn cách sử dụng các **môi trường ảo** và cách chúng hoạt động. + +Nếu bạn đã sẵn sàng sử dụng một **công cụ có thể quản lý tất cả mọi thứ** cho bạn (bao gồm cả việc cài đặt Python), hãy thử uv. + +/// + +## Tạo một Dự án + +Đầu tiên, tạo một thư mục cho dự án của bạn. + +Cách tôi thường làm là tạo một thư mục có tên `code` trong thư mục `home/user`. + +Và trong thư mục đó, tôi tạo một thư mục cho mỗi dự án. + +
+ +```console +// Đi đến thư mục home +$ cd +// Tạo một thư mục cho tất cả các dự án của bạn +$ mkdir code +// Vào thư mục code +$ cd code +// Tạo một thư mục cho dự án này +$ mkdir awesome-project +// Vào thư mục dự án +$ cd awesome-project +``` + +
+ +## Tạo một Môi trường ảo + +Khi bạn bắt đầu làm việc với một dự án Python **trong lần đầu**, hãy tạo một môi trường ảo **trong thư mục dự án của bạn**. + +/// tip + +Bạn cần làm điều này **một lần cho mỗi dự án**, không phải mỗi khi bạn làm việc. +/// + +//// tab | `venv` + +Để tạo một môi trường ảo, bạn có thể sử dụng module `venv` có sẵn của Python. + +
+ +```console +$ python -m venv .venv +``` + +
+ +/// details | Cách các lệnh hoạt động + +* `python`: sử dụng chương trình `python` +* `-m`: gọi một module như một script, chúng ta sẽ nói về module đó sau +* `venv`: sử dụng module `venv` được cài đặt sẵn của Python +* `.venv`: tạo môi trường ảo trong thư mục mới `.venv` + +/// + +//// + +//// tab | `uv` + +Nếu bạn có `uv` được cài đặt, bạn có thể sử dụng nó để tạo một môi trường ảo. + +
+ +```console +$ uv venv +``` + +
+ +/// tip + +Mặc định, `uv` sẽ tạo một môi trường ảo trong một thư mục có tên `.venv`. + +Nhưng bạn có thể tùy chỉnh nó bằng cách thêm một đối số với tên thư mục. + +/// + +//// + +Lệnh này tạo một môi trường ảo mới trong một thư mục có tên `.venv`. + +/// details | `.venv` hoặc tên khác + +Bạn có thể tạo môi trường ảo trong một thư mục khác, nhưng thường người ta quy ước đặt nó là `.venv`. + +/// + +## Kích hoạt Môi trường ảo + +Kích hoạt môi trường ảo mới để bất kỳ lệnh Python nào bạn chạy hoặc gói nào bạn cài đặt sẽ sử dụng nó. + +/// tip + +Làm điều này **mỗi khi** bạn bắt đầu một **phiên terminal mới** để làm việc trên dự án. + +/// + +//// tab | Linux, macOS + +
+ +```console +$ source .venv/bin/activate +``` + +
+ +//// + +//// tab | Windows PowerShell + +
+ +```console +$ .venv\Scripts\Activate.ps1 +``` + +
+ +//// + +//// tab | Windows Bash + +Nếu bạn sử dụng Bash cho Windows (ví dụ: Git Bash): + +
+ +```console +$ source .venv/Scripts/activate +``` + +
+ +//// + +/// tip + +Mỗi khi bạn cài đặt thêm một **package mới** trong môi trường đó, hãy **kích hoạt** môi trường đó lại. + +Điều này đảm bảo rằng khi bạn sử dụng một **chương trình dòng lệnh (CLI)** được cài đặt từ gói đó, bạn sẽ dùng bản cài đặt từ môi trường ảo của mình thay vì bản được cài đặt toàn cục khác có thể có phiên bản khác với phiên bản bạn cần. + +/// + +## Kiểm tra xem Môi trường ảo đã được Kích hoạt chưa + +Kiểm tra xem môi trường ảo đã được kích hoạt chưa (lệnh trước đó đã hoạt động). + +/// tip + +Điều này là **không bắt buộc**, nhưng nó là một cách tốt để **kiểm tra** rằng mọi thứ đang hoạt động như mong đợi và bạn đang sử dụng đúng môi trường ảo mà bạn đã định. + +/// + +//// tab | Linux, macOS, Windows Bash + +
+ +```console +$ which python + +/home/user/code/awesome-project/.venv/bin/python +``` + +
+ +Nếu nó hiển thị `python` binary tại `.venv/bin/python`, trong dự án của bạn (trong trường hợp `awesome-project`), thì tức là nó hoạt động. 🎉 + +//// + +//// tab | Windows PowerShell + +
+ +```console +$ Get-Command python + +C:\Users\user\code\awesome-project\.venv\Scripts\python +``` + +
+ +Nếu nó hiển thị `python` binary tại `.venv\Scripts\python`, trong dự án của bạn (trong trường hợp `awesome-project`), thì tức là nó hoạt động. 🎉 + +//// + +## Nâng cấp `pip` + +/// tip + +Nếu bạn sử dụng `uv` bạn sử dụng nó để cài đặt thay vì `pip`, thì bạn không cần cập nhật `pip`. 😎 + +/// + +Nếu bạn sử dụng `pip` để cài đặt gói (nó được cài đặt mặc định với Python), bạn nên **nâng cấp** nó lên phiên bản mới nhất. + +Nhiều lỗi khác nhau trong khi cài đặt gói được giải quyết chỉ bằng cách nâng cấp `pip` trước. + +/// tip + +Bạn thường làm điều này **một lần**, ngay sau khi bạn tạo môi trường ảo. + +/// + +Đảm bảo rằng môi trường ảo đã được kích hoạt (với lệnh trên) và sau đó chạy: + +
+ +```console +$ python -m pip install --upgrade pip + +---> 100% +``` + +
+ +## Thêm `.gitignore` + +Nếu bạn sử dụng **Git** (nên làm), hãy thêm một file `.gitignore` để Git bỏ qua mọi thứ trong `.venv`. + +/// tip + +Nếu bạn sử dụng `uv` để tạo môi trường ảo, nó đã tự động làm điều này cho bạn, bạn có thể bỏ qua bước này. 😎 + +/// + +/// tip + +Làm điều này **một lần**, ngay sau khi bạn tạo môi trường ảo. + +/// + +
+ +```console +$ echo "*" > .venv/.gitignore +``` + +
+ +/// details | Cách lệnh hoạt động + +* `echo "*"`: sẽ "in" văn bản `*` trong terminal (phần tiếp theo sẽ thay đổi điều đó một chút) +* `>`: bất kỳ văn bản nào được in ra terminal bởi lệnh trước `>` không được in ra mà thay vào đó được viết vào file ở phía bên phải của `>` +* `.gitignore`: tên của file mà văn bản sẽ được viết vào + +Và `*` với Git có nghĩa là "mọi thứ". Vì vậy, nó sẽ bỏ qua mọi thứ trong thư mục `.venv`. + +Lệnh này sẽ tạo một file `.gitignore` với nội dung: + +```gitignore +* +``` + +/// + +## Cài đặt gói (packages) + +Sau khi kích hoạt môi trường, bạn có thể cài đặt các gói trong đó. + +/// tip + +Thực hiện điều này **một lần** khi cài đặt hoặc cập nhật gói cần thiết cho dự án của bạn. + +Nếu bạn cần cập nhật phiên bản hoặc thêm một gói mới, bạn sẽ **thực hiện điều này lại**. + +/// + +### Cài đặt gói trực tiếp + +Nếu bạn cần cập nhật phiên bản hoặc thêm một gói mới, bạn sẽ **thực hiện điều này lại**. + +/// tip +Để quản lý dự án tốt hơn, hãy liệt kê tất cả các gói và phiên bản cần thiết trong một file (ví dụ `requirements.txt` hoặc `pyproject.toml`). + +/// + +//// tab | `pip` + +
+ +```console +$ pip install "fastapi[standard]" + +---> 100% +``` + +
+ +//// + +//// tab | `uv` + +Nếu bạn có `uv`: + +
+ +```console +$ uv pip install "fastapi[standard]" +---> 100% +``` + +
+ +//// + +### Cài đặt từ `requirements.txt` + +Nếu bạn có một tệp `requirements.txt`, bạn có thể sử dụng nó để cài đặt các gói. + +//// tab | `pip` + +
+ +```console +$ pip install -r requirements.txt +---> 100% +``` + +
+ +//// + +//// tab | `uv` + +Nếu bạn có `uv`: + +
+ +```console +$ uv pip install -r requirements.txt +---> 100% +``` + +
+ +//// + +/// details | `requirements.txt` + +Một tệp `requirements.txt` với một số gói sẽ trông như thế này: + +```requirements.txt +fastapi[standard]==0.113.0 +pydantic==2.8.0 +``` + +/// + +## Chạy Chương trình của bạn + +Sau khi kích hoạt môi trường ảo, bạn có thể chạy chương trình của mình, nó sẽ sử dụng Python trong môi trường ảo của bạn với các gói bạn đã cài đặt. + +
+ +```console +$ python main.py + +Hello World +``` + +
+ +## Cấu hình Trình soạn thảo của bạn + +Nếu bạn sử dụng một trình soạn thảo, hãy đảm bảo bạn cấu hình nó để sử dụng cùng môi trường ảo mà bạn đã tạo (trình soạn thảo sẽ tự động phát hiện môi trường ảo) để bạn có thể nhận được tính năng tự động hoàn thành câu lệnh (autocomplete) và in lỗi trực tiếp trong trình soạn thảo (inline errors). + +Ví dụ: + +* VS Code +* PyCharm + +/// tip + +Bạn thường chỉ cần làm điều này **một lần**, khi bạn tạo môi trường ảo. + +/// + +## Huỷ kích hoạt Môi trường ảo + +Khi bạn hoàn tất việc làm trên dự án của bạn, bạn có thể **huỷ kích hoạt** môi trường ảo. + +
+ +```console +$ deactivate +``` + +
+ +Như vậy, khi bạn chạy `python`, nó sẽ không chạy từ môi trường ảo đó với các gói đã cài đặt. + +## Sẵn sàng để Làm việc + +Bây giờ bạn đã sẵn sàng để làm việc trên dự án của mình rồi đấy. + +/// tip + +Bạn muốn hiểu tất cả những gì ở trên? + +Tiếp tục đọc. 👇🤓 + +/// + +## Tại sao cần Môi trường ảo + +Để làm việc với FastAPI, bạn cần cài đặt Python. + +Sau đó, bạn sẽ cần **cài đặt** FastAPI và bất kỳ **gói** nào mà bạn muốn sử dụng. + +Để cài đặt gói, bạn thường sử dụng lệnh `pip` có sẵn với Python (hoặc các phiên bản tương tự). + +Tuy nhiên, nếu bạn sử dụng `pip` trực tiếp, các gói sẽ được cài đặt trong **môi trường Python toàn cục** của bạn (phần cài đặt toàn cục của Python). + +### Vấn đề + +Vậy, vấn đề gì khi cài đặt gói trong môi trường Python toàn cục? + +Trong một vài thời điểm, bạn sẽ phải viết nhiều chương trình khác nhau phụ thuộc vào **các gói khác nhau**. Và một số dự án bạn thực hiện lại phụ thuộc vào **các phiên bản khác nhau** của cùng một gói. 😱 + +Ví dụ, bạn có thể tạo một dự án được gọi là `philosophers-stone`, chương trình này phụ thuộc vào một gói khác được gọi là **`harry`, sử dụng phiên bản `1`**. Vì vậy, bạn cần cài đặt `harry`. + +```mermaid +flowchart LR + stone(philosophers-stone) -->|phụ thuộc| harry-1[harry v1] +``` + +Sau đó, vào một vài thời điểm sau, bạn tạo một dự án khác được gọi là `prisoner-of-azkaban`, và dự án này cũng phụ thuộc vào `harry`, nhưng dự án này cần **`harry` phiên bản `3`**. + +```mermaid +flowchart LR + azkaban(prisoner-of-azkaban) --> |phụ thuộc| harry-3[harry v3] +``` + +Bây giờ, vấn đề là, nếu bạn cài đặt các gói toàn cục (trong môi trường toàn cục) thay vì trong một **môi trường ảo cục bộ**, bạn sẽ phải chọn phiên bản `harry` nào để cài đặt. + +Nếu bạn muốn chạy `philosophers-stone` bạn sẽ cần phải cài đặt `harry` phiên bản `1`, ví dụ với: + +
+ +```console +$ pip install "harry==1" +``` + +
+ +Và sau đó bạn sẽ có `harry` phiên bản `1` được cài đặt trong môi trường Python toàn cục của bạn. + +```mermaid +flowchart LR + subgraph global[môi trường toàn cục] + harry-1[harry v1] + end + subgraph stone-project[dự án philosophers-stone ] + stone(philosophers-stone) -->|phụ thuộc| harry-1 + end +``` + +Nhưng sau đó, nếu bạn muốn chạy `prisoner-of-azkaban`, bạn sẽ cần phải gỡ bỏ `harry` phiên bản `1` và cài đặt `harry` phiên bản `3` (hoặc chỉ cần cài đặt phiên bản `3` sẽ tự động gỡ bỏ phiên bản `1`). + +
+ +```console +$ pip install "harry==3" +``` + +
+ +Và sau đó bạn sẽ có `harry` phiên bản `3` được cài đặt trong môi trường Python toàn cục của bạn. + +Và nếu bạn cố gắng chạy `philosophers-stone` lại, có khả năng nó sẽ **không hoạt động** vì nó cần `harry` phiên bản `1`. + +```mermaid +flowchart LR + subgraph global[môi trường toàn cục] + harry-1[harry v1] + style harry-1 fill:#ccc,stroke-dasharray: 5 5 + harry-3[harry v3] + end + subgraph stone-project[dự án philosophers-stone ] + stone(philosophers-stone) -.-x|⛔️| harry-1 + end + subgraph azkaban-project[dự án prisoner-of-azkaban ] + azkaban(prisoner-of-azkaban) --> |phụ thuộc| harry-3 + end +``` + +/// tip + +Mặc dù các gói Python thường cố gắng **tránh các thay đổi làm hỏng code** trong **phiên bản mới**, nhưng để đảm bảo an toàn, bạn nên chủ động cài đặt phiên bản mới và chạy kiểm thử để xác nhận mọi thứ vẫn hoạt động đúng. + +/// + +Bây giờ, hãy hình dung về **nhiều** gói khác nhau mà tất cả các dự án của bạn phụ thuộc vào. Rõ ràng rất khó để quản lý. Điều này dẫn tới việc là bạn sẽ có nhiều dự án với **các phiên bản không tương thích** của các gói, và bạn có thể không biết tại sao một số thứ không hoạt động. + +Hơn nữa, tuỳ vào hệ điều hành của bạn (vd Linux, Windows, macOS), có thể đã có Python được cài đặt sẵn. Trong trường hợp ấy, một vài gói nhiều khả năng đã được cài đặt trước với các phiên bản **cần thiết cho hệ thống của bạn**. Nếu bạn cài đặt các gói trong môi trường Python toàn cục, bạn có thể sẽ **phá vỡ** một số chương trình đã được cài đặt sẵn cùng hệ thống. + +## Nơi các Gói được Cài đặt + +Khi bạn cài đặt Python, nó sẽ tạo ra một vài thư mục và tệp trong máy tính của bạn. + +Một vài thư mục này là những thư mục chịu trách nhiệm có tất cả các gói bạn cài đặt. + +Khi bạn chạy: + +
+ +```console +// Đừng chạy lệnh này ngay, đây chỉ là một ví dụ 🤓 +$ pip install "fastapi[standard]" +---> 100% +``` + +
+ +Lệnh này sẽ tải xuống một tệp nén với mã nguồn FastAPI, thường là từ PyPI. + +Nó cũng sẽ **tải xuống** các tệp cho các gói khác mà FastAPI phụ thuộc vào. + +Sau đó, nó sẽ **giải nén** tất cả các tệp đó và đưa chúng vào một thư mục trong máy tính của bạn. + +Mặc định, nó sẽ đưa các tệp đã tải xuống và giải nén vào thư mục được cài đặt cùng Python của bạn, đó là **môi trường toàn cục**. + +## Những Môi trường ảo là gì? + +Cách giải quyết cho vấn đề có tất cả các gói trong môi trường toàn cục là sử dụng một **môi trường ảo cho mỗi dự án** bạn làm việc. + +Một môi trường ảo là một **thư mục**, rất giống với môi trường toàn cục, trong đó bạn có thể cài đặt các gói cho một dự án. + +Vì vậy, mỗi dự án sẽ có một môi trường ảo riêng của nó (thư mục `.venv`) với các gói riêng của nó. + +```mermaid +flowchart TB + subgraph stone-project[dự án philosophers-stone ] + stone(philosophers-stone) --->|phụ thuộc| harry-1 + subgraph venv1[.venv] + harry-1[harry v1] + end + end + subgraph azkaban-project[dự án prisoner-of-azkaban ] + azkaban(prisoner-of-azkaban) --->|phụ thuộc| harry-3 + subgraph venv2[.venv] + harry-3[harry v3] + end + end + stone-project ~~~ azkaban-project +``` + +## Kích hoạt Môi trường ảo nghĩa là gì + +Khi bạn kích hoạt một môi trường ảo, ví dụ với: + +//// tab | Linux, macOS + +
+ +```console +$ source .venv/bin/activate +``` + +
+ +//// + +//// tab | Windows PowerShell + +
+ +```console +$ .venv\Scripts\Activate.ps1 +``` + +
+ +//// + +//// tab | Windows Bash + +Nếu bạn sử dụng Bash cho Windows (ví dụ Git Bash): + +
+ +```console +$ source .venv/Scripts/activate +``` + +
+ +//// + +Lệnh này sẽ tạo hoặc sửa đổi một số [biến môi trường](environment-variables.md){.internal-link target=_blank} mà sẽ được sử dụng cho các lệnh tiếp theo. + +Một trong số đó là biến `PATH`. + +/// tip + +Bạn có thể tìm hiểu thêm về biến `PATH` trong [Biến môi trường](environment-variables.md#path-environment-variable){.internal-link target=_blank} section. + +/// + +Kích hoạt môi trường ảo thêm đường dẫn `.venv/bin` (trên Linux và macOS) hoặc `.venv\Scripts` (trên Windows) vào biến `PATH`. + +Giả sử rằng trước khi kích hoạt môi trường, biến `PATH` như sau: + +//// tab | Linux, macOS + +```plaintext +/usr/bin:/bin:/usr/sbin:/sbin +``` + +Nghĩa là hệ thống sẽ tìm kiếm chương trình trong: + +* `/usr/bin` +* `/bin` +* `/usr/sbin` +* `/sbin` + +//// + +//// tab | Windows + +```plaintext +C:\Windows\System32 +``` + +Nghĩa là hệ thống sẽ tìm kiếm chương trình trong: + +* `C:\Windows\System32` + +//// + +Sau khi kích hoạt môi trường ảo, biến `PATH` sẽ như sau: + +//// tab | Linux, macOS + +```plaintext +/home/user/code/awesome-project/.venv/bin:/usr/bin:/bin:/usr/sbin:/sbin +``` + +Nghĩa là hệ thống sẽ bắt đầu tìm kiếm chương trình trong: + +```plaintext +/home/user/code/awesome-project/.venv/bin +``` + +trước khi tìm kiếm trong các thư mục khác. + +Vì vậy, khi bạn gõ `python` trong terminal, hệ thống sẽ tìm thấy chương trình Python trong: + +```plaintext +/home/user/code/awesome-project/.venv/bin/python +``` + +và sử dụng chương trình đó. + +//// + +//// tab | Windows + +```plaintext +C:\Users\user\code\awesome-project\.venv\Scripts;C:\Windows\System32 +``` + +Nghĩa là hệ thống sẽ bắt đầu tìm kiếm chương trình trong: + +```plaintext +C:\Users\user\code\awesome-project\.venv\Scripts +``` + +trước khi tìm kiếm trong các thư mục khác. + +Vì vậy, khi bạn gõ `python` trong terminal, hệ thống sẽ tìm thấy chương trình Python trong: + +```plaintext +C:\Users\user\code\awesome-project\.venv\Scripts\python +``` + +và sử dụng chương trình đó. + +//// + +Một chi tiết quan trọng là nó sẽ đưa địa chỉ của môi trường ảo vào **đầu** của biến `PATH`. Hệ thống sẽ tìm kiếm nó **trước** khi tìm kiếm bất kỳ Python nào khác có sẵn. Vì vậy, khi bạn chạy `python`, nó sẽ sử dụng Python **từ môi trường ảo** thay vì bất kỳ Python nào khác (ví dụ, Python từ môi trường toàn cục). + +Kích hoạt một môi trường ảo cũng thay đổi một vài thứ khác, nhưng đây là một trong những điều quan trọng nhất mà nó thực hiện. + +## Kiểm tra một Môi trường ảo + +Khi bạn kiểm tra một môi trường ảo đã được kích hoạt chưa, ví dụ với: + +//// tab | Linux, macOS, Windows Bash + +
+ +```console +$ which python + +/home/user/code/awesome-project/.venv/bin/python +``` + +
+ +//// + +//// tab | Windows PowerShell + +
+ +```console +$ Get-Command python + +C:\Users\user\code\awesome-project\.venv\Scripts\python +``` + +
+ +//// + + +Điều đó có nghĩa là chương trình `python` sẽ được sử dụng là chương trình **trong môi trường ảo**. + +Bạn sử dụng `which` trên Linux và macOS và `Get-Command` trên Windows PowerShell. + +Cách hoạt động của lệnh này là nó sẽ đi và kiểm tra biến `PATH`, đi qua **mỗi đường dẫn theo thứ tự**, tìm kiếm chương trình được gọi là `python`. Khi nó tìm thấy nó, nó sẽ **hiển thị cho bạn đường dẫn** đến chương trình đó. + +Điều quan trọng nhất là khi bạn gọi `python`, đó chính là chương trình `python` được thực thi. + +Vì vậy, bạn có thể xác nhận nếu bạn đang ở trong môi trường ảo đúng. + +/// tip + +Dễ dàng kích hoạt một môi trường ảo, cài đặt Python, và sau đó **chuyển đến một dự án khác**. + +Và dự án thứ hai **sẽ không hoạt động** vì bạn đang sử dụng **Python không đúng**, từ một môi trường ảo cho một dự án khác. + +Thật tiện lợi khi có thể kiểm tra `python` nào đang được sử dụng 🤓 + +/// + +## Tại sao lại Huỷ kích hoạt một Môi trường ảo + +Ví dụ, bạn có thể làm việc trên một dự án `philosophers-stone`, **kích hoạt môi trường ảo**, cài đặt các gói và làm việc với môi trường ảo đó. + +Sau đó, bạn muốn làm việc trên **dự án khác** `prisoner-of-azkaban`. + +Bạn đi đến dự án đó: + +
+ +```console +$ cd ~/code/prisoner-of-azkaban +``` + +
+ +Nếu bạn không tắt môi trường ảo cho `philosophers-stone`, khi bạn chạy `python` trong terminal, nó sẽ cố gắng sử dụng Python từ `philosophers-stone`. + +
+ +```console +$ cd ~/code/prisoner-of-azkaban + +$ python main.py + +// Lỗi khi import sirius, nó không được cài đặt 😱 +Traceback (most recent call last): + File "main.py", line 1, in + import sirius +``` + +
+ +Nếu bạn huỷ kích hoạt môi trường ảo hiện tại và kích hoạt môi trường ảo mới cho `prisoner-of-azkaban`, khi bạn chạy `python`, nó sẽ sử dụng Python từ môi trường ảo trong `prisoner-of-azkaban`. + +
+ +```console +$ cd ~/code/prisoner-of-azkaban + +// Bạn không cần phải ở trong thư mục trước để huỷ kích hoạt, bạn có thể làm điều đó ở bất kỳ đâu, ngay cả sau khi đi đến dự án khác 😎 +$ deactivate + +// Kích hoạt môi trường ảo trong prisoner-of-azkaban/.venv 🚀 +$ source .venv/bin/activate + +// Bây giờ khi bạn chạy python, nó sẽ tìm thấy gói sirius được cài đặt trong môi trường ảo này ✨ +$ python main.py + +I solemnly swear 🐺 + +(Tôi long trọng thề 🐺 - câu này được lấy từ Harry Potter, chú thích của người dịch) +``` + +
+ +## Các cách làm tương tự + +Đây là một hướng dẫn đơn giản để bạn có thể bắt đầu và hiểu cách mọi thứ hoạt động **bên trong**. + +Có nhiều **cách khác nhau** để quản lí các môi trường ảo, các gói phụ thuộc (requirements), và các dự án. + +Một khi bạn đã sẵn sàng và muốn sử dụng một công cụ để **quản lí cả dự án**, các gói phụ thuộc, các môi trường ảo, v.v. Tôi sẽ khuyên bạn nên thử uv. + +`uv` có thể làm nhiều thứ, chẳng hạn: + +* **Cài đặt Python** cho bạn, bao gồm nhiều phiên bản khác nhau +* Quản lí **các môi trường ảo** cho các dự án của bạn +* Cài đặt **các gói (packages)** +* Quản lí **các thành phần phụ thuộc và phiên bản** của các gói cho dự án của bạn +* Đảm bảo rằng bạn có một **tập hợp chính xác** các gói và phiên bản để cài đặt, bao gồm các thành phần phụ thuộc của chúng, để bạn có thể đảm bảo rằng bạn có thể chạy dự án của bạn trong sản xuất chính xác như trong máy tính của bạn trong khi phát triển, điều này được gọi là **locking** +* Và còn nhiều thứ khác nữa + +## Kết luận + +Nếu bạn đã đọc và hiểu hết những điều này, khá chắc là bây giờ bạn đã **biết nhiều hơn** về môi trường ảo so với kha khá lập trình viên khác đấy. 🤓 + +Những hiểu biết chi tiết này có thể sẽ hữu ích với bạn trong tương lai khi mà bạn cần gỡ lỗi một vài thứ phức tạp, và bạn đã có những hiểu biết về **ngọn ngành gốc rễ cách nó hoạt động**. 😎 From 8cde8dc2a9e02dd70746a11dd906909449749b44 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 7 Feb 2025 22:22:35 +0000 Subject: [PATCH 026/157] =?UTF-8?q?=E2=AC=86=20Bump=20pillow=20from=2011.0?= =?UTF-8?q?.0=20to=2011.1.0=20(#13300)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [pillow](https://github.com/python-pillow/Pillow) from 11.0.0 to 11.1.0. - [Release notes](https://github.com/python-pillow/Pillow/releases) - [Changelog](https://github.com/python-pillow/Pillow/blob/main/CHANGES.rst) - [Commits](https://github.com/python-pillow/Pillow/compare/11.0.0...11.1.0) --- updated-dependencies: - dependency-name: pillow dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Alejandra <90076947+alejsdev@users.noreply.github.com> --- requirements-docs.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-docs.txt b/requirements-docs.txt index 6f391675a..9af2a85d9 100644 --- a/requirements-docs.txt +++ b/requirements-docs.txt @@ -8,7 +8,7 @@ pyyaml >=5.3.1,<7.0.0 # For Material for MkDocs, Chinese search jieba==0.42.1 # For image processing by Material for MkDocs -pillow==11.0.0 +pillow==11.1.0 # For image processing by Material for MkDocs cairosvg==2.7.1 mkdocstrings[python]==0.26.1 From 83332ff9b272d5df8e6e979e1cbd3e72ee5b2e84 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 7 Feb 2025 22:22:54 +0000 Subject: [PATCH 027/157] =?UTF-8?q?=E2=AC=86=20Bump=20mkdocs-material=20fr?= =?UTF-8?q?om=209.5.18=20to=209.6.1=20(#13301)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 9.5.18 to 9.6.1. - [Release notes](https://github.com/squidfunk/mkdocs-material/releases) - [Changelog](https://github.com/squidfunk/mkdocs-material/blob/master/CHANGELOG) - [Commits](https://github.com/squidfunk/mkdocs-material/compare/9.5.18...9.6.1) --- updated-dependencies: - dependency-name: mkdocs-material dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Alejandra <90076947+alejsdev@users.noreply.github.com> --- requirements-docs.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-docs.txt b/requirements-docs.txt index 9af2a85d9..cd2e4e58e 100644 --- a/requirements-docs.txt +++ b/requirements-docs.txt @@ -1,6 +1,6 @@ -e . -r requirements-docs-tests.txt -mkdocs-material==9.5.18 +mkdocs-material==9.6.1 mdx-include >=1.4.1,<2.0.0 mkdocs-redirects>=1.2.1,<1.3.0 typer == 0.12.5 From 42a3b1526e771d81d0b90c7bccce2d097a7a7b2c Mon Sep 17 00:00:00 2001 From: github-actions Date: Fri, 7 Feb 2025 22:23:11 +0000 Subject: [PATCH 028/157] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [skip ci] --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index c52fb0f63..a6c7b24e8 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -9,6 +9,7 @@ hide: ### Translations +* 🌐 Add Vietnamese translation for `docs/vi/docs/environment-variables.md`. PR [#13287](https://github.com/fastapi/fastapi/pull/13287) by [@ptt3199](https://github.com/ptt3199). * 🌐 Add Vietnamese translation for `docs/vi/docs/fastapi-cli.md`. PR [#13294](https://github.com/fastapi/fastapi/pull/13294) by [@ptt3199](https://github.com/ptt3199). * 🌐 Add Ukrainian translation for `docs/uk/docs/features.md`. PR [#13308](https://github.com/fastapi/fastapi/pull/13308) by [@valentinDruzhinin](https://github.com/valentinDruzhinin). * 🌐 Add Ukrainian translation for `docs/uk/docs/learn/index.md`. PR [#13306](https://github.com/fastapi/fastapi/pull/13306) by [@valentinDruzhinin](https://github.com/valentinDruzhinin). From 49b18c87a0b98f36abc6962fc55a3b7bc151e51e Mon Sep 17 00:00:00 2001 From: github-actions Date: Fri, 7 Feb 2025 22:24:04 +0000 Subject: [PATCH 029/157] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [skip ci] --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index a6c7b24e8..b2020cb25 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -9,6 +9,7 @@ hide: ### Translations +* 🌐 Add Ukrainian translation for `docs/uk/docs/tutorial/static-files.md`. PR [#13285](https://github.com/fastapi/fastapi/pull/13285) by [@valentinDruzhinin](https://github.com/valentinDruzhinin). * 🌐 Add Vietnamese translation for `docs/vi/docs/environment-variables.md`. PR [#13287](https://github.com/fastapi/fastapi/pull/13287) by [@ptt3199](https://github.com/ptt3199). * 🌐 Add Vietnamese translation for `docs/vi/docs/fastapi-cli.md`. PR [#13294](https://github.com/fastapi/fastapi/pull/13294) by [@ptt3199](https://github.com/ptt3199). * 🌐 Add Ukrainian translation for `docs/uk/docs/features.md`. PR [#13308](https://github.com/fastapi/fastapi/pull/13308) by [@valentinDruzhinin](https://github.com/valentinDruzhinin). From 4740ccdcceb23ef825803421a64adbf36d5a84f3 Mon Sep 17 00:00:00 2001 From: github-actions Date: Fri, 7 Feb 2025 22:25:02 +0000 Subject: [PATCH 030/157] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [skip ci] --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index b2020cb25..dd53a3ba1 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -9,6 +9,7 @@ hide: ### Translations +* 🌐 Add Vietnamese translation for `docs/vi/docs/virtual-environments.md`. PR [#13282](https://github.com/fastapi/fastapi/pull/13282) by [@ptt3199](https://github.com/ptt3199). * 🌐 Add Ukrainian translation for `docs/uk/docs/tutorial/static-files.md`. PR [#13285](https://github.com/fastapi/fastapi/pull/13285) by [@valentinDruzhinin](https://github.com/valentinDruzhinin). * 🌐 Add Vietnamese translation for `docs/vi/docs/environment-variables.md`. PR [#13287](https://github.com/fastapi/fastapi/pull/13287) by [@ptt3199](https://github.com/ptt3199). * 🌐 Add Vietnamese translation for `docs/vi/docs/fastapi-cli.md`. PR [#13294](https://github.com/fastapi/fastapi/pull/13294) by [@ptt3199](https://github.com/ptt3199). From 828079cb6ea89a27c2aabf8f66ec9af94f496322 Mon Sep 17 00:00:00 2001 From: github-actions Date: Fri, 7 Feb 2025 22:25:56 +0000 Subject: [PATCH 031/157] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [skip ci] --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index dd53a3ba1..633c29661 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -21,6 +21,7 @@ hide: ### Internal +* ⬆ Bump pillow from 11.0.0 to 11.1.0. PR [#13300](https://github.com/fastapi/fastapi/pull/13300) by [@dependabot[bot]](https://github.com/apps/dependabot). * 👥 Update FastAPI People - Sponsors. PR [#13295](https://github.com/fastapi/fastapi/pull/13295) by [@tiangolo](https://github.com/tiangolo). * 👥 Update FastAPI People - Experts. PR [#13303](https://github.com/fastapi/fastapi/pull/13303) by [@tiangolo](https://github.com/tiangolo). * 👥 Update FastAPI GitHub topic repositories. PR [#13302](https://github.com/fastapi/fastapi/pull/13302) by [@tiangolo](https://github.com/tiangolo). From c6c45ae488aea220450e2b9223075f1eb621172b Mon Sep 17 00:00:00 2001 From: github-actions Date: Fri, 7 Feb 2025 22:27:05 +0000 Subject: [PATCH 032/157] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [skip ci] --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index 633c29661..449b880dc 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -21,6 +21,7 @@ hide: ### Internal +* ⬆ Bump mkdocs-material from 9.5.18 to 9.6.1. PR [#13301](https://github.com/fastapi/fastapi/pull/13301) by [@dependabot[bot]](https://github.com/apps/dependabot). * ⬆ Bump pillow from 11.0.0 to 11.1.0. PR [#13300](https://github.com/fastapi/fastapi/pull/13300) by [@dependabot[bot]](https://github.com/apps/dependabot). * 👥 Update FastAPI People - Sponsors. PR [#13295](https://github.com/fastapi/fastapi/pull/13295) by [@tiangolo](https://github.com/tiangolo). * 👥 Update FastAPI People - Experts. PR [#13303](https://github.com/fastapi/fastapi/pull/13303) by [@tiangolo](https://github.com/tiangolo). From ad33193f2c3d7732e804d0d5abdd08ab2ad4796d Mon Sep 17 00:00:00 2001 From: 11kkw <11kkw17@gmail.com> Date: Sun, 9 Feb 2025 23:54:09 +0900 Subject: [PATCH 033/157] =?UTF-8?q?=F0=9F=8C=90=20Add=20Korean=20translati?= =?UTF-8?q?on=20for=20`docs/ko/docs/tutorial/dependencies/dependencies-wit?= =?UTF-8?q?h-yield.md`=20(#13257)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dependencies/dependencies-with-yield.md | 275 ++++++++++++++++++ 1 file changed, 275 insertions(+) create mode 100644 docs/ko/docs/tutorial/dependencies/dependencies-with-yield.md diff --git a/docs/ko/docs/tutorial/dependencies/dependencies-with-yield.md b/docs/ko/docs/tutorial/dependencies/dependencies-with-yield.md new file mode 100644 index 000000000..ff174937d --- /dev/null +++ b/docs/ko/docs/tutorial/dependencies/dependencies-with-yield.md @@ -0,0 +1,275 @@ +# yield를 사용하는 의존성 + +FastAPI는 작업 완료 후 추가 단계를 수행하는 의존성을 지원합니다. + +이를 구현하려면 `return` 대신 `yield`를 사용하고, 추가로 실행할 단계 (코드)를 그 뒤에 작성하세요. + +/// tip | 팁 + +각 의존성마다 `yield`는 한 번만 사용해야 합니다. + +/// + +/// note | 기술 세부사항 + +다음과 함께 사용할 수 있는 모든 함수: + +* `@contextlib.contextmanager` 또는 +* `@contextlib.asynccontextmanager` + +는 **FastAPI**의 의존성으로 사용할 수 있습니다. + +사실, FastAPI는 내부적으로 이 두 데코레이터를 사용합니다. + +/// + +## `yield`를 사용하는 데이터베이스 의존성 + +예를 들어, 이 기능을 사용하면 데이터베이스 세션을 생성하고 작업이 끝난 후에 세션을 종료할 수 있습니다. + +응답을 생성하기 전에는 `yield`문을 포함하여 그 이전의 코드만이 실행됩니다: + +{* ../../docs_src/dependencies/tutorial007.py hl[2:4] *} + +yield된 값은 *경로 작업* 및 다른 의존성들에 주입되는 값 입니다: + +{* ../../docs_src/dependencies/tutorial007.py hl[4] *} + +`yield`문 다음의 코드는 응답을 생성한 후 보내기 전에 실행됩니다: + +{* ../../docs_src/dependencies/tutorial007.py hl[5:6] *} + +/// tip | 팁 + +`async` 함수와 일반 함수 모두 사용할 수 있습니다. + +**FastAPI**는 일반 의존성과 마찬가지로 각각의 함수를 올바르게 처리할 것입니다. + +/// + +## `yield`와 `try`를 사용하는 의존성 + +`yield`를 사용하는 의존성에서 `try` 블록을 사용한다면, 의존성을 사용하는 도중 발생한 모든 예외를 받을 수 있습니다. + +예를 들어, 다른 의존성이나 *경로 작업*의 중간에 데이터베이스 트랜잭션 "롤백"이 발생하거나 다른 오류가 발생한다면, 해당 예외를 의존성에서 받을 수 있습니다. + +따라서, 의존성 내에서 `except SomeException`을 사용하여 특정 예외를 처리할 수 있습니다. + +마찬가지로, `finally`를 사용하여 예외 발생 여부와 관계 없이 종료 단계까 실행되도록 할 수 있습니다. + +{* ../../docs_src/dependencies/tutorial007.py hl[3,5] *} + +## `yield`를 사용하는 하위 의존성 + +모든 크기와 형태의 하위 의존성과 하위 의존성의 "트리"도 가질 수 있으며, 이들 모두가 `yield`를 사용할 수 있습니다. + +**FastAPI**는 `yield`를 사용하는 각 의존성의 "종료 코드"가 올바른 순서로 실행되도록 보장합니다. + +예를 들어, `dependency_c`는 `dependency_b`에 의존할 수 있고, `dependency_b`는 `dependency_a`에 의존할 수 있습니다. + +{* ../../docs_src/dependencies/tutorial008_an_py39.py hl[6,14,22] *} + +이들 모두는 `yield`를 사용할 수 있습니다. + +이 경우 `dependency_c`는 종료 코드를 실행하기 위해, `dependency_b`의 값 (여기서는 `dep_b`로 명명)이 여전히 사용 가능해야 합니다. + +그리고, `dependency_b`는 종료 코드를 위해 `dependency_a`의 값 (여기서는 `dep_a`로 명명) 이 사용 가능해야 합니다. + +{* ../../docs_src/dependencies/tutorial008_an_py39.py hl[18:19,26:27] *} + +같은 방식으로, `yield`를 사용하는 의존성과 `return`을 사용하는 의존성을 함께 사용할 수 있으며, 이들 중 일부가 다른 것들에 의존할 수 있습니다. + +그리고 `yield`를 사용하는 다른 여러 의존성을 필요로 하는 단일 의존성을 가질 수도 있습니다. + +원하는 의존성을 원하는 대로 조합할 수 있습니다. + +**FastAPI**는 모든 것이 올바른 순서로 실행되도록 보장합니다. + +/// note | 기술 세부사항 + +파이썬의 Context Managers 덕분에 이 기능이 작동합니다. + +**FastAPI**는 이를 내부적으로 컨텍스트 관리자를 사용하여 구현합니다. + +/// + +## `yield`와 `HTTPException`를 사용하는 의존성 + +`yield`와 `try` 블록이 있는 의존성을 사용하여 예외를 처리할 수 있다는 것을 알게 되었습니다. + +같은 방식으로, `yield` 이후의 종료 코드에서 `HTTPException`이나 유사한 예외를 발생시킬 수 있습니다. + +/// tip | 팁 + +이는 다소 고급 기술이며, 대부분의 경우 경로 연산 함수 등 나머지 애플리케이션 코드 내부에서 예외 (`HTTPException` 포함)를 발생시킬 수 있으므로 실제로는 필요하지 않을 것입니다. + +하지만 필요한 경우 사용할 수 있습니다. 🤓 + +/// + +{* ../../docs_src/dependencies/tutorial008b_an_py39.py hl[18:22,31] *} + +예외를 처리하고(또는 추가로 다른 `HTTPException`을 발생시키기 위해) 사용할 수 있는 또 다른 방법은 [사용자 정의 예외 처리기](../handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank}를 생성하는 것 입니다. + +## `yield`와 `except`를 사용하는 의존성 + +`yield`를 사용하는 의존성에서 `except`를 사용하여 예외를 포착하고 예외를 다시 발생시키지 않거나 (또는 새 예외를 발생시키지 않으면), FastAPI는 해당 예외가 발생했는지 알 수 없습니다. 이는 일반적인 Python 방식과 동일합니다: + +{* ../../docs_src/dependencies/tutorial008c_an_py39.py hl[15:16] *} + +이 경우, `HTTPException`이나 유사한 예외를 발생시키지 않기 때문에 클라이언트는 HTTP 500 Internal Server Error 응답을 보게 되지만, 서버는 어떤 오류가 발생했는지에 대한 **로그**나 다른 표시를 전혀 가지지 않게 됩니다. 😱 + +### `yield`와 `except`를 사용하는 의존성에서 항상 `raise` 하기 + +`yield`가 있는 의존성에서 예외를 잡았을 때는 `HTTPException`이나 유사한 예외를 새로 발생시키지 않는 한, 반드시 원래의 예외를 다시 발생시켜야 합니다. + +`raise`를 사용하여 동일한 예외를 다시 발생시킬 수 있습니다: + +{* ../../docs_src/dependencies/tutorial008d_an_py39.py hl[17] *} + +이제 클라이언트는 동일한 *HTTP 500 Internal Server Error* 오류 응답을 받게 되지만, 서버 로그에는 사용자 정의 예외인 `InternalError"가 기록됩니다. 😎 + +## `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,operation: Can raise exceptions, including HTTPException + client ->> dep: Start request + Note over dep: Run code up to yield + opt raise Exception + dep -->> handler: Raise Exception + handler -->> client: HTTP error response + end + dep ->> operation: Run dependency, e.g. DB session + opt raise + operation -->> dep: Raise Exception (e.g. HTTPException) + opt handle + dep -->> dep: Can catch exception, raise a new HTTPException, raise other exception + end + handler -->> client: HTTP error response + 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 -->> tasks: Handle exceptions in the background task code + end +``` + +/// info | 정보 + +클라이언트에 **하나의 응답** 만 전송됩니다. 이는 오류 응답 중 하나일 수도 있고,*경로 작업*에서 생성된 응답일 수도 있습니다. + +이러한 응답 중 하나가 전송된 후에는 다른 응답을 보낼 수 없습니다. + +/// + +/// tip | 팁 + +이 다이어그램은 `HTTPException`을 보여주지만, `yield`를 사용하는 의존성에서 처리한 예외나 [사용자 정의 예외처리기](../handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank}.를 사용하여 처리한 다른 예외도 발생시킬 수 있습니다. + +어떤 예외가 발생하든, `HTTPException`을 포함하여 yield를 사용하는 의존성으로 전달됩니다. 대부분의 경우 예외를 다시 발생시키거나 새로운 예외를 발생시켜야 합니다. + +/// + +## `yield`, `HTTPException`, `except` 및 백그라운드 작업을 사용하는 의존성 + +/// warning | 경고 + +이러한 기술적 세부 사항은 대부분 필요하지 않으므로 이 섹션을 건너뛰고 아래에서 계속 진행해도 됩니다. + +이러한 세부 정보는 주로 FastAPI 0.106.0 이전 버전에서 `yield`가 있는 의존성의 리소스를 백그라운드 작업에서 사용했던 경우메 유용합니다. + +/// + +### `yield`와 `except`를 사용하는 의존성, 기술 세부사항 + +FastAPI 0.110.0 이전에는 `yield`가 포함된 의존성을 사용한 후 해당 의존성에서 `except`가 포함된 예외를 캡처하고 다시 예외를 발생시키지 않으면 예외가 자동으로 예외 핸들러 또는 내부 서버 오류 핸들러로 발생/전달되었습니다. + +이는 처리기 없이 전달된 예외(내부 서버 오류)에서 처리되지 않은 메모리 소비를 수정하고 일반 파이썬 코드의 동작과 일치하도록 하기 위해 0.110.0 버전에서 변경되었습니다. + +### 백그라운드 작업과 `yield`를 사용하는 의존성, 기술 세부사항 + +FastAPI 0.106.0 이전에는 `yield` 이후에 예외를 발생시키는 것이 불가능했습니다. `yield`가 있는 의존성 종료 코드는 응답이 전송된 이후에 실행되었기 때문에, [예외 처리기](../handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank}가 이미 실행된 상태였습니다. + +이는 주로 백그라운드 작업 내에서 의존성에서 "yield된" 동일한 객체를 사용할 수 있도록 하기 위해 이런 방식으로 설계되었습니다. 종료 코드는 백그라운드 작업이 완료된 후에 실행되었기 때문입니다 + +하지만 이렇게 하면 리소스를 불필요하게 양보한 의존성(예: 데이터베이스 연결)에서 보유하면서 응답이 네트워크를 통해 이동할 때까지 기다리는 것을 의미하기 때문에 FastAPI 0.106.0에서 변경되었습니다. + +/// tip | 팁 + +또한 백그라운드 작업은 일반적으로 자체 리소스(예: 자체 데이터베이스 연결)를 사용하여 별도로 처리해야 하는 독립적인 로직 집합입니다. + +따라서 이렇게 하면 코드가 더 깔끔해집니다. + +/// + +만약 이전에 이러한 동작에 의존했다면, 이제는 백그라운드 작업 내부에서 백그라운드 작업을 위한 리소스를 생성하고, `yield`가 있는 의존성의 리소스에 의존하지 않는 데이터만 내부적으로 사용해야합니다. + +예를 들어, 동일한 데이터베이스 세션을 사용하는 대신, 백그라운드 작업 내부에서 새로운 데이터베이스 세션을 생성하고 이 새로운 세션을 사용하여 데이터베이스에서 객체를 가져와야 합니다. 그리고 데이터베이스 객체를 백그라운드 작업 함수의 매개변수로 직접 전달하는 대신, 해당 객체의 ID를 전달한 다음 백그라운드 작업 함수 내부에서 객체를 다시 가져와야 합니다 + +## 컨텍스트 관리자 + +### "컨텍스트 관리자"란? + +"컨텍스트 관리자"는 Python에서 `with` 문에서 사용할 수 있는 모든 객체를 의미합니다. + +예를 들어, `with`를 사용하여 파일을 읽을 수 있습니다: + +```Python +with open("./somefile.txt") as f: + contents = f.read() + print(contents) +``` + +내부적으로 `open("./somefile.txt")` 는 "컨텍스트 관리자(Context Manager)"라고 불리는 객체를 생성합니다. + +`with` 블록이 끝나면, 예외가 발생했더라도 파일을 닫도록 보장합니다. + +`yield`가 있는 의존성을 생성하면 **FastAPI**는 내부적으로 이를 위한 컨텍스트 매니저를 생성하고 다른 관련 도구들과 결합합니다. + +### `yield`를 사용하는 의존성에서 컨텍스트 관리자 사용하기 + +/// warning | 경고 + +이것은 어느 정도 "고급" 개념입니다. + +**FastAPI**를 처음 시작하는 경우 지금은 이 부분을 건너뛰어도 좋습니다. + +/// + +Python에서는 다음을 통해 컨텍스트 관리자를 생성할 수 있습니다. 두 가지 메서드가 있는 클래스를 생성합니다: `__enter__()` and `__exit__()`. + +**FastAPI**의 `yield`가 있는 의존성 내에서 +`with` 또는 `async with`문을 사용하여 이들을 활용할 수 있습니다: + +{* ../../docs_src/dependencies/tutorial010.py hl[1:9,13] *} + +/// tip | 팁 + +컨텍스트 관리자를 생성하는 또 다른 방법은 다음과 같습니다: + +* `@contextlib.contextmanager` 또는 +* `@contextlib.asynccontextmanager` + +이들은 단일 `yield`가 있는 함수를 꾸미는 데 사용합니다. + +이것이 **FastAPI**가 `yield`가 있는 의존성을 위해 내부적으로 사용하는 방식입니다. + +하지만 FastAPI 의존성에는 이러한 데코레이터를 사용할 필요가 없습니다(그리고 사용해서도 안됩니다). + +FastAPI가 내부적으로 이를 처리해 줄 것입니다. + +/// From 57a9a64435351717ca5a29304cd7736de755c9b0 Mon Sep 17 00:00:00 2001 From: github-actions Date: Sun, 9 Feb 2025 14:54:33 +0000 Subject: [PATCH 034/157] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [skip ci] --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index 449b880dc..cda5e9626 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -9,6 +9,7 @@ hide: ### Translations +* 🌐 Add Korean translation for `docs/ko/docs/tutorial/dependencies/dependencies-with-yield.md`. PR [#13257](https://github.com/fastapi/fastapi/pull/13257) by [@11kkw](https://github.com/11kkw). * 🌐 Add Vietnamese translation for `docs/vi/docs/virtual-environments.md`. PR [#13282](https://github.com/fastapi/fastapi/pull/13282) by [@ptt3199](https://github.com/ptt3199). * 🌐 Add Ukrainian translation for `docs/uk/docs/tutorial/static-files.md`. PR [#13285](https://github.com/fastapi/fastapi/pull/13285) by [@valentinDruzhinin](https://github.com/valentinDruzhinin). * 🌐 Add Vietnamese translation for `docs/vi/docs/environment-variables.md`. PR [#13287](https://github.com/fastapi/fastapi/pull/13287) by [@ptt3199](https://github.com/ptt3199). From 126a9b33c92113748de4398c0bcb65600024b03c Mon Sep 17 00:00:00 2001 From: Emil Sadek Date: Mon, 10 Feb 2025 03:18:47 -0800 Subject: [PATCH 035/157] =?UTF-8?q?=F0=9F=93=9D=20Fix=20test=20badge=20(#1?= =?UTF-8?q?3313)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix test badge * Fix test badge in docs --------- Co-authored-by: Emil Sadek Co-authored-by: Alejandra <90076947+alejsdev@users.noreply.github.com> --- README.md | 2 +- docs/en/docs/index.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f6da22b21..d5d5ced52 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@

- Test + Test Coverage diff --git a/docs/en/docs/index.md b/docs/en/docs/index.md index cbe71c87d..4a2777f25 100644 --- a/docs/en/docs/index.md +++ b/docs/en/docs/index.md @@ -12,7 +12,7 @@

- Test + Test Coverage From eea196f4a505c2584fafda691f2a0033c4db6622 Mon Sep 17 00:00:00 2001 From: github-actions Date: Mon, 10 Feb 2025 11:19:36 +0000 Subject: [PATCH 036/157] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [skip ci] --- docs/en/docs/release-notes.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index cda5e9626..013cbfc7c 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -7,6 +7,10 @@ hide: ## Latest Changes +### Docs + +* 📝 Fix test badge. PR [#13313](https://github.com/fastapi/fastapi/pull/13313) by [@esadek](https://github.com/esadek). + ### Translations * 🌐 Add Korean translation for `docs/ko/docs/tutorial/dependencies/dependencies-with-yield.md`. PR [#13257](https://github.com/fastapi/fastapi/pull/13257) by [@11kkw](https://github.com/11kkw). From 5cbb81cc2686c50e1fe6da48986fac0498a157dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ph=C6=B0=C6=A1ng=20T=E1=BA=A5n=20Th=C3=A0nh?= <51350651+ptt3199@users.noreply.github.com> Date: Sat, 15 Feb 2025 18:08:22 +0700 Subject: [PATCH 037/157] =?UTF-8?q?=F0=9F=8C=90=20=20Add=20Vietnamese=20tr?= =?UTF-8?q?anslation=20for=20`docs/vi/docs/tutorial/static-files.md`=20(#1?= =?UTF-8?q?1291)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/vi/docs/tutorial/static-files.md | 40 +++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 docs/vi/docs/tutorial/static-files.md diff --git a/docs/vi/docs/tutorial/static-files.md b/docs/vi/docs/tutorial/static-files.md new file mode 100644 index 000000000..ecf8c2485 --- /dev/null +++ b/docs/vi/docs/tutorial/static-files.md @@ -0,0 +1,40 @@ +# Tệp tĩnh (Static Files) + +Bạn có thể triển khai tệp tĩnh tự động từ một thư mục bằng cách sử dụng StaticFiles. + +## Sử dụng `Tệp tĩnh` + +- Nhập `StaticFiles`. +- "Mount" a `StaticFiles()` instance in a specific path. + +{* ../../docs_src/static_files/tutorial001.py hl[2,6] *} + +/// note | Chi tiết kỹ thuật + +Bạn cũng có thể sử dụng `from starlette.staticfiles import StaticFiles`. + +**FastAPI** cung cấp cùng `starlette.staticfiles` như `fastapi.staticfiles` giúp đơn giản hóa việc sử dụng, nhưng nó thực sự đến từ Starlette. + +/// + +### "Mounting" là gì + +"Mounting" có nghĩa là thêm một ứng dụng "độc lập" hoàn chỉnh vào một đường dẫn cụ thể, sau đó ứng dụng đó sẽ chịu trách nhiệm xử lý tất cả các đường dẫn con. + +Điều này khác với việc sử dụng `APIRouter` vì một ứng dụng được gắn kết là hoàn toàn độc lập. OpenAPI và tài liệu từ ứng dụng chính của bạn sẽ không bao gồm bất kỳ thứ gì từ ứng dụng được gắn kết, v.v. + +Bạn có thể đọc thêm về điều này trong [Hướng dẫn Người dùng Nâng cao](../advanced/index.md){.internal-link target=\_blank}. + +## Chi tiết + +Đường dẫn đầu tiên `"/static"` là đường dẫn con mà "ứng dụng con" này sẽ được "gắn" vào. Vì vậy, bất kỳ đường dẫn nào bắt đầu bằng `"/static"` sẽ được xử lý bởi nó. + +Đường dẫn `directory="static"` là tên của thư mục chứa tệp tĩnh của bạn. + +Tham số `name="static"` đặt tên cho nó để có thể được sử dụng bên trong **FastAPI**. + +Tất cả các tham số này có thể khác với `static`, điều chỉnh chúng với phù hợp với ứng dụng của bạn. + +## Thông tin thêm + +Để biết thêm chi tiết và tùy chọn, hãy xem Starlette's docs about Static Files. From 3aeaa0a6a826466669d9faf13762524ee795e8b2 Mon Sep 17 00:00:00 2001 From: github-actions Date: Sat, 15 Feb 2025 11:08:48 +0000 Subject: [PATCH 038/157] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [skip ci] --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index 013cbfc7c..7e8f57054 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -13,6 +13,7 @@ hide: ### Translations +* 🌐 Add Vietnamese translation for `docs/vi/docs/tutorial/static-files.md`. PR [#11291](https://github.com/fastapi/fastapi/pull/11291) by [@ptt3199](https://github.com/ptt3199). * 🌐 Add Korean translation for `docs/ko/docs/tutorial/dependencies/dependencies-with-yield.md`. PR [#13257](https://github.com/fastapi/fastapi/pull/13257) by [@11kkw](https://github.com/11kkw). * 🌐 Add Vietnamese translation for `docs/vi/docs/virtual-environments.md`. PR [#13282](https://github.com/fastapi/fastapi/pull/13282) by [@ptt3199](https://github.com/ptt3199). * 🌐 Add Ukrainian translation for `docs/uk/docs/tutorial/static-files.md`. PR [#13285](https://github.com/fastapi/fastapi/pull/13285) by [@valentinDruzhinin](https://github.com/valentinDruzhinin). From 03b24b5a524dc6b27e10a58dac9538b737f0defa Mon Sep 17 00:00:00 2001 From: ScrollDude <93028356+Stepakinoyan@users.noreply.github.com> Date: Sat, 15 Feb 2025 20:15:23 +0900 Subject: [PATCH 039/157] =?UTF-8?q?=F0=9F=8C=90=20Add=20Russian=20translat?= =?UTF-8?q?ion=20for=20`docs/ru/docs/advanced/response-cookies.md`=20(#133?= =?UTF-8?q?27)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/ru/docs/advanced/response-cookies.md | 48 +++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 docs/ru/docs/advanced/response-cookies.md diff --git a/docs/ru/docs/advanced/response-cookies.md b/docs/ru/docs/advanced/response-cookies.md new file mode 100644 index 000000000..e04ff577c --- /dev/null +++ b/docs/ru/docs/advanced/response-cookies.md @@ -0,0 +1,48 @@ + +# Cookies в ответе + +## Использование параметра `Response` + +Вы можете объявить параметр типа `Response` в вашей функции эндпоинта. + +Затем установить cookies в этом временном объекте ответа. + +{* ../../docs_src/response_cookies/tutorial002.py hl[1, 8:9] *} + +После этого можно вернуть любой объект, как и раньше (например, `dict`, объект модели базы данных и так далее). + +Если вы указали `response_model`, он всё равно будет использоваться для фильтрации и преобразования возвращаемого объекта. + +**FastAPI** извлечет cookies (а также заголовки и коды состояния) из временного ответа и включит их в окончательный ответ, содержащий ваше возвращаемое значение, отфильтрованное через `response_model`. + +Вы также можете объявить параметр типа Response в зависимостях и устанавливать cookies (и заголовки) там. + +## Возвращение `Response` напрямую + +Вы также можете установить cookies, если возвращаете `Response` напрямую в вашем коде. + +Для этого создайте объект `Response`, как описано в разделе [Возвращение ответа напрямую](response-directly.md){.target=_blank}. + +Затем установите cookies и верните этот объект: + +{* ../../docs_src/response_cookies/tutorial001.py hl[10:12] *} + +/// tip | Примечание +Имейте в виду, что если вы возвращаете ответ напрямую, вместо использования параметра `Response`, **FastAPI** отправит его без дополнительной обработки. + +Убедитесь, что ваши данные имеют корректный тип. Например, они должны быть совместимы с JSON, если вы используете `JSONResponse`. + +Также убедитесь, что вы не отправляете данные, которые должны были быть отфильтрованы через `response_model`. +/// + +### Дополнительная информация + +/// note | Технические детали +Вы также можете использовать `from starlette.responses import Response` или `from starlette.responses import JSONResponse`. + +**FastAPI** предоставляет `fastapi.responses`, которые являются теми же объектами, что и `starlette.responses`, просто для удобства. Однако большинство доступных типов ответов поступает непосредственно из **Starlette**. + +Для установки заголовков и cookies `Response` используется часто, поэтому **FastAPI** также предоставляет его через `fastapi.responses`. +/// + +Чтобы увидеть все доступные параметры и настройки, ознакомьтесь с документацией Starlette. From 15afe2e301d06a64a356181a4e7216818316e731 Mon Sep 17 00:00:00 2001 From: github-actions Date: Sat, 15 Feb 2025 11:15:45 +0000 Subject: [PATCH 040/157] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [skip ci] --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index 7e8f57054..0166e6cc5 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -13,6 +13,7 @@ hide: ### Translations +* 🌐 Add Russian translation for `docs/ru/docs/advanced/response-cookies.md`. PR [#13327](https://github.com/fastapi/fastapi/pull/13327) by [@Stepakinoyan](https://github.com/Stepakinoyan). * 🌐 Add Vietnamese translation for `docs/vi/docs/tutorial/static-files.md`. PR [#11291](https://github.com/fastapi/fastapi/pull/11291) by [@ptt3199](https://github.com/ptt3199). * 🌐 Add Korean translation for `docs/ko/docs/tutorial/dependencies/dependencies-with-yield.md`. PR [#13257](https://github.com/fastapi/fastapi/pull/13257) by [@11kkw](https://github.com/11kkw). * 🌐 Add Vietnamese translation for `docs/vi/docs/virtual-environments.md`. PR [#13282](https://github.com/fastapi/fastapi/pull/13282) by [@ptt3199](https://github.com/ptt3199). From 39b4692525acfffdfba8e12ee674d9590b02b19f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lee=20Yesong=20=28=EC=9D=B4=EC=98=88=EC=86=A1=29?= Date: Sat, 15 Feb 2025 20:19:12 +0900 Subject: [PATCH 041/157] =?UTF-8?q?=F0=9F=8C=90=20Update=20Korean=20transl?= =?UTF-8?q?ation=20for=20`docs/ko/docs/tutorial/security/simple-oauth2.md`?= =?UTF-8?q?=20(#13335)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ko/docs/tutorial/security/simple-oauth2.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/ko/docs/tutorial/security/simple-oauth2.md b/docs/ko/docs/tutorial/security/simple-oauth2.md index ddc7430af..f10c4f588 100644 --- a/docs/ko/docs/tutorial/security/simple-oauth2.md +++ b/docs/ko/docs/tutorial/security/simple-oauth2.md @@ -32,7 +32,7 @@ OAuth2는 (우리가 사용하고 있는) "패스워드 플로우"을 사용할 * `instagram_basic`은 페이스북/인스타그램에서 사용합니다. * `https://www.googleapis.com/auth/drive`는 Google에서 사용합니다. -/// 정보 +/// info | 정보 OAuth2에서 "범위"는 필요한 특정 권한을 선언하는 문자열입니다. @@ -61,7 +61,7 @@ OAuth2의 경우 문자열일 뿐입니다. * `scope`는 선택적인 필드로 공백으로 구분된 문자열로 구성된 큰 문자열입니다. * `grant_type`(선택적으로 사용). -/// 팁 +/// tip | 팁 OAuth2 사양은 실제로 `password`라는 고정 값이 있는 `grant_type` 필드를 *요구*하지만 `OAuth2PasswordRequestForm`은 이를 강요하지 않습니다. @@ -72,7 +72,7 @@ OAuth2 사양은 실제로 `password`라는 고정 값이 있는 `grant_type` * `client_id`(선택적으로 사용) (예제에서는 필요하지 않습니다). * `client_secret`(선택적으로 사용) (예제에서는 필요하지 않습니다). -/// 정보 +/// info | 정보 `OAuth2PasswordRequestForm`은 `OAuth2PasswordBearer`와 같이 **FastAPI**에 대한 특수 클래스가 아닙니다. @@ -86,7 +86,7 @@ OAuth2 사양은 실제로 `password`라는 고정 값이 있는 `grant_type` ### 폼 데이터 사용하기 -/// 팁 +/// tip | 팁 종속성 클래스 `OAuth2PasswordRequestForm`의 인스턴스에는 공백으로 구분된 긴 문자열이 있는 `scope` 속성이 없고 대신 전송된 각 범위에 대한 실제 문자열 목록이 있는 `scopes` 속성이 있습니다. @@ -126,7 +126,7 @@ OAuth2 사양은 실제로 `password`라는 고정 값이 있는 `grant_type` 따라서 해커는 다른 시스템에서 동일한 암호를 사용하려고 시도할 수 없습니다(많은 사용자가 모든 곳에서 동일한 암호를 사용하므로 이는 위험할 수 있습니다). -//// tab | P파이썬 3.7 이상 +//// tab | 파이썬 3.7 이상 {* ../../docs_src/security/tutorial003.py hl[80:83] *} @@ -150,7 +150,7 @@ UserInDB( ) ``` -/// 정보 +/// info | 정보 `**user_dict`에 대한 자세한 설명은 [**추가 모델** 문서](../extra-models.md#about-user_indict){.internal-link target=_blank}를 다시 읽어봅시다. @@ -166,7 +166,7 @@ UserInDB( 이 간단한 예제에서는 완전히 안전하지 않고, 동일한 `username`을 토큰으로 반환합니다. -/// 팁 +/// tip | 팁 다음 장에서는 패스워드 해싱 및 JWT 토큰을 사용하여 실제 보안 구현을 볼 수 있습니다. @@ -176,7 +176,7 @@ UserInDB( {* ../../docs_src/security/tutorial003.py hl[85] *} -/// 팁 +/// tip | 팁 사양에 따라 이 예제와 동일하게 `access_token` 및 `token_type`이 포함된 JSON을 반환해야 합니다. @@ -202,7 +202,7 @@ UserInDB( {* ../../docs_src/security/tutorial003.py hl[58:66,69:72,90] *} -/// 정보 +/// info | 정보 여기서 반환하는 값이 `Bearer`인 추가 헤더 `WWW-Authenticate`도 사양의 일부입니다. From fc94d904c969ba1b85aa9d9a966ad8dee27e7652 Mon Sep 17 00:00:00 2001 From: github-actions Date: Sat, 15 Feb 2025 11:19:48 +0000 Subject: [PATCH 042/157] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [skip ci] --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index 0166e6cc5..8fcf68e5f 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -13,6 +13,7 @@ hide: ### Translations +* 🌐 Update Korean translation for `docs/ko/docs/tutorial/security/simple-oauth2.md`. PR [#13335](https://github.com/fastapi/fastapi/pull/13335) by [@yes0ng](https://github.com/yes0ng). * 🌐 Add Russian translation for `docs/ru/docs/advanced/response-cookies.md`. PR [#13327](https://github.com/fastapi/fastapi/pull/13327) by [@Stepakinoyan](https://github.com/Stepakinoyan). * 🌐 Add Vietnamese translation for `docs/vi/docs/tutorial/static-files.md`. PR [#11291](https://github.com/fastapi/fastapi/pull/11291) by [@ptt3199](https://github.com/ptt3199). * 🌐 Add Korean translation for `docs/ko/docs/tutorial/dependencies/dependencies-with-yield.md`. PR [#13257](https://github.com/fastapi/fastapi/pull/13257) by [@11kkw](https://github.com/11kkw). From 030012bf4c225a2b5dbb3c6b3443bd2051e614f8 Mon Sep 17 00:00:00 2001 From: 11kkw <11kkw17@gmail.com> Date: Sat, 15 Feb 2025 20:21:20 +0900 Subject: [PATCH 043/157] =?UTF-8?q?=F0=9F=8C=90=20Add=20Korean=20translati?= =?UTF-8?q?on=20for=20`docs/ko/docs/advanced/custom-response.md`=20(#13265?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/ko/docs/advanced/custom-response.md | 313 +++++++++++++++++++++++ 1 file changed, 313 insertions(+) create mode 100644 docs/ko/docs/advanced/custom-response.md diff --git a/docs/ko/docs/advanced/custom-response.md b/docs/ko/docs/advanced/custom-response.md new file mode 100644 index 000000000..2001956fa --- /dev/null +++ b/docs/ko/docs/advanced/custom-response.md @@ -0,0 +1,313 @@ +# 사용자 정의 응답 - HTML, Stream, 파일, 기타 + +기본적으로, **FastAPI** 응답을 `JSONResponse`를 사용하여 반환합니다. + +이를 재정의 하려면 [응답을 직접 반환하기](response-directly.md){.internal-link target=_blank}에서 본 것처럼 `Response`를 직접 반환하면 됩니다. + +그러나 `Response` (또는 `JSONResponse`와 같은 하위 클래스)를 직접 반환하면, 데이터가 자동으로 변환되지 않으며 (심지어 `response_model`을 선언했더라도), 문서화가 자동으로 생성되지 않습니다(예를 들어, 생성된 OpenAPI의 일부로 HTTP 헤더 `Content-Type`에 특정 "미디어 타입"을 포함하는 경우). + +하지만 *경로 작업 데코레이터*에서 `response_class` 매개변수를 사용하여 원하는 `Response`(예: 모든 `Response` 하위 클래스)를 선언할 수도 있습니다. + +*경로 작업 함수*에서 반환하는 내용은 해당 `Response`안에 포함됩니다. + +그리고 만약 그 `Response`가 `JSONResponse`와 `UJSONResponse`의 경우 처럼 JSON 미디어 타입(`application/json`)을 가지고 있다면, *경로 작업 데코레이터*에서 선언한 Pydantic의 `response_model`을 사용해 자동으로 변환(및 필터링) 됩니다. + +/// note | 참고 + +미디어 타입이 없는 응답 클래스를 사용하는 경우, FastAPI는 응답에 내용이 없을 것으로 예상하므로 생성된 OpenAPI 문서에서 응답 형식을 문서화하지 않습니다. + +/// + +## `ORJSONResponse` 사용하기 + +예를 들어, 성능을 극대화하려는 경우, orjson을 설치하여 사용하고 응답을 `ORJSONResponse`로 설정할 수 있습니다. + +사용하고자 하는 `Response` 클래스(하위 클래스)를 임포트한 후, **경로 작업 데코레이터*에서 선언하세요. + +대규모 응답의 경우, 딕셔너리를 반환하는 것보다 `Response`를 반환하는 것이 훨씬 빠릅니다. + +이유는 기본적으로, FastAPI가 내부의 모든 항목을 검사하고 JSON으로 직렬화할 수 있는지 확인하기 때문입니다. 이는 사용자 안내서에서 설명된 [JSON 호환 가능 인코더](../tutorial/encoder.md){.internal-link target=_blank}를 사용하는 방식과 동일합니다. 이를 통해 데이터베이스 모델과 같은 **임의의 객체**를 반환할 수 있습니다. + +하지만 반환하는 내용이 **JSON으로 직렬화 가능**하다고 확신하는 경우, 해당 내용을 응답 클래스에 직접 전달할 수 있으며, FastAPI가 반환 내용을 `jsonable_encoder`를 통해 처리한 뒤 응답 클래스에 전달하는 오버헤드를 피할 수 있습니다. + +{* ../../docs_src/custom_response/tutorial001b.py hl[2,7] *} + +/// info | 정보 + +`response_class` 매개변수는 응답의 "미디어 타입"을 정의하는 데에도 사용됩니다. + +이 경우, HTTP 헤더 `Content-Type`은 `application/json`으로 설정됩니다. + +그리고 이는 OpenAPI에 그대로 문서화됩니다. + +/// + +/// tip | 팁 + +`ORJSONResponse`는 FastAPI에서만 사용할 수 있고 Starlette에서는 사용할 수 없습니다. + +/// + +## HTML 응답 + +**FastAPI**에서 HTML 응답을 직접 반환하려면 `HTMLResponse`를 사용하세요. + +* `HTMLResponse`를 임포트 합니다. +* *경로 작업 데코레이터*의 `response_class` 매개변수로 `HTMLResponse`를 전달합니다. + +{* ../../docs_src/custom_response/tutorial002.py hl[2,7] *} + +/// info | 정보 + +`response_class` 매개변수는 응답의 "미디어 타입"을 정의하는 데에도 사용됩니다. + +이 경우, HTTP 헤더 `Content-Type`은 `text/html`로 설정됩니다. + +그리고 이는 OpenAPI에 그대로 문서화 됩니다. + +/// + +### `Response` 반환하기 + +[응답을 직접 반환하기](response-directly.md){.internal-link target=_blank}에서 본 것 처럼, *경로 작업*에서 응답을 직접 반환하여 재정의할 수도 있습니다. + +위의 예제와 동일하게 `HTMLResponse`를 반환하는 코드는 다음과 같을 수 있습니다: + +{* ../../docs_src/custom_response/tutorial003.py hl[2,7,19] *} + +/// warning | 경고 + +*경로 작업 함수*에서 직접 반환된 `Response`는 OpenAPI에 문서화되지 않습니다(예를들어, `Content-Type`이 문서화되지 않음) 자동 대화형 문서에서도 표시되지 않습니다. + +/// + +/// info | 정보 + +물론 실제 `Content-Type` 헤더, 상태 코드 등은 반환된 `Response` 객체에서 가져옵니다. + +/// + +### OpenAPI에 문서화하고 `Response` 재정의 하기 + +함수 내부에서 응답을 재정의하면서 동시에 OpenAPI에서 "미디어 타입"을 문서화하고 싶다면, `response_class` 매게변수를 사용하면서 `Response` 객체를 반환할 수 있습니다. + +이 경우 `response_class`는 OpenAPI *경로 작업*을 문서화하는 데만 사용되고, 실제로는 여러분이 반환한 `Response`가 그대로 사용됩니다. + +### `HTMLResponse`직접 반환하기 + +예를 들어, 다음과 같이 작성할 수 있습니다: + +{* ../../docs_src/custom_response/tutorial004.py hl[7,21,23] *} + +이 예제에서, `generate_html_response()` 함수는 HTML을 `str`로 반환하는 대신 이미 `Response`를 생성하고 반환합니다. + +`generate_html_response()`를 호출한 결과를 반환함으로써, 기본적인 **FastAPI** 기본 동작을 재정의 하는 `Response`를 이미 반환하고 있습니다. + +하지만 `response_class`에 `HTMLResponse`를 함께 전달했기 때문에, FastAPI는 이를 OpenAPI 및 대화형 문서에서 `text/html`로 HTML을 문서화 하는 방법을 알 수 있습니다. + + + +## 사용 가능한 응답들 + +다음은 사용할 수 있는 몇가지 응답들 입니다. + +`Response`를 사용하여 다른 어떤 것도 반환 할수 있으며, 직접 하위 클래스를 만들 수도 있습니다. + +/// note | 기술 세부사항 + +`from starlette.responses import HTMLResponse`를 사용할 수도 있습니다. + +**FastAPI**는 개발자인 여러분의 편의를 위해 `starlette.responses`를 `fastapi.responses`로 제공 하지만, 대부분의 사용 가능한 응답은 Starlette에서 직접 가져옵니다. + +/// + +### `Response` + +기본 `Response` 클래스는 다른 모든 응답 클래스의 부모 클래스 입니다. + +이 클래스를 직접 반환할 수 있습니다. + +다음 매개변수를 받을 수 있습니다: + +* `content` - `str` 또는 `bytes`. +* `status_code` - HTTP 상태코드를 나타내는 `int`. +* `headers` - 문자열로 이루어진 `dict`. +* `media_type` - 미디어 타입을 나타내는 `str` 예: `"text/html"`. + +FastAPI (실제로는 Starlette)가 자동으로 `Content-Length` 헤더를 포함시킵니다. 또한 `media_type`에 기반하여 `Content-Type` 헤더를 포함하며, 텍스트 타입의 경우 문자 집합을 추가 합니다. + +{* ../../docs_src/response_directly/tutorial002.py hl[1,18] *} + +### `HTMLResponse` + +텍스트 또는 바이트를 받아 HTML 응답을 반환합니다. 위에서 설명한 내용과 같습니다. + +### `PlainTextResponse` + +텍스트 또는 바이트를 받아 일반 텍스트 응답을 반환합니다. + +{* ../../docs_src/custom_response/tutorial005.py hl[2,7,9] *} + +### `JSONResponse` + +데이터를 받아 `application/json`으로 인코딩된 응답을 반환합니다. + +이는 위에서 설명했듯이 **FastAPI**에서 기본적으로 사용되는 응답 형식입니다. + +### `ORJSONResponse` + + `orjson`을 사용하여 빠른 JSON 응답을 제공하는 대안입니다. 위에서 설명한 내용과 같습니다. + +/// info | 정보 + +이를 사용하려면 `orjson`을 설치해야합니다. 예: `pip install orjson`. + +/// + +### `UJSONResponse` + +`ujson`을 사용한 또 다른 JSON 응답 형식입니다. + +/// info | 정보 + +이 응답을 사용하려면 `ujson`을 설치해야합니다. 예: 'pip install ujson`. + +/// + +/// warning | 경고 + +`ujson` 은 일부 예외 경우를 처리하는 데 있어 Python 내장 구현보다 덜 엄격합니다. + +/// + +{* ../../docs_src/custom_response/tutorial001.py hl[2,7] *} + +/// tip | 팁 + +`ORJSONResponse`가 더 빠른 대안일 가능성이 있습니다. + +/// + +### `RedirectResponse` + +HTTP 리디렉션 응답을 반환합니다. 기본적으로 상태 코드는 307(임시 리디렉션)으로 설정됩니다. + +`RedirectResponse`를 직접 반환할 수 있습니다. + +{* ../../docs_src/custom_response/tutorial006.py hl[2,9] *} + +--- + +또는 `response_class` 매개변수에서 사용할 수도 있습니다: + + +{* ../../docs_src/custom_response/tutorial006b.py hl[2,7,9] *} + +이 경우, *경로 작업* 함수에서 URL을 직접 반환할 수 있습니다. + +이 경우, 사용되는 `status_code`는 `RedirectResponse`의 기본값인 `307` 입니다. + +--- + +`status_code` 매개변수를 `response_class` 매개변수와 함께 사용할 수도 있습니다: + +{* ../../docs_src/custom_response/tutorial006c.py hl[2,7,9] *} + +### `StreamingResponse` + +비동기 제너레이터 또는 일반 제너레이터/이터레이터를 받아 응답 본문을 스트리밍 합니다. + +{* ../../docs_src/custom_response/tutorial007.py hl[2,14] *} + +#### 파일과 같은 객체를 사용한 `StreamingResponse` + +파일과 같은 객체(예: `open()`으로 반환된 객체)가 있는 경우, 해당 파일과 같은 객체를 반복(iterate)하는 제너레이터 함수를 만들 수 있습니다. + +이 방식으로, 파일 전체를 메모리에 먼저 읽어들일 필요 없이, 제너레이터 함수를 `StreamingResponse`에 전달하여 반환할 수 있습니다. + +이 방식은 클라우드 스토리지, 비디오 처리 등의 다양한 라이브러리와 함께 사용할 수 있습니다. + +{* ../../docs_src/custom_response/tutorial008.py hl[2,10:12,14] *} + +1. 이것이 제너레이터 함수입니다. `yield` 문을 포함하고 있으므로 "제너레이터 함수"입니다. +2. `with` 블록을 사용함으로써, 제너레이터 함수가 완료된 후 파일과 같은 객체가 닫히도록 합니다. 즉, 응답 전송이 끝난 후 닫힙니다. +3. 이 `yield from`은 함수가 `file_like`라는 객체를 반복(iterate)하도록 합니다. 반복된 각 부분은 이 제너레이터 함수(`iterfile`)에서 생성된 것처럼 `yield` 됩니다. + + 이렇게 하면 "생성(generating)" 작업을 내부적으로 다른 무언가에 위임하는 제너레이터 함수가 됩니다. + + 이 방식을 사용하면 `with` 블록 안에서 파일을 열 수 있어, 작업이 완료된 후 파일과 같은 객체가 닫히는 것을 보장할 수 있습니다. + +/// tip | 팁 + +여기서 표준 `open()`을 사용하고 있기 때문에 `async`와 `await`를 지원하지 않습니다. 따라서 경로 작업은 일반 `def`로 선언합니다. + +/// + +### `FileResponse` + +파일을 비동기로 스트리밍하여 응답합니다. + +다른 응답 유형과는 다른 인수를 사용하여 객체를 생성합니다: + +* `path` - 스트리밍할 파일의 경로. +* `headers` - 딕셔너리 형식의 사용자 정의 헤더. +* `media_type` - 미디어 타입을 나타내는 문자열. 설정되지 않은 경우 파일 이름이나 경로를 사용하여 추론합니다. +* `filename` - 설정된 경우 응답의 `Content-Disposition`에 포함됩니다. + +파일 응답에는 적절한 `Content-Length`, `Last-Modified`, 및 `ETag` 헤더가 포함됩니다. + +{* ../../docs_src/custom_response/tutorial009.py hl[2,10] *} + +또한 `response_class` 매개변수를 사용할 수도 있습니다: + +{* ../../docs_src/custom_response/tutorial009b.py hl[2,8,10] *} + +이 경우, 경로 작업 함수에서 파일 경로를 직접 반환할 수 있습니다. + +## 사용자 정의 응답 클래스 + +`Response`를 상속받아 사용자 정의 응답 클래스를 생성하고 사용할 수 있습니다. + +예를 들어, 포함된 `ORJSONResponse` 클래스에서 사용되지 않는 설정으로 orjson을 사용하고 싶다고 가정해봅시다. + +만약 들여쓰기 및 포맷된 JSON을 반환하고 싶다면, `orjson.OPT_INDENT_2` 옵션을 사용할 수 있습니다. + +`CustomORJSONResponse`를 생성할 수 있습니다. 여기서 핵심은 `Response.render(content)` 메서드를 생성하여 내용을 `bytes`로 반환하는 것입니다: + +{* ../../docs_src/custom_response/tutorial009c.py hl[9:14,17] *} + +이제 다음 대신: + +```json +{"message": "Hello World"} +``` + +이 응답은 이렇게 반환됩니다: + +```json +{ + "message": "Hello World" +} +``` + +물론 JSON 포맷팅보다 더 유용하게 활용할 방법을 찾을 수 있을 것입니다. 😉 + +## 기본 응답 클래스 + +**FastAPI** 클래스 객체 또는 `APIRouter`를 생성할 때 기본적으로 사용할 응답 클래스를 지정할 수 있습니다. + +이를 정의하는 매개변수는 `default_response_class`입니다. + +아래 예제에서 **FastAPI**는 모든 경로 작업에서 기본적으로 `JSONResponse` 대신 `ORJSONResponse`를 사용합니다. + +{* ../../docs_src/custom_response/tutorial010.py hl[2,4] *} + +/// tip | 팁 + +여전히 이전처럼 *경로 작업*에서 `response_class`를 재정의할 수 있습니다. + +/// + +## 추가 문서화 + +OpenAPI에서 `responses`를 사용하여 미디어 타입 및 기타 세부 정보를 선언할 수도 있습니다: [OpenAPI에서 추가 응답](additional-responses.md){.internal-link target=_blank}. From d51936754d9ab943181472edadc869c317d8a5b0 Mon Sep 17 00:00:00 2001 From: github-actions Date: Sat, 15 Feb 2025 11:21:51 +0000 Subject: [PATCH 044/157] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [skip ci] --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index 8fcf68e5f..ffb5224c1 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -13,6 +13,7 @@ hide: ### Translations +* 🌐 Add Korean translation for `docs/ko/docs/advanced/custom-response.md`. PR [#13265](https://github.com/fastapi/fastapi/pull/13265) by [@11kkw](https://github.com/11kkw). * 🌐 Update Korean translation for `docs/ko/docs/tutorial/security/simple-oauth2.md`. PR [#13335](https://github.com/fastapi/fastapi/pull/13335) by [@yes0ng](https://github.com/yes0ng). * 🌐 Add Russian translation for `docs/ru/docs/advanced/response-cookies.md`. PR [#13327](https://github.com/fastapi/fastapi/pull/13327) by [@Stepakinoyan](https://github.com/Stepakinoyan). * 🌐 Add Vietnamese translation for `docs/vi/docs/tutorial/static-files.md`. PR [#11291](https://github.com/fastapi/fastapi/pull/11291) by [@ptt3199](https://github.com/ptt3199). From 10a13d05c4d31978e0255f76baf54b394430c89c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 15 Feb 2025 11:22:43 +0000 Subject: [PATCH 045/157] =?UTF-8?q?=E2=AC=86=20Bump=20cloudflare/wrangler-?= =?UTF-8?q?action=20from=203.13=20to=203.14=20(#13350)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [cloudflare/wrangler-action](https://github.com/cloudflare/wrangler-action) from 3.13 to 3.14. - [Release notes](https://github.com/cloudflare/wrangler-action/releases) - [Changelog](https://github.com/cloudflare/wrangler-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/cloudflare/wrangler-action/compare/v3.13...v3.14) --- updated-dependencies: - dependency-name: cloudflare/wrangler-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/deploy-docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml index d9ed61910..0b3096143 100644 --- a/.github/workflows/deploy-docs.yml +++ b/.github/workflows/deploy-docs.yml @@ -64,7 +64,7 @@ jobs: BRANCH: ${{ ( github.event.workflow_run.head_repository.full_name == github.repository && github.event.workflow_run.head_branch == 'master' && 'main' ) || ( github.event.workflow_run.head_sha ) }} # TODO: Use v3 when it's fixed, probably in v3.11 # https://github.com/cloudflare/wrangler-action/issues/307 - uses: cloudflare/wrangler-action@v3.13 + uses: cloudflare/wrangler-action@v3.14 # uses: cloudflare/wrangler-action@v3 with: apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} From b81d29fc0060cb97f5fffb70344c640f3f98a9ad Mon Sep 17 00:00:00 2001 From: github-actions Date: Sat, 15 Feb 2025 11:23:13 +0000 Subject: [PATCH 046/157] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [skip ci] --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index ffb5224c1..c69b20e66 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -30,6 +30,7 @@ hide: ### Internal +* ⬆ Bump cloudflare/wrangler-action from 3.13 to 3.14. PR [#13350](https://github.com/fastapi/fastapi/pull/13350) by [@dependabot[bot]](https://github.com/apps/dependabot). * ⬆ Bump mkdocs-material from 9.5.18 to 9.6.1. PR [#13301](https://github.com/fastapi/fastapi/pull/13301) by [@dependabot[bot]](https://github.com/apps/dependabot). * ⬆ Bump pillow from 11.0.0 to 11.1.0. PR [#13300](https://github.com/fastapi/fastapi/pull/13300) by [@dependabot[bot]](https://github.com/apps/dependabot). * 👥 Update FastAPI People - Sponsors. PR [#13295](https://github.com/fastapi/fastapi/pull/13295) by [@tiangolo](https://github.com/tiangolo). From ac893a4446c3157fdf0ac4bbabcda08031676ea2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hyogeun=20Oh=20=28=EC=98=A4=ED=9A=A8=EA=B7=BC=29?= Date: Sat, 15 Feb 2025 20:37:58 +0900 Subject: [PATCH 047/157] =?UTF-8?q?=F0=9F=8C=90=20Update=20Korean=20transl?= =?UTF-8?q?ation=20for=20`docs/ko/docs/help-fastapi.md`=20(#13262)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/ko/docs/help-fastapi.md | 287 ++++++++++++++++++++++++----------- 1 file changed, 197 insertions(+), 90 deletions(-) diff --git a/docs/ko/docs/help-fastapi.md b/docs/ko/docs/help-fastapi.md index 932952b4a..06435d4bb 100644 --- a/docs/ko/docs/help-fastapi.md +++ b/docs/ko/docs/help-fastapi.md @@ -1,162 +1,269 @@ -* # FastAPI 지원 - 도움말 받기 +# FastAPI 지원 - 도움 받기 - **FastAPI** 가 마음에 드시나요? +**FastAPI** 가 마음에 드시나요? - FastAPI, 다른 사용자, 개발자를 응원하고 싶으신가요? +FastAPI, 다른 사용자, 개발자를 응원하고 싶으신가요? - 혹은 **FastAPI** 에 대해 도움이 필요하신가요? +혹은 **FastAPI** 에 대해 도움이 필요하신가요? - 아주 간단하게 응원할 수 있습니다 (몇 번의 클릭만으로). +아주 간단하게 응원할 수 있습니다 (몇 번의 클릭만으로). - 또한 도움을 받을 수 있는 방법도 몇 가지 있습니다. +또한 도움을 받을 수 있는 방법도 몇 가지 있습니다. - ## 뉴스레터 구독 +## 뉴스레터 구독 - [**FastAPI와 친구** 뉴스레터](https://github.com/fastapi/fastapi/blob/master/newsletter)를 구독하여 최신 정보를 유지할 수 있습니다{.internal-link target=_blank}: +[**FastAPI and friends** 뉴스레터](newsletter.md){.internal-link target=\_blank}를 구독하여 최신 정보를 유지할 수 있습니다: - - FastAPI 와 그 친구들에 대한 뉴스 🚀 - - 가이드 📝 - - 특징 ✨ - - 획기적인 변화 🚨 - - 팁과 요령 ✅ +* FastAPI and friends에 대한 뉴스 🚀 +* 가이드 📝 +* 기능 ✨ +* 획기적인 변화 🚨 +* 팁과 요령 ✅ - ## 트위터에서 FastAPI 팔로우하기 +## 트위터에서 FastAPI 팔로우하기 - [Follow @fastapi on **Twitter**](https://twitter.com/fastapi) 를 팔로우하여 **FastAPI** 에 대한 최신 뉴스를 얻을 수 있습니다. 🐦 +**Twitter**의 @fastapi를 팔로우하여 **FastAPI** 에 대한 최신 뉴스를 얻을 수 있습니다. 🐦 - ## Star **FastAPI** in GitHub +## Star **FastAPI** in GitHub - GitHub에서 FastAPI에 "star"를 붙일 수 있습니다(오른쪽 상단의 star 버튼을 클릭): https://github.com/fastapi/fastapi. ⭐️ +GitHub에서 FastAPI에 "star"를 붙일 수 있습니다 (오른쪽 상단의 star 버튼을 클릭): https://github.com/fastapi/fastapi. ⭐️ - 스타를 늘림으로써, 다른 사용자들이 좀 더 쉽게 찾을 수 있고, 많은 사람들에게 유용한 것임을 나타낼 수 있습니다. +스타를 늘림으로써, 다른 사용자들이 좀 더 쉽게 찾을 수 있고, 많은 사람들에게 유용한 것임을 나타낼 수 있습니다. - ## GitHub 저장소에서 릴리즈 확인 +## GitHub 저장소에서 릴리즈 확인 - GitHub에서 FastAPI를 "watch"할 수 있습니다 (오른쪽 상단 watch 버튼을 클릭): https://github.com/fastapi/fastapi. 👀 +GitHub에서 FastAPI를 "watch"할 수 있습니다 (오른쪽 상단 watch 버튼을 클릭): https://github.com/fastapi/fastapi. 👀 - 여기서 "Releases only"을 선택할 수 있습니다. +여기서 "Releases only"을 선택할 수 있습니다. - 이렇게하면, **FastAPI** 의 버그 수정 및 새로운 기능의 구현 등의 새로운 자료 (최신 버전)이 있을 때마다 (이메일) 통지를 받을 수 있습니다. +이렇게하면, **FastAPI** 의 버그 수정 및 새로운 기능의 구현 등의 새로운 자료 (최신 버전)이 있을 때마다 (이메일) 통지를 받을 수 있습니다. - ## 개발자와의 연결 +## 개발자와의 연결 - 개발자인 [me (Sebastián Ramírez / `tiangolo`)](https://tiangolo.com/) 와 연락을 취할 수 있습니다. +개발자(Sebastián Ramírez / `tiangolo`)와 연락을 취할 수 있습니다. - 여러분은 할 수 있습니다: +여러분은 할 수 있습니다: - - [**GitHub**에서 팔로우하기](https://github.com/tiangolo). - - 당신에게 도움이 될 저의 다른 오픈소스 프로젝트를 확인하십시오. - - 새로운 오픈소스 프로젝트를 만들었을 때 확인하려면 팔로우 하십시오. +* **GitHub**에서 팔로우하기.. + * 당신에게 도움이 될 저의 다른 오픈소스 프로젝트를 확인하십시오. + * 새로운 오픈소스 프로젝트를 만들었을 때 확인하려면 팔로우 하십시오. +* **Twitter** 또는 Mastodon에서 팔로우하기. + * FastAPI의 사용 용도를 알려주세요 (그것을 듣는 것을 좋아합니다). + * 발표나 새로운 툴 출시 소식을 받아보십시오. + * **Twitter**의 @fastapi를 팔로우 (별도 계정에서) 할 수 있습니다. +* **LinkedIn**에서 팔로우하기.. + * 새로운 툴의 발표나 출시 소식을 받아보십시오. (단, Twitter를 더 자주 사용합니다 🤷‍♂). +* **Dev.to** 또는 **Medium**에서 제가 작성한 내용을 읽어 보십시오 (또는 팔로우). + * 다른 기사나 아이디어들을 읽고, 제가 만들어왔던 툴에 대해서도 읽으십시오. + * 새로운 기사를 읽기 위해 팔로우 하십시오. - - [**Twitter**에서 팔로우하기](https://twitter.com/tiangolo). - - FastAPI의 사용 용도를 알려주세요 (그것을 듣는 것을 좋아합니다). - - 발표 또는 새로운 툴 출시할 때 들으십시오. - - [follow @fastapi on Twitter](https://twitter.com/fastapi) (별도 계정에서) 할 수 있습니다. +## **FastAPI**에 대한 트윗 - - [**Linkedin**에서의 연결](https://www.linkedin.com/in/tiangolo/). - - 새로운 툴의 발표나 릴리스를 들을 수 있습니다 (단, Twitter를 더 자주 사용합니다 🤷‍♂). +**FastAPI**에 대해 트윗 하고 FastAPI가 마음에 드는 이유를 알려주세요. 🎉 - - [**Dev.to**](https://dev.to/tiangolo) 또는 [**Medium**](https://medium.com/@tiangolo)에서 제가 작성한 내용을 읽어 보십시오(또는 팔로우). - - 다른 기사나 아이디어들을 읽고, 제가 만들어왔던 툴에 대해서도 읽으십시오. - - 새로운 기사를 읽기 위해 팔로우 하십시오. +**FastAPI**가 어떻게 사용되고 있는지, 어떤 점이 마음에 들었는지, 어떤 프로젝트/회사에서 사용하고 있는지 등에 대해 듣고 싶습니다. - ## **FastAPI**에 대한 트윗 +## FastAPI에 투표하기 - [**FastAPI**에 대해 트윗](https://twitter.com/compose/tweet?text=I'm loving @fastapi because... https://github.com/fastapi/fastapi) 하고 FastAPI가 마음에 드는 이유를 알려주세요. 🎉 +* Slant에서 **FastAPI** 에 대해 투표하십시오. +* AlternativeTo에서 **FastAPI** 에 대해 투표하십시오. +* StackShare에서 **FastAPI** 에 대해 투표하십시오. - **FastAPI**가 어떻게 사용되고 있는지, 어떤 점이 마음에 들었는지, 어떤 프로젝트/회사에서 사용하고 있는지 등에 대해 듣고 싶습니다. +## GitHub의 이슈로 다른사람 돕기 - ## FastAPI에 투표하기 +다른 사람들의 질문에 도움을 줄 수 있습니다: - - [Slant에서 **FastAPI** 에 대해 투표하십시오](https://www.slant.co/options/34241/~fastapi-review). - - [AlternativeTo**FastAPI** 에 대해 투표하십시오](https://alternativeto.net/software/fastapi/). +* GitHub 디스커션 +* GitHub 이슈 - ## GitHub의 이슈로 다른사람 돕기 +많은 경우, 여러분은 이미 그 질문에 대한 답을 알고 있을 수도 있습니다. 🤓 - [존재하는 이슈](https://github.com/fastapi/fastapi/issues)를 확인하고 그것을 시도하고 도와줄 수 있습니다. 대부분의 경우 이미 답을 알고 있는 질문입니다. 🤓 +만약 많은 사람들의 문제를 도와준다면, 공식적인 [FastAPI 전문가](fastapi-people.md#fastapi-experts){.internal-link target=\_blank} 가 될 것입니다. 🎉 - 많은 사람들의 문제를 도와준다면, 공식적인 [FastAPI 전문가](https://github.com/fastapi/fastapi/blob/master/docs/en/docs/fastapi-people.md#experts) 가 될 수 있습니다{.internal-link target=_blank}. 🎉 +가장 중요한 점은: 친절하려고 노력하는 것입니다. 사람들은 좌절감을 안고 오며, 많은 경우 최선의 방식으로 질문하지 않을 수도 있습니다. 하지만 최대한 친절하게 대하려고 노력하세요. 🤗 - ## GitHub 저장소 보기 +**FastAPI** 커뮤니티의 목표는 친절하고 환영하는 것입니다. 동시에, 괴롭힘이나 무례한 행동을 받아들이지 마세요. 우리는 서로를 돌봐야 합니다. - GitHub에서 FastAPI를 "watch"할 수 있습니다 (오른쪽 상단 watch 버튼을 클릭): https://github.com/fastapi/fastapi. 👀 +--- - "Releases only" 대신 "Watching"을 선택하면 다른 사용자가 새로운 issue를 생성할 때 알림이 수신됩니다. +다른 사람들의 질문 (디스커션 또는 이슈에서) 해결을 도울 수 있는 방법은 다음과 같습니다. - 그런 다음 이런 issues를 해결 할 수 있도록 도움을 줄 수 있습니다. +### 질문 이해하기 - ## 이슈 생성하기 +* 질문하는 사람이 가진 **목적**과 사용 사례를 이해할 수 있는지 확인하세요. - GitHub 저장소에 [새로운 이슈 생성](https://github.com/fastapi/fastapi/issues/new/choose) 을 할 수 있습니다, 예를들면 다음과 같습니다: +* 질문 (대부분은 질문입니다)이 **명확**한지 확인하세요. - - **질문**을 하거나 **문제**에 대해 질문합니다. - - 새로운 **기능**을 제안 합니다. +* 많은 경우, 사용자가 가정한 해결책에 대한 질문을 하지만, 더 **좋은** 해결책이 있을 수 있습니다. 문제와 사용 사례를 더 잘 이해하면 더 나은 **대안적인 해결책**을 제안할 수 있습니다. - **참고**: 만약 이슈를 생성한다면, 저는 여러분에게 다른 사람들을 도와달라고 부탁할 것입니다. 😉 +* 질문을 이해할 수 없다면, 더 **자세한 정보**를 요청하세요. - ## Pull Request를 만드십시오 +### 문제 재현하기 - Pull Requests를 이용하여 소스코드에 [컨트리뷰트](https://github.com/fastapi/fastapi/blob/master/docs/en/docs/contributing.md){.internal-link target=_blank} 할 수 있습니다. 예를 들면 다음과 같습니다: +대부분의 경우, 질문은 질문자의 **원본 코드**와 관련이 있습니다. - - 문서에서 찾은 오타를 수정할 때. +많은 경우, 코드의 일부만 복사해서 올리지만, 그것만으로는 **문제를 재현**하기에 충분하지 않습니다. - - FastAPI를 [편집하여](https://github.com/fastapi/fastapi/edit/master/docs/en/data/external_links.yml) 작성했거나 찾은 문서, 비디오 또는 팟캐스트를 공유할 때. +* 질문자에게 최소한의 재현 가능한 예제를 제공해달라고 요청하세요. 이렇게 하면 코드를 **복사-붙여넣기**하여 직접 실행하고, 동일한 오류나 동작을 확인하거나 사용 사례를 더 잘 이해할 수 있습니다. - - 해당 섹션의 시작 부분에 링크를 추가했는지 확인하십시오. +* 너그러운 마음이 든다면, 문제 설명만을 기반으로 직접 **예제를 만들어**볼 수도 있습니다. 하지만, 이는 시간이 많이 걸릴 수 있으므로, 먼저 질문을 명확히 해달라고 요청하는 것이 좋습니다. - - 당신의 언어로 [문서 번역하는데](https://github.com/fastapi/fastapi/blob/master/docs/en/docs/contributing.md#translations){.internal-link target=_blank} 기여할 때. +### 해결책 제안하기 - - 또한 다른 사용자가 만든 번역을 검토하는데 도움을 줄 수도 있습니다. +* 질문을 충분히 이해한 후에는 가능한 **답변**을 제공할 수 있습니다. - - 새로운 문서의 섹션을 제안할 때. +* 많은 경우, 질문자의 **근본적인 문제나 사용 사례**를 이해하는 것이 중요합니다. 그들이 시도하는 방법보다 더 나은 해결책이 있을 수 있기 때문입니다. - - 기존 문제/버그를 수정할 때. +### 해결 요청하기 - - 새로운 feature를 추가할 때. +질문자가 답변을 확인하고 나면, 당신이 문제를 해결했을 가능성이 높습니다. 축하합니다, **당신은 영웅입니다**! 🦸 - ## 채팅에 참여하십시오 +* 이제 문제를 해결했다면, 질문자에게 다음을 요청할 수 있습니다. - 👥 [디스코드 채팅 서버](https://discord.gg/VQjSZaeJmf) 👥 에 가입하고 FastAPI 커뮤니티에서 다른 사람들과 어울리세요. + * GitHub 디스커션에서: 댓글을 **답변**으로 표시하도록 요청하세요. + * GitHub 이슈에서: 이슈를 **닫아달라고** 요청하세요. - /// tip +## GitHub 저장소 보기 - 질문이 있는 경우, [GitHub 이슈 ](https://github.com/fastapi/fastapi/issues/new/choose) 에서 질문하십시오, [FastAPI 전문가](https://github.com/fastapi/fastapi/blob/master/docs/en/docs/fastapi-people.md#experts) 의 도움을 받을 가능성이 높습니다{.internal-link target=_blank} . +GitHub에서 FastAPI를 "watch"할 수 있습니다 (오른쪽 상단 watch 버튼을 클릭): https://github.com/fastapi/fastapi. 👀 - /// +"Releases only" 대신 "Watching"을 선택하면, 새로운 이슈나 질문이 생성될 때 알림을 받을 수 있습니다. 또한, 특정하게 새로운 이슈, 디스커션, PR 등만 알림 받도록 설정할 수도 있습니다. - ``` - 다른 일반적인 대화에서만 채팅을 사용하십시오. - ``` +그런 다음 이런 이슈들을 해결 할 수 있도록 도움을 줄 수 있습니다. - 기존 [지터 채팅](https://gitter.im/fastapi/fastapi) 이 있지만 채널과 고급기능이 없어서 대화를 하기가 조금 어렵기 때문에 지금은 디스코드가 권장되는 시스템입니다. +## 이슈 생성하기 - ### 질문을 위해 채팅을 사용하지 마십시오 +GitHub 저장소에 새로운 이슈 생성을 할 수 있습니다, 예를들면 다음과 같습니다: - 채팅은 더 많은 "자유로운 대화"를 허용하기 때문에, 너무 일반적인 질문이나 대답하기 어려운 질문을 쉽게 질문을 할 수 있으므로, 답변을 받지 못할 수 있습니다. +* **질문**을 하거나 **문제**에 대해 질문합니다. +* 새로운 **기능**을 제안 합니다. - GitHub 이슈에서의 템플릿은 올바른 질문을 작성하도록 안내하여 더 쉽게 좋은 답변을 얻거나 질문하기 전에 스스로 문제를 해결할 수도 있습니다. 그리고 GitHub에서는 시간이 조금 걸리더라도 항상 모든 것에 답할 수 있습니다. 채팅 시스템에서는 개인적으로 그렇게 할 수 없습니다. 😅 +**참고**: 만약 이슈를 생성한다면, 저는 여러분에게 다른 사람들을 도와달라고 부탁할 것입니다. 😉 - 채팅 시스템에서의 대화 또한 GitHub에서 처럼 쉽게 검색할 수 없기 때문에 대화 중에 질문과 답변이 손실될 수 있습니다. 그리고 GitHub 이슈에 있는 것만 [FastAPI 전문가](https://github.com/fastapi/fastapi/blob/master/docs/en/docs/fastapi-people.md#experts)가 되는 것으로 간주되므로{.internal-link target=_blank} , GitHub 이슈에서 더 많은 관심을 받을 것입니다. +## Pull Requests 리뷰하기 - 반면, 채팅 시스템에는 수천 명의 사용자가 있기 때문에, 거의 항상 대화 상대를 찾을 가능성이 높습니다. 😄 +다른 사람들의 pull request를 리뷰하는 데 도움을 줄 수 있습니다. - ## 개발자 스폰서가 되십시오 +다시 한번 말하지만, 최대한 친절하게 리뷰해 주세요. 🤗 - [GitHub 스폰서](https://github.com/sponsors/tiangolo) 를 통해 개발자를 경제적으로 지원할 수 있습니다. +--- - 감사하다는 말로 커피를 ☕️ 한잔 사줄 수 있습니다. 😄 +Pull Rrquest를 리뷰할 때 고려해야 할 사항과 방법은 다음과 같습니다: - 또한 FastAPI의 실버 또는 골드 스폰서가 될 수 있습니다. 🏅🎉 +### 문제 이해하기 - ## FastAPI를 강화하는 도구의 스폰서가 되십시오 +* 먼저, 해당 pull request가 해결하려는 **문제를 이해하는지** 확인하세요. GitHub 디스커션 또는 이슈에서 더 긴 논의가 있었을 수도 있습니다. - 문서에서 보았듯이, FastAPI는 Starlette과 Pydantic 라는 거인의 어깨에 타고 있습니다. +* Pull request가 필요하지 않을 가능성도 있습니다. **다른 방식**으로 문제를 해결할 수 있다면, 그 방법을 제안하거나 질문할 수 있습니다. - 다음의 스폰서가 될 수 있습니다 +### 스타일에 너무 신경 쓰지 않기 - - [Samuel Colvin (Pydantic)](https://github.com/sponsors/samuelcolvin) - - [Encode (Starlette, Uvicorn)](https://github.com/sponsors/encode) +* 커밋 메시지 스타일 같은 것에 너무 신경 쓰지 않아도 됩니다. 저는 직접 커밋을 수정하여 squash and merge를 수행할 것입니다. - ------ +* 코드 스타일 규칙도 걱정할 필요 없습니다. 이미 자동화된 도구들이 이를 검사하고 있습니다. - 감사합니다! 🚀 +스타일이나 일관성 관련 요청이 필요한 경우, 제가 직접 요청하거나 필요한 변경 사항을 추가 커밋으로 수정할 것입니다. + +### 코드 확인하기 + +* 코드를 읽고, **논리적으로 타당**한지 확인한 후 로컬에서 실행하여 문제가 해결되는지 확인하세요. + +* 그런 다음, 확인했다고 **댓글**을 남겨 주세요. 그래야 제가 검토했음을 알 수 있습니다. + +/// info + +불행히도, 제가 단순히 여러 개의 승인만으로 PR을 신뢰할 수는 없습니다. + +3개, 5개 이상의 승인이 달린 PR이 실제로는 깨져 있거나, 버그가 있거나, 주장하는 문제를 해결하지 못하는 경우가 여러 번 있었습니다. 😅 + +따라서, 정말로 코드를 읽고 실행한 뒤, 댓글로 확인 내용을 남겨 주는 것이 매우 중요합니다. 🤓 + +/// + +* PR을 더 단순하게 만들 수 있다면 그렇게 요청할 수 있지만, 너무 까다로울 필요는 없습니다. 주관적인 견해가 많이 있을 수 있기 때문입니다 (그리고 저도 제 견해가 있을 거예요 🙈). 따라서 핵심적인 부분에 집중하는 것이 좋습니다. + +### 테스트 + +* PR에 **테스트**가 포함되어 있는지 확인하는 데 도움을 주세요. + +* PR을 적용하기 전에 테스트가 **실패**하는지 확인하세요. 🚨 + +* PR을 적용한 후 테스트가 **통과**하는지 확인하세요. ✅ + +* 많은 PR에는 테스트가 없습니다. 테스트를 추가하도록 **상기**시켜줄 수도 있고, 직접 테스트를 **제안**할 수도 있습니다. 이는 시간이 많이 소요되는 부분 중 하나이며, 그 부분을 많이 도와줄 수 있습니다. + +* 그리고 시도한 내용을 댓글로 남겨주세요. 그러면 제가 확인했다는 걸 알 수 있습니다. 🤓 + +## Pull Request를 만드십시오 + +Pull Requests를 이용하여 소스코드에 [컨트리뷰트](contributing.md){.internal-link target=\_blank} 할 수 있습니다. 예를 들면 다음과 같습니다: + +* 문서에서 발견한 오타를 수정할 때. +* FastAPI 관련 문서, 비디오 또는 팟캐스트를 작성했거나 발견하여 이 파일을 편집하여 공유할 때. + * 해당 섹션의 시작 부분에 링크를 추가해야 합니다. +* 당신의 언어로 [문서 번역하는데](contributing.md#translations){.internal-link target=\_blank} 기여할 때. + * 다른 사람이 작성한 번역을 검토하는 것도 도울 수 있습니다. +* 새로운 문서의 섹션을 제안할 때. +* 기존 문제/버그를 수정할 때. + * 테스트를 반드시 추가해야 합니다. +* 새로운 feature를 추가할 때. + * 테스트를 반드시 추가해야 합니다. + * 관련 문서가 필요하다면 반드시 추가해야 합니다. + +## FastAPI 유지 관리에 도움 주기 + +**FastAPI**의 유지 관리를 도와주세요! 🤓 + +할 일이 많고, 그 중 대부분은 **여러분**이 할 수 있습니다. + +지금 할 수 있는 주요 작업은: + +* [GitHub에서 다른 사람들의 질문에 도움 주기](#github_1){.internal-link target=_blank} (위의 섹션을 참조하세요). +* [Pull Request 리뷰하기](#pull-requests){.internal-link target=_blank} (위의 섹션을 참조하세요). + +이 두 작업이 **가장 많은 시간을 소모**하는 일입니다. 그것이 FastAPI 유지 관리의 주요 작업입니다. + +이 작업을 도와주신다면, **FastAPI 유지 관리에 도움을 주는 것**이며 그것이 **더 빠르고 더 잘 발전하는 것**을 보장하는 것입니다. 🚀 + +## 채팅에 참여하십시오 + +👥 디스코드 채팅 서버 👥 에 가입하고 FastAPI 커뮤니티에서 다른 사람들과 어울리세요. + +/// tip + +질문이 있는 경우, GitHub 디스커션 에서 질문하십시오, [FastAPI Experts](fastapi-people.md#fastapi-experts){.internal-link target=_blank} 의 도움을 받을 가능성이 높습니다. + +다른 일반적인 대화에서만 채팅을 사용하십시오. + +/// + +### 질문을 위해 채팅을 사용하지 마십시오 + +채팅은 더 많은 "자유로운 대화"를 허용하기 때문에, 너무 일반적인 질문이나 대답하기 어려운 질문을 쉽게 질문을 할 수 있으므로, 답변을 받지 못할 수 있습니다. + +GitHub 이슈에서의 템플릿은 올바른 질문을 작성하도록 안내하여 더 쉽게 좋은 답변을 얻거나 질문하기 전에 스스로 문제를 해결할 수도 있습니다. 그리고 GitHub에서는 시간이 조금 걸리더라도 항상 모든 것에 답할 수 있습니다. 채팅 시스템에서는 개인적으로 그렇게 할 수 없습니다. 😅 + +채팅 시스템에서의 대화 또한 GitHub에서 처럼 쉽게 검색할 수 없기 때문에 대화 중에 질문과 답변이 손실될 수 있습니다. 그리고 GitHub 이슈에 있는 것만 [FastAPI Expert](fastapi-people.md#fastapi-experts){.internal-link target=_blank}가 되는 것으로 간주되므로, GitHub 이슈에서 더 많은 관심을 받을 것입니다. + +반면, 채팅 시스템에는 수천 명의 사용자가 있기 때문에, 거의 항상 대화 상대를 찾을 가능성이 높습니다. 😄 + +## 개발자 스폰서가 되십시오 + +GitHub 스폰서 를 통해 개발자를 경제적으로 지원할 수 있습니다. + +감사하다는 말로 커피를 ☕️ 한잔 사줄 수 있습니다. 😄 + +또한 FastAPI의 실버 또는 골드 스폰서가 될 수 있습니다. 🏅🎉 + +## FastAPI를 강화하는 도구의 스폰서가 되십시오 + +문서에서 보았듯이, FastAPI는 Starlette과 Pydantic 라는 거인의 어깨에 타고 있습니다. + +다음의 스폰서가 될 수 있습니다 + +* Samuel Colvin (Pydantic) +* Encode (Starlette, Uvicorn) + +--- + +감사합니다! 🚀 From db554ca09426dfa7031a1b07a8b016b48c82c222 Mon Sep 17 00:00:00 2001 From: github-actions Date: Sat, 15 Feb 2025 11:38:21 +0000 Subject: [PATCH 048/157] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [skip ci] --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index c69b20e66..88bff8dbc 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -13,6 +13,7 @@ hide: ### Translations +* 🌐 Update Korean translation for `docs/ko/docs/help-fastapi.md`. PR [#13262](https://github.com/fastapi/fastapi/pull/13262) by [@Zerohertz](https://github.com/Zerohertz). * 🌐 Add Korean translation for `docs/ko/docs/advanced/custom-response.md`. PR [#13265](https://github.com/fastapi/fastapi/pull/13265) by [@11kkw](https://github.com/11kkw). * 🌐 Update Korean translation for `docs/ko/docs/tutorial/security/simple-oauth2.md`. PR [#13335](https://github.com/fastapi/fastapi/pull/13335) by [@yes0ng](https://github.com/yes0ng). * 🌐 Add Russian translation for `docs/ru/docs/advanced/response-cookies.md`. PR [#13327](https://github.com/fastapi/fastapi/pull/13327) by [@Stepakinoyan](https://github.com/Stepakinoyan). From 261bc2d3875ad95ec570a2cb15df6a54af39207f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Haoyu=20=28Daniel=29=20YANG=20=E6=9D=A8=E6=B5=A9=E5=AE=87?= Date: Sat, 15 Feb 2025 12:42:54 +0100 Subject: [PATCH 049/157] =?UTF-8?q?=E2=9C=8F=EF=B8=8F=20Remove=20duplicate?= =?UTF-8?q?=20title=20in=20docs=20`body-multiple-params`=20(#13345)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/en/docs/tutorial/body-multiple-params.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/en/docs/tutorial/body-multiple-params.md b/docs/en/docs/tutorial/body-multiple-params.md index 9fced9652..71b308bb4 100644 --- a/docs/en/docs/tutorial/body-multiple-params.md +++ b/docs/en/docs/tutorial/body-multiple-params.md @@ -10,8 +10,6 @@ And you can also declare body parameters as optional, by setting the default to {* ../../docs_src/body_multiple_params/tutorial001_an_py310.py hl[18:20] *} -## Multiple body parameters - /// note Notice that, in this case, the `item` that would be taken from the body is optional. As it has a `None` default value. From 540d8ff398ececd31c5299d0f6823523efb69c5d Mon Sep 17 00:00:00 2001 From: github-actions Date: Sat, 15 Feb 2025 11:43:16 +0000 Subject: [PATCH 050/157] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [skip ci] --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index 88bff8dbc..d5440404b 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -9,6 +9,7 @@ hide: ### Docs +* ✏️ Remove duplicate title in docs `body-multiple-params`. PR [#13345](https://github.com/fastapi/fastapi/pull/13345) by [@DanielYang59](https://github.com/DanielYang59). * 📝 Fix test badge. PR [#13313](https://github.com/fastapi/fastapi/pull/13313) by [@esadek](https://github.com/esadek). ### Translations From 1e6d95ce6d9ab5bc13796d2d068540e9c765e4c1 Mon Sep 17 00:00:00 2001 From: alv2017 Date: Sat, 15 Feb 2025 16:37:48 +0200 Subject: [PATCH 051/157] =?UTF-8?q?=E2=9C=85=20Simplify=20tests=20for=20`d?= =?UTF-8?q?ependency=5Ftesting`=20(#13223)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Alejandra <90076947+alejsdev@users.noreply.github.com> --- .../test_tutorial001.py | 53 +++++++++---- .../test_tutorial001_an.py | 56 -------------- .../test_tutorial001_an_py310.py | 75 ------------------- .../test_tutorial001_an_py39.py | 75 ------------------- .../test_tutorial001_py310.py | 75 ------------------- 5 files changed, 40 insertions(+), 294 deletions(-) delete mode 100644 tests/test_tutorial/test_testing_dependencies/test_tutorial001_an.py delete mode 100644 tests/test_tutorial/test_testing_dependencies/test_tutorial001_an_py310.py delete mode 100644 tests/test_tutorial/test_testing_dependencies/test_tutorial001_an_py39.py delete mode 100644 tests/test_tutorial/test_testing_dependencies/test_tutorial001_py310.py diff --git a/tests/test_tutorial/test_testing_dependencies/test_tutorial001.py b/tests/test_tutorial/test_testing_dependencies/test_tutorial001.py index af26307f5..00ee6ab1e 100644 --- a/tests/test_tutorial/test_testing_dependencies/test_tutorial001.py +++ b/tests/test_tutorial/test_testing_dependencies/test_tutorial001.py @@ -1,25 +1,48 @@ -from docs_src.dependency_testing.tutorial001 import ( - app, - client, - test_override_in_items, - test_override_in_items_with_params, - test_override_in_items_with_q, +import importlib +from types import ModuleType + +import pytest + +from ...utils import needs_py39, needs_py310 + + +@pytest.fixture( + name="test_module", + params=[ + "tutorial001", + pytest.param("tutorial001_py310", marks=needs_py310), + "tutorial001_an", + pytest.param("tutorial001_an_py39", marks=needs_py39), + pytest.param("tutorial001_an_py310", marks=needs_py310), + ], ) +def get_test_module(request: pytest.FixtureRequest) -> ModuleType: + mod: ModuleType = importlib.import_module( + f"docs_src.dependency_testing.{request.param}" + ) + return mod -def test_override_in_items_run(): +def test_override_in_items_run(test_module: ModuleType): + test_override_in_items = test_module.test_override_in_items + test_override_in_items() -def test_override_in_items_with_q_run(): +def test_override_in_items_with_q_run(test_module: ModuleType): + test_override_in_items_with_q = test_module.test_override_in_items_with_q + test_override_in_items_with_q() -def test_override_in_items_with_params_run(): +def test_override_in_items_with_params_run(test_module: ModuleType): + test_override_in_items_with_params = test_module.test_override_in_items_with_params + test_override_in_items_with_params() -def test_override_in_users(): +def test_override_in_users(test_module: ModuleType): + client = test_module.client response = client.get("/users/") assert response.status_code == 200, response.text assert response.json() == { @@ -28,7 +51,8 @@ def test_override_in_users(): } -def test_override_in_users_with_q(): +def test_override_in_users_with_q(test_module: ModuleType): + client = test_module.client response = client.get("/users/?q=foo") assert response.status_code == 200, response.text assert response.json() == { @@ -37,7 +61,8 @@ def test_override_in_users_with_q(): } -def test_override_in_users_with_params(): +def test_override_in_users_with_params(test_module: ModuleType): + client = test_module.client response = client.get("/users/?q=foo&skip=100&limit=200") assert response.status_code == 200, response.text assert response.json() == { @@ -46,7 +71,9 @@ def test_override_in_users_with_params(): } -def test_normal_app(): +def test_normal_app(test_module: ModuleType): + app = test_module.app + client = test_module.client app.dependency_overrides = None response = client.get("/items/?q=foo&skip=100&limit=200") assert response.status_code == 200, response.text diff --git a/tests/test_tutorial/test_testing_dependencies/test_tutorial001_an.py b/tests/test_tutorial/test_testing_dependencies/test_tutorial001_an.py deleted file mode 100644 index fc1f9149a..000000000 --- a/tests/test_tutorial/test_testing_dependencies/test_tutorial001_an.py +++ /dev/null @@ -1,56 +0,0 @@ -from docs_src.dependency_testing.tutorial001_an import ( - app, - client, - test_override_in_items, - test_override_in_items_with_params, - test_override_in_items_with_q, -) - - -def test_override_in_items_run(): - test_override_in_items() - - -def test_override_in_items_with_q_run(): - test_override_in_items_with_q() - - -def test_override_in_items_with_params_run(): - test_override_in_items_with_params() - - -def test_override_in_users(): - response = client.get("/users/") - assert response.status_code == 200, response.text - assert response.json() == { - "message": "Hello Users!", - "params": {"q": None, "skip": 5, "limit": 10}, - } - - -def test_override_in_users_with_q(): - response = client.get("/users/?q=foo") - assert response.status_code == 200, response.text - assert response.json() == { - "message": "Hello Users!", - "params": {"q": "foo", "skip": 5, "limit": 10}, - } - - -def test_override_in_users_with_params(): - response = client.get("/users/?q=foo&skip=100&limit=200") - assert response.status_code == 200, response.text - assert response.json() == { - "message": "Hello Users!", - "params": {"q": "foo", "skip": 5, "limit": 10}, - } - - -def test_normal_app(): - app.dependency_overrides = None - response = client.get("/items/?q=foo&skip=100&limit=200") - assert response.status_code == 200, response.text - assert response.json() == { - "message": "Hello Items!", - "params": {"q": "foo", "skip": 100, "limit": 200}, - } diff --git a/tests/test_tutorial/test_testing_dependencies/test_tutorial001_an_py310.py b/tests/test_tutorial/test_testing_dependencies/test_tutorial001_an_py310.py deleted file mode 100644 index a3d27f47f..000000000 --- a/tests/test_tutorial/test_testing_dependencies/test_tutorial001_an_py310.py +++ /dev/null @@ -1,75 +0,0 @@ -from ...utils import needs_py310 - - -@needs_py310 -def test_override_in_items_run(): - from docs_src.dependency_testing.tutorial001_an_py310 import test_override_in_items - - test_override_in_items() - - -@needs_py310 -def test_override_in_items_with_q_run(): - from docs_src.dependency_testing.tutorial001_an_py310 import ( - test_override_in_items_with_q, - ) - - test_override_in_items_with_q() - - -@needs_py310 -def test_override_in_items_with_params_run(): - from docs_src.dependency_testing.tutorial001_an_py310 import ( - test_override_in_items_with_params, - ) - - test_override_in_items_with_params() - - -@needs_py310 -def test_override_in_users(): - from docs_src.dependency_testing.tutorial001_an_py310 import client - - response = client.get("/users/") - assert response.status_code == 200, response.text - assert response.json() == { - "message": "Hello Users!", - "params": {"q": None, "skip": 5, "limit": 10}, - } - - -@needs_py310 -def test_override_in_users_with_q(): - from docs_src.dependency_testing.tutorial001_an_py310 import client - - response = client.get("/users/?q=foo") - assert response.status_code == 200, response.text - assert response.json() == { - "message": "Hello Users!", - "params": {"q": "foo", "skip": 5, "limit": 10}, - } - - -@needs_py310 -def test_override_in_users_with_params(): - from docs_src.dependency_testing.tutorial001_an_py310 import client - - response = client.get("/users/?q=foo&skip=100&limit=200") - assert response.status_code == 200, response.text - assert response.json() == { - "message": "Hello Users!", - "params": {"q": "foo", "skip": 5, "limit": 10}, - } - - -@needs_py310 -def test_normal_app(): - from docs_src.dependency_testing.tutorial001_an_py310 import app, client - - app.dependency_overrides = None - response = client.get("/items/?q=foo&skip=100&limit=200") - assert response.status_code == 200, response.text - assert response.json() == { - "message": "Hello Items!", - "params": {"q": "foo", "skip": 100, "limit": 200}, - } diff --git a/tests/test_tutorial/test_testing_dependencies/test_tutorial001_an_py39.py b/tests/test_tutorial/test_testing_dependencies/test_tutorial001_an_py39.py deleted file mode 100644 index f03ed5e07..000000000 --- a/tests/test_tutorial/test_testing_dependencies/test_tutorial001_an_py39.py +++ /dev/null @@ -1,75 +0,0 @@ -from ...utils import needs_py39 - - -@needs_py39 -def test_override_in_items_run(): - from docs_src.dependency_testing.tutorial001_an_py39 import test_override_in_items - - test_override_in_items() - - -@needs_py39 -def test_override_in_items_with_q_run(): - from docs_src.dependency_testing.tutorial001_an_py39 import ( - test_override_in_items_with_q, - ) - - test_override_in_items_with_q() - - -@needs_py39 -def test_override_in_items_with_params_run(): - from docs_src.dependency_testing.tutorial001_an_py39 import ( - test_override_in_items_with_params, - ) - - test_override_in_items_with_params() - - -@needs_py39 -def test_override_in_users(): - from docs_src.dependency_testing.tutorial001_an_py39 import client - - response = client.get("/users/") - assert response.status_code == 200, response.text - assert response.json() == { - "message": "Hello Users!", - "params": {"q": None, "skip": 5, "limit": 10}, - } - - -@needs_py39 -def test_override_in_users_with_q(): - from docs_src.dependency_testing.tutorial001_an_py39 import client - - response = client.get("/users/?q=foo") - assert response.status_code == 200, response.text - assert response.json() == { - "message": "Hello Users!", - "params": {"q": "foo", "skip": 5, "limit": 10}, - } - - -@needs_py39 -def test_override_in_users_with_params(): - from docs_src.dependency_testing.tutorial001_an_py39 import client - - response = client.get("/users/?q=foo&skip=100&limit=200") - assert response.status_code == 200, response.text - assert response.json() == { - "message": "Hello Users!", - "params": {"q": "foo", "skip": 5, "limit": 10}, - } - - -@needs_py39 -def test_normal_app(): - from docs_src.dependency_testing.tutorial001_an_py39 import app, client - - app.dependency_overrides = None - response = client.get("/items/?q=foo&skip=100&limit=200") - assert response.status_code == 200, response.text - assert response.json() == { - "message": "Hello Items!", - "params": {"q": "foo", "skip": 100, "limit": 200}, - } diff --git a/tests/test_tutorial/test_testing_dependencies/test_tutorial001_py310.py b/tests/test_tutorial/test_testing_dependencies/test_tutorial001_py310.py deleted file mode 100644 index 776b916ff..000000000 --- a/tests/test_tutorial/test_testing_dependencies/test_tutorial001_py310.py +++ /dev/null @@ -1,75 +0,0 @@ -from ...utils import needs_py310 - - -@needs_py310 -def test_override_in_items_run(): - from docs_src.dependency_testing.tutorial001_py310 import test_override_in_items - - test_override_in_items() - - -@needs_py310 -def test_override_in_items_with_q_run(): - from docs_src.dependency_testing.tutorial001_py310 import ( - test_override_in_items_with_q, - ) - - test_override_in_items_with_q() - - -@needs_py310 -def test_override_in_items_with_params_run(): - from docs_src.dependency_testing.tutorial001_py310 import ( - test_override_in_items_with_params, - ) - - test_override_in_items_with_params() - - -@needs_py310 -def test_override_in_users(): - from docs_src.dependency_testing.tutorial001_py310 import client - - response = client.get("/users/") - assert response.status_code == 200, response.text - assert response.json() == { - "message": "Hello Users!", - "params": {"q": None, "skip": 5, "limit": 10}, - } - - -@needs_py310 -def test_override_in_users_with_q(): - from docs_src.dependency_testing.tutorial001_py310 import client - - response = client.get("/users/?q=foo") - assert response.status_code == 200, response.text - assert response.json() == { - "message": "Hello Users!", - "params": {"q": "foo", "skip": 5, "limit": 10}, - } - - -@needs_py310 -def test_override_in_users_with_params(): - from docs_src.dependency_testing.tutorial001_py310 import client - - response = client.get("/users/?q=foo&skip=100&limit=200") - assert response.status_code == 200, response.text - assert response.json() == { - "message": "Hello Users!", - "params": {"q": "foo", "skip": 5, "limit": 10}, - } - - -@needs_py310 -def test_normal_app(): - from docs_src.dependency_testing.tutorial001_py310 import app, client - - app.dependency_overrides = None - response = client.get("/items/?q=foo&skip=100&limit=200") - assert response.status_code == 200, response.text - assert response.json() == { - "message": "Hello Items!", - "params": {"q": "foo", "skip": 100, "limit": 200}, - } From cbd7b986e7108ea5cb79eb653e37e0073bd22645 Mon Sep 17 00:00:00 2001 From: github-actions Date: Sat, 15 Feb 2025 14:38:14 +0000 Subject: [PATCH 052/157] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [skip ci] --- docs/en/docs/release-notes.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index d5440404b..28821455a 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -7,6 +7,10 @@ hide: ## Latest Changes +### Refactors + +* ✅ Simplify tests for `dependency_testing`. PR [#13223](https://github.com/fastapi/fastapi/pull/13223) by [@alv2017](https://github.com/alv2017). + ### Docs * ✏️ Remove duplicate title in docs `body-multiple-params`. PR [#13345](https://github.com/fastapi/fastapi/pull/13345) by [@DanielYang59](https://github.com/DanielYang59). From f6872dd29849a07a8cfd475c7c56dfa3f3ea5808 Mon Sep 17 00:00:00 2001 From: alv2017 Date: Sat, 15 Feb 2025 16:42:41 +0200 Subject: [PATCH 053/157] =?UTF-8?q?=E2=9C=85=20Simplify=20tests=20for=20`a?= =?UTF-8?q?pp=5Ftesting`=20(#13220)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Alejandra <90076947+alejsdev@users.noreply.github.com> --- .../test_tutorial/test_testing/test_main_b.py | 25 +++++++++++++++++-- .../test_testing/test_main_b_an.py | 10 -------- .../test_testing/test_main_b_an_py310.py | 13 ---------- .../test_testing/test_main_b_an_py39.py | 13 ---------- .../test_testing/test_main_b_py310.py | 13 ---------- 5 files changed, 23 insertions(+), 51 deletions(-) delete mode 100644 tests/test_tutorial/test_testing/test_main_b_an.py delete mode 100644 tests/test_tutorial/test_testing/test_main_b_an_py310.py delete mode 100644 tests/test_tutorial/test_testing/test_main_b_an_py39.py delete mode 100644 tests/test_tutorial/test_testing/test_main_b_py310.py diff --git a/tests/test_tutorial/test_testing/test_main_b.py b/tests/test_tutorial/test_testing/test_main_b.py index 1e1836f5b..aa7f969c6 100644 --- a/tests/test_tutorial/test_testing/test_main_b.py +++ b/tests/test_tutorial/test_testing/test_main_b.py @@ -1,7 +1,28 @@ -from docs_src.app_testing.app_b import test_main +import importlib +from types import ModuleType +import pytest -def test_app(): +from ...utils import needs_py39, needs_py310 + + +@pytest.fixture( + name="test_module", + params=[ + "app_b.test_main", + pytest.param("app_b_py310.test_main", marks=needs_py310), + "app_b_an.test_main", + pytest.param("app_b_an_py39.test_main", marks=needs_py39), + pytest.param("app_b_an_py310.test_main", marks=needs_py310), + ], +) +def get_test_module(request: pytest.FixtureRequest) -> ModuleType: + mod: ModuleType = importlib.import_module(f"docs_src.app_testing.{request.param}") + return mod + + +def test_app(test_module: ModuleType): + test_main = test_module test_main.test_create_existing_item() test_main.test_create_item() test_main.test_create_item_bad_token() diff --git a/tests/test_tutorial/test_testing/test_main_b_an.py b/tests/test_tutorial/test_testing/test_main_b_an.py deleted file mode 100644 index e53fc3224..000000000 --- a/tests/test_tutorial/test_testing/test_main_b_an.py +++ /dev/null @@ -1,10 +0,0 @@ -from docs_src.app_testing.app_b_an import test_main - - -def test_app(): - test_main.test_create_existing_item() - test_main.test_create_item() - test_main.test_create_item_bad_token() - test_main.test_read_nonexistent_item() - test_main.test_read_item() - test_main.test_read_item_bad_token() diff --git a/tests/test_tutorial/test_testing/test_main_b_an_py310.py b/tests/test_tutorial/test_testing/test_main_b_an_py310.py deleted file mode 100644 index c974e5dc1..000000000 --- a/tests/test_tutorial/test_testing/test_main_b_an_py310.py +++ /dev/null @@ -1,13 +0,0 @@ -from ...utils import needs_py310 - - -@needs_py310 -def test_app(): - from docs_src.app_testing.app_b_an_py310 import test_main - - test_main.test_create_existing_item() - test_main.test_create_item() - test_main.test_create_item_bad_token() - test_main.test_read_nonexistent_item() - test_main.test_read_item() - test_main.test_read_item_bad_token() diff --git a/tests/test_tutorial/test_testing/test_main_b_an_py39.py b/tests/test_tutorial/test_testing/test_main_b_an_py39.py deleted file mode 100644 index 71f99726c..000000000 --- a/tests/test_tutorial/test_testing/test_main_b_an_py39.py +++ /dev/null @@ -1,13 +0,0 @@ -from ...utils import needs_py39 - - -@needs_py39 -def test_app(): - from docs_src.app_testing.app_b_an_py39 import test_main - - test_main.test_create_existing_item() - test_main.test_create_item() - test_main.test_create_item_bad_token() - test_main.test_read_nonexistent_item() - test_main.test_read_item() - test_main.test_read_item_bad_token() diff --git a/tests/test_tutorial/test_testing/test_main_b_py310.py b/tests/test_tutorial/test_testing/test_main_b_py310.py deleted file mode 100644 index e30cdc073..000000000 --- a/tests/test_tutorial/test_testing/test_main_b_py310.py +++ /dev/null @@ -1,13 +0,0 @@ -from ...utils import needs_py310 - - -@needs_py310 -def test_app(): - from docs_src.app_testing.app_b_py310 import test_main - - test_main.test_create_existing_item() - test_main.test_create_item() - test_main.test_create_item_bad_token() - test_main.test_read_nonexistent_item() - test_main.test_read_item() - test_main.test_read_item_bad_token() From 2c937aabefe45b109edde6bc929721c2343804eb Mon Sep 17 00:00:00 2001 From: github-actions Date: Sat, 15 Feb 2025 14:43:05 +0000 Subject: [PATCH 054/157] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [skip ci] --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index 28821455a..9eacfea86 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -9,6 +9,7 @@ hide: ### Refactors +* ✅ Simplify tests for `app_testing`. PR [#13220](https://github.com/fastapi/fastapi/pull/13220) by [@alv2017](https://github.com/alv2017). * ✅ Simplify tests for `dependency_testing`. PR [#13223](https://github.com/fastapi/fastapi/pull/13223) by [@alv2017](https://github.com/alv2017). ### Docs From 9ec452a154e15e4af0f57f51a4fe1fee58879c4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Sat, 15 Feb 2025 17:23:59 +0100 Subject: [PATCH 055/157] =?UTF-8?q?=F0=9F=93=9D=20Update=20docs=20for=20Qu?= =?UTF-8?q?ery=20Params=20and=20String=20Validations,=20remove=20obsolete?= =?UTF-8?q?=20Ellipsis=20docs=20(`...`)=20(#13377)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../tutorial/query-params-str-validations.md | 16 --- .../tutorial/query-params-str-validations.md | 28 ----- .../tutorial/query-params-str-validations.md | 117 +++--------------- .../tutorial/query-params-str-validations.md | 16 --- .../tutorial/query-params-str-validations.md | 28 ----- .../tutorial/query-params-str-validations.md | 27 ---- .../tutorial006b.py | 11 -- .../tutorial006b_an.py | 12 -- .../tutorial006b_an_py39.py | 13 -- .../tutorial006c.py | 2 +- .../tutorial006c_an.py | 2 +- .../tutorial006c_an_py310.py | 2 +- .../tutorial006c_an_py39.py | 2 +- .../tutorial006c_py310.py | 2 +- .../tutorial006d.py | 11 -- .../tutorial006d_an.py | 12 -- .../tutorial006d_an_py39.py | 13 -- 17 files changed, 20 insertions(+), 294 deletions(-) delete mode 100644 docs_src/query_params_str_validations/tutorial006b.py delete mode 100644 docs_src/query_params_str_validations/tutorial006b_an.py delete mode 100644 docs_src/query_params_str_validations/tutorial006b_an_py39.py delete mode 100644 docs_src/query_params_str_validations/tutorial006d.py delete mode 100644 docs_src/query_params_str_validations/tutorial006d_an.py delete mode 100644 docs_src/query_params_str_validations/tutorial006d_an_py39.py diff --git a/docs/de/docs/tutorial/query-params-str-validations.md b/docs/de/docs/tutorial/query-params-str-validations.md index f181d501c..de8879ce8 100644 --- a/docs/de/docs/tutorial/query-params-str-validations.md +++ b/docs/de/docs/tutorial/query-params-str-validations.md @@ -315,22 +315,6 @@ Wenn Sie einen Parameter erforderlich machen wollen, während Sie `Query` verwen {* ../../docs_src/query_params_str_validations/tutorial006_an_py39.py hl[9] *} -### Erforderlich mit Ellipse (`...`) - -Es gibt eine Alternative, die explizit deklariert, dass ein Wert erforderlich ist. Sie können als Default das Literal `...` setzen: - -{* ../../docs_src/query_params_str_validations/tutorial006b_an_py39.py hl[9] *} - -/// info - -Falls Sie das `...` bisher noch nicht gesehen haben: Es ist ein spezieller einzelner Wert, Teil von Python und wird „Ellipsis“ genannt (Deutsch: Ellipse). - -Es wird von Pydantic und FastAPI verwendet, um explizit zu deklarieren, dass ein Wert erforderlich ist. - -/// - -Dies wird **FastAPI** wissen lassen, dass dieser Parameter erforderlich ist. - ### Erforderlich, kann `None` sein Sie können deklarieren, dass ein Parameter `None` akzeptiert, aber dennoch erforderlich ist. Das zwingt Clients, den Wert zu senden, selbst wenn er `None` ist. diff --git a/docs/em/docs/tutorial/query-params-str-validations.md b/docs/em/docs/tutorial/query-params-str-validations.md index dbaab5735..fd077bf8f 100644 --- a/docs/em/docs/tutorial/query-params-str-validations.md +++ b/docs/em/docs/tutorial/query-params-str-validations.md @@ -148,22 +148,6 @@ q: Union[str, None] = Query(default=None, min_length=3) {* ../../docs_src/query_params_str_validations/tutorial006.py hl[7] *} -### ✔ ⏮️ ❕ (`...`) - -📤 🎛 🌌 🎯 📣 👈 💲 ✔. 👆 💪 ⚒ `default` 🔢 🔑 💲 `...`: - -{* ../../docs_src/query_params_str_validations/tutorial006b.py hl[7] *} - -/// info - -🚥 👆 🚫 👀 👈 `...` ⏭: ⚫️ 🎁 👁 💲, ⚫️ 🍕 🐍 & 🤙 "❕". - -⚫️ ⚙️ Pydantic & FastAPI 🎯 📣 👈 💲 ✔. - -/// - -👉 🔜 ➡️ **FastAPI** 💭 👈 👉 🔢 ✔. - ### ✔ ⏮️ `None` 👆 💪 📣 👈 🔢 💪 🚫 `None`, ✋️ 👈 ⚫️ ✔. 👉 🔜 ⚡ 👩‍💻 📨 💲, 🚥 💲 `None`. @@ -178,18 +162,6 @@ Pydantic, ❔ ⚫️❔ 🏋️ 🌐 💽 🔬 & 🛠️ FastAPI, ✔️ /// -### ⚙️ Pydantic `Required` ↩️ ❕ (`...`) - -🚥 👆 💭 😬 ⚙️ `...`, 👆 💪 🗄 & ⚙️ `Required` ⚪️➡️ Pydantic: - -{* ../../docs_src/query_params_str_validations/tutorial006d.py hl[2,8] *} - -/// tip - -💭 👈 🌅 💼, 🕐❔ 🕳 🚚, 👆 💪 🎯 🚫 `default` 🔢, 👆 🛎 🚫 ✔️ ⚙️ `...` 🚫 `Required`. - -/// - ## 🔢 🔢 📇 / 💗 💲 🕐❔ 👆 🔬 🔢 🔢 🎯 ⏮️ `Query` 👆 💪 📣 ⚫️ 📨 📇 💲, ⚖️ 🙆‍♀ 🎏 🌌, 📨 💗 💲. diff --git a/docs/en/docs/tutorial/query-params-str-validations.md b/docs/en/docs/tutorial/query-params-str-validations.md index 1bf16334d..511099186 100644 --- a/docs/en/docs/tutorial/query-params-str-validations.md +++ b/docs/en/docs/tutorial/query-params-str-validations.md @@ -6,13 +6,13 @@ Let's take this application as example: {* ../../docs_src/query_params_str_validations/tutorial001_py310.py hl[7] *} -The query parameter `q` is of type `Union[str, None]` (or `str | None` in Python 3.10), that means that it's of type `str` but could also be `None`, and indeed, the default value is `None`, so FastAPI will know it's not required. +The query parameter `q` is of type `str | None`, that means that it's of type `str` but could also be `None`, and indeed, the default value is `None`, so FastAPI will know it's not required. /// note FastAPI will know that the value of `q` is not required because of the default value `= None`. -The `Union` in `Union[str, None]` will allow your editor to give you better support and detect errors. +Having `str | None` will allow your editor to give you better support and detect errors. /// @@ -25,29 +25,9 @@ We are going to enforce that even though `q` is optional, whenever it is provide To achieve that, first import: * `Query` from `fastapi` -* `Annotated` from `typing` (or from `typing_extensions` in Python below 3.9) +* `Annotated` from `typing` -//// tab | Python 3.10+ - -In Python 3.9 or above, `Annotated` is part of the standard library, so you can import it from `typing`. - -```Python hl_lines="1 3" -{!> ../../docs_src/query_params_str_validations/tutorial002_an_py310.py!} -``` - -//// - -//// tab | Python 3.8+ - -In versions of Python below Python 3.9 you import `Annotated` from `typing_extensions`. - -It will already be installed with FastAPI. - -```Python hl_lines="3-4" -{!> ../../docs_src/query_params_str_validations/tutorial002_an.py!} -``` - -//// +{* ../../docs_src/query_params_str_validations/tutorial002_an_py310.py hl[1,3] *} /// info @@ -145,54 +125,23 @@ As in this case (without using `Annotated`) we have to replace the default value So: -```Python -q: Union[str, None] = Query(default=None) -``` - -...makes the parameter optional, with a default value of `None`, the same as: - -```Python -q: Union[str, None] = None -``` - -And in Python 3.10 and above: - ```Python q: str | None = Query(default=None) ``` ...makes the parameter optional, with a default value of `None`, the same as: -```Python -q: str | None = None -``` - -But the `Query` versions declare it explicitly as being a query parameter. - -/// info - -Keep in mind that the most important part to make a parameter optional is the part: ```Python -= None -``` - -or the: - -```Python -= Query(default=None) +q: str | None = None ``` -as it will use that `None` as the default value, and that way make the parameter **not required**. - -The `Union[str, None]` part allows your editor to provide better support, but it is not what tells FastAPI that this parameter is not required. - -/// +But the `Query` version declares it explicitly as being a query parameter. Then, we can pass more parameters to `Query`. In this case, the `max_length` parameter that applies to strings: ```Python -q: Union[str, None] = Query(default=None, max_length=50) +q: str | None = Query(default=None, max_length=50) ``` This will validate the data, show a clear error when the data is not valid, and document the parameter in the OpenAPI schema *path operation*. @@ -201,7 +150,7 @@ This will validate the data, show a clear error when the data is not valid, and Keep in mind that when using `Query` inside of `Annotated` you cannot use the `default` parameter for `Query`. -Instead use the actual default value of the function parameter. Otherwise, it would be inconsistent. +Instead, use the actual default value of the function parameter. Otherwise, it would be inconsistent. For example, this is not allowed: @@ -255,7 +204,7 @@ This specific regular expression pattern checks that the received parameter valu If you feel lost with all these **"regular expression"** ideas, don't worry. They are a hard topic for many people. You can still do a lot of stuff without needing regular expressions yet. -But whenever you need them and go and learn them, know that you can already use them directly in **FastAPI**. +Now you know that whenever you need them you can use them in **FastAPI**. ### Pydantic v1 `regex` instead of `pattern` @@ -296,7 +245,7 @@ q: str instead of: ```Python -q: Union[str, None] = None +q: str | None = None ``` But we are now declaring it with `Query`, for example like: @@ -304,15 +253,7 @@ But we are now declaring it with `Query`, for example like: //// tab | Annotated ```Python -q: Annotated[Union[str, None], Query(min_length=3)] = None -``` - -//// - -//// tab | non-Annotated - -```Python -q: Union[str, None] = Query(default=None, min_length=3) +q: Annotated[str | None, Query(min_length=3)] = None ``` //// @@ -321,42 +262,14 @@ So, when you need to declare a value as required while using `Query`, you can si {* ../../docs_src/query_params_str_validations/tutorial006_an_py39.py hl[9] *} -### Required with Ellipsis (`...`) - -There's an alternative way to explicitly declare that a value is required. You can set the default to the literal value `...`: - -{* ../../docs_src/query_params_str_validations/tutorial006b_an_py39.py hl[9] *} - -/// info - -If you hadn't seen that `...` before: it is a special single value, it is part of Python and is called "Ellipsis". - -It is used by Pydantic and FastAPI to explicitly declare that a value is required. - -/// - -This will let **FastAPI** know that this parameter is required. - ### Required, can be `None` You can declare that a parameter can accept `None`, but that it's still required. This would force clients to send a value, even if the value is `None`. -To do that, you can declare that `None` is a valid type but still use `...` as the default: +To do that, you can declare that `None` is a valid type but simply do not declare a default value: {* ../../docs_src/query_params_str_validations/tutorial006c_an_py310.py hl[9] *} -/// tip - -Pydantic, which is what powers all the data validation and serialization in FastAPI, has a special behavior when you use `Optional` or `Union[Something, None]` without a default value, you can read more about it in the Pydantic docs about Required fields. - -/// - -/// tip - -Remember that in most of the cases, when something is required, you can simply omit the default, so you normally don't have to use `...`. - -/// - ## Query parameter list / multiple values When you define a query parameter explicitly with `Query` you can also declare it to receive a list of values, or said in another way, to receive multiple values. @@ -396,7 +309,7 @@ The interactive API docs will update accordingly, to allow multiple values: ### Query parameter list / multiple values with defaults -And you can also define a default `list` of values if none are provided: +You can also define a default `list` of values if none are provided: {* ../../docs_src/query_params_str_validations/tutorial012_an_py39.py hl[9] *} @@ -419,7 +332,7 @@ the default of `q` will be: `["foo", "bar"]` and your response will be: #### Using just `list` -You can also use `list` directly instead of `List[str]` (or `list[str]` in Python 3.9+): +You can also use `list` directly instead of `list[str]`: {* ../../docs_src/query_params_str_validations/tutorial013_an_py39.py hl[9] *} @@ -427,7 +340,7 @@ You can also use `list` directly instead of `List[str]` (or `list[str]` in Pytho Keep in mind that in this case, FastAPI won't check the contents of the list. -For example, `List[int]` would check (and document) that the contents of the list are integers. But `list` alone wouldn't. +For example, `list[int]` would check (and document) that the contents of the list are integers. But `list` alone wouldn't. /// diff --git a/docs/es/docs/tutorial/query-params-str-validations.md b/docs/es/docs/tutorial/query-params-str-validations.md index f378b9dce..9cb76156f 100644 --- a/docs/es/docs/tutorial/query-params-str-validations.md +++ b/docs/es/docs/tutorial/query-params-str-validations.md @@ -321,22 +321,6 @@ Así que, cuando necesites declarar un valor como requerido mientras usas `Query {* ../../docs_src/query_params_str_validations/tutorial006_an_py39.py hl[9] *} -### Requerido con Puntos suspensivos (`...`) - -Hay una manera alternativa de declarar explícitamente que un valor es requerido. Puedes establecer el valor por defecto al valor literal `...`: - -{* ../../docs_src/query_params_str_validations/tutorial006b_an_py39.py hl[9] *} - -/// info | Información - -Si no habías visto eso `...` antes: es un valor especial único, es parte de Python y se llama "Ellipsis". - -Se usa por Pydantic y FastAPI para declarar explícitamente que un valor es requerido. - -/// - -Esto le permitirá a **FastAPI** saber que este parámetro es requerido. - ### Requerido, puede ser `None` Puedes declarar que un parámetro puede aceptar `None`, pero que aún así es requerido. Esto obligaría a los clientes a enviar un valor, incluso si el valor es `None`. diff --git a/docs/ru/docs/tutorial/query-params-str-validations.md b/docs/ru/docs/tutorial/query-params-str-validations.md index 32a98ff22..13b7015db 100644 --- a/docs/ru/docs/tutorial/query-params-str-validations.md +++ b/docs/ru/docs/tutorial/query-params-str-validations.md @@ -291,22 +291,6 @@ q: Union[str, None] = Query(default=None, min_length=3) {* ../../docs_src/query_params_str_validations/tutorial006_an_py39.py hl[9] *} -### Обязательный параметр с Ellipsis (`...`) - -Альтернативный способ указать обязательность параметра запроса - это указать параметр `default` через многоточие `...`: - -{* ../../docs_src/query_params_str_validations/tutorial006b_an_py39.py hl[9] *} - -/// info | Дополнительная информация - -Если вы ранее не сталкивались с `...`: это специальное значение, часть языка Python и называется "Ellipsis". - -Используется в Pydantic и FastAPI для определения, что значение требуется обязательно. - -/// - -Таким образом, **FastAPI** определяет, что параметр является обязательным. - ### Обязательный параметр с `None` Вы можете определить, что параметр может принимать `None`, но всё ещё является обязательным. Это может потребоваться для того, чтобы пользователи явно указали параметр, даже если его значение будет `None`. @@ -321,18 +305,6 @@ Pydantic, мощь которого используется в FastAPI для /// -### Использование Pydantic's `Required` вместо Ellipsis (`...`) - -Если вас смущает `...`, вы можете использовать `Required` из Pydantic: - -{* ../../docs_src/query_params_str_validations/tutorial006d_an_py39.py hl[4,10] *} - -/// tip | Подсказка - -Запомните, когда вам необходимо объявить query-параметр обязательным, вы можете просто не указывать параметр `default`. Таким образом, вам редко придётся использовать `...` или `Required`. - -/// - ## Множество значений для query-параметра Для query-параметра `Query` можно указать, что он принимает список значений (множество значений). diff --git a/docs/zh/docs/tutorial/query-params-str-validations.md b/docs/zh/docs/tutorial/query-params-str-validations.md index 2fba671f7..c2f9a7e9f 100644 --- a/docs/zh/docs/tutorial/query-params-str-validations.md +++ b/docs/zh/docs/tutorial/query-params-str-validations.md @@ -108,21 +108,6 @@ q: Union[str, None] = Query(default=None, min_length=3) {* ../../docs_src/query_params_str_validations/tutorial006.py hl[7] *} -### 使用省略号(`...`)声明必需参数 - -有另一种方法可以显式的声明一个值是必需的,即将默认参数的默认值设为 `...` : - -{* ../../docs_src/query_params_str_validations/tutorial006b.py hl[7] *} - -/// info - -如果你之前没见过 `...` 这种用法:它是一个特殊的单独值,它是 Python 的一部分并且被称为“Ellipsis”(意为省略号 —— 译者注)。 -Pydantic 和 FastAPI 使用它来显式的声明需要一个值。 - -/// - -这将使 **FastAPI** 知道此查询参数是必需的。 - ### 使用`None`声明必需参数 你可以声明一个参数可以接收`None`值,但它仍然是必需的。这将强制客户端发送一个值,即使该值是`None`。 @@ -137,18 +122,6 @@ Pydantic 是 FastAPI 中所有数据验证和序列化的核心,当你在没 /// -### 使用Pydantic中的`Required`代替省略号(`...`) - -如果你觉得使用 `...` 不舒服,你也可以从 Pydantic 导入并使用 `Required`: - -{* ../../docs_src/query_params_str_validations/tutorial006d.py hl[2,8] *} - -/// tip - -请记住,在大多数情况下,当你需要某些东西时,可以简单地省略 `default` 参数,因此你通常不必使用 `...` 或 `Required` - -/// - ## 查询参数列表 / 多个值 当你使用 `Query` 显式地定义查询参数时,你还可以声明它去接收一组值,或换句话来说,接收多个值。 diff --git a/docs_src/query_params_str_validations/tutorial006b.py b/docs_src/query_params_str_validations/tutorial006b.py deleted file mode 100644 index a8d69c889..000000000 --- a/docs_src/query_params_str_validations/tutorial006b.py +++ /dev/null @@ -1,11 +0,0 @@ -from fastapi import FastAPI, Query - -app = FastAPI() - - -@app.get("/items/") -async def read_items(q: str = Query(default=..., min_length=3)): - results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]} - if q: - results.update({"q": q}) - return results diff --git a/docs_src/query_params_str_validations/tutorial006b_an.py b/docs_src/query_params_str_validations/tutorial006b_an.py deleted file mode 100644 index ea3b02583..000000000 --- a/docs_src/query_params_str_validations/tutorial006b_an.py +++ /dev/null @@ -1,12 +0,0 @@ -from fastapi import FastAPI, Query -from typing_extensions import Annotated - -app = FastAPI() - - -@app.get("/items/") -async def read_items(q: Annotated[str, Query(min_length=3)] = ...): - results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]} - if q: - results.update({"q": q}) - return results diff --git a/docs_src/query_params_str_validations/tutorial006b_an_py39.py b/docs_src/query_params_str_validations/tutorial006b_an_py39.py deleted file mode 100644 index 687a9f544..000000000 --- a/docs_src/query_params_str_validations/tutorial006b_an_py39.py +++ /dev/null @@ -1,13 +0,0 @@ -from typing import Annotated - -from fastapi import FastAPI, Query - -app = FastAPI() - - -@app.get("/items/") -async def read_items(q: Annotated[str, Query(min_length=3)] = ...): - results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]} - if q: - results.update({"q": q}) - return results diff --git a/docs_src/query_params_str_validations/tutorial006c.py b/docs_src/query_params_str_validations/tutorial006c.py index 2ac148c94..0a0e820da 100644 --- a/docs_src/query_params_str_validations/tutorial006c.py +++ b/docs_src/query_params_str_validations/tutorial006c.py @@ -6,7 +6,7 @@ app = FastAPI() @app.get("/items/") -async def read_items(q: Union[str, None] = Query(default=..., min_length=3)): +async def read_items(q: Union[str, None] = Query(min_length=3)): results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]} if q: results.update({"q": q}) diff --git a/docs_src/query_params_str_validations/tutorial006c_an.py b/docs_src/query_params_str_validations/tutorial006c_an.py index 10bf26a57..55c4f4adc 100644 --- a/docs_src/query_params_str_validations/tutorial006c_an.py +++ b/docs_src/query_params_str_validations/tutorial006c_an.py @@ -7,7 +7,7 @@ app = FastAPI() @app.get("/items/") -async def read_items(q: Annotated[Union[str, None], Query(min_length=3)] = ...): +async def read_items(q: Annotated[Union[str, None], Query(min_length=3)]): results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]} if q: results.update({"q": q}) diff --git a/docs_src/query_params_str_validations/tutorial006c_an_py310.py b/docs_src/query_params_str_validations/tutorial006c_an_py310.py index 1ab0a7d53..2995d9c97 100644 --- a/docs_src/query_params_str_validations/tutorial006c_an_py310.py +++ b/docs_src/query_params_str_validations/tutorial006c_an_py310.py @@ -6,7 +6,7 @@ app = FastAPI() @app.get("/items/") -async def read_items(q: Annotated[str | None, Query(min_length=3)] = ...): +async def read_items(q: Annotated[str | None, Query(min_length=3)]): results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]} if q: results.update({"q": q}) diff --git a/docs_src/query_params_str_validations/tutorial006c_an_py39.py b/docs_src/query_params_str_validations/tutorial006c_an_py39.py index ac1273331..76a1cd49a 100644 --- a/docs_src/query_params_str_validations/tutorial006c_an_py39.py +++ b/docs_src/query_params_str_validations/tutorial006c_an_py39.py @@ -6,7 +6,7 @@ app = FastAPI() @app.get("/items/") -async def read_items(q: Annotated[Union[str, None], Query(min_length=3)] = ...): +async def read_items(q: Annotated[Union[str, None], Query(min_length=3)]): results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]} if q: results.update({"q": q}) diff --git a/docs_src/query_params_str_validations/tutorial006c_py310.py b/docs_src/query_params_str_validations/tutorial006c_py310.py index 82dd9e5d7..88b499c7a 100644 --- a/docs_src/query_params_str_validations/tutorial006c_py310.py +++ b/docs_src/query_params_str_validations/tutorial006c_py310.py @@ -4,7 +4,7 @@ app = FastAPI() @app.get("/items/") -async def read_items(q: str | None = Query(default=..., min_length=3)): +async def read_items(q: str | None = Query(min_length=3)): results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]} if q: results.update({"q": q}) diff --git a/docs_src/query_params_str_validations/tutorial006d.py b/docs_src/query_params_str_validations/tutorial006d.py deleted file mode 100644 index a8d69c889..000000000 --- a/docs_src/query_params_str_validations/tutorial006d.py +++ /dev/null @@ -1,11 +0,0 @@ -from fastapi import FastAPI, Query - -app = FastAPI() - - -@app.get("/items/") -async def read_items(q: str = Query(default=..., min_length=3)): - results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]} - if q: - results.update({"q": q}) - return results diff --git a/docs_src/query_params_str_validations/tutorial006d_an.py b/docs_src/query_params_str_validations/tutorial006d_an.py deleted file mode 100644 index ea3b02583..000000000 --- a/docs_src/query_params_str_validations/tutorial006d_an.py +++ /dev/null @@ -1,12 +0,0 @@ -from fastapi import FastAPI, Query -from typing_extensions import Annotated - -app = FastAPI() - - -@app.get("/items/") -async def read_items(q: Annotated[str, Query(min_length=3)] = ...): - results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]} - if q: - results.update({"q": q}) - return results diff --git a/docs_src/query_params_str_validations/tutorial006d_an_py39.py b/docs_src/query_params_str_validations/tutorial006d_an_py39.py deleted file mode 100644 index 687a9f544..000000000 --- a/docs_src/query_params_str_validations/tutorial006d_an_py39.py +++ /dev/null @@ -1,13 +0,0 @@ -from typing import Annotated - -from fastapi import FastAPI, Query - -app = FastAPI() - - -@app.get("/items/") -async def read_items(q: Annotated[str, Query(min_length=3)] = ...): - results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]} - if q: - results.update({"q": q}) - return results From 2e788bbbdce346ab53f972cc87ecceeba2299bbe Mon Sep 17 00:00:00 2001 From: github-actions Date: Sat, 15 Feb 2025 16:24:24 +0000 Subject: [PATCH 056/157] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [skip ci] --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index 9eacfea86..6a3ec5b73 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -14,6 +14,7 @@ hide: ### Docs +* 📝 Update docs for Query Params and String Validations, remove obsolete Ellipsis docs (`...`). PR [#13377](https://github.com/fastapi/fastapi/pull/13377) by [@tiangolo](https://github.com/tiangolo). * ✏️ Remove duplicate title in docs `body-multiple-params`. PR [#13345](https://github.com/fastapi/fastapi/pull/13345) by [@DanielYang59](https://github.com/DanielYang59). * 📝 Fix test badge. PR [#13313](https://github.com/fastapi/fastapi/pull/13313) by [@esadek](https://github.com/esadek). From 08b817a84294a7e606f8437d11ee75fd3ef207a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Sat, 15 Feb 2025 17:28:09 +0100 Subject: [PATCH 057/157] =?UTF-8?q?=F0=9F=94=A5=20Remove=20manual=20type?= =?UTF-8?q?=20annotations=20in=20JWT=20tutorial=20to=20avoid=20typing=20ex?= =?UTF-8?q?pectations=20(JWT=20doesn't=20provide=20more=20types)=20(#13378?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs_src/security/tutorial004.py | 2 +- docs_src/security/tutorial004_an.py | 2 +- docs_src/security/tutorial004_an_py310.py | 2 +- docs_src/security/tutorial004_an_py39.py | 2 +- docs_src/security/tutorial004_py310.py | 2 +- docs_src/security/tutorial005_an.py | 2 +- docs_src/security/tutorial005_an_py310.py | 2 +- docs_src/security/tutorial005_an_py39.py | 6 +++--- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/docs_src/security/tutorial004.py b/docs_src/security/tutorial004.py index 91d161b8a..222589618 100644 --- a/docs_src/security/tutorial004.py +++ b/docs_src/security/tutorial004.py @@ -95,7 +95,7 @@ async def get_current_user(token: str = Depends(oauth2_scheme)): ) try: payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM]) - username: str = payload.get("sub") + username = payload.get("sub") if username is None: raise credentials_exception token_data = TokenData(username=username) diff --git a/docs_src/security/tutorial004_an.py b/docs_src/security/tutorial004_an.py index df50754af..e2221cd39 100644 --- a/docs_src/security/tutorial004_an.py +++ b/docs_src/security/tutorial004_an.py @@ -96,7 +96,7 @@ async def get_current_user(token: Annotated[str, Depends(oauth2_scheme)]): ) try: payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM]) - username: str = payload.get("sub") + username = payload.get("sub") if username is None: raise credentials_exception token_data = TokenData(username=username) diff --git a/docs_src/security/tutorial004_an_py310.py b/docs_src/security/tutorial004_an_py310.py index eff54ef01..a3f74fc0e 100644 --- a/docs_src/security/tutorial004_an_py310.py +++ b/docs_src/security/tutorial004_an_py310.py @@ -95,7 +95,7 @@ async def get_current_user(token: Annotated[str, Depends(oauth2_scheme)]): ) try: payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM]) - username: str = payload.get("sub") + username = payload.get("sub") if username is None: raise credentials_exception token_data = TokenData(username=username) diff --git a/docs_src/security/tutorial004_an_py39.py b/docs_src/security/tutorial004_an_py39.py index 0455b500c..b33d677ed 100644 --- a/docs_src/security/tutorial004_an_py39.py +++ b/docs_src/security/tutorial004_an_py39.py @@ -95,7 +95,7 @@ async def get_current_user(token: Annotated[str, Depends(oauth2_scheme)]): ) try: payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM]) - username: str = payload.get("sub") + username = payload.get("sub") if username is None: raise credentials_exception token_data = TokenData(username=username) diff --git a/docs_src/security/tutorial004_py310.py b/docs_src/security/tutorial004_py310.py index 78bee22a3..d46ce26bf 100644 --- a/docs_src/security/tutorial004_py310.py +++ b/docs_src/security/tutorial004_py310.py @@ -94,7 +94,7 @@ async def get_current_user(token: str = Depends(oauth2_scheme)): ) try: payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM]) - username: str = payload.get("sub") + username = payload.get("sub") if username is None: raise credentials_exception token_data = TokenData(username=username) diff --git a/docs_src/security/tutorial005_an.py b/docs_src/security/tutorial005_an.py index 5b67cb145..2e8bb3bdb 100644 --- a/docs_src/security/tutorial005_an.py +++ b/docs_src/security/tutorial005_an.py @@ -117,7 +117,7 @@ async def get_current_user( ) try: payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM]) - username: str = payload.get("sub") + username = payload.get("sub") if username is None: raise credentials_exception token_scopes = payload.get("scopes", []) diff --git a/docs_src/security/tutorial005_an_py310.py b/docs_src/security/tutorial005_an_py310.py index 297193e35..90781587f 100644 --- a/docs_src/security/tutorial005_an_py310.py +++ b/docs_src/security/tutorial005_an_py310.py @@ -116,7 +116,7 @@ async def get_current_user( ) try: payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM]) - username: str = payload.get("sub") + username = payload.get("sub") if username is None: raise credentials_exception token_scopes = payload.get("scopes", []) diff --git a/docs_src/security/tutorial005_an_py39.py b/docs_src/security/tutorial005_an_py39.py index 1acf47bdc..a5192d8d6 100644 --- a/docs_src/security/tutorial005_an_py39.py +++ b/docs_src/security/tutorial005_an_py39.py @@ -1,5 +1,5 @@ from datetime import datetime, timedelta, timezone -from typing import Annotated, List, Union +from typing import Annotated, Union import jwt from fastapi import Depends, FastAPI, HTTPException, Security, status @@ -44,7 +44,7 @@ class Token(BaseModel): class TokenData(BaseModel): username: Union[str, None] = None - scopes: List[str] = [] + scopes: list[str] = [] class User(BaseModel): @@ -116,7 +116,7 @@ async def get_current_user( ) try: payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM]) - username: str = payload.get("sub") + username = payload.get("sub") if username is None: raise credentials_exception token_scopes = payload.get("scopes", []) From 2b3b4161220da0ed854f0d556aa497a8e3e574db Mon Sep 17 00:00:00 2001 From: github-actions Date: Sat, 15 Feb 2025 16:28:29 +0000 Subject: [PATCH 058/157] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [skip ci] --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index 6a3ec5b73..7d8ca5ba0 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -14,6 +14,7 @@ hide: ### Docs +* 🔥 Remove manual type annotations in JWT tutorial to avoid typing expectations (JWT doesn't provide more types). PR [#13378](https://github.com/fastapi/fastapi/pull/13378) by [@tiangolo](https://github.com/tiangolo). * 📝 Update docs for Query Params and String Validations, remove obsolete Ellipsis docs (`...`). PR [#13377](https://github.com/fastapi/fastapi/pull/13377) by [@tiangolo](https://github.com/tiangolo). * ✏️ Remove duplicate title in docs `body-multiple-params`. PR [#13345](https://github.com/fastapi/fastapi/pull/13345) by [@DanielYang59](https://github.com/DanielYang59). * 📝 Fix test badge. PR [#13313](https://github.com/fastapi/fastapi/pull/13313) by [@esadek](https://github.com/esadek). From 5451d05bc84fc8888dfc9cd8a9f3b2c3987c751e Mon Sep 17 00:00:00 2001 From: alv2017 Date: Sat, 15 Feb 2025 18:31:57 +0200 Subject: [PATCH 059/157] =?UTF-8?q?=E2=9C=85=20Simplify=20tests=20for=20`q?= =?UTF-8?q?uery=5Fparams=5Fstr=5Fvalidations`=20(#13218)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Alejandra <90076947+alejsdev@users.noreply.github.com> --- .../test_tutorial010.py | 23 ++- .../test_tutorial010_an.py | 168 ----------------- .../test_tutorial010_an_py310.py | 175 ------------------ .../test_tutorial010_an_py39.py | 175 ------------------ .../test_tutorial010_py310.py | 175 ------------------ .../test_tutorial011.py | 31 +++- .../test_tutorial011_an.py | 108 ----------- .../test_tutorial011_an_py310.py | 118 ------------ .../test_tutorial011_an_py39.py | 118 ------------ .../test_tutorial011_py310.py | 118 ------------ .../test_tutorial011_py39.py | 118 ------------ .../test_tutorial012.py | 29 ++- .../test_tutorial012_an.py | 96 ---------- .../test_tutorial012_an_py39.py | 106 ----------- .../test_tutorial012_py39.py | 106 ----------- .../test_tutorial013.py | 28 ++- .../test_tutorial013_an.py | 96 ---------- .../test_tutorial013_an_py39.py | 106 ----------- .../test_tutorial014.py | 30 ++- .../test_tutorial014_an.py | 81 -------- .../test_tutorial014_an_py310.py | 91 --------- .../test_tutorial014_an_py39.py | 91 --------- .../test_tutorial014_py310.py | 91 --------- 23 files changed, 117 insertions(+), 2161 deletions(-) delete mode 100644 tests/test_tutorial/test_query_params_str_validations/test_tutorial010_an.py delete mode 100644 tests/test_tutorial/test_query_params_str_validations/test_tutorial010_an_py310.py delete mode 100644 tests/test_tutorial/test_query_params_str_validations/test_tutorial010_an_py39.py delete mode 100644 tests/test_tutorial/test_query_params_str_validations/test_tutorial010_py310.py delete mode 100644 tests/test_tutorial/test_query_params_str_validations/test_tutorial011_an.py delete mode 100644 tests/test_tutorial/test_query_params_str_validations/test_tutorial011_an_py310.py delete mode 100644 tests/test_tutorial/test_query_params_str_validations/test_tutorial011_an_py39.py delete mode 100644 tests/test_tutorial/test_query_params_str_validations/test_tutorial011_py310.py delete mode 100644 tests/test_tutorial/test_query_params_str_validations/test_tutorial011_py39.py delete mode 100644 tests/test_tutorial/test_query_params_str_validations/test_tutorial012_an.py delete mode 100644 tests/test_tutorial/test_query_params_str_validations/test_tutorial012_an_py39.py delete mode 100644 tests/test_tutorial/test_query_params_str_validations/test_tutorial012_py39.py delete mode 100644 tests/test_tutorial/test_query_params_str_validations/test_tutorial013_an.py delete mode 100644 tests/test_tutorial/test_query_params_str_validations/test_tutorial013_an_py39.py delete mode 100644 tests/test_tutorial/test_query_params_str_validations/test_tutorial014_an.py delete mode 100644 tests/test_tutorial/test_query_params_str_validations/test_tutorial014_an_py310.py delete mode 100644 tests/test_tutorial/test_query_params_str_validations/test_tutorial014_an_py39.py delete mode 100644 tests/test_tutorial/test_query_params_str_validations/test_tutorial014_py310.py diff --git a/tests/test_tutorial/test_query_params_str_validations/test_tutorial010.py b/tests/test_tutorial/test_query_params_str_validations/test_tutorial010.py index 4f52d6ff7..e08e16963 100644 --- a/tests/test_tutorial/test_query_params_str_validations/test_tutorial010.py +++ b/tests/test_tutorial/test_query_params_str_validations/test_tutorial010.py @@ -1,14 +1,29 @@ +import importlib + import pytest from dirty_equals import IsDict from fastapi._compat import PYDANTIC_VERSION_MINOR_TUPLE from fastapi.testclient import TestClient +from ...utils import needs_py39, needs_py310 + -@pytest.fixture(name="client") -def get_client(): - from docs_src.query_params_str_validations.tutorial010 import app +@pytest.fixture( + name="client", + params=[ + "tutorial010", + pytest.param("tutorial010_py310", marks=needs_py310), + "tutorial010_an", + pytest.param("tutorial010_an_py39", marks=needs_py39), + pytest.param("tutorial010_an_py310", marks=needs_py310), + ], +) +def get_client(request: pytest.FixtureRequest): + mod = importlib.import_module( + f"docs_src.query_params_str_validations.{request.param}" + ) - client = TestClient(app) + client = TestClient(mod.app) return client diff --git a/tests/test_tutorial/test_query_params_str_validations/test_tutorial010_an.py b/tests/test_tutorial/test_query_params_str_validations/test_tutorial010_an.py deleted file mode 100644 index 5daca1e70..000000000 --- a/tests/test_tutorial/test_query_params_str_validations/test_tutorial010_an.py +++ /dev/null @@ -1,168 +0,0 @@ -import pytest -from dirty_equals import IsDict -from fastapi._compat import PYDANTIC_VERSION_MINOR_TUPLE -from fastapi.testclient import TestClient - - -@pytest.fixture(name="client") -def get_client(): - from docs_src.query_params_str_validations.tutorial010_an import app - - client = TestClient(app) - return client - - -def test_query_params_str_validations_no_query(client: TestClient): - response = client.get("/items/") - assert response.status_code == 200 - assert response.json() == {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]} - - -def test_query_params_str_validations_item_query_fixedquery(client: TestClient): - response = client.get("/items/", params={"item-query": "fixedquery"}) - assert response.status_code == 200 - assert response.json() == { - "items": [{"item_id": "Foo"}, {"item_id": "Bar"}], - "q": "fixedquery", - } - - -def test_query_params_str_validations_q_fixedquery(client: TestClient): - response = client.get("/items/", params={"q": "fixedquery"}) - assert response.status_code == 200 - assert response.json() == {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]} - - -def test_query_params_str_validations_item_query_nonregexquery(client: TestClient): - response = client.get("/items/", params={"item-query": "nonregexquery"}) - assert response.status_code == 422 - assert response.json() == IsDict( - { - "detail": [ - { - "type": "string_pattern_mismatch", - "loc": ["query", "item-query"], - "msg": "String should match pattern '^fixedquery$'", - "input": "nonregexquery", - "ctx": {"pattern": "^fixedquery$"}, - } - ] - } - ) | IsDict( - # TODO: remove when deprecating Pydantic v1 - { - "detail": [ - { - "ctx": {"pattern": "^fixedquery$"}, - "loc": ["query", "item-query"], - "msg": 'string does not match regex "^fixedquery$"', - "type": "value_error.str.regex", - } - ] - } - ) - - -def test_openapi_schema(client: TestClient): - response = client.get("/openapi.json") - assert response.status_code == 200, response.text - assert response.json() == { - "openapi": "3.1.0", - "info": {"title": "FastAPI", "version": "0.1.0"}, - "paths": { - "/items/": { - "get": { - "responses": { - "200": { - "description": "Successful Response", - "content": {"application/json": {"schema": {}}}, - }, - "422": { - "description": "Validation Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HTTPValidationError" - } - } - }, - }, - }, - "summary": "Read Items", - "operationId": "read_items_items__get", - "parameters": [ - { - "description": "Query string for the items to search in the database that have a good match", - "required": False, - "deprecated": True, - "schema": IsDict( - { - "anyOf": [ - { - "type": "string", - "minLength": 3, - "maxLength": 50, - "pattern": "^fixedquery$", - }, - {"type": "null"}, - ], - "title": "Query string", - "description": "Query string for the items to search in the database that have a good match", - # See https://github.com/pydantic/pydantic/blob/80353c29a824c55dea4667b328ba8f329879ac9f/tests/test_fastapi.sh#L25-L34. - **( - {"deprecated": True} - if PYDANTIC_VERSION_MINOR_TUPLE >= (2, 10) - else {} - ), - } - ) - | IsDict( - # TODO: remove when deprecating Pydantic v1 - { - "title": "Query string", - "maxLength": 50, - "minLength": 3, - "pattern": "^fixedquery$", - "type": "string", - "description": "Query string for the items to search in the database that have a good match", - } - ), - "name": "item-query", - "in": "query", - } - ], - } - } - }, - "components": { - "schemas": { - "ValidationError": { - "title": "ValidationError", - "required": ["loc", "msg", "type"], - "type": "object", - "properties": { - "loc": { - "title": "Location", - "type": "array", - "items": { - "anyOf": [{"type": "string"}, {"type": "integer"}] - }, - }, - "msg": {"title": "Message", "type": "string"}, - "type": {"title": "Error Type", "type": "string"}, - }, - }, - "HTTPValidationError": { - "title": "HTTPValidationError", - "type": "object", - "properties": { - "detail": { - "title": "Detail", - "type": "array", - "items": {"$ref": "#/components/schemas/ValidationError"}, - } - }, - }, - } - }, - } diff --git a/tests/test_tutorial/test_query_params_str_validations/test_tutorial010_an_py310.py b/tests/test_tutorial/test_query_params_str_validations/test_tutorial010_an_py310.py deleted file mode 100644 index 89da4d82e..000000000 --- a/tests/test_tutorial/test_query_params_str_validations/test_tutorial010_an_py310.py +++ /dev/null @@ -1,175 +0,0 @@ -import pytest -from dirty_equals import IsDict -from fastapi._compat import PYDANTIC_VERSION_MINOR_TUPLE -from fastapi.testclient import TestClient - -from ...utils import needs_py310 - - -@pytest.fixture(name="client") -def get_client(): - from docs_src.query_params_str_validations.tutorial010_an_py310 import app - - client = TestClient(app) - return client - - -@needs_py310 -def test_query_params_str_validations_no_query(client: TestClient): - response = client.get("/items/") - assert response.status_code == 200 - assert response.json() == {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]} - - -@needs_py310 -def test_query_params_str_validations_item_query_fixedquery(client: TestClient): - response = client.get("/items/", params={"item-query": "fixedquery"}) - assert response.status_code == 200 - assert response.json() == { - "items": [{"item_id": "Foo"}, {"item_id": "Bar"}], - "q": "fixedquery", - } - - -@needs_py310 -def test_query_params_str_validations_q_fixedquery(client: TestClient): - response = client.get("/items/", params={"q": "fixedquery"}) - assert response.status_code == 200 - assert response.json() == {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]} - - -@needs_py310 -def test_query_params_str_validations_item_query_nonregexquery(client: TestClient): - response = client.get("/items/", params={"item-query": "nonregexquery"}) - assert response.status_code == 422 - assert response.json() == IsDict( - { - "detail": [ - { - "type": "string_pattern_mismatch", - "loc": ["query", "item-query"], - "msg": "String should match pattern '^fixedquery$'", - "input": "nonregexquery", - "ctx": {"pattern": "^fixedquery$"}, - } - ] - } - ) | IsDict( - # TODO: remove when deprecating Pydantic v1 - { - "detail": [ - { - "ctx": {"pattern": "^fixedquery$"}, - "loc": ["query", "item-query"], - "msg": 'string does not match regex "^fixedquery$"', - "type": "value_error.str.regex", - } - ] - } - ) - - -@needs_py310 -def test_openapi_schema(client: TestClient): - response = client.get("/openapi.json") - assert response.status_code == 200, response.text - assert response.json() == { - "openapi": "3.1.0", - "info": {"title": "FastAPI", "version": "0.1.0"}, - "paths": { - "/items/": { - "get": { - "responses": { - "200": { - "description": "Successful Response", - "content": {"application/json": {"schema": {}}}, - }, - "422": { - "description": "Validation Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HTTPValidationError" - } - } - }, - }, - }, - "summary": "Read Items", - "operationId": "read_items_items__get", - "parameters": [ - { - "description": "Query string for the items to search in the database that have a good match", - "required": False, - "deprecated": True, - "schema": IsDict( - { - "anyOf": [ - { - "type": "string", - "minLength": 3, - "maxLength": 50, - "pattern": "^fixedquery$", - }, - {"type": "null"}, - ], - "title": "Query string", - "description": "Query string for the items to search in the database that have a good match", - # See https://github.com/pydantic/pydantic/blob/80353c29a824c55dea4667b328ba8f329879ac9f/tests/test_fastapi.sh#L25-L34. - **( - {"deprecated": True} - if PYDANTIC_VERSION_MINOR_TUPLE >= (2, 10) - else {} - ), - } - ) - | IsDict( - # TODO: remove when deprecating Pydantic v1 - { - "title": "Query string", - "maxLength": 50, - "minLength": 3, - "pattern": "^fixedquery$", - "type": "string", - "description": "Query string for the items to search in the database that have a good match", - } - ), - "name": "item-query", - "in": "query", - } - ], - } - } - }, - "components": { - "schemas": { - "ValidationError": { - "title": "ValidationError", - "required": ["loc", "msg", "type"], - "type": "object", - "properties": { - "loc": { - "title": "Location", - "type": "array", - "items": { - "anyOf": [{"type": "string"}, {"type": "integer"}] - }, - }, - "msg": {"title": "Message", "type": "string"}, - "type": {"title": "Error Type", "type": "string"}, - }, - }, - "HTTPValidationError": { - "title": "HTTPValidationError", - "type": "object", - "properties": { - "detail": { - "title": "Detail", - "type": "array", - "items": {"$ref": "#/components/schemas/ValidationError"}, - } - }, - }, - } - }, - } diff --git a/tests/test_tutorial/test_query_params_str_validations/test_tutorial010_an_py39.py b/tests/test_tutorial/test_query_params_str_validations/test_tutorial010_an_py39.py deleted file mode 100644 index f5f692b06..000000000 --- a/tests/test_tutorial/test_query_params_str_validations/test_tutorial010_an_py39.py +++ /dev/null @@ -1,175 +0,0 @@ -import pytest -from dirty_equals import IsDict -from fastapi._compat import PYDANTIC_VERSION_MINOR_TUPLE -from fastapi.testclient import TestClient - -from ...utils import needs_py39 - - -@pytest.fixture(name="client") -def get_client(): - from docs_src.query_params_str_validations.tutorial010_an_py39 import app - - client = TestClient(app) - return client - - -@needs_py39 -def test_query_params_str_validations_no_query(client: TestClient): - response = client.get("/items/") - assert response.status_code == 200 - assert response.json() == {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]} - - -@needs_py39 -def test_query_params_str_validations_item_query_fixedquery(client: TestClient): - response = client.get("/items/", params={"item-query": "fixedquery"}) - assert response.status_code == 200 - assert response.json() == { - "items": [{"item_id": "Foo"}, {"item_id": "Bar"}], - "q": "fixedquery", - } - - -@needs_py39 -def test_query_params_str_validations_q_fixedquery(client: TestClient): - response = client.get("/items/", params={"q": "fixedquery"}) - assert response.status_code == 200 - assert response.json() == {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]} - - -@needs_py39 -def test_query_params_str_validations_item_query_nonregexquery(client: TestClient): - response = client.get("/items/", params={"item-query": "nonregexquery"}) - assert response.status_code == 422 - assert response.json() == IsDict( - { - "detail": [ - { - "type": "string_pattern_mismatch", - "loc": ["query", "item-query"], - "msg": "String should match pattern '^fixedquery$'", - "input": "nonregexquery", - "ctx": {"pattern": "^fixedquery$"}, - } - ] - } - ) | IsDict( - # TODO: remove when deprecating Pydantic v1 - { - "detail": [ - { - "ctx": {"pattern": "^fixedquery$"}, - "loc": ["query", "item-query"], - "msg": 'string does not match regex "^fixedquery$"', - "type": "value_error.str.regex", - } - ] - } - ) - - -@needs_py39 -def test_openapi_schema(client: TestClient): - response = client.get("/openapi.json") - assert response.status_code == 200, response.text - assert response.json() == { - "openapi": "3.1.0", - "info": {"title": "FastAPI", "version": "0.1.0"}, - "paths": { - "/items/": { - "get": { - "responses": { - "200": { - "description": "Successful Response", - "content": {"application/json": {"schema": {}}}, - }, - "422": { - "description": "Validation Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HTTPValidationError" - } - } - }, - }, - }, - "summary": "Read Items", - "operationId": "read_items_items__get", - "parameters": [ - { - "description": "Query string for the items to search in the database that have a good match", - "required": False, - "deprecated": True, - "schema": IsDict( - { - "anyOf": [ - { - "type": "string", - "minLength": 3, - "maxLength": 50, - "pattern": "^fixedquery$", - }, - {"type": "null"}, - ], - "title": "Query string", - "description": "Query string for the items to search in the database that have a good match", - # See https://github.com/pydantic/pydantic/blob/80353c29a824c55dea4667b328ba8f329879ac9f/tests/test_fastapi.sh#L25-L34. - **( - {"deprecated": True} - if PYDANTIC_VERSION_MINOR_TUPLE >= (2, 10) - else {} - ), - } - ) - | IsDict( - # TODO: remove when deprecating Pydantic v1 - { - "title": "Query string", - "maxLength": 50, - "minLength": 3, - "pattern": "^fixedquery$", - "type": "string", - "description": "Query string for the items to search in the database that have a good match", - } - ), - "name": "item-query", - "in": "query", - } - ], - } - } - }, - "components": { - "schemas": { - "ValidationError": { - "title": "ValidationError", - "required": ["loc", "msg", "type"], - "type": "object", - "properties": { - "loc": { - "title": "Location", - "type": "array", - "items": { - "anyOf": [{"type": "string"}, {"type": "integer"}] - }, - }, - "msg": {"title": "Message", "type": "string"}, - "type": {"title": "Error Type", "type": "string"}, - }, - }, - "HTTPValidationError": { - "title": "HTTPValidationError", - "type": "object", - "properties": { - "detail": { - "title": "Detail", - "type": "array", - "items": {"$ref": "#/components/schemas/ValidationError"}, - } - }, - }, - } - }, - } diff --git a/tests/test_tutorial/test_query_params_str_validations/test_tutorial010_py310.py b/tests/test_tutorial/test_query_params_str_validations/test_tutorial010_py310.py deleted file mode 100644 index 5b62c969f..000000000 --- a/tests/test_tutorial/test_query_params_str_validations/test_tutorial010_py310.py +++ /dev/null @@ -1,175 +0,0 @@ -import pytest -from dirty_equals import IsDict -from fastapi._compat import PYDANTIC_VERSION_MINOR_TUPLE -from fastapi.testclient import TestClient - -from ...utils import needs_py310 - - -@pytest.fixture(name="client") -def get_client(): - from docs_src.query_params_str_validations.tutorial010_py310 import app - - client = TestClient(app) - return client - - -@needs_py310 -def test_query_params_str_validations_no_query(client: TestClient): - response = client.get("/items/") - assert response.status_code == 200 - assert response.json() == {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]} - - -@needs_py310 -def test_query_params_str_validations_item_query_fixedquery(client: TestClient): - response = client.get("/items/", params={"item-query": "fixedquery"}) - assert response.status_code == 200 - assert response.json() == { - "items": [{"item_id": "Foo"}, {"item_id": "Bar"}], - "q": "fixedquery", - } - - -@needs_py310 -def test_query_params_str_validations_q_fixedquery(client: TestClient): - response = client.get("/items/", params={"q": "fixedquery"}) - assert response.status_code == 200 - assert response.json() == {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]} - - -@needs_py310 -def test_query_params_str_validations_item_query_nonregexquery(client: TestClient): - response = client.get("/items/", params={"item-query": "nonregexquery"}) - assert response.status_code == 422 - assert response.json() == IsDict( - { - "detail": [ - { - "type": "string_pattern_mismatch", - "loc": ["query", "item-query"], - "msg": "String should match pattern '^fixedquery$'", - "input": "nonregexquery", - "ctx": {"pattern": "^fixedquery$"}, - } - ] - } - ) | IsDict( - # TODO: remove when deprecating Pydantic v1 - { - "detail": [ - { - "ctx": {"pattern": "^fixedquery$"}, - "loc": ["query", "item-query"], - "msg": 'string does not match regex "^fixedquery$"', - "type": "value_error.str.regex", - } - ] - } - ) - - -@needs_py310 -def test_openapi_schema(client: TestClient): - response = client.get("/openapi.json") - assert response.status_code == 200, response.text - assert response.json() == { - "openapi": "3.1.0", - "info": {"title": "FastAPI", "version": "0.1.0"}, - "paths": { - "/items/": { - "get": { - "responses": { - "200": { - "description": "Successful Response", - "content": {"application/json": {"schema": {}}}, - }, - "422": { - "description": "Validation Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HTTPValidationError" - } - } - }, - }, - }, - "summary": "Read Items", - "operationId": "read_items_items__get", - "parameters": [ - { - "description": "Query string for the items to search in the database that have a good match", - "required": False, - "deprecated": True, - "schema": IsDict( - { - "anyOf": [ - { - "type": "string", - "minLength": 3, - "maxLength": 50, - "pattern": "^fixedquery$", - }, - {"type": "null"}, - ], - "title": "Query string", - "description": "Query string for the items to search in the database that have a good match", - # See https://github.com/pydantic/pydantic/blob/80353c29a824c55dea4667b328ba8f329879ac9f/tests/test_fastapi.sh#L25-L34. - **( - {"deprecated": True} - if PYDANTIC_VERSION_MINOR_TUPLE >= (2, 10) - else {} - ), - } - ) - | IsDict( - # TODO: remove when deprecating Pydantic v1 - { - "title": "Query string", - "maxLength": 50, - "minLength": 3, - "pattern": "^fixedquery$", - "type": "string", - "description": "Query string for the items to search in the database that have a good match", - } - ), - "name": "item-query", - "in": "query", - } - ], - } - } - }, - "components": { - "schemas": { - "ValidationError": { - "title": "ValidationError", - "required": ["loc", "msg", "type"], - "type": "object", - "properties": { - "loc": { - "title": "Location", - "type": "array", - "items": { - "anyOf": [{"type": "string"}, {"type": "integer"}] - }, - }, - "msg": {"title": "Message", "type": "string"}, - "type": {"title": "Error Type", "type": "string"}, - }, - }, - "HTTPValidationError": { - "title": "HTTPValidationError", - "type": "object", - "properties": { - "detail": { - "title": "Detail", - "type": "array", - "items": {"$ref": "#/components/schemas/ValidationError"}, - } - }, - }, - } - }, - } diff --git a/tests/test_tutorial/test_query_params_str_validations/test_tutorial011.py b/tests/test_tutorial/test_query_params_str_validations/test_tutorial011.py index 5ba39b05d..f4da25752 100644 --- a/tests/test_tutorial/test_query_params_str_validations/test_tutorial011.py +++ b/tests/test_tutorial/test_query_params_str_validations/test_tutorial011.py @@ -1,26 +1,47 @@ +import importlib + +import pytest from dirty_equals import IsDict from fastapi.testclient import TestClient -from docs_src.query_params_str_validations.tutorial011 import app +from ...utils import needs_py39, needs_py310 + + +@pytest.fixture( + name="client", + params=[ + "tutorial011", + pytest.param("tutorial011_py39", marks=needs_py310), + pytest.param("tutorial011_py310", marks=needs_py310), + "tutorial011_an", + pytest.param("tutorial011_an_py39", marks=needs_py39), + pytest.param("tutorial011_an_py310", marks=needs_py310), + ], +) +def get_client(request: pytest.FixtureRequest): + mod = importlib.import_module( + f"docs_src.query_params_str_validations.{request.param}" + ) -client = TestClient(app) + client = TestClient(mod.app) + return client -def test_multi_query_values(): +def test_multi_query_values(client: TestClient): url = "/items/?q=foo&q=bar" response = client.get(url) assert response.status_code == 200, response.text assert response.json() == {"q": ["foo", "bar"]} -def test_query_no_values(): +def test_query_no_values(client: TestClient): url = "/items/" response = client.get(url) assert response.status_code == 200, response.text assert response.json() == {"q": None} -def test_openapi_schema(): +def test_openapi_schema(client: TestClient): response = client.get("/openapi.json") assert response.status_code == 200, response.text assert response.json() == { diff --git a/tests/test_tutorial/test_query_params_str_validations/test_tutorial011_an.py b/tests/test_tutorial/test_query_params_str_validations/test_tutorial011_an.py deleted file mode 100644 index 3942ea77a..000000000 --- a/tests/test_tutorial/test_query_params_str_validations/test_tutorial011_an.py +++ /dev/null @@ -1,108 +0,0 @@ -from dirty_equals import IsDict -from fastapi.testclient import TestClient - -from docs_src.query_params_str_validations.tutorial011_an import app - -client = TestClient(app) - - -def test_multi_query_values(): - url = "/items/?q=foo&q=bar" - response = client.get(url) - assert response.status_code == 200, response.text - assert response.json() == {"q": ["foo", "bar"]} - - -def test_query_no_values(): - url = "/items/" - response = client.get(url) - assert response.status_code == 200, response.text - assert response.json() == {"q": None} - - -def test_openapi_schema(): - response = client.get("/openapi.json") - assert response.status_code == 200, response.text - assert response.json() == { - "openapi": "3.1.0", - "info": {"title": "FastAPI", "version": "0.1.0"}, - "paths": { - "/items/": { - "get": { - "responses": { - "200": { - "description": "Successful Response", - "content": {"application/json": {"schema": {}}}, - }, - "422": { - "description": "Validation Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HTTPValidationError" - } - } - }, - }, - }, - "summary": "Read Items", - "operationId": "read_items_items__get", - "parameters": [ - { - "required": False, - "schema": IsDict( - { - "anyOf": [ - {"type": "array", "items": {"type": "string"}}, - {"type": "null"}, - ], - "title": "Q", - } - ) - | IsDict( - # TODO: remove when deprecating Pydantic v1 - { - "title": "Q", - "type": "array", - "items": {"type": "string"}, - } - ), - "name": "q", - "in": "query", - } - ], - } - } - }, - "components": { - "schemas": { - "ValidationError": { - "title": "ValidationError", - "required": ["loc", "msg", "type"], - "type": "object", - "properties": { - "loc": { - "title": "Location", - "type": "array", - "items": { - "anyOf": [{"type": "string"}, {"type": "integer"}] - }, - }, - "msg": {"title": "Message", "type": "string"}, - "type": {"title": "Error Type", "type": "string"}, - }, - }, - "HTTPValidationError": { - "title": "HTTPValidationError", - "type": "object", - "properties": { - "detail": { - "title": "Detail", - "type": "array", - "items": {"$ref": "#/components/schemas/ValidationError"}, - } - }, - }, - } - }, - } diff --git a/tests/test_tutorial/test_query_params_str_validations/test_tutorial011_an_py310.py b/tests/test_tutorial/test_query_params_str_validations/test_tutorial011_an_py310.py deleted file mode 100644 index f2ec38c95..000000000 --- a/tests/test_tutorial/test_query_params_str_validations/test_tutorial011_an_py310.py +++ /dev/null @@ -1,118 +0,0 @@ -import pytest -from dirty_equals import IsDict -from fastapi.testclient import TestClient - -from ...utils import needs_py310 - - -@pytest.fixture(name="client") -def get_client(): - from docs_src.query_params_str_validations.tutorial011_an_py310 import app - - client = TestClient(app) - return client - - -@needs_py310 -def test_multi_query_values(client: TestClient): - url = "/items/?q=foo&q=bar" - response = client.get(url) - assert response.status_code == 200, response.text - assert response.json() == {"q": ["foo", "bar"]} - - -@needs_py310 -def test_query_no_values(client: TestClient): - url = "/items/" - response = client.get(url) - assert response.status_code == 200, response.text - assert response.json() == {"q": None} - - -@needs_py310 -def test_openapi_schema(client: TestClient): - response = client.get("/openapi.json") - assert response.status_code == 200, response.text - assert response.json() == { - "openapi": "3.1.0", - "info": {"title": "FastAPI", "version": "0.1.0"}, - "paths": { - "/items/": { - "get": { - "responses": { - "200": { - "description": "Successful Response", - "content": {"application/json": {"schema": {}}}, - }, - "422": { - "description": "Validation Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HTTPValidationError" - } - } - }, - }, - }, - "summary": "Read Items", - "operationId": "read_items_items__get", - "parameters": [ - { - "required": False, - "schema": IsDict( - { - "anyOf": [ - {"type": "array", "items": {"type": "string"}}, - {"type": "null"}, - ], - "title": "Q", - } - ) - | IsDict( - # TODO: remove when deprecating Pydantic v1 - { - "title": "Q", - "type": "array", - "items": {"type": "string"}, - } - ), - "name": "q", - "in": "query", - } - ], - } - } - }, - "components": { - "schemas": { - "ValidationError": { - "title": "ValidationError", - "required": ["loc", "msg", "type"], - "type": "object", - "properties": { - "loc": { - "title": "Location", - "type": "array", - "items": { - "anyOf": [{"type": "string"}, {"type": "integer"}] - }, - }, - "msg": {"title": "Message", "type": "string"}, - "type": {"title": "Error Type", "type": "string"}, - }, - }, - "HTTPValidationError": { - "title": "HTTPValidationError", - "type": "object", - "properties": { - "detail": { - "title": "Detail", - "type": "array", - "items": {"$ref": "#/components/schemas/ValidationError"}, - } - }, - }, - } - }, - } diff --git a/tests/test_tutorial/test_query_params_str_validations/test_tutorial011_an_py39.py b/tests/test_tutorial/test_query_params_str_validations/test_tutorial011_an_py39.py deleted file mode 100644 index cd7b15679..000000000 --- a/tests/test_tutorial/test_query_params_str_validations/test_tutorial011_an_py39.py +++ /dev/null @@ -1,118 +0,0 @@ -import pytest -from dirty_equals import IsDict -from fastapi.testclient import TestClient - -from ...utils import needs_py39 - - -@pytest.fixture(name="client") -def get_client(): - from docs_src.query_params_str_validations.tutorial011_an_py39 import app - - client = TestClient(app) - return client - - -@needs_py39 -def test_multi_query_values(client: TestClient): - url = "/items/?q=foo&q=bar" - response = client.get(url) - assert response.status_code == 200, response.text - assert response.json() == {"q": ["foo", "bar"]} - - -@needs_py39 -def test_query_no_values(client: TestClient): - url = "/items/" - response = client.get(url) - assert response.status_code == 200, response.text - assert response.json() == {"q": None} - - -@needs_py39 -def test_openapi_schema(client: TestClient): - response = client.get("/openapi.json") - assert response.status_code == 200, response.text - assert response.json() == { - "openapi": "3.1.0", - "info": {"title": "FastAPI", "version": "0.1.0"}, - "paths": { - "/items/": { - "get": { - "responses": { - "200": { - "description": "Successful Response", - "content": {"application/json": {"schema": {}}}, - }, - "422": { - "description": "Validation Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HTTPValidationError" - } - } - }, - }, - }, - "summary": "Read Items", - "operationId": "read_items_items__get", - "parameters": [ - { - "required": False, - "schema": IsDict( - { - "anyOf": [ - {"type": "array", "items": {"type": "string"}}, - {"type": "null"}, - ], - "title": "Q", - } - ) - | IsDict( - # TODO: remove when deprecating Pydantic v1 - { - "title": "Q", - "type": "array", - "items": {"type": "string"}, - } - ), - "name": "q", - "in": "query", - } - ], - } - } - }, - "components": { - "schemas": { - "ValidationError": { - "title": "ValidationError", - "required": ["loc", "msg", "type"], - "type": "object", - "properties": { - "loc": { - "title": "Location", - "type": "array", - "items": { - "anyOf": [{"type": "string"}, {"type": "integer"}] - }, - }, - "msg": {"title": "Message", "type": "string"}, - "type": {"title": "Error Type", "type": "string"}, - }, - }, - "HTTPValidationError": { - "title": "HTTPValidationError", - "type": "object", - "properties": { - "detail": { - "title": "Detail", - "type": "array", - "items": {"$ref": "#/components/schemas/ValidationError"}, - } - }, - }, - } - }, - } diff --git a/tests/test_tutorial/test_query_params_str_validations/test_tutorial011_py310.py b/tests/test_tutorial/test_query_params_str_validations/test_tutorial011_py310.py deleted file mode 100644 index bdc729516..000000000 --- a/tests/test_tutorial/test_query_params_str_validations/test_tutorial011_py310.py +++ /dev/null @@ -1,118 +0,0 @@ -import pytest -from dirty_equals import IsDict -from fastapi.testclient import TestClient - -from ...utils import needs_py310 - - -@pytest.fixture(name="client") -def get_client(): - from docs_src.query_params_str_validations.tutorial011_py310 import app - - client = TestClient(app) - return client - - -@needs_py310 -def test_multi_query_values(client: TestClient): - url = "/items/?q=foo&q=bar" - response = client.get(url) - assert response.status_code == 200, response.text - assert response.json() == {"q": ["foo", "bar"]} - - -@needs_py310 -def test_query_no_values(client: TestClient): - url = "/items/" - response = client.get(url) - assert response.status_code == 200, response.text - assert response.json() == {"q": None} - - -@needs_py310 -def test_openapi_schema(client: TestClient): - response = client.get("/openapi.json") - assert response.status_code == 200, response.text - assert response.json() == { - "openapi": "3.1.0", - "info": {"title": "FastAPI", "version": "0.1.0"}, - "paths": { - "/items/": { - "get": { - "responses": { - "200": { - "description": "Successful Response", - "content": {"application/json": {"schema": {}}}, - }, - "422": { - "description": "Validation Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HTTPValidationError" - } - } - }, - }, - }, - "summary": "Read Items", - "operationId": "read_items_items__get", - "parameters": [ - { - "required": False, - "schema": IsDict( - { - "anyOf": [ - {"type": "array", "items": {"type": "string"}}, - {"type": "null"}, - ], - "title": "Q", - } - ) - | IsDict( - # TODO: remove when deprecating Pydantic v1 - { - "title": "Q", - "type": "array", - "items": {"type": "string"}, - } - ), - "name": "q", - "in": "query", - } - ], - } - } - }, - "components": { - "schemas": { - "ValidationError": { - "title": "ValidationError", - "required": ["loc", "msg", "type"], - "type": "object", - "properties": { - "loc": { - "title": "Location", - "type": "array", - "items": { - "anyOf": [{"type": "string"}, {"type": "integer"}] - }, - }, - "msg": {"title": "Message", "type": "string"}, - "type": {"title": "Error Type", "type": "string"}, - }, - }, - "HTTPValidationError": { - "title": "HTTPValidationError", - "type": "object", - "properties": { - "detail": { - "title": "Detail", - "type": "array", - "items": {"$ref": "#/components/schemas/ValidationError"}, - } - }, - }, - } - }, - } diff --git a/tests/test_tutorial/test_query_params_str_validations/test_tutorial011_py39.py b/tests/test_tutorial/test_query_params_str_validations/test_tutorial011_py39.py deleted file mode 100644 index 26ac56b2f..000000000 --- a/tests/test_tutorial/test_query_params_str_validations/test_tutorial011_py39.py +++ /dev/null @@ -1,118 +0,0 @@ -import pytest -from dirty_equals import IsDict -from fastapi.testclient import TestClient - -from ...utils import needs_py39 - - -@pytest.fixture(name="client") -def get_client(): - from docs_src.query_params_str_validations.tutorial011_py39 import app - - client = TestClient(app) - return client - - -@needs_py39 -def test_multi_query_values(client: TestClient): - url = "/items/?q=foo&q=bar" - response = client.get(url) - assert response.status_code == 200, response.text - assert response.json() == {"q": ["foo", "bar"]} - - -@needs_py39 -def test_query_no_values(client: TestClient): - url = "/items/" - response = client.get(url) - assert response.status_code == 200, response.text - assert response.json() == {"q": None} - - -@needs_py39 -def test_openapi_schema(client: TestClient): - response = client.get("/openapi.json") - assert response.status_code == 200, response.text - assert response.json() == { - "openapi": "3.1.0", - "info": {"title": "FastAPI", "version": "0.1.0"}, - "paths": { - "/items/": { - "get": { - "responses": { - "200": { - "description": "Successful Response", - "content": {"application/json": {"schema": {}}}, - }, - "422": { - "description": "Validation Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HTTPValidationError" - } - } - }, - }, - }, - "summary": "Read Items", - "operationId": "read_items_items__get", - "parameters": [ - { - "required": False, - "schema": IsDict( - { - "anyOf": [ - {"type": "array", "items": {"type": "string"}}, - {"type": "null"}, - ], - "title": "Q", - } - ) - | IsDict( - # TODO: remove when deprecating Pydantic v1 - { - "title": "Q", - "type": "array", - "items": {"type": "string"}, - } - ), - "name": "q", - "in": "query", - } - ], - } - } - }, - "components": { - "schemas": { - "ValidationError": { - "title": "ValidationError", - "required": ["loc", "msg", "type"], - "type": "object", - "properties": { - "loc": { - "title": "Location", - "type": "array", - "items": { - "anyOf": [{"type": "string"}, {"type": "integer"}] - }, - }, - "msg": {"title": "Message", "type": "string"}, - "type": {"title": "Error Type", "type": "string"}, - }, - }, - "HTTPValidationError": { - "title": "HTTPValidationError", - "type": "object", - "properties": { - "detail": { - "title": "Detail", - "type": "array", - "items": {"$ref": "#/components/schemas/ValidationError"}, - } - }, - }, - } - }, - } diff --git a/tests/test_tutorial/test_query_params_str_validations/test_tutorial012.py b/tests/test_tutorial/test_query_params_str_validations/test_tutorial012.py index 1436db384..549a90519 100644 --- a/tests/test_tutorial/test_query_params_str_validations/test_tutorial012.py +++ b/tests/test_tutorial/test_query_params_str_validations/test_tutorial012.py @@ -1,25 +1,44 @@ +import importlib + +import pytest from fastapi.testclient import TestClient -from docs_src.query_params_str_validations.tutorial012 import app +from ...utils import needs_py39 + + +@pytest.fixture( + name="client", + params=[ + "tutorial012", + pytest.param("tutorial012_py39", marks=needs_py39), + "tutorial012_an", + pytest.param("tutorial012_an_py39", marks=needs_py39), + ], +) +def get_client(request: pytest.FixtureRequest): + mod = importlib.import_module( + f"docs_src.query_params_str_validations.{request.param}" + ) -client = TestClient(app) + client = TestClient(mod.app) + return client -def test_default_query_values(): +def test_default_query_values(client: TestClient): url = "/items/" response = client.get(url) assert response.status_code == 200, response.text assert response.json() == {"q": ["foo", "bar"]} -def test_multi_query_values(): +def test_multi_query_values(client: TestClient): url = "/items/?q=baz&q=foobar" response = client.get(url) assert response.status_code == 200, response.text assert response.json() == {"q": ["baz", "foobar"]} -def test_openapi_schema(): +def test_openapi_schema(client: TestClient): response = client.get("/openapi.json") assert response.status_code == 200, response.text assert response.json() == { diff --git a/tests/test_tutorial/test_query_params_str_validations/test_tutorial012_an.py b/tests/test_tutorial/test_query_params_str_validations/test_tutorial012_an.py deleted file mode 100644 index 270763f1d..000000000 --- a/tests/test_tutorial/test_query_params_str_validations/test_tutorial012_an.py +++ /dev/null @@ -1,96 +0,0 @@ -from fastapi.testclient import TestClient - -from docs_src.query_params_str_validations.tutorial012_an import app - -client = TestClient(app) - - -def test_default_query_values(): - url = "/items/" - response = client.get(url) - assert response.status_code == 200, response.text - assert response.json() == {"q": ["foo", "bar"]} - - -def test_multi_query_values(): - url = "/items/?q=baz&q=foobar" - response = client.get(url) - assert response.status_code == 200, response.text - assert response.json() == {"q": ["baz", "foobar"]} - - -def test_openapi_schema(): - response = client.get("/openapi.json") - assert response.status_code == 200, response.text - assert response.json() == { - "openapi": "3.1.0", - "info": {"title": "FastAPI", "version": "0.1.0"}, - "paths": { - "/items/": { - "get": { - "responses": { - "200": { - "description": "Successful Response", - "content": {"application/json": {"schema": {}}}, - }, - "422": { - "description": "Validation Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HTTPValidationError" - } - } - }, - }, - }, - "summary": "Read Items", - "operationId": "read_items_items__get", - "parameters": [ - { - "required": False, - "schema": { - "title": "Q", - "type": "array", - "items": {"type": "string"}, - "default": ["foo", "bar"], - }, - "name": "q", - "in": "query", - } - ], - } - } - }, - "components": { - "schemas": { - "ValidationError": { - "title": "ValidationError", - "required": ["loc", "msg", "type"], - "type": "object", - "properties": { - "loc": { - "title": "Location", - "type": "array", - "items": { - "anyOf": [{"type": "string"}, {"type": "integer"}] - }, - }, - "msg": {"title": "Message", "type": "string"}, - "type": {"title": "Error Type", "type": "string"}, - }, - }, - "HTTPValidationError": { - "title": "HTTPValidationError", - "type": "object", - "properties": { - "detail": { - "title": "Detail", - "type": "array", - "items": {"$ref": "#/components/schemas/ValidationError"}, - } - }, - }, - } - }, - } diff --git a/tests/test_tutorial/test_query_params_str_validations/test_tutorial012_an_py39.py b/tests/test_tutorial/test_query_params_str_validations/test_tutorial012_an_py39.py deleted file mode 100644 index 548391683..000000000 --- a/tests/test_tutorial/test_query_params_str_validations/test_tutorial012_an_py39.py +++ /dev/null @@ -1,106 +0,0 @@ -import pytest -from fastapi.testclient import TestClient - -from ...utils import needs_py39 - - -@pytest.fixture(name="client") -def get_client(): - from docs_src.query_params_str_validations.tutorial012_an_py39 import app - - client = TestClient(app) - return client - - -@needs_py39 -def test_default_query_values(client: TestClient): - url = "/items/" - response = client.get(url) - assert response.status_code == 200, response.text - assert response.json() == {"q": ["foo", "bar"]} - - -@needs_py39 -def test_multi_query_values(client: TestClient): - url = "/items/?q=baz&q=foobar" - response = client.get(url) - assert response.status_code == 200, response.text - assert response.json() == {"q": ["baz", "foobar"]} - - -@needs_py39 -def test_openapi_schema(client: TestClient): - response = client.get("/openapi.json") - assert response.status_code == 200, response.text - assert response.json() == { - "openapi": "3.1.0", - "info": {"title": "FastAPI", "version": "0.1.0"}, - "paths": { - "/items/": { - "get": { - "responses": { - "200": { - "description": "Successful Response", - "content": {"application/json": {"schema": {}}}, - }, - "422": { - "description": "Validation Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HTTPValidationError" - } - } - }, - }, - }, - "summary": "Read Items", - "operationId": "read_items_items__get", - "parameters": [ - { - "required": False, - "schema": { - "title": "Q", - "type": "array", - "items": {"type": "string"}, - "default": ["foo", "bar"], - }, - "name": "q", - "in": "query", - } - ], - } - } - }, - "components": { - "schemas": { - "ValidationError": { - "title": "ValidationError", - "required": ["loc", "msg", "type"], - "type": "object", - "properties": { - "loc": { - "title": "Location", - "type": "array", - "items": { - "anyOf": [{"type": "string"}, {"type": "integer"}] - }, - }, - "msg": {"title": "Message", "type": "string"}, - "type": {"title": "Error Type", "type": "string"}, - }, - }, - "HTTPValidationError": { - "title": "HTTPValidationError", - "type": "object", - "properties": { - "detail": { - "title": "Detail", - "type": "array", - "items": {"$ref": "#/components/schemas/ValidationError"}, - } - }, - }, - } - }, - } diff --git a/tests/test_tutorial/test_query_params_str_validations/test_tutorial012_py39.py b/tests/test_tutorial/test_query_params_str_validations/test_tutorial012_py39.py deleted file mode 100644 index e7d745154..000000000 --- a/tests/test_tutorial/test_query_params_str_validations/test_tutorial012_py39.py +++ /dev/null @@ -1,106 +0,0 @@ -import pytest -from fastapi.testclient import TestClient - -from ...utils import needs_py39 - - -@pytest.fixture(name="client") -def get_client(): - from docs_src.query_params_str_validations.tutorial012_py39 import app - - client = TestClient(app) - return client - - -@needs_py39 -def test_default_query_values(client: TestClient): - url = "/items/" - response = client.get(url) - assert response.status_code == 200, response.text - assert response.json() == {"q": ["foo", "bar"]} - - -@needs_py39 -def test_multi_query_values(client: TestClient): - url = "/items/?q=baz&q=foobar" - response = client.get(url) - assert response.status_code == 200, response.text - assert response.json() == {"q": ["baz", "foobar"]} - - -@needs_py39 -def test_openapi_schema(client: TestClient): - response = client.get("/openapi.json") - assert response.status_code == 200, response.text - assert response.json() == { - "openapi": "3.1.0", - "info": {"title": "FastAPI", "version": "0.1.0"}, - "paths": { - "/items/": { - "get": { - "responses": { - "200": { - "description": "Successful Response", - "content": {"application/json": {"schema": {}}}, - }, - "422": { - "description": "Validation Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HTTPValidationError" - } - } - }, - }, - }, - "summary": "Read Items", - "operationId": "read_items_items__get", - "parameters": [ - { - "required": False, - "schema": { - "title": "Q", - "type": "array", - "items": {"type": "string"}, - "default": ["foo", "bar"], - }, - "name": "q", - "in": "query", - } - ], - } - } - }, - "components": { - "schemas": { - "ValidationError": { - "title": "ValidationError", - "required": ["loc", "msg", "type"], - "type": "object", - "properties": { - "loc": { - "title": "Location", - "type": "array", - "items": { - "anyOf": [{"type": "string"}, {"type": "integer"}] - }, - }, - "msg": {"title": "Message", "type": "string"}, - "type": {"title": "Error Type", "type": "string"}, - }, - }, - "HTTPValidationError": { - "title": "HTTPValidationError", - "type": "object", - "properties": { - "detail": { - "title": "Detail", - "type": "array", - "items": {"$ref": "#/components/schemas/ValidationError"}, - } - }, - }, - } - }, - } diff --git a/tests/test_tutorial/test_query_params_str_validations/test_tutorial013.py b/tests/test_tutorial/test_query_params_str_validations/test_tutorial013.py index 1ba1fdf61..f2f5f7a85 100644 --- a/tests/test_tutorial/test_query_params_str_validations/test_tutorial013.py +++ b/tests/test_tutorial/test_query_params_str_validations/test_tutorial013.py @@ -1,25 +1,43 @@ +import importlib + +import pytest from fastapi.testclient import TestClient -from docs_src.query_params_str_validations.tutorial013 import app +from ...utils import needs_py39 + + +@pytest.fixture( + name="client", + params=[ + "tutorial013", + "tutorial013_an", + pytest.param("tutorial013_an_py39", marks=needs_py39), + ], +) +def get_client(request: pytest.FixtureRequest): + mod = importlib.import_module( + f"docs_src.query_params_str_validations.{request.param}" + ) -client = TestClient(app) + client = TestClient(mod.app) + return client -def test_multi_query_values(): +def test_multi_query_values(client: TestClient): url = "/items/?q=foo&q=bar" response = client.get(url) assert response.status_code == 200, response.text assert response.json() == {"q": ["foo", "bar"]} -def test_query_no_values(): +def test_query_no_values(client: TestClient): url = "/items/" response = client.get(url) assert response.status_code == 200, response.text assert response.json() == {"q": []} -def test_openapi_schema(): +def test_openapi_schema(client: TestClient): response = client.get("/openapi.json") assert response.status_code == 200, response.text assert response.json() == { diff --git a/tests/test_tutorial/test_query_params_str_validations/test_tutorial013_an.py b/tests/test_tutorial/test_query_params_str_validations/test_tutorial013_an.py deleted file mode 100644 index 343261748..000000000 --- a/tests/test_tutorial/test_query_params_str_validations/test_tutorial013_an.py +++ /dev/null @@ -1,96 +0,0 @@ -from fastapi.testclient import TestClient - -from docs_src.query_params_str_validations.tutorial013_an import app - -client = TestClient(app) - - -def test_multi_query_values(): - url = "/items/?q=foo&q=bar" - response = client.get(url) - assert response.status_code == 200, response.text - assert response.json() == {"q": ["foo", "bar"]} - - -def test_query_no_values(): - url = "/items/" - response = client.get(url) - assert response.status_code == 200, response.text - assert response.json() == {"q": []} - - -def test_openapi_schema(): - response = client.get("/openapi.json") - assert response.status_code == 200, response.text - assert response.json() == { - "openapi": "3.1.0", - "info": {"title": "FastAPI", "version": "0.1.0"}, - "paths": { - "/items/": { - "get": { - "responses": { - "200": { - "description": "Successful Response", - "content": {"application/json": {"schema": {}}}, - }, - "422": { - "description": "Validation Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HTTPValidationError" - } - } - }, - }, - }, - "summary": "Read Items", - "operationId": "read_items_items__get", - "parameters": [ - { - "required": False, - "schema": { - "title": "Q", - "type": "array", - "items": {}, - "default": [], - }, - "name": "q", - "in": "query", - } - ], - } - } - }, - "components": { - "schemas": { - "ValidationError": { - "title": "ValidationError", - "required": ["loc", "msg", "type"], - "type": "object", - "properties": { - "loc": { - "title": "Location", - "type": "array", - "items": { - "anyOf": [{"type": "string"}, {"type": "integer"}] - }, - }, - "msg": {"title": "Message", "type": "string"}, - "type": {"title": "Error Type", "type": "string"}, - }, - }, - "HTTPValidationError": { - "title": "HTTPValidationError", - "type": "object", - "properties": { - "detail": { - "title": "Detail", - "type": "array", - "items": {"$ref": "#/components/schemas/ValidationError"}, - } - }, - }, - } - }, - } diff --git a/tests/test_tutorial/test_query_params_str_validations/test_tutorial013_an_py39.py b/tests/test_tutorial/test_query_params_str_validations/test_tutorial013_an_py39.py deleted file mode 100644 index 537d6325b..000000000 --- a/tests/test_tutorial/test_query_params_str_validations/test_tutorial013_an_py39.py +++ /dev/null @@ -1,106 +0,0 @@ -import pytest -from fastapi.testclient import TestClient - -from ...utils import needs_py39 - - -@pytest.fixture(name="client") -def get_client(): - from docs_src.query_params_str_validations.tutorial013_an_py39 import app - - client = TestClient(app) - return client - - -@needs_py39 -def test_multi_query_values(client: TestClient): - url = "/items/?q=foo&q=bar" - response = client.get(url) - assert response.status_code == 200, response.text - assert response.json() == {"q": ["foo", "bar"]} - - -@needs_py39 -def test_query_no_values(client: TestClient): - url = "/items/" - response = client.get(url) - assert response.status_code == 200, response.text - assert response.json() == {"q": []} - - -@needs_py39 -def test_openapi_schema(client: TestClient): - response = client.get("/openapi.json") - assert response.status_code == 200, response.text - assert response.json() == { - "openapi": "3.1.0", - "info": {"title": "FastAPI", "version": "0.1.0"}, - "paths": { - "/items/": { - "get": { - "responses": { - "200": { - "description": "Successful Response", - "content": {"application/json": {"schema": {}}}, - }, - "422": { - "description": "Validation Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HTTPValidationError" - } - } - }, - }, - }, - "summary": "Read Items", - "operationId": "read_items_items__get", - "parameters": [ - { - "required": False, - "schema": { - "title": "Q", - "type": "array", - "items": {}, - "default": [], - }, - "name": "q", - "in": "query", - } - ], - } - } - }, - "components": { - "schemas": { - "ValidationError": { - "title": "ValidationError", - "required": ["loc", "msg", "type"], - "type": "object", - "properties": { - "loc": { - "title": "Location", - "type": "array", - "items": { - "anyOf": [{"type": "string"}, {"type": "integer"}] - }, - }, - "msg": {"title": "Message", "type": "string"}, - "type": {"title": "Error Type", "type": "string"}, - }, - }, - "HTTPValidationError": { - "title": "HTTPValidationError", - "type": "object", - "properties": { - "detail": { - "title": "Detail", - "type": "array", - "items": {"$ref": "#/components/schemas/ValidationError"}, - } - }, - }, - } - }, - } diff --git a/tests/test_tutorial/test_query_params_str_validations/test_tutorial014.py b/tests/test_tutorial/test_query_params_str_validations/test_tutorial014.py index 7bce7590c..edd40bb1a 100644 --- a/tests/test_tutorial/test_query_params_str_validations/test_tutorial014.py +++ b/tests/test_tutorial/test_query_params_str_validations/test_tutorial014.py @@ -1,23 +1,43 @@ +import importlib + +import pytest from fastapi.testclient import TestClient -from docs_src.query_params_str_validations.tutorial014 import app +from ...utils import needs_py39, needs_py310 + + +@pytest.fixture( + name="client", + params=[ + "tutorial014", + pytest.param("tutorial014_py310", marks=needs_py310), + "tutorial014_an", + pytest.param("tutorial014_an_py39", marks=needs_py39), + pytest.param("tutorial014_an_py310", marks=needs_py310), + ], +) +def get_client(request: pytest.FixtureRequest): + mod = importlib.import_module( + f"docs_src.query_params_str_validations.{request.param}" + ) -client = TestClient(app) + client = TestClient(mod.app) + return client -def test_hidden_query(): +def test_hidden_query(client: TestClient): response = client.get("/items?hidden_query=somevalue") assert response.status_code == 200, response.text assert response.json() == {"hidden_query": "somevalue"} -def test_no_hidden_query(): +def test_no_hidden_query(client: TestClient): response = client.get("/items") assert response.status_code == 200, response.text assert response.json() == {"hidden_query": "Not found"} -def test_openapi_schema(): +def test_openapi_schema(client: TestClient): response = client.get("/openapi.json") assert response.status_code == 200, response.text assert response.json() == { diff --git a/tests/test_tutorial/test_query_params_str_validations/test_tutorial014_an.py b/tests/test_tutorial/test_query_params_str_validations/test_tutorial014_an.py deleted file mode 100644 index 2182e87b7..000000000 --- a/tests/test_tutorial/test_query_params_str_validations/test_tutorial014_an.py +++ /dev/null @@ -1,81 +0,0 @@ -from fastapi.testclient import TestClient - -from docs_src.query_params_str_validations.tutorial014_an import app - -client = TestClient(app) - - -def test_hidden_query(): - response = client.get("/items?hidden_query=somevalue") - assert response.status_code == 200, response.text - assert response.json() == {"hidden_query": "somevalue"} - - -def test_no_hidden_query(): - response = client.get("/items") - assert response.status_code == 200, response.text - assert response.json() == {"hidden_query": "Not found"} - - -def test_openapi_schema(): - response = client.get("/openapi.json") - assert response.status_code == 200, response.text - assert response.json() == { - "openapi": "3.1.0", - "info": {"title": "FastAPI", "version": "0.1.0"}, - "paths": { - "/items/": { - "get": { - "summary": "Read Items", - "operationId": "read_items_items__get", - "responses": { - "200": { - "description": "Successful Response", - "content": {"application/json": {"schema": {}}}, - }, - "422": { - "description": "Validation Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HTTPValidationError" - } - } - }, - }, - }, - } - } - }, - "components": { - "schemas": { - "HTTPValidationError": { - "title": "HTTPValidationError", - "type": "object", - "properties": { - "detail": { - "title": "Detail", - "type": "array", - "items": {"$ref": "#/components/schemas/ValidationError"}, - } - }, - }, - "ValidationError": { - "title": "ValidationError", - "required": ["loc", "msg", "type"], - "type": "object", - "properties": { - "loc": { - "title": "Location", - "type": "array", - "items": { - "anyOf": [{"type": "string"}, {"type": "integer"}] - }, - }, - "msg": {"title": "Message", "type": "string"}, - "type": {"title": "Error Type", "type": "string"}, - }, - }, - } - }, - } diff --git a/tests/test_tutorial/test_query_params_str_validations/test_tutorial014_an_py310.py b/tests/test_tutorial/test_query_params_str_validations/test_tutorial014_an_py310.py deleted file mode 100644 index 344004d01..000000000 --- a/tests/test_tutorial/test_query_params_str_validations/test_tutorial014_an_py310.py +++ /dev/null @@ -1,91 +0,0 @@ -import pytest -from fastapi.testclient import TestClient - -from ...utils import needs_py310 - - -@pytest.fixture(name="client") -def get_client(): - from docs_src.query_params_str_validations.tutorial014_an_py310 import app - - client = TestClient(app) - return client - - -@needs_py310 -def test_hidden_query(client: TestClient): - response = client.get("/items?hidden_query=somevalue") - assert response.status_code == 200, response.text - assert response.json() == {"hidden_query": "somevalue"} - - -@needs_py310 -def test_no_hidden_query(client: TestClient): - response = client.get("/items") - assert response.status_code == 200, response.text - assert response.json() == {"hidden_query": "Not found"} - - -@needs_py310 -def test_openapi_schema(client: TestClient): - response = client.get("/openapi.json") - assert response.status_code == 200, response.text - assert response.json() == { - "openapi": "3.1.0", - "info": {"title": "FastAPI", "version": "0.1.0"}, - "paths": { - "/items/": { - "get": { - "summary": "Read Items", - "operationId": "read_items_items__get", - "responses": { - "200": { - "description": "Successful Response", - "content": {"application/json": {"schema": {}}}, - }, - "422": { - "description": "Validation Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HTTPValidationError" - } - } - }, - }, - }, - } - } - }, - "components": { - "schemas": { - "HTTPValidationError": { - "title": "HTTPValidationError", - "type": "object", - "properties": { - "detail": { - "title": "Detail", - "type": "array", - "items": {"$ref": "#/components/schemas/ValidationError"}, - } - }, - }, - "ValidationError": { - "title": "ValidationError", - "required": ["loc", "msg", "type"], - "type": "object", - "properties": { - "loc": { - "title": "Location", - "type": "array", - "items": { - "anyOf": [{"type": "string"}, {"type": "integer"}] - }, - }, - "msg": {"title": "Message", "type": "string"}, - "type": {"title": "Error Type", "type": "string"}, - }, - }, - } - }, - } diff --git a/tests/test_tutorial/test_query_params_str_validations/test_tutorial014_an_py39.py b/tests/test_tutorial/test_query_params_str_validations/test_tutorial014_an_py39.py deleted file mode 100644 index 5d4f6df3d..000000000 --- a/tests/test_tutorial/test_query_params_str_validations/test_tutorial014_an_py39.py +++ /dev/null @@ -1,91 +0,0 @@ -import pytest -from fastapi.testclient import TestClient - -from ...utils import needs_py310 - - -@pytest.fixture(name="client") -def get_client(): - from docs_src.query_params_str_validations.tutorial014_an_py39 import app - - client = TestClient(app) - return client - - -@needs_py310 -def test_hidden_query(client: TestClient): - response = client.get("/items?hidden_query=somevalue") - assert response.status_code == 200, response.text - assert response.json() == {"hidden_query": "somevalue"} - - -@needs_py310 -def test_no_hidden_query(client: TestClient): - response = client.get("/items") - assert response.status_code == 200, response.text - assert response.json() == {"hidden_query": "Not found"} - - -@needs_py310 -def test_openapi_schema(client: TestClient): - response = client.get("/openapi.json") - assert response.status_code == 200, response.text - assert response.json() == { - "openapi": "3.1.0", - "info": {"title": "FastAPI", "version": "0.1.0"}, - "paths": { - "/items/": { - "get": { - "summary": "Read Items", - "operationId": "read_items_items__get", - "responses": { - "200": { - "description": "Successful Response", - "content": {"application/json": {"schema": {}}}, - }, - "422": { - "description": "Validation Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HTTPValidationError" - } - } - }, - }, - }, - } - } - }, - "components": { - "schemas": { - "HTTPValidationError": { - "title": "HTTPValidationError", - "type": "object", - "properties": { - "detail": { - "title": "Detail", - "type": "array", - "items": {"$ref": "#/components/schemas/ValidationError"}, - } - }, - }, - "ValidationError": { - "title": "ValidationError", - "required": ["loc", "msg", "type"], - "type": "object", - "properties": { - "loc": { - "title": "Location", - "type": "array", - "items": { - "anyOf": [{"type": "string"}, {"type": "integer"}] - }, - }, - "msg": {"title": "Message", "type": "string"}, - "type": {"title": "Error Type", "type": "string"}, - }, - }, - } - }, - } diff --git a/tests/test_tutorial/test_query_params_str_validations/test_tutorial014_py310.py b/tests/test_tutorial/test_query_params_str_validations/test_tutorial014_py310.py deleted file mode 100644 index dad49fb12..000000000 --- a/tests/test_tutorial/test_query_params_str_validations/test_tutorial014_py310.py +++ /dev/null @@ -1,91 +0,0 @@ -import pytest -from fastapi.testclient import TestClient - -from ...utils import needs_py310 - - -@pytest.fixture(name="client") -def get_client(): - from docs_src.query_params_str_validations.tutorial014_py310 import app - - client = TestClient(app) - return client - - -@needs_py310 -def test_hidden_query(client: TestClient): - response = client.get("/items?hidden_query=somevalue") - assert response.status_code == 200, response.text - assert response.json() == {"hidden_query": "somevalue"} - - -@needs_py310 -def test_no_hidden_query(client: TestClient): - response = client.get("/items") - assert response.status_code == 200, response.text - assert response.json() == {"hidden_query": "Not found"} - - -@needs_py310 -def test_openapi_schema(client: TestClient): - response = client.get("/openapi.json") - assert response.status_code == 200, response.text - assert response.json() == { - "openapi": "3.1.0", - "info": {"title": "FastAPI", "version": "0.1.0"}, - "paths": { - "/items/": { - "get": { - "summary": "Read Items", - "operationId": "read_items_items__get", - "responses": { - "200": { - "description": "Successful Response", - "content": {"application/json": {"schema": {}}}, - }, - "422": { - "description": "Validation Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HTTPValidationError" - } - } - }, - }, - }, - } - } - }, - "components": { - "schemas": { - "HTTPValidationError": { - "title": "HTTPValidationError", - "type": "object", - "properties": { - "detail": { - "title": "Detail", - "type": "array", - "items": {"$ref": "#/components/schemas/ValidationError"}, - } - }, - }, - "ValidationError": { - "title": "ValidationError", - "required": ["loc", "msg", "type"], - "type": "object", - "properties": { - "loc": { - "title": "Location", - "type": "array", - "items": { - "anyOf": [{"type": "string"}, {"type": "integer"}] - }, - }, - "msg": {"title": "Message", "type": "string"}, - "type": {"title": "Error Type", "type": "string"}, - }, - }, - } - }, - } From e24a5002929245ceb0b5fa8e72f684ad6421b3a8 Mon Sep 17 00:00:00 2001 From: github-actions Date: Sat, 15 Feb 2025 16:32:36 +0000 Subject: [PATCH 060/157] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [skip ci] --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index 7d8ca5ba0..01665ac54 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -9,6 +9,7 @@ hide: ### Refactors +* ✅ Simplify tests for `query_params_str_validations`. PR [#13218](https://github.com/fastapi/fastapi/pull/13218) by [@alv2017](https://github.com/alv2017). * ✅ Simplify tests for `app_testing`. PR [#13220](https://github.com/fastapi/fastapi/pull/13220) by [@alv2017](https://github.com/alv2017). * ✅ Simplify tests for `dependency_testing`. PR [#13223](https://github.com/fastapi/fastapi/pull/13223) by [@alv2017](https://github.com/alv2017). From e24299b2ffb027967c4cedb25dade42121edc17f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Haoyu=20=28Daniel=29=20YANG=20=E6=9D=A8=E6=B5=A9=E5=AE=87?= Date: Sat, 15 Feb 2025 17:33:33 +0100 Subject: [PATCH 061/157] =?UTF-8?q?=F0=9F=93=9D=20Add=20more=20precise=20d?= =?UTF-8?q?escription=20of=20HTTP=20status=20code=20range=20in=20docs=20(#?= =?UTF-8?q?13347)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/en/docs/tutorial/response-status-code.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/en/docs/tutorial/response-status-code.md b/docs/en/docs/tutorial/response-status-code.md index 711042a46..41bf02a8f 100644 --- a/docs/en/docs/tutorial/response-status-code.md +++ b/docs/en/docs/tutorial/response-status-code.md @@ -53,16 +53,16 @@ These status codes have a name associated to recognize them, but the important p In short: -* `100` and above are for "Information". You rarely use them directly. Responses with these status codes cannot have a body. -* **`200`** and above are for "Successful" responses. These are the ones you would use the most. +* `100 - 199` are for "Information". You rarely use them directly. Responses with these status codes cannot have a body. +* **`200 - 299`** are for "Successful" responses. These are the ones you would use the most. * `200` is the default status code, which means everything was "OK". * Another example would be `201`, "Created". It is commonly used after creating a new record in the database. * A special case is `204`, "No Content". This response is used when there is no content to return to the client, and so the response must not have a body. -* **`300`** and above are for "Redirection". Responses with these status codes may or may not have a body, except for `304`, "Not Modified", which must not have one. -* **`400`** and above are for "Client error" responses. These are the second type you would probably use the most. +* **`300 - 399`** are for "Redirection". Responses with these status codes may or may not have a body, except for `304`, "Not Modified", which must not have one. +* **`400 - 499`** are for "Client error" responses. These are the second type you would probably use the most. * An example is `404`, for a "Not Found" response. * For generic errors from the client, you can just use `400`. -* `500` and above are for server errors. You almost never use them directly. When something goes wrong at some part in your application code, or server, it will automatically return one of these status codes. +* `500 - 599` are for server errors. You almost never use them directly. When something goes wrong at some part in your application code, or server, it will automatically return one of these status codes. /// tip From 7e67a91b08c0076ea09c69f9c52fac630c5cad7b Mon Sep 17 00:00:00 2001 From: github-actions Date: Sat, 15 Feb 2025 16:33:58 +0000 Subject: [PATCH 062/157] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [skip ci] --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index 01665ac54..fa1ff20f5 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -15,6 +15,7 @@ hide: ### Docs +* 📝 Add more precise description of HTTP status code range in docs. PR [#13347](https://github.com/fastapi/fastapi/pull/13347) by [@DanielYang59](https://github.com/DanielYang59). * 🔥 Remove manual type annotations in JWT tutorial to avoid typing expectations (JWT doesn't provide more types). PR [#13378](https://github.com/fastapi/fastapi/pull/13378) by [@tiangolo](https://github.com/tiangolo). * 📝 Update docs for Query Params and String Validations, remove obsolete Ellipsis docs (`...`). PR [#13377](https://github.com/fastapi/fastapi/pull/13377) by [@tiangolo](https://github.com/tiangolo). * ✏️ Remove duplicate title in docs `body-multiple-params`. PR [#13345](https://github.com/fastapi/fastapi/pull/13345) by [@DanielYang59](https://github.com/DanielYang59). From c868581ce7a108bfbef7c3fb4b42fa663dfdea97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Tue, 18 Feb 2025 16:18:14 +0100 Subject: [PATCH 063/157] =?UTF-8?q?=F0=9F=94=A7=20Update=20sponsors:=20Add?= =?UTF-8?q?=20LambdaTest=20(#13389)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/en/data/sponsors.yml | 3 +++ docs/en/docs/img/sponsors/lambdatest.png | Bin 0 -> 6320 bytes 2 files changed, 3 insertions(+) create mode 100644 docs/en/docs/img/sponsors/lambdatest.png diff --git a/docs/en/data/sponsors.yml b/docs/en/data/sponsors.yml index f9bf33ae9..91b23937c 100644 --- a/docs/en/data/sponsors.yml +++ b/docs/en/data/sponsors.yml @@ -58,3 +58,6 @@ bronze: - url: https://testdriven.io/courses/tdd-fastapi/ title: Learn to build high-quality web apps with best practices img: https://fastapi.tiangolo.com/img/sponsors/testdriven.svg + - url: https://lambdatest.com/?utm_source=fastapi&utm_medium=partner&utm_campaign=sponsor&utm_term=opensource&utm_content=webpage + title: LambdaTest, AI-Powered Cloud-based Test Orchestration Platform + img: https://fastapi.tiangolo.com/img/sponsors/lambdatest.png diff --git a/docs/en/docs/img/sponsors/lambdatest.png b/docs/en/docs/img/sponsors/lambdatest.png new file mode 100644 index 0000000000000000000000000000000000000000..674cbcb893dfec5a29dc72452215734603dcede6 GIT binary patch literal 6320 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 aNn)J05mscJEx>;p5Rc`b$`#9)2K^5%?KE=$ literal 0 HcmV?d00001 From 235300c1d2a7d8a011685adc1c9aa2c195c0368f Mon Sep 17 00:00:00 2001 From: github-actions Date: Tue, 18 Feb 2025 15:18:42 +0000 Subject: [PATCH 064/157] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [skip ci] --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index fa1ff20f5..c61e9d7e5 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -41,6 +41,7 @@ hide: ### Internal +* 🔧 Update sponsors: Add LambdaTest. PR [#13389](https://github.com/fastapi/fastapi/pull/13389) by [@tiangolo](https://github.com/tiangolo). * ⬆ Bump cloudflare/wrangler-action from 3.13 to 3.14. PR [#13350](https://github.com/fastapi/fastapi/pull/13350) by [@dependabot[bot]](https://github.com/apps/dependabot). * ⬆ Bump mkdocs-material from 9.5.18 to 9.6.1. PR [#13301](https://github.com/fastapi/fastapi/pull/13301) by [@dependabot[bot]](https://github.com/apps/dependabot). * ⬆ Bump pillow from 11.0.0 to 11.1.0. PR [#13300](https://github.com/fastapi/fastapi/pull/13300) by [@dependabot[bot]](https://github.com/apps/dependabot). From e157cf4b9625b2ccdd6a3214ba552bcde9452912 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hyogeun=20Oh=20=28=EC=98=A4=ED=9A=A8=EA=B7=BC=29?= Date: Wed, 19 Feb 2025 01:52:15 +0900 Subject: [PATCH 065/157] =?UTF-8?q?=F0=9F=90=9B=20Fix=20issue=20with=20Swa?= =?UTF-8?q?gger=20theme=20change=20example=20in=20the=20official=20tutoria?= =?UTF-8?q?l=20(#13289)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs_src/configure_swagger_ui/tutorial002.py | 2 +- .../test_tutorial/test_configure_swagger_ui/test_tutorial002.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs_src/configure_swagger_ui/tutorial002.py b/docs_src/configure_swagger_ui/tutorial002.py index cc569ce45..cc75c2196 100644 --- a/docs_src/configure_swagger_ui/tutorial002.py +++ b/docs_src/configure_swagger_ui/tutorial002.py @@ -1,6 +1,6 @@ from fastapi import FastAPI -app = FastAPI(swagger_ui_parameters={"syntaxHighlight.theme": "obsidian"}) +app = FastAPI(swagger_ui_parameters={"syntaxHighlight": {"theme": "obsidian"}}) @app.get("/users/{username}") diff --git a/tests/test_tutorial/test_configure_swagger_ui/test_tutorial002.py b/tests/test_tutorial/test_configure_swagger_ui/test_tutorial002.py index 166901188..d06a385b5 100644 --- a/tests/test_tutorial/test_configure_swagger_ui/test_tutorial002.py +++ b/tests/test_tutorial/test_configure_swagger_ui/test_tutorial002.py @@ -12,7 +12,7 @@ def test_swagger_ui(): '"syntaxHighlight": false' not in response.text ), "not used parameters should not be included" assert ( - '"syntaxHighlight.theme": "obsidian"' in response.text + '"syntaxHighlight": {"theme": "obsidian"}' in response.text ), "parameters with middle dots should be included in a JSON compatible way" assert ( '"dom_id": "#swagger-ui"' in response.text From 286fd694eaf006481c9fdc59a8324e4405fd0683 Mon Sep 17 00:00:00 2001 From: github-actions Date: Tue, 18 Feb 2025 16:52:41 +0000 Subject: [PATCH 066/157] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [skip ci] --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index c61e9d7e5..5f3d98e5c 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -15,6 +15,7 @@ hide: ### Docs +* 🐛 Fix issue with Swagger theme change example in the official tutorial. PR [#13289](https://github.com/fastapi/fastapi/pull/13289) by [@Zerohertz](https://github.com/Zerohertz). * 📝 Add more precise description of HTTP status code range in docs. PR [#13347](https://github.com/fastapi/fastapi/pull/13347) by [@DanielYang59](https://github.com/DanielYang59). * 🔥 Remove manual type annotations in JWT tutorial to avoid typing expectations (JWT doesn't provide more types). PR [#13378](https://github.com/fastapi/fastapi/pull/13378) by [@tiangolo](https://github.com/tiangolo). * 📝 Update docs for Query Params and String Validations, remove obsolete Ellipsis docs (`...`). PR [#13377](https://github.com/fastapi/fastapi/pull/13377) by [@tiangolo](https://github.com/tiangolo). From 70137c0f7d9534b30ef4ec39f3cea471ade9e567 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Tue, 18 Feb 2025 19:44:00 +0100 Subject: [PATCH 067/157] =?UTF-8?q?=F0=9F=94=A7=20Update=20team:=20Add=20L?= =?UTF-8?q?udovico=20(#13390)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/en/data/members.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/en/data/members.yml b/docs/en/data/members.yml index cf016eae1..7ec16e917 100644 --- a/docs/en/data/members.yml +++ b/docs/en/data/members.yml @@ -17,3 +17,6 @@ members: - login: patrick91 avatar_url: https://avatars.githubusercontent.com/u/667029 url: https://github.com/patrick91 +- login: luzzodev + avatar_url: https://avatars.githubusercontent.com/u/27291415 + url: https://github.com/luzzodev From 8c9c536c0a277125ca95c0d9ef19e2c6a39d1db8 Mon Sep 17 00:00:00 2001 From: github-actions Date: Tue, 18 Feb 2025 18:44:23 +0000 Subject: [PATCH 068/157] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [skip ci] --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index 5f3d98e5c..58e6ca6be 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -42,6 +42,7 @@ hide: ### Internal +* 🔧 Update team: Add Ludovico. PR [#13390](https://github.com/fastapi/fastapi/pull/13390) by [@tiangolo](https://github.com/tiangolo). * 🔧 Update sponsors: Add LambdaTest. PR [#13389](https://github.com/fastapi/fastapi/pull/13389) by [@tiangolo](https://github.com/tiangolo). * ⬆ Bump cloudflare/wrangler-action from 3.13 to 3.14. PR [#13350](https://github.com/fastapi/fastapi/pull/13350) by [@dependabot[bot]](https://github.com/apps/dependabot). * ⬆ Bump mkdocs-material from 9.5.18 to 9.6.1. PR [#13301](https://github.com/fastapi/fastapi/pull/13301) by [@dependabot[bot]](https://github.com/apps/dependabot). From 001473ab66dfae4f56365c61d50432dad5bbb2fe Mon Sep 17 00:00:00 2001 From: Aman Kumar <62641343+bullet-ant@users.noreply.github.com> Date: Thu, 20 Feb 2025 19:39:14 +0530 Subject: [PATCH 069/157] =?UTF-8?q?=F0=9F=93=9D=20Fix=20typos=20in=20virtu?= =?UTF-8?q?al=20environments=20documentation=20(#13396)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/en/docs/virtual-environments.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/en/docs/virtual-environments.md b/docs/en/docs/virtual-environments.md index b75be18c3..4f65b3b80 100644 --- a/docs/en/docs/virtual-environments.md +++ b/docs/en/docs/virtual-environments.md @@ -668,7 +668,7 @@ After activating the virtual environment, the `PATH` variable would look somethi /home/user/code/awesome-project/.venv/bin:/usr/bin:/bin:/usr/sbin:/sbin ``` -That means that the system will now start looking first look for programs in: +That means that the system will now start looking first for programs in: ```plaintext /home/user/code/awesome-project/.venv/bin @@ -692,7 +692,7 @@ and use that one. C:\Users\user\code\awesome-project\.venv\Scripts;C:\Windows\System32 ``` -That means that the system will now start looking first look for programs in: +That means that the system will now start looking first for programs in: ```plaintext C:\Users\user\code\awesome-project\.venv\Scripts From 3aee64b94f008c141cc8205ef3ccebf3978588dd Mon Sep 17 00:00:00 2001 From: github-actions Date: Thu, 20 Feb 2025 14:09:37 +0000 Subject: [PATCH 070/157] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [skip ci] --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index 58e6ca6be..c196a5197 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -15,6 +15,7 @@ hide: ### Docs +* 📝 Fix typos in virtual environments documentation. PR [#13396](https://github.com/fastapi/fastapi/pull/13396) by [@bullet-ant](https://github.com/bullet-ant). * 🐛 Fix issue with Swagger theme change example in the official tutorial. PR [#13289](https://github.com/fastapi/fastapi/pull/13289) by [@Zerohertz](https://github.com/Zerohertz). * 📝 Add more precise description of HTTP status code range in docs. PR [#13347](https://github.com/fastapi/fastapi/pull/13347) by [@DanielYang59](https://github.com/DanielYang59). * 🔥 Remove manual type annotations in JWT tutorial to avoid typing expectations (JWT doesn't provide more types). PR [#13378](https://github.com/fastapi/fastapi/pull/13378) by [@tiangolo](https://github.com/tiangolo). From 6ebe7539080531008a9752e8e050b099eef23885 Mon Sep 17 00:00:00 2001 From: Valentyn Date: Thu, 20 Feb 2025 09:13:44 -0500 Subject: [PATCH 071/157] =?UTF-8?q?=F0=9F=8C=90=20Add=20Ukrainian=20transl?= =?UTF-8?q?ation=20for=20`docs/uk/docs/tutorial/request-forms-and-files.md?= =?UTF-8?q?`=20(#13386)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../docs/tutorial/request-forms-and-files.md | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 docs/uk/docs/tutorial/request-forms-and-files.md diff --git a/docs/uk/docs/tutorial/request-forms-and-files.md b/docs/uk/docs/tutorial/request-forms-and-files.md new file mode 100644 index 000000000..a089ef945 --- /dev/null +++ b/docs/uk/docs/tutorial/request-forms-and-files.md @@ -0,0 +1,41 @@ +# Запити з формами та файлами + +У FastAPI Ви можете одночасно отримувати файли та поля форми, використовуючи `File` і `Form`. + +/// info | Інформація + +Щоб отримувати завантажені файли та/або дані форми, спочатку встановіть python-multipart. + +Переконайтеся, що Ви створили [віртуальне середовище](../virtual-environments.md){.internal-link target=_blank}, активували його, а потім встановили бібліотеку, наприклад: + +```console +$ pip install python-multipart +``` + +/// + +## Імпорт `File` та `Form` + +{* ../../docs_src/request_forms_and_files/tutorial001_an_py39.py hl[3] *} + +## Оголошення параметрів `File` та `Form` + +Створіть параметри файлів та форми так само як і для `Body` або `Query`: + +{* ../../docs_src/request_forms_and_files/tutorial001_an_py39.py hl[10:12] *} + +Файли та поля форми будуть завантажені як формові дані, і Ви отримаєте як файли, так і введені користувачем поля. + +Ви також можете оголосити деякі файли як `bytes`, а деякі як `UploadFile`. + +/// warning | Увага + +Ви можете оголосити кілька параметрів `File` і `Form` в операції *шляху*, але не можете одночасно оголошувати `Body`-поля, які очікуєте отримати у форматі JSON, оскільки запит матиме тіло, закодоване за допомогою `multipart/form-data`, а не `application/json`. + +Це не обмеження **FastAPI**, а частина протоколу HTTP. + +/// + +## Підсумок + +Використовуйте `File` та `Form` разом, коли вам потрібно отримувати дані форми та файли в одному запиті. From 498ba94bfc11f7bb91844162b34fa4ccb4d9f07b Mon Sep 17 00:00:00 2001 From: github-actions Date: Thu, 20 Feb 2025 14:14:07 +0000 Subject: [PATCH 072/157] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [skip ci] --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index c196a5197..baf0b015f 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -25,6 +25,7 @@ hide: ### Translations +* 🌐 Add Ukrainian translation for `docs/uk/docs/tutorial/request-forms-and-files.md`. PR [#13386](https://github.com/fastapi/fastapi/pull/13386) by [@valentinDruzhinin](https://github.com/valentinDruzhinin). * 🌐 Update Korean translation for `docs/ko/docs/help-fastapi.md`. PR [#13262](https://github.com/fastapi/fastapi/pull/13262) by [@Zerohertz](https://github.com/Zerohertz). * 🌐 Add Korean translation for `docs/ko/docs/advanced/custom-response.md`. PR [#13265](https://github.com/fastapi/fastapi/pull/13265) by [@11kkw](https://github.com/11kkw). * 🌐 Update Korean translation for `docs/ko/docs/tutorial/security/simple-oauth2.md`. PR [#13335](https://github.com/fastapi/fastapi/pull/13335) by [@yes0ng](https://github.com/yes0ng). From b397ad9e52a8606e4ea1233f6f66bfd221c0e79b Mon Sep 17 00:00:00 2001 From: Valentyn Date: Thu, 20 Feb 2025 09:16:09 -0500 Subject: [PATCH 073/157] =?UTF-8?q?=F0=9F=8C=90=20Add=20Ukrainian=20transl?= =?UTF-8?q?ation=20for=20`docs/uk/docs/tutorial/request-form-models.md`=20?= =?UTF-8?q?(#13384)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/uk/docs/tutorial/request-form-models.md | 78 ++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 docs/uk/docs/tutorial/request-form-models.md diff --git a/docs/uk/docs/tutorial/request-form-models.md b/docs/uk/docs/tutorial/request-form-models.md new file mode 100644 index 000000000..7f5759e79 --- /dev/null +++ b/docs/uk/docs/tutorial/request-form-models.md @@ -0,0 +1,78 @@ +# Моделі форм (Form Models) + +У FastAPI Ви можете використовувати **Pydantic-моделі** для оголошення **полів форми**. + +/// info | Інформація + +Щоб використовувати форми, спочатку встановіть python-multipart. + +Переконайтеся, що Ви створили [віртуальне середовище](../virtual-environments.md){.internal-link target=_blank}, активували його, а потім встановили бібліотеку, наприклад: + +```console +$ pip install python-multipart +``` + +/// + +/// note | Підказка + +Ця функція підтримується, починаючи з FastAPI версії `0.113.0`. 🤓 + +/// + +## Використання Pydantic-моделей для форм + +Вам просто потрібно оголосити **Pydantic-модель** з полями, які Ви хочете отримати як **поля форми**, а потім оголосити параметр як `Form`: + +{* ../../docs_src/request_form_models/tutorial001_an_py39.py hl[9:11,15] *} + +**FastAPI** **витягне** дані для **кожного поля** з **формових даних** у запиті та надасть вам Pydantic-модель, яку Ви визначили. + +## Перевірка документації + +Ви можете перевірити це в UI документації за `/docs`: + +

+ +
+ +## Заборона додаткових полів форми + +У деяких особливих випадках (ймовірно, рідко) Ви можете **обмежити** форму лише тими полями, які були оголошені в Pydantic-моделі, і **заборонити** будь-які **додаткові** поля. + +/// note | Підказка + +Ця функція підтримується, починаючи з FastAPI версії `0.114.0`. 🤓 + +/// + +Ви можете використати конфігурацію Pydantic-моделі, щоб заборонити `forbid` будь-які додаткові `extra` поля: + +{* ../../docs_src/request_form_models/tutorial002_an_py39.py hl[12] *} + +Якщо клієнт спробує надіслати додаткові дані, він отримає **відповідь з помилкою**. + +Наприклад, якщо клієнт спробує надіслати наступні поля форми: + +* `username`: `Rick` +* `password`: `Portal Gun` +* `extra`: `Mr. Poopybutthole` + +Він отримає відповідь із помилкою, яка повідомляє, що поле `extra` не дозволено: + +```json +{ + "detail": [ + { + "type": "extra_forbidden", + "loc": ["body", "extra"], + "msg": "Extra inputs are not permitted", + "input": "Mr. Poopybutthole" + } + ] +} +``` + +## Підсумок + +Ви можете використовувати Pydantic-моделі для оголошення полів форми у FastAPI. 😎 From 920110276a551ca20fb6a9a8a190d242b28a5151 Mon Sep 17 00:00:00 2001 From: github-actions Date: Thu, 20 Feb 2025 14:16:32 +0000 Subject: [PATCH 074/157] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [skip ci] --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index baf0b015f..59501b554 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -25,6 +25,7 @@ hide: ### Translations +* 🌐 Add Ukrainian translation for `docs/uk/docs/tutorial/request-form-models.md`. PR [#13384](https://github.com/fastapi/fastapi/pull/13384) by [@valentinDruzhinin](https://github.com/valentinDruzhinin). * 🌐 Add Ukrainian translation for `docs/uk/docs/tutorial/request-forms-and-files.md`. PR [#13386](https://github.com/fastapi/fastapi/pull/13386) by [@valentinDruzhinin](https://github.com/valentinDruzhinin). * 🌐 Update Korean translation for `docs/ko/docs/help-fastapi.md`. PR [#13262](https://github.com/fastapi/fastapi/pull/13262) by [@Zerohertz](https://github.com/Zerohertz). * 🌐 Add Korean translation for `docs/ko/docs/advanced/custom-response.md`. PR [#13265](https://github.com/fastapi/fastapi/pull/13265) by [@11kkw](https://github.com/11kkw). From 987d2f9a92bcb8f453a41d7e3230e00cb9a8d8db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Thu, 20 Feb 2025 18:49:13 +0100 Subject: [PATCH 075/157] =?UTF-8?q?=F0=9F=94=A7=20Update=20sponsors:=20add?= =?UTF-8?q?=20CodeRabbit=20(#13402)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 1 + docs/en/data/sponsors.yml | 3 +++ docs/en/docs/img/sponsors/coderabbit-banner.png | Bin 0 -> 9522 bytes docs/en/docs/img/sponsors/coderabbit.png | Bin 0 -> 20695 bytes docs/en/overrides/main.html | 6 ++++++ 5 files changed, 10 insertions(+) create mode 100644 docs/en/docs/img/sponsors/coderabbit-banner.png create mode 100644 docs/en/docs/img/sponsors/coderabbit.png diff --git a/README.md b/README.md index d5d5ced52..9a1260b90 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,7 @@ The key features are: + diff --git a/docs/en/data/sponsors.yml b/docs/en/data/sponsors.yml index 91b23937c..b994e533a 100644 --- a/docs/en/data/sponsors.yml +++ b/docs/en/data/sponsors.yml @@ -32,6 +32,9 @@ gold: - url: https://docs.render.com/deploy-fastapi?utm_source=deploydoc&utm_medium=referral&utm_campaign=fastapi title: Deploy & scale any full-stack web app on Render. Focus on building apps, not infra. img: https://fastapi.tiangolo.com/img/sponsors/render.svg + - url: https://www.coderabbit.ai/?utm_source=fastapi&utm_medium=badge&utm_campaign=fastapi + title: Cut Code Review Time & Bugs in Half with CodeRabbit + img: https://fastapi.tiangolo.com/img/sponsors/coderabbit.png silver: - url: https://github.com/deepset-ai/haystack/ title: Build powerful search from composable, open source building blocks diff --git a/docs/en/docs/img/sponsors/coderabbit-banner.png b/docs/en/docs/img/sponsors/coderabbit-banner.png new file mode 100644 index 0000000000000000000000000000000000000000..da3bb348204a39d83203e4580ad213dddce5ac99 GIT binary patch literal 9522 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 YVD9}kX5O^}o_9i$mQ;`^7dQ0(A0pkdO#lD@ literal 0 HcmV?d00001 diff --git a/docs/en/docs/img/sponsors/coderabbit.png b/docs/en/docs/img/sponsors/coderabbit.png new file mode 100644 index 0000000000000000000000000000000000000000..74c25e88466fa1b5251f7136bb86103cf79e874f GIT binary patch literal 20695 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 O0000