diff --git a/.github/workflows/issue-manager.yml b/.github/workflows/issue-manager.yml index 439084434..6a7e6143e 100644 --- a/.github/workflows/issue-manager.yml +++ b/.github/workflows/issue-manager.yml @@ -39,5 +39,9 @@ jobs: "waiting": { "delay": 2628000, "message": "As this PR has been waiting for the original user for a while but seems to be inactive, it's now going to be closed. But if there's anyone interested, feel free to create a new PR." + }, + "invalid": { + "delay": 0, + "message": "This was marked as invalid and will be closed now. If this is an error, please provide additional details." } } diff --git a/.github/workflows/label-approved.yml b/.github/workflows/label-approved.yml index e44584cf1..11176bed8 100644 --- a/.github/workflows/label-approved.yml +++ b/.github/workflows/label-approved.yml @@ -20,7 +20,9 @@ jobs: env: GITHUB_CONTEXT: ${{ toJson(github) }} run: echo "$GITHUB_CONTEXT" - - uses: docker://tiangolo/label-approved:0.0.4 + - uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v5 with: python-version: "3.11" - name: Setup uv diff --git a/.github/workflows/smokeshow.yml b/.github/workflows/smokeshow.yml index e9aa1093f..daff8e244 100644 --- a/.github/workflows/smokeshow.yml +++ b/.github/workflows/smokeshow.yml @@ -21,6 +21,7 @@ jobs: env: GITHUB_CONTEXT: ${{ toJson(github) }} run: echo "$GITHUB_CONTEXT" + - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: '3.9' diff --git a/docs/en/data/members.yml b/docs/en/data/members.yml index 0069f8c75..a3a6b912d 100644 --- a/docs/en/data/members.yml +++ b/docs/en/data/members.yml @@ -11,6 +11,9 @@ members: - login: svlandeg avatar_url: https://avatars.githubusercontent.com/u/8796347 url: https://github.com/svlandeg +- login: YuriiMotov + avatar_url: https://avatars.githubusercontent.com/u/109919500 + url: https://github.com/YuriiMotov - login: estebanx64 avatar_url: https://avatars.githubusercontent.com/u/10840422 url: https://github.com/estebanx64 diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index 30f98970a..ebdf8820c 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -7,8 +7,24 @@ hide: ## Latest Changes +### Translations + +* 🌐 Add Traditional Chinese translation for `docs/zh-hant/docs/deployment/cloud.md`. PR [#12440](https://github.com/fastapi/fastapi/pull/12440) by [@codingjenny](https://github.com/codingjenny). +* 🌐 Update Portuguese translation for `docs/pt/docs/python-types.md`. PR [#12428](https://github.com/fastapi/fastapi/pull/12428) by [@ceb10n](https://github.com/ceb10n). +* 🌐 Add Russian translation for `docs/ru/docs/environment-variables.md`. PR [#12436](https://github.com/fastapi/fastapi/pull/12436) by [@wisderfin](https://github.com/wisderfin). +* 🌐 Add Traditional Chinese translation for `docs/zh-hant/docs/resources/index.md`. PR [#12443](https://github.com/fastapi/fastapi/pull/12443) by [@codingjenny](https://github.com/codingjenny). +* 🌐 Add Traditional Chinese translation for `docs/zh-hant/docs/about/index.md`. PR [#12438](https://github.com/fastapi/fastapi/pull/12438) by [@codingjenny](https://github.com/codingjenny). +* 🌐 Add Portuguese translation for `docs/pt/docs/tutorial/query-param-models.md`. PR [#12414](https://github.com/fastapi/fastapi/pull/12414) by [@ceb10n](https://github.com/ceb10n). +* 🌐 Remove Portuguese translation for `docs/pt/docs/deployment.md`. PR [#12427](https://github.com/fastapi/fastapi/pull/12427) by [@ceb10n](https://github.com/ceb10n). +* 🌐 Add Portuguese translation for `docs/pt/docs/tutorial/body-updates.md`. PR [#12381](https://github.com/fastapi/fastapi/pull/12381) by [@andersonrocha0](https://github.com/andersonrocha0). +* 🌐 Add Portuguese translation for `docs/pt/docs/advanced/response-cookies.md`. PR [#12417](https://github.com/fastapi/fastapi/pull/12417) by [@Paulofalcao2002](https://github.com/Paulofalcao2002). + ### Internal +* 👷 Update issue manager workflow . PR [#12457](https://github.com/fastapi/fastapi/pull/12457) by [@alejsdev](https://github.com/alejsdev). +* 🔧 Update team, include YuriiMotov 🚀. PR [#12453](https://github.com/fastapi/fastapi/pull/12453) by [@tiangolo](https://github.com/tiangolo). +* 👷 Refactor label-approved, make it an internal script instead of an external GitHub Action. PR [#12280](https://github.com/fastapi/fastapi/pull/12280) by [@tiangolo](https://github.com/tiangolo). +* 👷 Fix smokeshow, checkout files on CI. PR [#12434](https://github.com/fastapi/fastapi/pull/12434) by [@tiangolo](https://github.com/tiangolo). * 👷 Use uv in CI. PR [#12281](https://github.com/fastapi/fastapi/pull/12281) by [@tiangolo](https://github.com/tiangolo). * ⬆ Update httpx requirement from <0.25.0,>=0.23.0 to >=0.23.0,<0.28.0. PR [#11509](https://github.com/fastapi/fastapi/pull/11509) by [@dependabot[bot]](https://github.com/apps/dependabot). diff --git a/docs/pt/docs/advanced/response-cookies.md b/docs/pt/docs/advanced/response-cookies.md new file mode 100644 index 000000000..d0821b5b2 --- /dev/null +++ b/docs/pt/docs/advanced/response-cookies.md @@ -0,0 +1,55 @@ +# Cookies de Resposta + +## Usando um parâmetro `Response` + +Você pode declarar um parâmetro do tipo `Response` na sua *função de operação de rota*. + +E então você pode definir cookies nesse objeto de resposta *temporário*. + +```Python hl_lines="1 8-9" +{!../../docs_src/response_cookies/tutorial002.py!} +``` + +Em seguida, você pode retornar qualquer objeto que precise, como normalmente faria (um `dict`, um modelo de banco de dados, etc). + +E se você declarou um `response_model`, ele ainda será usado para filtrar e converter o objeto que você retornou. + +**FastAPI** usará essa resposta *temporária* para extrair os cookies (também os cabeçalhos e código de status) e os colocará na resposta final que contém o valor que você retornou, filtrado por qualquer `response_model`. + +Você também pode declarar o parâmetro `Response` em dependências e definir cookies (e cabeçalhos) nelas. + +## Retornando uma `Response` diretamente + +Você também pode criar cookies ao retornar uma `Response` diretamente no seu código. + +Para fazer isso, você pode criar uma resposta como descrito em [Retornando uma Resposta Diretamente](response-directly.md){.internal-link target=_blank}. + +Então, defina os cookies nela e a retorne: + +```Python hl_lines="10-12" +{!../../docs_src/response_cookies/tutorial001.py!} +``` + +/// tip | Dica + +Lembre-se de que se você retornar uma resposta diretamente em vez de usar o parâmetro `Response`, FastAPI a retornará diretamente. + +Portanto, você terá que garantir que seus dados sejam do tipo correto. E.g. será compatível com JSON se você estiver retornando um `JSONResponse`. + +E também que você não esteja enviando nenhum dado que deveria ter sido filtrado por um `response_model`. + +/// + +### Mais informações + +/// note | "Detalhes Técnicos" + +Você também poderia usar `from starlette.responses import Response` ou `from starlette.responses import JSONResponse`. + +**FastAPI** fornece as mesmas `starlette.responses` em `fastapi.responses` apenas como uma conveniência para você, o desenvolvedor. Mas a maioria das respostas disponíveis vem diretamente do Starlette. + +E como o `Response` pode ser usado frequentemente para definir cabeçalhos e cookies, o **FastAPI** também o fornece em `fastapi.Response`. + +/// + +Para ver todos os parâmetros e opções disponíveis, verifique a documentação no Starlette. diff --git a/docs/pt/docs/deployment.md b/docs/pt/docs/deployment.md deleted file mode 100644 index 6874a2529..000000000 --- a/docs/pt/docs/deployment.md +++ /dev/null @@ -1,414 +0,0 @@ -# Implantação - -Implantar uma aplicação **FastAPI** é relativamente fácil. - -Existem vários modos de realizar o _deploy_ dependendo de seu caso de uso específico e as ferramentas que você utiliza. - -Você verá mais sobre alguns modos de fazer o _deploy_ nas próximas seções. - -## Versões do FastAPI - -**FastAPI** já está sendo utilizado em produção em muitas aplicações e sistemas. E a cobertura de teste é mantida a 100%. Mas seu desenvolvimento continua andando rapidamente. - -Novos recursos são adicionados frequentemente, _bugs_ são corrigidos regularmente, e o código está continuamente melhorando. - -É por isso que as versões atuais estão ainda no `0.x.x`, isso reflete que cada versão poderia ter potencialmente alterações que podem quebrar. Isso segue as convenções de Versionamento Semântico. - -Você pode criar aplicações para produção com **FastAPI** bem agora (e você provavelmente já faça isso por um tempo), você tem que ter certeza de utilizar uma versão que funcione corretamente com o resto do seu código. - -### Anote sua versão `fastapi` - -A primeira coisa que você deve fazer é "fixar" a versão do **FastAPI** que está utilizando para a última versão específica que você sabe que funciona corretamente para a sua aplicação. - -Por exemplo, vamos dizer que você esteja utilizando a versão `0.45.0` no seu _app_. - -Se você usa um arquivo `requirements.txt`, dá para especificar a versão assim: - -```txt -fastapi==0.45.0 -``` - -isso significa que você pode usar exatamente a versão `0.45.0`. - -Ou você poderia fixar assim: - -```txt -fastapi>=0.45.0,<0.46.0 -``` - -o que significa que você pode usar as versões `0.45.0` ou acima, mas menor que `0.46.0`. Por exemplo, a versão `0.45.2` poderia ser aceita. - -Se você usa qualquer outra ferramenta para gerenciar suas instalações, como Poetry, Pipenv ou outro, todos terão um modo que você possa usar para definir versões específicas para seus pacotes. - -### Versões disponíveis - -Você pode ver as versões disponíveis (por exemplo, para verificar qual é a versão atual) nas [Notas de Lançamento](release-notes.md){.internal-link target=_blank}. - -### Sobre as versões - -Seguindo as convenções do Versionamento Semântico, qualquer versão abaixo de `1.0.0` pode potencialmente adicionar mudanças que quebrem. - -FastAPI também segue a convenção que qualquer versão de _"PATCH"_ seja para ajustes de _bugs_ e mudanças que não quebrem a aplicação. - -/// tip - -O _"PATCH"_ é o último número, por exemplo, em `0.2.3`, a versão do _PATCH_ é `3`. - -/// - -Então, você poderia ser capaz de fixar para uma versão como: - -```txt -fastapi>=0.45.0,<0.46.0 -``` - -Mudanças que quebram e novos recursos são adicionados em versões _"MINOR"_. - -/// tip - -O _"MINOR"_ é o número do meio, por exemplo, em `0.2.3`, a versão _MINOR_ é `2`. - -/// - -### Atualizando as versões FastAPI - -Você pode adicionar testes em sua aplicação. - -Com o **FastAPI** é muito fácil (graças ao Starlette), verifique a documentação: [Testando](tutorial/testing.md){.internal-link target=_blank} - -Após você ter os testes, então você pode fazer o _upgrade_ da versão **FastAPI** para uma mais recente, e ter certeza que todo seu código esteja funcionando corretamente rodando seus testes. - -Se tudo estiver funcionando, ou após você fazer as alterações necessárias, e todos seus testes estiverem passando, então você poderá fixar o `fastapi` para a versão mais recente. - -### Sobre Starlette - -Você não deve fixar a versão do `starlette`. - -Versões diferentes do **FastAPI** irão utilizar uma versão mais nova específica do Starlette. - -Então, você pode deixar que o **FastAPI** use a versão correta do Starlette. - -### Sobre Pydantic - -Pydantic inclui os testes para **FastAPI** em seus próprios testes, então novas versões do Pydantic (acima de `1.0.0`) são sempre compatíveis com FastAPI. - -Você pode fixar o Pydantic para qualquer versão acima de `1.0.0` e abaixo de `2.0.0` que funcionará. - -Por exemplo: - -```txt -pydantic>=1.2.0,<2.0.0 -``` - -## Docker - -Nessa seção você verá instruções e _links_ para guias de saber como: - -* Fazer uma imagem/container da sua aplicação **FastAPI** com máxima performance. Em aproximadamente **5 min**. -* (Opcionalmente) entender o que você, como desenvolvedor, precisa saber sobre HTTPS. -* Inicializar um _cluster_ Docker Swarm Mode com HTTPS automático, mesmo em um simples servidor de $5 dólares/mês. Em aproximadamente **20 min**. -* Gere e implante uma aplicação **FastAPI** completa, usando seu _cluster_ Docker Swarm, com HTTPS etc. Em aproxiamadamente **10 min**. - -Você pode usar **Docker** para implantação. Ele tem várias vantagens como segurança, replicabilidade, desenvolvimento simplificado etc. - -Se você está usando Docker, você pode utilizar a imagem Docker oficial: - -### tiangolo/uvicorn-gunicorn-fastapi - -Essa imagem tem um mecanismo incluído de "auto-ajuste", para que você possa apenas adicionar seu código e ter uma alta performance automaticamente. E sem fazer sacrifícios. - -Mas você pode ainda mudar e atualizar todas as configurações com variáveis de ambiente ou arquivos de configuração. - -/// tip - -Para ver todas as configurações e opções, vá para a página da imagem do Docker: tiangolo/uvicorn-gunicorn-fastapi. - -/// - -### Crie um `Dockerfile` - -* Vá para o diretório de seu projeto. -* Crie um `Dockerfile` com: - -```Dockerfile -FROM tiangolo/uvicorn-gunicorn-fastapi:python3.7 - -COPY ./app /app -``` - -#### Grandes aplicações - -Se você seguiu a seção sobre criação de [Grandes Aplicações com Múltiplos Arquivos](tutorial/bigger-applications.md){.internal-link target=_blank}, seu `Dockerfile` poderia parecer como: - -```Dockerfile -FROM tiangolo/uvicorn-gunicorn-fastapi:python3.7 - -COPY ./app /app/app -``` - -#### Raspberry Pi e outras arquiteturas - -Se você estiver rodando Docker em um Raspberry Pi (que possui um processador ARM) ou qualquer outra arquitetura, você pode criar um `Dockerfile` do zero, baseado em uma imagem base Python (que é multi-arquitetural) e utilizar Uvicorn sozinho. - -Nesse caso, seu `Dockerfile` poderia parecer assim: - -```Dockerfile -FROM python:3.7 - -RUN pip install fastapi uvicorn - -EXPOSE 80 - -COPY ./app /app - -CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"] -``` - -### Crie o código **FastAPI** - -* Crie um diretório `app` e entre nele. -* Crie um arquivo `main.py` com: - -```Python -from fastapi import FastAPI - -app = FastAPI() - - -@app.get("/") -def read_root(): - return {"Hello": "World"} - - -@app.get("/items/{item_id}") -def read_item(item_id: int, q: str = None): - return {"item_id": item_id, "q": q} -``` - -* Você deve ter uma estrutura de diretórios assim: - -``` -. -├── app -│ └── main.py -└── Dockerfile -``` - -### Construa a imagem Docker - -* Vá para o diretório do projeto (onde seu `Dockerfile` está, contendo seu diretório `app`. -* Construa sua imagem FastAPI: - -
- -```console -$ docker build -t myimage . - ----> 100% -``` - -
- -### Inicie o container Docker - -* Rode um container baseado em sua imagem: - -
- -```console -$ docker run -d --name mycontainer -p 80:80 myimage -``` - -
- -Agora você tem um servidor FastAPI otimizado em um container Docker. Auto-ajustado para seu servidor atual (e número de núcleos de CPU). - -### Verifique - -Você deve ser capaz de verificar na URL de seu container Docker, por exemplo: http://192.168.99.100/items/5?q=somequery ou http://127.0.0.1/items/5?q=somequery (ou equivalente, usando seu _host_ Docker). - -Você verá algo como: - -```JSON -{"item_id": 5, "q": "somequery"} -``` - -### API interativa de documetação - -Agora você pode ir para http://192.168.99.100/docs ou http://127.0.0.1/docs (ou equivalente, usando seu _host_ Docker). - -Você verá a API interativa de documentação (fornecida por Swagger UI): - -![Swagger UI](https://fastapi.tiangolo.com/img/index/index-01-swagger-ui-simple.png) - -### APIs alternativas de documentação - -E você pode também ir para http://192.168.99.100/redoc ou http://127.0.0.1/redoc (ou equivalente, usando seu _host_ Docker). - -Você verá a documentação automática alternativa (fornecida por ReDoc): - -![ReDoc](https://fastapi.tiangolo.com/img/index/index-02-redoc-simple.png) - -## HTTPS - -### Sobre HTTPS - -É fácil assumir que HTTPS seja algo que esteja apenas "habilitado" ou não. - -Mas ele é um pouquinho mais complexo do que isso. - -/// tip - -Se você está com pressa ou não se importa, continue na próxima seção com instruções passo a passo para configurar tudo. - -/// - -Para aprender o básico de HTTPS, pela perspectiva de um consumidor, verifique https://howhttps.works/. - -Agora, pela perspectiva de um desenvolvedor, aqui estão algumas coisas para se ter em mente enquanto se pensa sobre HTTPS: - -* Para HTTPS, o servidor precisa ter "certificados" gerados por terceiros. - * Esses certificados são na verdade adquiridos por terceiros, não "gerados". -* Certificados tem um prazo de uso. - * Eles expiram. - * E então eles precisam ser renovados, adquiridos novamente por terceiros. -* A encriptação da conexão acontece no nível TCP. - * TCP é uma camada abaixo do HTTP. - * Então, o controle de certificado e encriptação é feito antes do HTTP. -* TCP não conhece nada sobre "domínios". Somente sobre endereços IP. - * A informação sobre o domínio requisitado vai nos dados HTTP. -* Os certificados HTTPS "certificam" um certo domínio, mas o protocolo e a encriptação acontecem no nível TCP, antes de saber qual domínio está sendo lidado. -* Por padrão, isso significa que você pode ter somente um certificado HTTPS por endereço IP. - * Não importa quão grande é seu servidor ou quão pequena cada aplicação que você tenha possar ser. - * No entanto, existe uma solução para isso. -* Existe uma extensão para o protocolo TLS (o que controla a encriptação no nível TCP, antes do HTTP) chamada SNI. - * Essa extensão SNI permite um único servidor (com um único endereço IP) a ter vários certificados HTTPS e servir múltiplas aplicações/domínios HTTPS. - * Para que isso funcione, um único componente (programa) rodando no servidor, ouvindo no endereço IP público, deve ter todos os certificados HTTPS no servidor. -* Após obter uma conexão segura, o protocolo de comunicação ainda é HTTP. - * O conteúdo está encriptado, mesmo embora ele esteja sendo enviado com o protocolo HTTP. - -É uma prática comum ter um servidor HTTP/programa rodando no servidor (a máquina, _host_ etc.) e gerenciar todas as partes HTTP: enviar as requisições HTTP decriptadas para a aplicação HTTP rodando no mesmo servidor (a aplicação **FastAPI**, nesse caso), pega a resposta HTTP da aplicação, encripta utilizando o certificado apropriado e enviando de volta para o cliente usando HTTPS. Esse servidor é frequentemente chamado TLS _Termination Proxy_. - -### Vamos encriptar - -Antes de encriptar, esses certificados HTTPS foram vendidos por terceiros de confiança. - -O processo para adquirir um desses certificados costumava ser chato, exigia muita papelada e eram bem caros. - -Mas então _Let's Encrypt_ foi criado. - -É um projeto da Fundação Linux.Ele fornece certificados HTTPS de graça. De um jeito automatizado. Esses certificados utilizam todos os padrões de segurança criptográfica, e tem vida curta (cerca de 3 meses), para que a segurança seja melhor devido ao seu curto período de vida. - -Os domínios são seguramente verificados e os certificados são gerados automaticamente. Isso também permite automatizar a renovação desses certificados. - -A idéia é automatizar a aquisição e renovação desses certificados, para que você possa ter um HTTPS seguro, grátis, para sempre. - -### Traefik - -Traefik é um _proxy_ reverso / _load balancer_ de alta performance. Ele pode fazer o trabalho do _"TLS Termination Proxy"_ (à parte de outros recursos). - -Ele tem integração com _Let's Encrypt_. Assim, ele pode controlar todas as partes HTTPS, incluindo a aquisição e renovação de certificados. - -Ele também tem integrações com Docker. Assim, você pode declarar seus domínios em cada configuração de aplicação e leitura dessas configurações, gerando os certificados HTTPS e servindo o HTTPS para sua aplicação automaticamente, sem exigir qualquer mudança em sua configuração. - ---- - -Com essas ferramentas e informações, continue com a próxima seção para combinar tudo. - -## _Cluster_ de Docker Swarm Mode com Traefik e HTTPS - -Você pode ter um _cluster_ de Docker Swarm Mode configurado em minutos (cerca de 20) com o Traefik controlando HTTPS (incluindo aquisição e renovação de certificados). - -Utilizando o Docker Swarm Mode, você pode iniciar com um _"cluster"_ de apenas uma máquina (que pode até ser um servidor por 5 dólares / mês) e então você pode aumentar conforme a necessidade adicionando mais servidores. - -Para configurar um _cluster_ Docker Swarm Mode com Traefik controlando HTTPS, siga essa orientação: - -### Docker Swarm Mode and Traefik for an HTTPS cluster - -### Faça o _deploy_ de uma aplicação FastAPI - -O jeito mais fácil de configurar tudo pode ser utilizando o [Gerador de Projetos **FastAPI**](project-generation.md){.internal-link target=_blank}. - -Ele é designado para ser integrado com esse _cluster_ Docker Swarm com Traefik e HTTPS descrito acima. - -Você pode gerar um projeto em cerca de 2 minutos. - -O projeto gerado tem instruções para fazer o _deploy_, fazendo isso leva outros 2 minutos. - -## Alternativamente, faça o _deploy_ **FastAPI** sem Docker - -Você pode fazer o _deploy_ do **FastAPI** diretamente sem o Docker também. - -Você apenas precisa instalar um servidor ASGI compatível como: - -//// tab | Uvicorn - -* Uvicorn, um servidor ASGI peso leve, construído sobre uvloop e httptools. - -
- -```console -$ pip install "uvicorn[standard]" - ----> 100% -``` - -
- -//// - -//// tab | Hypercorn - -* Hypercorn, um servidor ASGI também compatível com HTTP/2. - -
- -```console -$ pip install hypercorn - ----> 100% -``` - -
- -...ou qualquer outro servidor ASGI. - -//// - -E rode sua applicação do mesmo modo que você tem feito nos tutoriais, mas sem a opção `--reload`, por exemplo: - -//// tab | Uvicorn - -
- -```console -$ uvicorn main:app --host 0.0.0.0 --port 80 - -INFO: Uvicorn running on http://0.0.0.0:80 (Press CTRL+C to quit) -``` - -
- -//// - -//// tab | Hypercorn - -
- -```console -$ hypercorn main:app --bind 0.0.0.0:80 - -Running on 0.0.0.0:8080 over http (CTRL + C to quit) -``` - -
- -//// - -Você deve querer configurar mais algumas ferramentas para ter certeza que ele seja reinicializado automaticamante se ele parar. - -Você também deve querer instalar Gunicorn e utilizar ele como um gerenciador para o Uvicorn, ou usar Hypercorn com múltiplos _workers_. - -Tenha certeza de ajustar o número de _workers_ etc. - -Mas se você estiver fazendo tudo isso, você pode apenas usar uma imagem Docker que fará isso automaticamente. diff --git a/docs/pt/docs/python-types.md b/docs/pt/docs/python-types.md index 05faa860c..a0938259f 100644 --- a/docs/pt/docs/python-types.md +++ b/docs/pt/docs/python-types.md @@ -1,18 +1,18 @@ # Introdução aos tipos Python -**Python 3.6 +** tem suporte para "type hints" opcionais. +O Python possui suporte para "dicas de tipo" ou "type hints" (também chamado de "anotações de tipo" ou "type annotations") -Esses **"type hints"** são uma nova sintaxe (desde Python 3.6+) que permite declarar o tipo de uma variável. +Esses **"type hints"** são uma sintaxe especial que permite declarar o tipo de uma variável. Ao declarar tipos para suas variáveis, editores e ferramentas podem oferecer um melhor suporte. -Este é apenas um **tutorial rápido / atualização** sobre type hints Python. Ele cobre apenas o mínimo necessário para usá-los com o **FastAPI** ... que é realmente muito pouco. +Este é apenas um **tutorial rápido / atualização** sobre type hints do Python. Ele cobre apenas o mínimo necessário para usá-los com o **FastAPI**... que é realmente muito pouco. O **FastAPI** é baseado nesses type hints, eles oferecem muitas vantagens e benefícios. Mas mesmo que você nunca use o **FastAPI**, você se beneficiaria de aprender um pouco sobre eles. -/// note | "Nota" +/// note | Nota Se você é um especialista em Python e já sabe tudo sobre type hints, pule para o próximo capítulo. @@ -35,8 +35,8 @@ John Doe A função faz o seguinte: * Pega um `first_name` e `last_name`. -* Converte a primeira letra de cada uma em maiúsculas com `title ()`. -* Concatena com um espaço no meio. +* Converte a primeira letra de cada uma em maiúsculas com `title()`. +* Concatena com um espaço no meio. ```Python hl_lines="2" {!../../docs_src/python_types/tutorial001.py!} @@ -48,7 +48,7 @@ A função faz o seguinte: Mas agora imagine que você estava escrevendo do zero. -Em algum momento você teria iniciado a definição da função, já tinha os parâmetros prontos ... +Em algum momento você teria iniciado a definição da função, já tinha os parâmetros prontos... Mas então você deve chamar "esse método que converte a primeira letra em maiúscula". @@ -96,37 +96,37 @@ Isso não é o mesmo que declarar valores padrão como seria com: Estamos usando dois pontos (`:`), não é igual a (`=`). -E adicionar type hints normalmente não muda o que acontece do que aconteceria sem elas. +E adicionar type hints normalmente não muda o que acontece do que aconteceria sem eles. Mas agora, imagine que você está novamente no meio da criação dessa função, mas com type hints. -No mesmo ponto, você tenta acionar o preenchimento automático com o `Ctrl Space` e vê: +No mesmo ponto, você tenta acionar o preenchimento automático com o `Ctrl+Space` e vê: -Com isso, você pode rolar, vendo as opções, até encontrar o que "toca uma campainha": +Com isso, você pode rolar, vendo as opções, até encontrar o que "soa familiar": ## Mais motivação -Marque esta função, ela já possui type hints: +Verifique esta função, ela já possui type hints: ```Python hl_lines="1" {!../../docs_src/python_types/tutorial003.py!} ``` -Como o editor conhece os tipos de variáveis, você não apenas obtém a conclusão, mas também as verificações de erro: +Como o editor conhece os tipos de variáveis, você não obtém apenas o preenchimento automático, mas também as verificações de erro: -Agora você sabe que precisa corrigí-lo, converta `age` em uma string com `str (age)`: +Agora você sabe que precisa corrigí-lo, converta `age` em uma string com `str(age)`: ```Python hl_lines="2" {!../../docs_src/python_types/tutorial004.py!} ``` -## Tipos de declaração +## Declarando Tipos Você acabou de ver o local principal para declarar type hints. Como parâmetros de função. @@ -151,39 +151,77 @@ Você pode usar, por exemplo: Existem algumas estruturas de dados que podem conter outros valores, como `dict`, `list`, `set` e `tuple`. E os valores internos também podem ter seu próprio tipo. -Para declarar esses tipos e os tipos internos, você pode usar o módulo Python padrão `typing`. +Estes tipos que possuem tipos internos são chamados de tipos "**genéricos**". E é possível declará-los mesmo com os seus tipos internos. -Ele existe especificamente para suportar esses type hints. +Para declarar esses tipos e os tipos internos, você pode usar o módulo Python padrão `typing`. Ele existe especificamente para suportar esses type hints. -#### `List` +#### Versões mais recentes do Python -Por exemplo, vamos definir uma variável para ser uma `lista` de `str`. +A sintaxe utilizando `typing` é **compatível** com todas as versões, desde o Python 3.6 até as últimas, incluindo o Python 3.9, 3.10, etc. -Em `typing`, importe `List` (com um `L` maiúsculo): +Conforme o Python evolui, **novas versões** chegam com suporte melhorado para esses type annotations, e em muitos casos, você não precisará nem importar e utilizar o módulo `typing` para declarar os type annotations. + +Se você pode escolher uma versão mais recente do Python para o seu projeto, você poderá aproveitar isso ao seu favor. + +Em todos os documentos existem exemplos compatíveis com cada versão do Python (quando existem diferenças). + +Por exemplo, "**Python 3.6+**" significa que é compatível com o Python 3.6 ou superior (incluindo o 3.7, 3.8, 3.9, 3.10, etc). E "**Python 3.9+**" significa que é compatível com o Python 3.9 ou mais recente (incluindo o 3.10, etc). + +Se você pode utilizar a **versão mais recente do Python**, utilize os exemplos para as últimas versões. Eles terão as **melhores e mais simples sintaxes**, como por exemplo, "**Python 3.10+**". + +#### List + +Por exemplo, vamos definir uma variável para ser uma `list` de `str`. + +//// tab | Python 3.9+ + +Declare uma variável com a mesma sintaxe com dois pontos (`:`) + +Como tipo, coloque `list`. + +Como a lista é o tipo que contém algum tipo interno, você coloca o tipo dentro de colchetes: ```Python hl_lines="1" -{!../../docs_src/python_types/tutorial006.py!} +{!> ../../docs_src/python_types/tutorial006_py39.py!} ``` -Declare a variável com a mesma sintaxe de dois pontos (`:`). +//// -Como o tipo, coloque a `List`. +//// tab | Python 3.8+ -Como a lista é um tipo que contém alguns tipos internos, você os coloca entre colchetes: +De `typing`, importe `List` (com o `L` maiúsculo): + +```Python hl_lines="1" +{!> ../../docs_src/python_types/tutorial006.py!} +``` + +Declare uma variável com a mesma sintaxe com dois pontos (`:`) + +Como tipo, coloque o `List` que você importou de `typing`. + +Como a lista é o tipo que contém algum tipo interno, você coloca o tipo dentro de colchetes: ```Python hl_lines="4" -{!../../docs_src/python_types/tutorial006.py!} +{!> ../../docs_src/python_types/tutorial006.py!} ``` -/// tip | "Dica" +//// + +/// info | Informação -Esses tipos internos entre colchetes são chamados de "parâmetros de tipo". +Estes tipos internos dentro dos colchetes são chamados "parâmetros de tipo" (type parameters). -Nesse caso, `str` é o parâmetro de tipo passado para `List`. +Neste caso, `str` é o parâmetro de tipo passado para `List` (ou `list` no Python 3.9 ou superior). /// -Isso significa que: "a variável `items` é uma `list`, e cada um dos itens desta lista é uma `str`". +Isso significa: "a variável `items` é uma `list`, e cada um dos itens desta lista é uma `str`". + +/// tip | Dica + +Se você usa o Python 3.9 ou superior, você não precisa importar `List` de `typing`. Você pode utilizar o mesmo tipo `list` no lugar. + +/// Ao fazer isso, seu editor pode fornecer suporte mesmo durante o processamento de itens da lista: @@ -195,20 +233,32 @@ Observe que a variável `item` é um dos elementos da lista `items`. E, ainda assim, o editor sabe que é um `str` e fornece suporte para isso. -#### `Tuple` e `Set` +#### Tuple e Set Você faria o mesmo para declarar `tuple`s e `set`s: -```Python hl_lines="1 4" -{!../../docs_src/python_types/tutorial007.py!} +//// tab | Python 3.9+ + +```Python hl_lines="1" +{!> ../../docs_src/python_types/tutorial007_py39.py!} +``` + +//// + +//// tab | Python 3.8+ + +```Python hl_lines="1 4" +{!> ../../docs_src/python_types/tutorial007.py!} ``` +//// + Isso significa que: * A variável `items_t` é uma `tuple` com 3 itens, um `int`, outro `int` e uma `str`. * A variável `items_s` é um `set`, e cada um de seus itens é do tipo `bytes`. -#### `Dict` +#### Dict Para definir um `dict`, você passa 2 parâmetros de tipo, separados por vírgulas. @@ -216,38 +266,185 @@ O primeiro parâmetro de tipo é para as chaves do `dict`. O segundo parâmetro de tipo é para os valores do `dict`: -```Python hl_lines="1 4" -{!../../docs_src/python_types/tutorial008.py!} +//// tab | Python 3.9+ + +```Python hl_lines="1" +{!> ../../docs_src/python_types/tutorial008_py39.py!} ``` +//// + +//// tab | Python 3.8+ + +```Python hl_lines="1 4" +{!> ../../docs_src/python_types/tutorial008.py!} +``` + +//// + Isso significa que: * A variável `prices` é um dict`: * As chaves deste `dict` são do tipo `str` (digamos, o nome de cada item). * Os valores deste `dict` são do tipo `float` (digamos, o preço de cada item). -#### `Opcional` +#### Union -Você também pode usar o `Opcional` para declarar que uma variável tem um tipo, como `str`, mas que é "opcional", o que significa que também pode ser `None`: +Você pode declarar que uma variável pode ser de qualquer um dentre **diversos tipos**. Por exemplo, um `int` ou um `str`. + +No Python 3.6 e superior (incluindo o Python 3.10), você pode utilizar o tipo `Union` de `typing`, e colocar dentro dos colchetes os possíveis tipos aceitáveis. + +No Python 3.10 também existe uma **nova sintaxe** onde você pode colocar os possívels tipos separados por uma barra vertical (`|`). + +//// tab | Python 3.10+ + +```Python hl_lines="1" +{!> ../../docs_src/python_types/tutorial008b_py310.py!} +``` + +//// + +//// tab | Python 3.8+ + +```Python hl_lines="1 4" +{!> ../../docs_src/python_types/tutorial008b.py!} +``` + +//// + +Em ambos os casos, isso significa que `item` poderia ser um `int` ou um `str`. + + +#### Possívelmente `None` + +Você pode declarar que um valor pode ter um tipo, como `str`, mas que ele também pode ser `None`. + +No Python 3.6 e superior (incluindo o Python 3.10) você pode declará-lo importando e utilizando `Optional` do módulo `typing`. ```Python hl_lines="1 4" {!../../docs_src/python_types/tutorial009.py!} ``` -O uso de `Opcional [str]` em vez de apenas `str` permitirá que o editor o ajude a detectar erros, onde você pode estar assumindo que um valor é sempre um `str`, quando na verdade também pode ser `None`. +O uso de `Optional[str]` em vez de apenas `str` permitirá que o editor o ajude a detectar erros, onde você pode estar assumindo que um valor é sempre um `str`, quando na verdade também pode ser `None`. + +`Optional[Something]` é na verdade um atalho para `Union[Something, None]`, eles são equivalentes. + +Isso também significa que no Python 3.10, você pode utilizar `Something | None`: + +//// tab | Python 3.10+ + +```Python hl_lines="1" +{!> ../../docs_src/python_types/tutorial009_py310.py!} +``` + +//// + +//// tab | Python 3.8+ + +```Python hl_lines="1 4" +{!> ../../docs_src/python_types/tutorial009.py!} +``` + +//// + +//// tab | Python 3.8+ alternative + +```Python hl_lines="1 4" +{!> ../../docs_src/python_types/tutorial009b.py!} +``` + +//// + +#### Utilizando `Union` ou `Optional` + +Se você está utilizando uma versão do Python abaixo da 3.10, aqui vai uma dica do meu ponto de vista bem **subjetivo**: + +* 🚨 Evite utilizar `Optional[SomeType]` +* No lugar, ✨ **use `Union[SomeType, None]`** ✨. + +Ambos são equivalentes, e no final das contas, eles são o mesmo. Mas eu recomendaria o `Union` ao invés de `Optional` porque a palavra **Optional** parece implicar que o valor é opcional, quando na verdade significa "isso pode ser `None`", mesmo que ele não seja opcional e ainda seja obrigatório. + +Eu penso que `Union[SomeType, None]` é mais explícito sobre o que ele significa. + +Isso é apenas sobre palavras e nomes. Mas estas palavras podem afetar como os seus colegas de trabalho pensam sobre o código. + +Por exemplo, vamos pegar esta função: + +```Python hl_lines="1 4" +{!../../docs_src/python_types/tutorial009c.py!} +``` + +O paâmetro `name` é definido como `Optional[str]`, mas ele **não é opcional**, você não pode chamar a função sem o parâmetro: + +```Python +say_hi() # Oh, no, this throws an error! 😱 +``` + +O parâmetro `name` **ainda é obrigatório** (não *opicional*) porque ele não possui um valor padrão. Mesmo assim, `name` aceita `None` como valor: + +```Python +say_hi(name=None) # This works, None is valid 🎉 +``` + +A boa notícia é, quando você estiver no Python 3.10 você não precisará se preocupar mais com isso, pois você poderá simplesmente utilizar o `|` para definir uniões de tipos: + +```Python hl_lines="1 4" +{!../../docs_src/python_types/tutorial009c_py310.py!} +``` + +E então você não precisará mais se preocupar com nomes como `Optional` e `Union`. 😎 #### Tipos genéricos -Esses tipos que usam parâmetros de tipo entre colchetes, como: +Esses tipos que usam parâmetros de tipo entre colchetes são chamados **tipos genéricos** ou **genéricos**. Por exemplo: + +//// tab | Python 3.10+ + +Você pode utilizar os mesmos tipos internos como genéricos (com colchetes e tipos dentro): + +* `list` +* `tuple` +* `set` +* `dict` + +E o mesmo como no Python 3.8, do módulo `typing`: + +* `Union` +* `Optional` (o mesmo que com o 3.8) +* ...entro outros. + +No Python 3.10, como uma alternativa para a utilização dos genéricos `Union` e `Optional`, você pode usar a barra vertical (`|`) para declarar uniões de tipos. Isso é muito melhor e mais simples. + +//// + +//// tab | Python 3.9+ + +Você pode utilizar os mesmos tipos internos como genéricos (com colchetes e tipos dentro): + +* `list` +* `tuple` +* `set` +* `dict` + +E o mesmo como no Python 3.8, do módulo `typing`: + +* `Union` +* `Optional` +* ...entro outros. + +//// + +//// tab | Python 3.8+ * `List` * `Tuple` * `Set` * `Dict` -* `Opcional` -* ...e outros. +* `Union` +* `Optional` +* ...entro outros. -são chamados **tipos genéricos** ou **genéricos**. +//// ### Classes como tipos @@ -255,7 +452,7 @@ Você também pode declarar uma classe como o tipo de uma variável. Digamos que você tenha uma classe `Person`, com um nome: -```Python hl_lines="1 2 3" +```Python hl_lines="1-3" {!../../docs_src/python_types/tutorial010.py!} ``` @@ -269,9 +466,13 @@ E então, novamente, você recebe todo o suporte do editor: +Perceba que isso significa que "`one_person` é uma **instância** da classe `Person`". + +Isso não significa que "`one_person` é a **classe** chamada `Person`". + ## Modelos Pydantic - Pydantic é uma biblioteca Python para executar a validação de dados. +O Pydantic é uma biblioteca Python para executar a validação de dados. Você declara a "forma" dos dados como classes com atributos. @@ -283,21 +484,93 @@ E você recebe todo o suporte do editor com esse objeto resultante. Retirado dos documentos oficiais dos Pydantic: +//// tab | Python 3.10+ + ```Python -{!../../docs_src/python_types/tutorial011.py!} +{!> ../../docs_src/python_types/tutorial011_py310.py!} ``` -/// info | "Informação" +//// + +//// tab | Python 3.9+ + +```Python +{!> ../../docs_src/python_types/tutorial011_py39.py!} +``` -Para saber mais sobre o Pydantic, verifique seus documentos . +//// + +//// tab | Python 3.8+ + +```Python +{!> ../../docs_src/python_types/tutorial011.py!} +``` + +//// + +/// info | Informação + +Para saber mais sobre o Pydantic, verifique a sua documentação. /// -**FastAPI** é todo baseado em Pydantic. +O **FastAPI** é todo baseado em Pydantic. Você verá muito mais disso na prática no [Tutorial - Guia do usuário](tutorial/index.md){.internal-link target=_blank}. -## Type hints em **FastAPI** +/// tip | Dica + +O Pydantic tem um comportamento especial quando você usa `Optional` ou `Union[Something, None]` sem um valor padrão. Você pode ler mais sobre isso na documentação do Pydantic sobre campos Opcionais Obrigatórios. + +/// + + +## Type Hints com Metadados de Anotações + +O Python possui uma funcionalidade que nos permite incluir **metadados adicionais** nos type hints utilizando `Annotated`. + +//// tab | Python 3.9+ + +No Python 3.9, `Annotated` é parte da biblioteca padrão, então você pode importá-lo de `typing`. + +```Python hl_lines="1 4" +{!> ../../docs_src/python_types/tutorial013_py39.py!} +``` + +//// + +//// tab | Python 3.8+ + +Em versões abaixo do Python 3.9, você importa `Annotated` de `typing_extensions`. + +Ele já estará instalado com o **FastAPI**. + +```Python hl_lines="1 4" +{!> ../../docs_src/python_types/tutorial013.py!} +``` + +//// + +O Python em si não faz nada com este `Annotated`. E para editores e outras ferramentas, o tipo ainda é `str`. + +Mas você pode utilizar este espaço dentro do `Annotated` para fornecer ao **FastAPI** metadata adicional sobre como você deseja que a sua aplicação se comporte. + +O importante aqui de se lembrar é que **o primeiro *type parameter*** que você informar ao `Annotated` é o **tipo de fato**. O resto é apenas metadado para outras ferramentas. + +Por hora, você precisa apenas saber que o `Annotated` existe, e que ele é Python padrão. 😎 + +Mais tarde você verá o quão **poderoso** ele pode ser. + +/// tip | Dica + +O fato de que isso é **Python padrão** significa que você ainda obtém a **melhor experiência de desenvolvedor possível** no seu editor, com as ferramentas que você utiliza para analisar e refatorar o seu código, etc. ✨ + +E também que o seu código será muito compatível com diversas outras ferramentas e bibliotecas Python. 🚀 + +/// + + +## Type hints no **FastAPI** O **FastAPI** aproveita esses type hints para fazer várias coisas. @@ -306,20 +579,20 @@ Com o **FastAPI**, você declara parâmetros com type hints e obtém: * **Suporte ao editor**. * **Verificações de tipo**. -... e **FastAPI** usa as mesmas declarações para: +... e o **FastAPI** usa as mesmas declarações para: -* **Definir requisitos**: dos parâmetros do caminho da solicitação, parâmetros da consulta, cabeçalhos, corpos, dependências, etc. +* **Definir requisitos**: dos parâmetros de rota, parâmetros da consulta, cabeçalhos, corpos, dependências, etc. * **Converter dados**: da solicitação para o tipo necessário. * **Validar dados**: provenientes de cada solicitação: - * A geração de **erros automáticos** retornou ao cliente quando os dados são inválidos. -* **Documente** a API usando OpenAPI: + * Gerando **erros automáticos** retornados ao cliente quando os dados são inválidos. +* **Documentar** a API usando OpenAPI: * que é usado pelas interfaces de usuário da documentação interativa automática. Tudo isso pode parecer abstrato. Não se preocupe. Você verá tudo isso em ação no [Tutorial - Guia do usuário](tutorial/index.md){.internal-link target=_blank}. O importante é que, usando tipos padrão de Python, em um único local (em vez de adicionar mais classes, decoradores, etc.), o **FastAPI** fará muito trabalho para você. -/// info | "Informação" +/// info | Informação Se você já passou por todo o tutorial e voltou para ver mais sobre os tipos, um bom recurso é a "cheat sheet" do `mypy` . diff --git a/docs/pt/docs/tutorial/body-updates.md b/docs/pt/docs/tutorial/body-updates.md new file mode 100644 index 000000000..1a0455c1c --- /dev/null +++ b/docs/pt/docs/tutorial/body-updates.md @@ -0,0 +1,204 @@ +# Corpo - Atualizações + +## Atualização de dados existentes com `PUT` + +Para atualizar um item, você pode usar a operação HTTP `PUT`. + +Você pode usar `jsonable_encoder` para converter os dados de entrada em dados que podem ser armazenados como JSON (por exemplo, com um banco de dados NoSQL). Por exemplo, convertendo `datetime` em `str`. + +//// tab | Python 3.10+ + +```Python hl_lines="28-33" +{!> ../../../docs_src/body_updates/tutorial001_py310.py!} +``` + +//// + +//// tab | Python 3.9+ + +```Python hl_lines="30-35" +{!> ../../../docs_src/body_updates/tutorial001_py39.py!} +``` + +//// + +//// tab | Python 3.8+ + +```Python hl_lines="30-35" +{!> ../../../docs_src/body_updates/tutorial001.py!} +``` + +//// + +`PUT` é usado para receber dados que devem substituir os dados existentes. + +### Aviso sobre a substituição + +Isso significa que, se você quiser atualizar o item `bar` usando `PUT` com um corpo contendo: + +```Python +{ + "name": "Barz", + "price": 3, + "description": None, +} +``` + +Como ele não inclui o atributo já armazenado `"tax": 20.2`, o modelo de entrada assumiria o valor padrão de `"tax": 10.5`. + +E os dados seriam salvos com esse "novo" `tax` de `10.5`. + +## Atualizações parciais com `PATCH` + +Você também pode usar a operação HTTP `PATCH` para *atualizar* parcialmente os dados. + +Isso significa que você pode enviar apenas os dados que deseja atualizar, deixando o restante intacto. + +/// note | Nota + +`PATCH` é menos comumente usado e conhecido do que `PUT`. + +E muitas equipes usam apenas `PUT`, mesmo para atualizações parciais. + +Você é **livre** para usá-los como preferir, **FastAPI** não impõe restrições. + +Mas este guia te dá uma ideia de como eles são destinados a serem usados. + +/// + +### Usando o parâmetro `exclude_unset` do Pydantic + +Se você quiser receber atualizações parciais, é muito útil usar o parâmetro `exclude_unset` no método `.model_dump()` do modelo do Pydantic. + +Como `item.model_dump(exclude_unset=True)`. + +/// info | Informação + +No Pydantic v1, o método que era chamado `.dict()` e foi depreciado (mas ainda suportado) no Pydantic v2. Agora, deve-se usar o método `.model_dump()`. + +Os exemplos aqui usam `.dict()` para compatibilidade com o Pydantic v1, mas você deve usar `.model_dump()` a partir do Pydantic v2. + +/// + +Isso gera um `dict` com apenas os dados definidos ao criar o modelo `item`, excluindo os valores padrão. + +Então, você pode usar isso para gerar um `dict` com apenas os dados definidos (enviados na solicitação), omitindo valores padrão: + +//// tab | Python 3.10+ + +```Python hl_lines="32" +{!> ../../../docs_src/body_updates/tutorial002_py310.py!} +``` + +//// + +//// tab | Python 3.9+ + +```Python hl_lines="34" +{!> ../../../docs_src/body_updates/tutorial002_py39.py!} +``` + +//// + +//// tab | Python 3.8+ + +```Python hl_lines="34" +{!> ../../../docs_src/body_updates/tutorial002.py!} +``` + +//// + +### Usando o parâmetro `update` do Pydantic + +Agora, você pode criar uma cópia do modelo existente usando `.model_copy()`, e passar o parâmetro `update` com um `dict` contendo os dados para atualizar. + +/// info | Informação + +No Pydantic v1, o método era chamado `.copy()`, ele foi depreciado (mas ainda suportado) no Pydantic v2, e renomeado para `.model_copy()`. + +Os exemplos aqui usam `.copy()` para compatibilidade com o Pydantic v1, mas você deve usar `.model_copy()` com o Pydantic v2. + +/// + +Como `stored_item_model.model_copy(update=update_data)`: + +//// tab | Python 3.10+ + +```Python hl_lines="33" +{!> ../../../docs_src/body_updates/tutorial002_py310.py!} +``` + +//// + +//// tab | Python 3.9+ + +```Python hl_lines="35" +{!> ../../../docs_src/body_updates/tutorial002_py39.py!} +``` + +//// + +//// tab | Python 3.8+ + +```Python hl_lines="35" +{!> ../../../docs_src/body_updates/tutorial002.py!} +``` + +//// + +### Recapitulando as atualizações parciais + +Resumindo, para aplicar atualizações parciais você pode: + +* (Opcionalmente) usar `PATCH` em vez de `PUT`. +* Recuperar os dados armazenados. +* Colocar esses dados em um modelo do Pydantic. +* Gerar um `dict` sem valores padrão a partir do modelo de entrada (usando `exclude_unset`). + * Dessa forma, você pode atualizar apenas os valores definidos pelo usuário, em vez de substituir os valores já armazenados com valores padrão em seu modelo. +* Criar uma cópia do modelo armazenado, atualizando seus atributos com as atualizações parciais recebidas (usando o parâmetro `update`). +* Converter o modelo copiado em algo que possa ser armazenado no seu banco de dados (por exemplo, usando o `jsonable_encoder`). + * Isso é comparável ao uso do método `.model_dump()`, mas garante (e converte) os valores para tipos de dados que possam ser convertidos em JSON, por exemplo, `datetime` para `str`. +* Salvar os dados no seu banco de dados. +* Retornar o modelo atualizado. + +//// tab | Python 3.10+ + +```Python hl_lines="28-35" +{!> ../../../docs_src/body_updates/tutorial002_py310.py!} +``` + +//// + +//// tab | Python 3.9+ + +```Python hl_lines="30-37" +{!> ../../../docs_src/body_updates/tutorial002_py39.py!} +``` + +//// + +//// tab | Python 3.8+ + +```Python hl_lines="30-37" +{!> ../../../docs_src/body_updates/tutorial002.py!} +``` + +//// + +/// tip | Dica + +Você pode realmente usar essa mesma técnica com uma operação HTTP `PUT`. + +Mas o exemplo aqui usa `PATCH` porque foi criado para esses casos de uso. + +/// + +/// note | Nota + +Observe que o modelo de entrada ainda é validado. + +Portanto, se você quiser receber atualizações parciais que possam omitir todos os atributos, precisará ter um modelo com todos os atributos marcados como opcionais (com valores padrão ou `None`). + +Para distinguir os modelos com todos os valores opcionais para **atualizações** e modelos com valores obrigatórios para **criação**, você pode usar as ideias descritas em [Modelos Adicionais](extra-models.md){.internal-link target=_blank}. + +/// diff --git a/docs/pt/docs/tutorial/query-param-models.md b/docs/pt/docs/tutorial/query-param-models.md new file mode 100644 index 000000000..854183fb4 --- /dev/null +++ b/docs/pt/docs/tutorial/query-param-models.md @@ -0,0 +1,197 @@ +# Modelos de Parâmetros de Consulta + +Se você possui um grupo de **parâmetros de consultas** que são relacionados, você pode criar um **modelo Pydantic** para declará-los. + +Isso permitiria que você **reutilizasse o modelo** em **diversos lugares**, e também declarasse validações e metadados de todos os parâmetros de uma única vez. 😎 + +/// note | Nota + +Isso é suportado desde o FastAPI versão `0.115.0`. 🤓 + +/// + +## Parâmetros de Consulta com um Modelo Pydantic + +Declare os **parâmetros de consulta** que você precisa em um **modelo Pydantic**, e então declare o parâmetro como `Query`: + +//// tab | Python 3.10+ + +```Python hl_lines="9-13 17" +{!> ../../docs_src/query_param_models/tutorial001_an_py310.py!} +``` + +//// + +//// tab | Python 3.9+ + +```Python hl_lines="8-12 16" +{!> ../../docs_src/query_param_models/tutorial001_an_py39.py!} +``` + +//// + +//// tab | Python 3.8+ + +```Python hl_lines="10-14 18" +{!> ../../docs_src/query_param_models/tutorial001_an.py!} +``` + +//// + +//// tab | Python 3.10+ non-Annotated + +/// tip | Dica + +Prefira utilizar a versão `Annotated` se possível. + +/// + +```Python hl_lines="9-13 17" +{!> ../../docs_src/query_param_models/tutorial001_py310.py!} +``` + +//// + +//// tab | Python 3.9+ non-Annotated + +/// tip | Dica + +Prefira utilizar a versão `Annotated` se possível. + +/// + +```Python hl_lines="8-12 16" +{!> ../../docs_src/query_param_models/tutorial001_py39.py!} +``` + +//// + +//// tab | Python 3.8+ non-Annotated + +/// tip | Dica + +Prefira utilizar a versão `Annotated` se possível. + +/// + +```Python hl_lines="9-13 17" +{!> ../../docs_src/query_param_models/tutorial001_py310.py!} +``` + +//// + +O **FastAPI** **extrairá** os dados para **cada campo** dos **parâmetros de consulta** presentes na requisição, e fornecerá o modelo Pydantic que você definiu. + + +## Verifique os Documentos + +Você pode ver os parâmetros de consulta nos documentos de IU em `/docs`: + +
+ +
+ +## Restrinja Parâmetros de Consulta Extras + +Em alguns casos especiais (provavelmente não muito comuns), você queira **restrinjir** os parâmetros de consulta que deseja receber. + +Você pode usar a configuração do modelo Pydantic para `forbid` (proibir) qualquer campo `extra`: + +//// tab | Python 3.10+ + +```Python hl_lines="10" +{!> ../../docs_src/query_param_models/tutorial002_an_py310.py!} +``` + +//// + +//// tab | Python 3.9+ + +```Python hl_lines="9" +{!> ../../docs_src/query_param_models/tutorial002_an_py39.py!} +``` + +//// + +//// tab | Python 3.8+ + +```Python hl_lines="11" +{!> ../../docs_src/query_param_models/tutorial002_an.py!} +``` + +//// + +//// tab | Python 3.10+ non-Annotated + +/// tip | Dica + +Prefira utilizar a versão `Annotated` se possível. + +/// + +```Python hl_lines="10" +{!> ../../docs_src/query_param_models/tutorial002_py310.py!} +``` + +//// + +//// tab | Python 3.9+ non-Annotated + +/// tip | Dica + +Prefira utilizar a versão `Annotated` se possível. + +/// + +```Python hl_lines="9" +{!> ../../docs_src/query_param_models/tutorial002_py39.py!} +``` + +//// + +//// tab | Python 3.8+ non-Annotated + +/// tip | Dica + +Prefira utilizar a versão `Annotated` se possível. + +/// + +```Python hl_lines="11" +{!> ../../docs_src/query_param_models/tutorial002.py!} +``` + +//// + +Caso um cliente tente enviar alguns dados **extras** nos **parâmetros de consulta**, eles receberão um retorno de **erro**. + +Por exemplo, se o cliente tentar enviar um parâmetro de consulta `tool` com o valor `plumbus`, como: + +```http +https://example.com/items/?limit=10&tool=plumbus +``` + +Eles receberão um retorno de **erro** informando-os que o parâmentro de consulta `tool` não é permitido: + +```json +{ + "detail": [ + { + "type": "extra_forbidden", + "loc": ["query", "tool"], + "msg": "Extra inputs are not permitted", + "input": "plumbus" + } + ] +} +``` + +## Resumo + +Você pode utilizar **modelos Pydantic** para declarar **parâmetros de consulta** no **FastAPI**. 😎 + +/// tip | Dica + +Alerta de spoiler: você também pode utilizar modelos Pydantic para declarar cookies e cabeçalhos, mas você irá ler sobre isso mais a frente no tutorial. 🤫 + +/// diff --git a/docs/ru/docs/environment-variables.md b/docs/ru/docs/environment-variables.md new file mode 100644 index 000000000..a6c7b0c77 --- /dev/null +++ b/docs/ru/docs/environment-variables.md @@ -0,0 +1,297 @@ +# Переменные окружения + +/// tip + +Если вы уже знаете, что такое «переменные окружения» и как их использовать, можете пропустить это. + +/// + +Переменная окружения (также известная как «**env var**») - это переменная, которая живет **вне** кода Python, в **операционной системе**, и может быть прочитана вашим кодом Python (или другими программами). + +Переменные окружения могут быть полезны для работы с **настройками** приложений, как часть **установки** Python и т.д. + +## Создание и использование переменных окружения + +Можно **создавать** и использовать переменные окружения в **оболочке (терминале)**, не прибегая к помощи Python: + +//// tab | Linux, macOS, Windows Bash + +
+ +```console +// Вы можете создать переменную окружения MY_NAME с помощью +$ export MY_NAME="Wade Wilson" + +// Затем её можно использовать в других программах, например +$ echo "Hello $MY_NAME" + +Hello Wade Wilson +``` + +
+ +//// + +//// tab | Windows PowerShell + +
+ +```console +// Создайте переменную окружения MY_NAME +$ $Env:MY_NAME = "Wade Wilson" + +// Используйте её с другими программами, например +$ echo "Hello $Env:MY_NAME" + +Hello Wade Wilson +``` + +
+ +//// + +## Чтение переменных окружения в python + +Так же существует возможность создания переменных окружения **вне** Python, в терминале (или любым другим способом), а затем **чтения их в Python**. + +Например, у вас есть файл `main.py`: + +```Python hl_lines="3" +import os + +name = os.getenv("MY_NAME", "World") +print(f"Hello {name} from Python") +``` + +/// tip + +Второй аргумент `os.getenv()` - это возвращаемое по умолчанию значение. + +Если значение не указано, то по умолчанию оно равно `None`. В данном случае мы указываем `«World»` в качестве значения по умолчанию. +/// + +Затем можно запустить эту программу на Python: + +//// tab | Linux, macOS, Windows Bash + +
+ +```console +// Здесь мы еще не устанавливаем переменную окружения +$ python main.py + +// Поскольку мы не задали переменную окружения, мы получим значение по умолчанию + +Hello World from Python + +// Но если мы сначала создадим переменную окружения +$ export MY_NAME="Wade Wilson" + +// А затем снова запустим программу +$ python main.py + +// Теперь она прочитает переменную окружения + +Hello Wade Wilson from Python +``` + +
+ +//// + +//// tab | Windows PowerShell + +
+ +```console +// Здесь мы еще не устанавливаем переменную окружения +$ python main.py + +// Поскольку мы не задали переменную окружения, мы получим значение по умолчанию + +Hello World from Python + +// Но если мы сначала создадим переменную окружения +$ $Env:MY_NAME = "Wade Wilson" + +// А затем снова запустим программу +$ python main.py + +// Теперь она может прочитать переменную окружения + +Hello Wade Wilson from Python +``` + +
+ +//// + +Поскольку переменные окружения могут быть установлены вне кода, но могут быть прочитаны кодом, и их не нужно хранить (фиксировать в `git`) вместе с остальными файлами, их принято использовать для конфигураций или **настроек**. + +Вы также можете создать переменную окружения только для **конкретного вызова программы**, которая будет доступна только для этой программы и только на время ее выполнения. + +Для этого создайте её непосредственно перед самой программой, в той же строке: + +
+ +```console +// Создайте переменную окружения MY_NAME в строке для этого вызова программы +$ MY_NAME="Wade Wilson" python main.py + +// Теперь она может прочитать переменную окружения + +Hello Wade Wilson from Python + +// После этого переменная окружения больше не существует +$ python main.py + +Hello World from Python +``` + +
+ +/// tip + +Подробнее об этом можно прочитать на сайте The Twelve-Factor App: Config. + +/// + +## Типизация и Валидация + +Эти переменные окружения могут работать только с **текстовыми строками**, поскольку они являются внешними по отношению к Python и должны быть совместимы с другими программами и остальной системой (и даже с различными операционными системами, такими как Linux, Windows, macOS). + +Это означает, что **любое значение**, считанное в Python из переменной окружения, **будет `str`**, и любое преобразование к другому типу или любая проверка должны быть выполнены в коде. + +Подробнее об использовании переменных окружения для работы с **настройками приложения** вы узнаете в [Расширенное руководство пользователя - Настройки и переменные среды](./advanced/settings.md){.internal-link target=_blank}. + +## Переменная окружения `PATH` + +Существует **специальная** переменная окружения **`PATH`**, которая используется операционными системами (Linux, macOS, Windows) для поиска программ для запуска. + +Значение переменной `PATH` - это длинная строка, состоящая из каталогов, разделенных двоеточием `:` в Linux и macOS, и точкой с запятой `;` в Windows. + +Например, переменная окружения `PATH` может выглядеть следующим образом: + +//// tab | Linux, macOS + +```plaintext +/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin +``` + +Это означает, что система должна искать программы в каталогах: + +* `/usr/local/bin` +* `/usr/bin` +* `/bin` +* `/usr/sbin` +* `/sbin` + +//// + +//// tab | Windows + +```plaintext +C:\Program Files\Python312\Scripts;C:\Program Files\Python312;C:\Windows\System32 +``` + +Это означает, что система должна искать программы в каталогах: + +* `C:\Program Files\Python312\Scripts` +* `C:\Program Files\Python312` +* `C:\Windows\System32` + +//// + +Когда вы вводите **команду** в терминале, операционная система **ищет** программу в **каждой из тех директорий**, которые перечислены в переменной окружения `PATH`. + +Например, когда вы вводите `python` в терминале, операционная система ищет программу под названием `python` в **первой директории** в этом списке. + +Если она ее находит, то **использует ее**. В противном случае она продолжает искать в **других каталогах**. + +### Установка Python и обновление `PATH` + +При установке Python вас могут спросить, нужно ли обновить переменную окружения `PATH`. + +//// tab | Linux, macOS + +Допустим, вы устанавливаете Python, и он оказывается в каталоге `/opt/custompython/bin`. + +Если вы скажете «да», чтобы обновить переменную окружения `PATH`, то программа установки добавит `/opt/custompython/bin` в переменную окружения `PATH`. + +Это может выглядеть следующим образом: + +```plaintext +/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/custompython/bin +``` + +Таким образом, когда вы набираете `python` в терминале, система найдет программу Python в `/opt/custompython/bin` (последний каталог) и использует ее. + +//// + +//// tab | Windows + +Допустим, вы устанавливаете Python, и он оказывается в каталоге `C:\opt\custompython\bin`. + +Если вы согласитесь обновить переменную окружения `PATH`, то программа установки добавит `C:\opt\custompython\bin` в переменную окружения `PATH`. + +```plaintext +C:\Program Files\Python312\Scripts;C:\Program Files\Python312;C:\Windows\System32;C:\opt\custompython\bin +``` + +Таким образом, когда вы набираете `python` в терминале, система найдет программу Python в `C:\opt\custompython\bin` (последний каталог) и использует ее. + +//// + +Итак, если вы напечатаете: + +
+ +```console +$ python +``` + +
+ +//// tab | Linux, macOS + +Система **найдет** программу `python` в `/opt/custompython/bin` и запустит ее. + +Это примерно эквивалентно набору текста: + +
+ +```console +$ /opt/custompython/bin/python +``` + +
+ +//// + +//// tab | Windows + +Система **найдет** программу `python` в каталоге `C:\opt\custompython\bin\python` и запустит ее. + +Это примерно эквивалентно набору текста: + +
+ +```console +$ C:\opt\custompython\bin\python +``` + +
+ +//// + +Эта информация будет полезна при изучении [Виртуальных окружений](virtual-environments.md){.internal-link target=_blank}. + +## Вывод + +Благодаря этому вы должны иметь базовое представление о том, что такое **переменные окружения** и как использовать их в Python. + +Подробнее о них вы также можете прочитать в статье о переменных окружения на википедии. + +Во многих случаях не всегда очевидно, как переменные окружения могут быть полезны и применимы. Но они постоянно появляются в различных сценариях разработки, поэтому знать о них полезно. + +Например, эта информация понадобится вам в следующем разделе, посвященном [Виртуальным окружениям](virtual-environments.md). diff --git a/docs/zh-hant/docs/about/index.md b/docs/zh-hant/docs/about/index.md new file mode 100644 index 000000000..5dcee68f2 --- /dev/null +++ b/docs/zh-hant/docs/about/index.md @@ -0,0 +1,3 @@ +# 關於 FastAPI + +關於 FastAPI、其設計、靈感來源等更多資訊。 🤓 diff --git a/docs/zh-hant/docs/deployment/cloud.md b/docs/zh-hant/docs/deployment/cloud.md new file mode 100644 index 000000000..5d645b6c2 --- /dev/null +++ b/docs/zh-hant/docs/deployment/cloud.md @@ -0,0 +1,17 @@ +# 在雲端部署 FastAPI + +你幾乎可以使用 **任何雲端供應商** 來部署你的 FastAPI 應用程式。 + +在大多數情況下,主要的雲端供應商都有部署 FastAPI 的指南。 + +## 雲端供應商 - 贊助商 + +一些雲端供應商 ✨ [**贊助 FastAPI**](../help-fastapi.md#sponsor-the-author){.internal-link target=_blank} ✨,這確保了 FastAPI 及其 **生態系統** 持續健康地 **發展**。 + +這也展現了他們對 FastAPI 和其 **社群**(包括你)的真正承諾,他們不僅希望為你提供 **優質的服務**,還希望確保你擁有一個 **良好且健康的框架**:FastAPI。🙇 + +你可能會想嘗試他們的服務,以下有他們的指南: + +* Platform.sh +* Porter +* Coherence diff --git a/docs/zh-hant/docs/resources/index.md b/docs/zh-hant/docs/resources/index.md new file mode 100644 index 000000000..f4c70a3a0 --- /dev/null +++ b/docs/zh-hant/docs/resources/index.md @@ -0,0 +1,3 @@ +# 資源 + +額外的資源、外部連結、文章等。 ✈️ diff --git a/scripts/label_approved.py b/scripts/label_approved.py new file mode 100644 index 000000000..271444504 --- /dev/null +++ b/scripts/label_approved.py @@ -0,0 +1,60 @@ +import logging +from typing import Literal + +from github import Github +from github.PullRequestReview import PullRequestReview +from pydantic import BaseModel, SecretStr +from pydantic_settings import BaseSettings + + +class LabelSettings(BaseModel): + await_label: str | None = None + number: int + + +default_config = {"approved-2": LabelSettings(await_label="awaiting-review", number=2)} + + +class Settings(BaseSettings): + github_repository: str + token: SecretStr + debug: bool | None = False + config: dict[str, LabelSettings] | Literal[""] = default_config + + +settings = Settings() +if settings.debug: + logging.basicConfig(level=logging.DEBUG) +else: + logging.basicConfig(level=logging.INFO) +logging.debug(f"Using config: {settings.json()}") +g = Github(settings.token.get_secret_value()) +repo = g.get_repo(settings.github_repository) +for pr in repo.get_pulls(state="open"): + logging.info(f"Checking PR: #{pr.number}") + pr_labels = list(pr.get_labels()) + pr_label_by_name = {label.name: label for label in pr_labels} + reviews = list(pr.get_reviews()) + review_by_user: dict[str, PullRequestReview] = {} + for review in reviews: + if review.user.login in review_by_user: + stored_review = review_by_user[review.user.login] + if review.submitted_at >= stored_review.submitted_at: + review_by_user[review.user.login] = review + else: + review_by_user[review.user.login] = review + approved_reviews = [ + review for review in review_by_user.values() if review.state == "APPROVED" + ] + config = settings.config or default_config + for approved_label, conf in config.items(): + logging.debug(f"Processing config: {conf.json()}") + if conf.await_label is None or (conf.await_label in pr_label_by_name): + logging.debug(f"Processable PR: {pr.number}") + if len(approved_reviews) >= conf.number: + logging.info(f"Adding label to PR: {pr.number}") + pr.add_to_labels(approved_label) + if conf.await_label: + logging.info(f"Removing label from PR: {pr.number}") + pr.remove_from_labels(conf.await_label) +logging.info("Finished")