mirror of https://github.com/tiangolo/fastapi.git
Merge branch 'master' into jsonable_encoder
This commit is contained in:
commit
7c2a56dad5
|
|
@ -13,8 +13,18 @@ hide:
|
|||
|
||||
### Translations
|
||||
|
||||
* 🌐 Update translations for pt (update-all). PR [#14912](https://github.com/fastapi/fastapi/pull/14912) by [@YuriiMotov](https://github.com/YuriiMotov).
|
||||
* 🌐 Update translations for es (update-all and add-missing). PR [#14911](https://github.com/fastapi/fastapi/pull/14911) by [@YuriiMotov](https://github.com/YuriiMotov).
|
||||
* 🌐 Update translations for zh (update-all). PR [#14917](https://github.com/fastapi/fastapi/pull/14917) by [@YuriiMotov](https://github.com/YuriiMotov).
|
||||
* 🌐 Update translations for uk (update-all). PR [#14914](https://github.com/fastapi/fastapi/pull/14914) by [@YuriiMotov](https://github.com/YuriiMotov).
|
||||
* 🌐 Update translations for tr (update-all). PR [#14913](https://github.com/fastapi/fastapi/pull/14913) by [@YuriiMotov](https://github.com/YuriiMotov).
|
||||
* 🌐 Update translations for ru (update-outdated). PR [#14909](https://github.com/fastapi/fastapi/pull/14909) by [@YuriiMotov](https://github.com/YuriiMotov).
|
||||
|
||||
### Internal
|
||||
|
||||
* ⬆ Bump cryptography from 46.0.4 to 46.0.5. PR [#14892](https://github.com/fastapi/fastapi/pull/14892) by [@dependabot[bot]](https://github.com/apps/dependabot).
|
||||
* ⬆ Bump pillow from 12.1.0 to 12.1.1. PR [#14899](https://github.com/fastapi/fastapi/pull/14899) by [@dependabot[bot]](https://github.com/apps/dependabot).
|
||||
|
||||
## 0.129.0
|
||||
|
||||
### Breaking Changes
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ Cada uno de esos `dict`s de response puede tener una clave `model`, conteniendo
|
|||
|
||||
Por ejemplo, para declarar otro response con un código de estado `404` y un modelo Pydantic `Message`, puedes escribir:
|
||||
|
||||
{* ../../docs_src/additional_responses/tutorial001_py39.py hl[18,22] *}
|
||||
{* ../../docs_src/additional_responses/tutorial001_py310.py hl[18,22] *}
|
||||
|
||||
/// note | Nota
|
||||
|
||||
|
|
@ -203,7 +203,7 @@ Por ejemplo, puedes declarar un response con un código de estado `404` que usa
|
|||
|
||||
Y un response con un código de estado `200` que usa tu `response_model`, pero incluye un `example` personalizado:
|
||||
|
||||
{* ../../docs_src/additional_responses/tutorial003_py39.py hl[20:31] *}
|
||||
{* ../../docs_src/additional_responses/tutorial003_py310.py hl[20:31] *}
|
||||
|
||||
Todo se combinará e incluirá en tu OpenAPI, y se mostrará en la documentación de la API:
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ No la clase en sí (que ya es un callable), sino una instance de esa clase.
|
|||
|
||||
Para hacer eso, declaramos un método `__call__`:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial011_an_py39.py hl[12] *}
|
||||
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[12] *}
|
||||
|
||||
En este caso, este `__call__` es lo que **FastAPI** usará para comprobar parámetros adicionales y sub-dependencias, y es lo que llamará para pasar un valor al parámetro en tu *path operation function* más adelante.
|
||||
|
||||
|
|
@ -26,7 +26,7 @@ En este caso, este `__call__` es lo que **FastAPI** usará para comprobar parám
|
|||
|
||||
Y ahora, podemos usar `__init__` para declarar los parámetros de la instance que podemos usar para "parametrizar" la dependencia:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial011_an_py39.py hl[9] *}
|
||||
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[9] *}
|
||||
|
||||
En este caso, **FastAPI** nunca tocará ni se preocupará por `__init__`, lo usaremos directamente en nuestro código.
|
||||
|
||||
|
|
@ -34,7 +34,7 @@ En este caso, **FastAPI** nunca tocará ni se preocupará por `__init__`, lo usa
|
|||
|
||||
Podríamos crear una instance de esta clase con:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial011_an_py39.py hl[18] *}
|
||||
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[18] *}
|
||||
|
||||
Y de esa manera podemos "parametrizar" nuestra dependencia, que ahora tiene `"bar"` dentro de ella, como el atributo `checker.fixed_content`.
|
||||
|
||||
|
|
@ -50,7 +50,7 @@ checker(q="somequery")
|
|||
|
||||
...y pasará lo que eso retorne como el valor de la dependencia en nuestra *path operation function* como el parámetro `fixed_content_included`:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial011_an_py39.py hl[22] *}
|
||||
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[22] *}
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,61 @@
|
|||
# Tipos avanzados de Python { #advanced-python-types }
|
||||
|
||||
Aquí tienes algunas ideas adicionales que podrían ser útiles al trabajar con tipos de Python.
|
||||
|
||||
## Usar `Union` u `Optional` { #using-union-or-optional }
|
||||
|
||||
Si por alguna razón tu código no puede usar `|`, por ejemplo si no está en una anotación de tipos sino en algo como `response_model=`, en lugar de usar la barra vertical (`|`) puedes usar `Union` de `typing`.
|
||||
|
||||
Por ejemplo, podrías declarar que algo podría ser un `str` o `None`:
|
||||
|
||||
```python
|
||||
from typing import Union
|
||||
|
||||
|
||||
def say_hi(name: Union[str, None]):
|
||||
print(f"Hi {name}!")
|
||||
```
|
||||
|
||||
`typing` también tiene un atajo para declarar que algo podría ser `None`, con `Optional`.
|
||||
|
||||
Aquí va un Consejo desde mi punto de vista muy subjetivo:
|
||||
|
||||
* 🚨 Evita usar `Optional[SomeType]`
|
||||
* En su lugar ✨ **usa `Union[SomeType, None]`** ✨.
|
||||
|
||||
Ambas son equivalentes y por debajo son lo mismo, pero recomendaría `Union` en lugar de `Optional` porque la palabra "**optional**" parecería implicar que el valor es opcional, y en realidad significa "puede ser `None`", incluso si no es opcional y sigue siendo requerido.
|
||||
|
||||
Creo que `Union[SomeType, None]` es más explícito respecto a lo que significa.
|
||||
|
||||
Se trata solo de palabras y nombres. Pero esas palabras pueden afectar cómo tú y tu equipo piensan sobre el código.
|
||||
|
||||
Como ejemplo, tomemos esta función:
|
||||
|
||||
```python
|
||||
from typing import Optional
|
||||
|
||||
|
||||
def say_hi(name: Optional[str]):
|
||||
print(f"Hey {name}!")
|
||||
```
|
||||
|
||||
El parámetro `name` está definido como `Optional[str]`, pero **no es opcional**, no puedes llamar a la función sin el parámetro:
|
||||
|
||||
```Python
|
||||
say_hi() # ¡Oh, no, esto lanza un error! 😱
|
||||
```
|
||||
|
||||
El parámetro `name` **sigue siendo requerido** (no es *opcional*) porque no tiene un valor por defecto. Aun así, `name` acepta `None` como valor:
|
||||
|
||||
```Python
|
||||
say_hi(name=None) # Esto funciona, None es válido 🎉
|
||||
```
|
||||
|
||||
La buena noticia es que, en la mayoría de los casos, podrás simplemente usar `|` para definir uniones de tipos:
|
||||
|
||||
```python
|
||||
def say_hi(name: str | None):
|
||||
print(f"Hey {name}!")
|
||||
```
|
||||
|
||||
Así que, normalmente no tienes que preocuparte por nombres como `Optional` y `Union`. 😎
|
||||
|
|
@ -32,11 +32,11 @@ Para un ejemplo simple, consideremos una estructura de archivos similar a la des
|
|||
|
||||
El archivo `main.py` tendría:
|
||||
|
||||
{* ../../docs_src/async_tests/app_a_py39/main.py *}
|
||||
{* ../../docs_src/async_tests/app_a_py310/main.py *}
|
||||
|
||||
El archivo `test_main.py` tendría los tests para `main.py`, podría verse así ahora:
|
||||
|
||||
{* ../../docs_src/async_tests/app_a_py39/test_main.py *}
|
||||
{* ../../docs_src/async_tests/app_a_py310/test_main.py *}
|
||||
|
||||
## Ejecútalo { #run-it }
|
||||
|
||||
|
|
@ -56,7 +56,7 @@ $ pytest
|
|||
|
||||
El marcador `@pytest.mark.anyio` le dice a pytest que esta función de test debe ser llamada asíncronamente:
|
||||
|
||||
{* ../../docs_src/async_tests/app_a_py39/test_main.py hl[7] *}
|
||||
{* ../../docs_src/async_tests/app_a_py310/test_main.py hl[7] *}
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
|
|
@ -66,7 +66,7 @@ Nota que la función de test ahora es `async def` en lugar de solo `def` como an
|
|||
|
||||
Luego podemos crear un `AsyncClient` con la app y enviar requests asíncronos a ella, usando `await`.
|
||||
|
||||
{* ../../docs_src/async_tests/app_a_py39/test_main.py hl[9:12] *}
|
||||
{* ../../docs_src/async_tests/app_a_py310/test_main.py hl[9:12] *}
|
||||
|
||||
Esto es equivalente a:
|
||||
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ $ fastapi run --forwarded-allow-ips="*"
|
|||
|
||||
Por ejemplo, digamos que defines una *path operation* `/items/`:
|
||||
|
||||
{* ../../docs_src/behind_a_proxy/tutorial001_01_py39.py hl[6] *}
|
||||
{* ../../docs_src/behind_a_proxy/tutorial001_01_py310.py hl[6] *}
|
||||
|
||||
Si el cliente intenta ir a `/items`, por defecto, sería redirigido a `/items/`.
|
||||
|
||||
|
|
@ -115,7 +115,7 @@ En este caso, el path original `/app` realmente sería servido en `/api/v1/app`.
|
|||
|
||||
Aunque todo tu código esté escrito asumiendo que solo existe `/app`.
|
||||
|
||||
{* ../../docs_src/behind_a_proxy/tutorial001_py39.py hl[6] *}
|
||||
{* ../../docs_src/behind_a_proxy/tutorial001_py310.py hl[6] *}
|
||||
|
||||
Y el proxy estaría **"eliminando"** el **prefijo del path** sobre la marcha antes de transmitir el request al servidor de aplicaciones (probablemente Uvicorn a través de FastAPI CLI), manteniendo a tu aplicación convencida de que está siendo servida en `/app`, así que no tienes que actualizar todo tu código para incluir el prefijo `/api/v1`.
|
||||
|
||||
|
|
@ -193,7 +193,7 @@ Puedes obtener el `root_path` actual utilizado por tu aplicación para cada requ
|
|||
|
||||
Aquí lo estamos incluyendo en el mensaje solo con fines de demostración.
|
||||
|
||||
{* ../../docs_src/behind_a_proxy/tutorial001_py39.py hl[8] *}
|
||||
{* ../../docs_src/behind_a_proxy/tutorial001_py310.py hl[8] *}
|
||||
|
||||
Luego, si inicias Uvicorn con:
|
||||
|
||||
|
|
@ -220,7 +220,7 @@ El response sería algo como:
|
|||
|
||||
Alternativamente, si no tienes una forma de proporcionar una opción de línea de comandos como `--root-path` o su equivalente, puedes configurar el parámetro `root_path` al crear tu app de FastAPI:
|
||||
|
||||
{* ../../docs_src/behind_a_proxy/tutorial002_py39.py hl[3] *}
|
||||
{* ../../docs_src/behind_a_proxy/tutorial002_py310.py hl[3] *}
|
||||
|
||||
Pasar el `root_path` a `FastAPI` sería el equivalente a pasar la opción de línea de comandos `--root-path` a Uvicorn o Hypercorn.
|
||||
|
||||
|
|
@ -400,7 +400,7 @@ Si pasas una lista personalizada de `servers` y hay un `root_path` (porque tu AP
|
|||
|
||||
Por ejemplo:
|
||||
|
||||
{* ../../docs_src/behind_a_proxy/tutorial003_py39.py hl[4:7] *}
|
||||
{* ../../docs_src/behind_a_proxy/tutorial003_py310.py hl[4:7] *}
|
||||
|
||||
Generará un esquema de OpenAPI como:
|
||||
|
||||
|
|
@ -455,7 +455,7 @@ Si no especificas el parámetro `servers` y `root_path` es igual a `/`, la propi
|
|||
|
||||
Si no quieres que **FastAPI** incluya un server automático usando el `root_path`, puedes usar el parámetro `root_path_in_servers=False`:
|
||||
|
||||
{* ../../docs_src/behind_a_proxy/tutorial004_py39.py hl[9] *}
|
||||
{* ../../docs_src/behind_a_proxy/tutorial004_py310.py hl[9] *}
|
||||
|
||||
y entonces no lo incluirá en el esquema de OpenAPI.
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ Esto se debe a que, por defecto, FastAPI inspeccionará cada elemento dentro y s
|
|||
|
||||
Pero si estás seguro de que el contenido que estás devolviendo es **serializable con JSON**, puedes pasarlo directamente a la clase de response y evitar la sobrecarga extra que FastAPI tendría al pasar tu contenido de retorno a través de `jsonable_encoder` antes de pasarlo a la clase de response.
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial001b_py39.py hl[2,7] *}
|
||||
{* ../../docs_src/custom_response/tutorial001b_py310.py hl[2,7] *}
|
||||
|
||||
/// info | Información
|
||||
|
||||
|
|
@ -55,7 +55,7 @@ Para devolver un response con HTML directamente desde **FastAPI**, usa `HTMLResp
|
|||
* Importa `HTMLResponse`.
|
||||
* Pasa `HTMLResponse` como parámetro `response_class` de tu *path operation decorator*.
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial002_py39.py hl[2,7] *}
|
||||
{* ../../docs_src/custom_response/tutorial002_py310.py hl[2,7] *}
|
||||
|
||||
/// info | Información
|
||||
|
||||
|
|
@ -73,7 +73,7 @@ Como se ve en [Devolver una Response directamente](response-directly.md){.intern
|
|||
|
||||
El mismo ejemplo de arriba, devolviendo una `HTMLResponse`, podría verse así:
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial003_py39.py hl[2,7,19] *}
|
||||
{* ../../docs_src/custom_response/tutorial003_py310.py hl[2,7,19] *}
|
||||
|
||||
/// warning | Advertencia
|
||||
|
||||
|
|
@ -97,7 +97,7 @@ El `response_class` solo se usará para documentar el OpenAPI *path operation*,
|
|||
|
||||
Por ejemplo, podría ser algo así:
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial004_py39.py hl[7,21,23] *}
|
||||
{* ../../docs_src/custom_response/tutorial004_py310.py hl[7,21,23] *}
|
||||
|
||||
En este ejemplo, la función `generate_html_response()` ya genera y devuelve una `Response` en lugar de devolver el HTML en un `str`.
|
||||
|
||||
|
|
@ -136,7 +136,7 @@ Acepta los siguientes parámetros:
|
|||
|
||||
FastAPI (de hecho Starlette) incluirá automáticamente un header Content-Length. También incluirá un header Content-Type, basado en el `media_type` y añadiendo un conjunto de caracteres para tipos de texto.
|
||||
|
||||
{* ../../docs_src/response_directly/tutorial002_py39.py hl[1,18] *}
|
||||
{* ../../docs_src/response_directly/tutorial002_py310.py hl[1,18] *}
|
||||
|
||||
### `HTMLResponse` { #htmlresponse }
|
||||
|
||||
|
|
@ -146,7 +146,7 @@ Toma algún texto o bytes y devuelve un response HTML, como leíste arriba.
|
|||
|
||||
Toma algún texto o bytes y devuelve un response de texto plano.
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial005_py39.py hl[2,7,9] *}
|
||||
{* ../../docs_src/custom_response/tutorial005_py310.py hl[2,7,9] *}
|
||||
|
||||
### `JSONResponse` { #jsonresponse }
|
||||
|
||||
|
|
@ -180,7 +180,7 @@ Esto requiere instalar `ujson`, por ejemplo, con `pip install ujson`.
|
|||
|
||||
///
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial001_py39.py hl[2,7] *}
|
||||
{* ../../docs_src/custom_response/tutorial001_py310.py hl[2,7] *}
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
|
|
@ -194,15 +194,15 @@ Devuelve una redirección HTTP. Usa un código de estado 307 (Redirección Tempo
|
|||
|
||||
Puedes devolver un `RedirectResponse` directamente:
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial006_py39.py hl[2,9] *}
|
||||
{* ../../docs_src/custom_response/tutorial006_py310.py hl[2,9] *}
|
||||
|
||||
---
|
||||
|
||||
O puedes usarlo en el parámetro `response_class`:
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial006b_py39.py hl[2,7,9] *}
|
||||
{* ../../docs_src/custom_response/tutorial006b_py310.py hl[2,7,9] *}
|
||||
|
||||
Si haces eso, entonces puedes devolver la URL directamente desde tu *path operation function*.
|
||||
Si haces eso, entonces puedes devolver la URL directamente desde tu *path operation* function.
|
||||
|
||||
En este caso, el `status_code` utilizado será el por defecto para `RedirectResponse`, que es `307`.
|
||||
|
||||
|
|
@ -210,13 +210,13 @@ En este caso, el `status_code` utilizado será el por defecto para `RedirectResp
|
|||
|
||||
También puedes usar el parámetro `status_code` combinado con el parámetro `response_class`:
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial006c_py39.py hl[2,7,9] *}
|
||||
{* ../../docs_src/custom_response/tutorial006c_py310.py hl[2,7,9] *}
|
||||
|
||||
### `StreamingResponse` { #streamingresponse }
|
||||
|
||||
Toma un generador `async` o un generador/iterador normal y transmite el cuerpo del response.
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial007_py39.py hl[2,14] *}
|
||||
{* ../../docs_src/custom_response/tutorial007_py310.py hl[2,14] *}
|
||||
|
||||
#### Usando `StreamingResponse` con objetos similares a archivos { #using-streamingresponse-with-file-like-objects }
|
||||
|
||||
|
|
@ -226,7 +226,7 @@ De esa manera, no tienes que leerlo todo primero en memoria, y puedes pasar esa
|
|||
|
||||
Esto incluye muchos paquetes para interactuar con almacenamiento en la nube, procesamiento de video y otros.
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial008_py39.py hl[2,10:12,14] *}
|
||||
{* ../../docs_src/custom_response/tutorial008_py310.py hl[2,10:12,14] *}
|
||||
|
||||
1. Esta es la función generadora. Es una "función generadora" porque contiene declaraciones `yield` dentro.
|
||||
2. Al usar un bloque `with`, nos aseguramos de que el objeto similar a un archivo se cierre después de que la función generadora termine. Así, después de que termina de enviar el response.
|
||||
|
|
@ -255,11 +255,11 @@ Toma un conjunto diferente de argumentos para crear un instance que los otros ti
|
|||
|
||||
Los responses de archivos incluirán los headers apropiados `Content-Length`, `Last-Modified` y `ETag`.
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial009_py39.py hl[2,10] *}
|
||||
{* ../../docs_src/custom_response/tutorial009_py310.py hl[2,10] *}
|
||||
|
||||
También puedes usar el parámetro `response_class`:
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial009b_py39.py hl[2,8,10] *}
|
||||
{* ../../docs_src/custom_response/tutorial009b_py310.py hl[2,8,10] *}
|
||||
|
||||
En este caso, puedes devolver la path del archivo directamente desde tu *path operation* function.
|
||||
|
||||
|
|
@ -273,7 +273,7 @@ Digamos que quieres que devuelva JSON con sangría y formato, por lo que quieres
|
|||
|
||||
Podrías crear un `CustomORJSONResponse`. Lo principal que tienes que hacer es crear un método `Response.render(content)` que devuelva el contenido como `bytes`:
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial009c_py39.py hl[9:14,17] *}
|
||||
{* ../../docs_src/custom_response/tutorial009c_py310.py hl[9:14,17] *}
|
||||
|
||||
Ahora en lugar de devolver:
|
||||
|
||||
|
|
@ -299,7 +299,7 @@ El parámetro que define esto es `default_response_class`.
|
|||
|
||||
En el ejemplo a continuación, **FastAPI** usará `ORJSONResponse` por defecto, en todas las *path operations*, en lugar de `JSONResponse`.
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial010_py39.py hl[2,4] *}
|
||||
{* ../../docs_src/custom_response/tutorial010_py310.py hl[2,4] *}
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ En ese caso, simplemente puedes intercambiar los `dataclasses` estándar con `py
|
|||
|
||||
6. Aquí estamos regresando un diccionario que contiene `items`, que es una lista de dataclasses.
|
||||
|
||||
FastAPI todavía es capaz de <abbr title="convertir los datos a un formato que pueda transmitirse">serializar</abbr> los datos a JSON.
|
||||
FastAPI todavía es capaz de <dfn title="convertir los datos a un formato que pueda transmitirse">serializar</dfn> los datos a JSON.
|
||||
|
||||
7. Aquí el `response_model` está usando una anotación de tipo de una lista de dataclasses `Author`.
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ Comencemos con un ejemplo y luego veámoslo en detalle.
|
|||
|
||||
Creamos una función asíncrona `lifespan()` con `yield` así:
|
||||
|
||||
{* ../../docs_src/events/tutorial003_py39.py hl[16,19] *}
|
||||
{* ../../docs_src/events/tutorial003_py310.py hl[16,19] *}
|
||||
|
||||
Aquí estamos simulando la operación costosa de *startup* de cargar el modelo poniendo la función del (falso) modelo en el diccionario con modelos de machine learning antes del `yield`. Este código será ejecutado **antes** de que la aplicación **comience a tomar requests**, durante el *startup*.
|
||||
|
||||
|
|
@ -48,7 +48,7 @@ Quizás necesites iniciar una nueva versión, o simplemente te cansaste de ejecu
|
|||
|
||||
Lo primero que hay que notar es que estamos definiendo una función asíncrona con `yield`. Esto es muy similar a las Dependencias con `yield`.
|
||||
|
||||
{* ../../docs_src/events/tutorial003_py39.py hl[14:19] *}
|
||||
{* ../../docs_src/events/tutorial003_py310.py hl[14:19] *}
|
||||
|
||||
La primera parte de la función, antes del `yield`, será ejecutada **antes** de que la aplicación comience.
|
||||
|
||||
|
|
@ -60,7 +60,7 @@ Si revisas, la función está decorada con un `@asynccontextmanager`.
|
|||
|
||||
Eso convierte a la función en algo llamado un "**async context manager**".
|
||||
|
||||
{* ../../docs_src/events/tutorial003_py39.py hl[1,13] *}
|
||||
{* ../../docs_src/events/tutorial003_py310.py hl[1,13] *}
|
||||
|
||||
Un **context manager** en Python es algo que puedes usar en un statement `with`, por ejemplo, `open()` puede ser usado como un context manager:
|
||||
|
||||
|
|
@ -82,7 +82,7 @@ En nuestro ejemplo de código arriba, no lo usamos directamente, pero se lo pasa
|
|||
|
||||
El parámetro `lifespan` de la app de `FastAPI` toma un **async context manager**, por lo que podemos pasar nuestro nuevo `lifespan` async context manager a él.
|
||||
|
||||
{* ../../docs_src/events/tutorial003_py39.py hl[22] *}
|
||||
{* ../../docs_src/events/tutorial003_py310.py hl[22] *}
|
||||
|
||||
## Eventos Alternativos (obsoleto) { #alternative-events-deprecated }
|
||||
|
||||
|
|
@ -104,7 +104,7 @@ Estas funciones pueden ser declaradas con `async def` o `def` normal.
|
|||
|
||||
Para añadir una función que debería ejecutarse antes de que la aplicación inicie, declárala con el evento `"startup"`:
|
||||
|
||||
{* ../../docs_src/events/tutorial001_py39.py hl[8] *}
|
||||
{* ../../docs_src/events/tutorial001_py310.py hl[8] *}
|
||||
|
||||
En este caso, la función manejadora del evento `startup` inicializará los ítems de la "base de datos" (solo un `dict`) con algunos valores.
|
||||
|
||||
|
|
@ -116,7 +116,7 @@ Y tu aplicación no comenzará a recibir requests hasta que todos los manejadore
|
|||
|
||||
Para añadir una función que debería ejecutarse cuando la aplicación se esté cerrando, declárala con el evento `"shutdown"`:
|
||||
|
||||
{* ../../docs_src/events/tutorial002_py39.py hl[6] *}
|
||||
{* ../../docs_src/events/tutorial002_py310.py hl[6] *}
|
||||
|
||||
Aquí, la función manejadora del evento `shutdown` escribirá una línea de texto `"Application shutdown"` a un archivo `log.txt`.
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
Como **FastAPI** está basado en la especificación **OpenAPI**, sus APIs se pueden describir en un formato estándar que muchas herramientas entienden.
|
||||
|
||||
Esto facilita generar **documentación** actualizada, paquetes de cliente (<abbr title="Software Development Kits – Kits de Desarrollo de Software">**SDKs**</abbr>) en múltiples lenguajes y **escribir pruebas** o **flujos de automatización** que se mantengan sincronizados con tu código.
|
||||
Esto facilita generar **documentación** actualizada, paquetes de cliente (<abbr title="Software Development Kits - Kits de Desarrollo de Software">**SDKs**</abbr>) en múltiples lenguajes y **escribir pruebas** o **flujos de automatización** que se mantengan sincronizados con tu código.
|
||||
|
||||
En esta guía, aprenderás a generar un **SDK de TypeScript** para tu backend con FastAPI.
|
||||
|
||||
|
|
@ -40,7 +40,7 @@ Algunas de estas soluciones también pueden ser open source u ofrecer niveles gr
|
|||
|
||||
Empecemos con una aplicación simple de FastAPI:
|
||||
|
||||
{* ../../docs_src/generate_clients/tutorial001_py39.py hl[7:9,12:13,16:17,21] *}
|
||||
{* ../../docs_src/generate_clients/tutorial001_py310.py hl[7:9,12:13,16:17,21] *}
|
||||
|
||||
Nota que las *path operations* definen los modelos que usan para el payload del request y el payload del response, usando los modelos `Item` y `ResponseMessage`.
|
||||
|
||||
|
|
@ -98,7 +98,7 @@ En muchos casos tu app de FastAPI será más grande, y probablemente usarás tag
|
|||
|
||||
Por ejemplo, podrías tener una sección para **items** y otra sección para **users**, y podrían estar separadas por tags:
|
||||
|
||||
{* ../../docs_src/generate_clients/tutorial002_py39.py hl[21,26,34] *}
|
||||
{* ../../docs_src/generate_clients/tutorial002_py310.py hl[21,26,34] *}
|
||||
|
||||
### Genera un Cliente TypeScript con tags { #generate-a-typescript-client-with-tags }
|
||||
|
||||
|
|
@ -145,7 +145,7 @@ Por ejemplo, aquí está usando el primer tag (probablemente tendrás solo un ta
|
|||
|
||||
Puedes entonces pasar esa función personalizada a **FastAPI** como el parámetro `generate_unique_id_function`:
|
||||
|
||||
{* ../../docs_src/generate_clients/tutorial003_py39.py hl[6:7,10] *}
|
||||
{* ../../docs_src/generate_clients/tutorial003_py310.py hl[6:7,10] *}
|
||||
|
||||
### Genera un Cliente TypeScript con operation IDs personalizados { #generate-a-typescript-client-with-custom-operation-ids }
|
||||
|
||||
|
|
@ -167,7 +167,7 @@ Pero para el cliente generado podríamos **modificar** los operation IDs de Open
|
|||
|
||||
Podríamos descargar el JSON de OpenAPI a un archivo `openapi.json` y luego podríamos **remover ese tag prefijado** con un script como este:
|
||||
|
||||
{* ../../docs_src/generate_clients/tutorial004_py39.py *}
|
||||
{* ../../docs_src/generate_clients/tutorial004_py310.py *}
|
||||
|
||||
//// tab | Node.js
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ En esta sección veremos cómo usar otros middlewares.
|
|||
|
||||
## Agregando middlewares ASGI { #adding-asgi-middlewares }
|
||||
|
||||
Como **FastAPI** está basado en Starlette e implementa la especificación <abbr title="Asynchronous Server Gateway Interface – Interfaz de puerta de enlace de servidor asíncrona">ASGI</abbr>, puedes usar cualquier middleware ASGI.
|
||||
Como **FastAPI** está basado en Starlette e implementa la especificación <abbr title="Asynchronous Server Gateway Interface - Interfaz de puerta de enlace de servidor asíncrona">ASGI</abbr>, puedes usar cualquier middleware ASGI.
|
||||
|
||||
Un middleware no tiene que estar hecho para FastAPI o Starlette para funcionar, siempre que siga la especificación ASGI.
|
||||
|
||||
|
|
@ -57,13 +57,13 @@ Impone que todas las requests entrantes deben ser `https` o `wss`.
|
|||
|
||||
Cualquier request entrante a `http` o `ws` será redirigida al esquema seguro.
|
||||
|
||||
{* ../../docs_src/advanced_middleware/tutorial001_py39.py hl[2,6] *}
|
||||
{* ../../docs_src/advanced_middleware/tutorial001_py310.py hl[2,6] *}
|
||||
|
||||
## `TrustedHostMiddleware` { #trustedhostmiddleware }
|
||||
|
||||
Impone que todas las requests entrantes tengan correctamente configurado el header `Host`, para proteger contra ataques de HTTP Host Header.
|
||||
|
||||
{* ../../docs_src/advanced_middleware/tutorial002_py39.py hl[2,6:8] *}
|
||||
{* ../../docs_src/advanced_middleware/tutorial002_py310.py hl[2,6:8] *}
|
||||
|
||||
Se soportan los siguientes argumentos:
|
||||
|
||||
|
|
@ -78,7 +78,7 @@ Maneja responses GZip para cualquier request que incluya `"gzip"` en el header `
|
|||
|
||||
El middleware manejará tanto responses estándar como en streaming.
|
||||
|
||||
{* ../../docs_src/advanced_middleware/tutorial003_py39.py hl[2,6] *}
|
||||
{* ../../docs_src/advanced_middleware/tutorial003_py310.py hl[2,6] *}
|
||||
|
||||
Se soportan los siguientes argumentos:
|
||||
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ Los webhooks están disponibles en OpenAPI 3.1.0 y superiores, soportados por Fa
|
|||
|
||||
Cuando creas una aplicación de **FastAPI**, hay un atributo `webhooks` que puedes usar para definir *webhooks*, de la misma manera que definirías *path operations*, por ejemplo con `@app.webhooks.post()`.
|
||||
|
||||
{* ../../docs_src/openapi_webhooks/tutorial001_py39.py hl[9:13,36:53] *}
|
||||
{* ../../docs_src/openapi_webhooks/tutorial001_py310.py hl[9:12,15:20] *}
|
||||
|
||||
Los webhooks que defines terminarán en el esquema de **OpenAPI** y en la interfaz automática de **documentación**.
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ Puedes establecer el `operationId` de OpenAPI para ser usado en tu *path operati
|
|||
|
||||
Tendrías que asegurarte de que sea único para cada operación.
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial001_py39.py hl[6] *}
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial001_py310.py hl[6] *}
|
||||
|
||||
### Usar el nombre de la *path operation function* como el operationId { #using-the-path-operation-function-name-as-the-operationid }
|
||||
|
||||
|
|
@ -20,7 +20,7 @@ Si quieres usar los nombres de las funciones de tus APIs como `operationId`s, pu
|
|||
|
||||
Deberías hacerlo después de agregar todas tus *path operations*.
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial002_py39.py hl[2, 12:21, 24] *}
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial002_py310.py hl[2, 12:21, 24] *}
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
|
|
@ -40,7 +40,7 @@ Incluso si están en diferentes módulos (archivos de Python).
|
|||
|
||||
Para excluir una *path operation* del esquema OpenAPI generado (y por lo tanto, de los sistemas de documentación automática), utiliza el parámetro `include_in_schema` y configúralo en `False`:
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial003_py39.py hl[6] *}
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial003_py310.py hl[6] *}
|
||||
|
||||
## Descripción avanzada desde el docstring { #advanced-description-from-docstring }
|
||||
|
||||
|
|
@ -92,7 +92,7 @@ Puedes extender el esquema de OpenAPI para una *path operation* usando el parám
|
|||
|
||||
Este `openapi_extra` puede ser útil, por ejemplo, para declarar [Extensiones de OpenAPI](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#specificationExtensions):
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial005_py39.py hl[6] *}
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial005_py310.py hl[6] *}
|
||||
|
||||
Si abres la documentación automática de la API, tu extensión aparecerá en la parte inferior de la *path operation* específica.
|
||||
|
||||
|
|
@ -139,9 +139,9 @@ Por ejemplo, podrías decidir leer y validar el request con tu propio código, s
|
|||
|
||||
Podrías hacer eso con `openapi_extra`:
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial006_py39.py hl[19:36, 39:40] *}
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial006_py310.py hl[19:36, 39:40] *}
|
||||
|
||||
En este ejemplo, no declaramos ningún modelo Pydantic. De hecho, el request body ni siquiera se <abbr title="converted from some plain format, like bytes, into Python objects - convertido de algún formato plano, como bytes, a objetos de Python">parse</abbr> como JSON, se lee directamente como `bytes`, y la función `magic_data_reader()` sería la encargada de parsearlo de alguna manera.
|
||||
En este ejemplo, no declaramos ningún modelo Pydantic. De hecho, el request body ni siquiera es <dfn title="convertido desde algún formato plano, como bytes, a objetos de Python">parseado</dfn> como JSON, se lee directamente como `bytes`, y la función `magic_data_reader()` sería la encargada de parsearlo de alguna manera.
|
||||
|
||||
Sin embargo, podemos declarar el esquema esperado para el request body.
|
||||
|
||||
|
|
@ -153,7 +153,7 @@ Y podrías hacer esto incluso si el tipo de datos en el request no es JSON.
|
|||
|
||||
Por ejemplo, en esta aplicación no usamos la funcionalidad integrada de FastAPI para extraer el JSON Schema de los modelos Pydantic ni la validación automática para JSON. De hecho, estamos declarando el tipo de contenido del request como YAML, no JSON:
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial007_py39.py hl[15:20, 22] *}
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial007_py310.py hl[15:20, 22] *}
|
||||
|
||||
Sin embargo, aunque no estamos usando la funcionalidad integrada por defecto, aún estamos usando un modelo Pydantic para generar manualmente el JSON Schema para los datos que queremos recibir en YAML.
|
||||
|
||||
|
|
@ -161,7 +161,7 @@ Luego usamos el request directamente, y extraemos el cuerpo como `bytes`. Esto s
|
|||
|
||||
Y luego en nuestro código, parseamos ese contenido YAML directamente, y nuevamente estamos usando el mismo modelo Pydantic para validar el contenido YAML:
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial007_py39.py hl[24:31] *}
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial007_py310.py hl[24:31] *}
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
|
|
|
|||
|
|
@ -20,9 +20,9 @@ Puedes declarar un parámetro de tipo `Response` en tu *path operation function*
|
|||
|
||||
Y luego puedes establecer el `status_code` en ese objeto de response *temporal*.
|
||||
|
||||
{* ../../docs_src/response_change_status_code/tutorial001_py39.py hl[1,9,12] *}
|
||||
{* ../../docs_src/response_change_status_code/tutorial001_py310.py hl[1,9,12] *}
|
||||
|
||||
Y luego puedes devolver cualquier objeto que necesites, como lo harías normalmente (un `dict`, un modelo de base de datos, etc.).
|
||||
Y luego puedes devolver cualquier objeto que necesites, como lo harías normalmente (un `dict`, un modelo de base de datos, etc).
|
||||
|
||||
Y si declaraste un `response_model`, todavía se utilizará para filtrar y convertir el objeto que devolviste.
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ Puedes declarar un parámetro de tipo `Response` en tu *path operation function*
|
|||
|
||||
Y luego puedes establecer cookies en ese objeto de response *temporal*.
|
||||
|
||||
{* ../../docs_src/response_cookies/tutorial002_py39.py hl[1, 8:9] *}
|
||||
{* ../../docs_src/response_cookies/tutorial002_py310.py hl[1, 8:9] *}
|
||||
|
||||
Y entonces puedes devolver cualquier objeto que necesites, como normalmente lo harías (un `dict`, un modelo de base de datos, etc).
|
||||
|
||||
|
|
@ -24,7 +24,7 @@ Para hacer eso, puedes crear un response como se describe en [Devolver un Respon
|
|||
|
||||
Luego establece Cookies en ella, y luego devuélvela:
|
||||
|
||||
{* ../../docs_src/response_cookies/tutorial001_py39.py hl[10:12] *}
|
||||
{* ../../docs_src/response_cookies/tutorial001_py310.py hl[10:12] *}
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ Digamos que quieres devolver un response en <a href="https://en.wikipedia.org/wi
|
|||
|
||||
Podrías poner tu contenido XML en un string, poner eso en un `Response`, y devolverlo:
|
||||
|
||||
{* ../../docs_src/response_directly/tutorial002_py39.py hl[1,18] *}
|
||||
{* ../../docs_src/response_directly/tutorial002_py310.py hl[1,18] *}
|
||||
|
||||
## Notas { #notes }
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ Puedes declarar un parámetro de tipo `Response` en tu *path operation function*
|
|||
|
||||
Y luego puedes establecer headers en ese objeto de response *temporal*.
|
||||
|
||||
{* ../../docs_src/response_headers/tutorial002_py39.py hl[1, 7:8] *}
|
||||
{* ../../docs_src/response_headers/tutorial002_py310.py hl[1, 7:8] *}
|
||||
|
||||
Y luego puedes devolver cualquier objeto que necesites, como harías normalmente (un `dict`, un modelo de base de datos, etc).
|
||||
|
||||
|
|
@ -22,7 +22,7 @@ También puedes agregar headers cuando devuelves un `Response` directamente.
|
|||
|
||||
Crea un response como se describe en [Retorna un Response Directamente](response-directly.md){.internal-link target=_blank} y pasa los headers como un parámetro adicional:
|
||||
|
||||
{* ../../docs_src/response_headers/tutorial001_py39.py hl[10:12] *}
|
||||
{* ../../docs_src/response_headers/tutorial001_py310.py hl[10:12] *}
|
||||
|
||||
/// note | Detalles Técnicos
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ Luego, cuando escribes ese nombre de usuario y contraseña, el navegador los env
|
|||
* Devuelve un objeto de tipo `HTTPBasicCredentials`:
|
||||
* Contiene el `username` y `password` enviados.
|
||||
|
||||
{* ../../docs_src/security/tutorial006_an_py39.py hl[4,8,12] *}
|
||||
{* ../../docs_src/security/tutorial006_an_py310.py hl[4,8,12] *}
|
||||
|
||||
Cuando intentas abrir la URL por primera vez (o haces clic en el botón "Execute" en la documentación) el navegador te pedirá tu nombre de usuario y contraseña:
|
||||
|
||||
|
|
@ -40,7 +40,7 @@ Para manejar eso, primero convertimos el `username` y `password` a `bytes` codif
|
|||
|
||||
Luego podemos usar `secrets.compare_digest()` para asegurar que `credentials.username` es `"stanleyjobson"`, y que `credentials.password` es `"swordfish"`.
|
||||
|
||||
{* ../../docs_src/security/tutorial007_an_py39.py hl[1,12:24] *}
|
||||
{* ../../docs_src/security/tutorial007_an_py310.py hl[1,12:24] *}
|
||||
|
||||
Esto sería similar a:
|
||||
|
||||
|
|
@ -104,4 +104,4 @@ De esa manera, usando `secrets.compare_digest()` en el código de tu aplicación
|
|||
|
||||
Después de detectar que las credenciales son incorrectas, regresa un `HTTPException` con un código de estado 401 (el mismo que se devuelve cuando no se proporcionan credenciales) y agrega el header `WWW-Authenticate` para que el navegador muestre el prompt de inicio de sesión nuevamente:
|
||||
|
||||
{* ../../docs_src/security/tutorial007_an_py39.py hl[26:30] *}
|
||||
{* ../../docs_src/security/tutorial007_an_py310.py hl[26:30] *}
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ De la misma forma que con los modelos de Pydantic, declaras atributos de clase c
|
|||
|
||||
Puedes usar todas las mismas funcionalidades de validación y herramientas que usas para los modelos de Pydantic, como diferentes tipos de datos y validaciones adicionales con `Field()`.
|
||||
|
||||
{* ../../docs_src/settings/tutorial001_py39.py hl[2,5:8,11] *}
|
||||
{* ../../docs_src/settings/tutorial001_py310.py hl[2,5:8,11] *}
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
|
|
@ -70,7 +70,7 @@ Luego convertirá y validará los datos. Así que, cuando uses ese objeto `setti
|
|||
|
||||
Luego puedes usar el nuevo objeto `settings` en tu aplicación:
|
||||
|
||||
{* ../../docs_src/settings/tutorial001_py39.py hl[18:20] *}
|
||||
{* ../../docs_src/settings/tutorial001_py310.py hl[18:20] *}
|
||||
|
||||
### Ejecutar el servidor { #run-the-server }
|
||||
|
||||
|
|
@ -104,11 +104,11 @@ Podrías poner esas configuraciones en otro archivo de módulo como viste en [Ap
|
|||
|
||||
Por ejemplo, podrías tener un archivo `config.py` con:
|
||||
|
||||
{* ../../docs_src/settings/app01_py39/config.py *}
|
||||
{* ../../docs_src/settings/app01_py310/config.py *}
|
||||
|
||||
Y luego usarlo en un archivo `main.py`:
|
||||
|
||||
{* ../../docs_src/settings/app01_py39/main.py hl[3,11:13] *}
|
||||
{* ../../docs_src/settings/app01_py310/main.py hl[3,11:13] *}
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
|
|
@ -126,7 +126,7 @@ Esto podría ser especialmente útil durante las pruebas, ya que es muy fácil s
|
|||
|
||||
Proveniente del ejemplo anterior, tu archivo `config.py` podría verse como:
|
||||
|
||||
{* ../../docs_src/settings/app02_an_py39/config.py hl[10] *}
|
||||
{* ../../docs_src/settings/app02_an_py310/config.py hl[10] *}
|
||||
|
||||
Nota que ahora no creamos un instance por defecto `settings = Settings()`.
|
||||
|
||||
|
|
@ -134,7 +134,7 @@ Nota que ahora no creamos un instance por defecto `settings = Settings()`.
|
|||
|
||||
Ahora creamos una dependencia que devuelve un nuevo `config.Settings()`.
|
||||
|
||||
{* ../../docs_src/settings/app02_an_py39/main.py hl[6,12:13] *}
|
||||
{* ../../docs_src/settings/app02_an_py310/main.py hl[6,12:13] *}
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
|
|
@ -146,13 +146,13 @@ Por ahora puedes asumir que `get_settings()` es una función normal.
|
|||
|
||||
Y luego podemos requerirlo desde la *path operation function* como una dependencia y usarlo donde lo necesitemos.
|
||||
|
||||
{* ../../docs_src/settings/app02_an_py39/main.py hl[17,19:21] *}
|
||||
{* ../../docs_src/settings/app02_an_py310/main.py hl[17,19:21] *}
|
||||
|
||||
### Configuraciones y pruebas { #settings-and-testing }
|
||||
|
||||
Luego sería muy fácil proporcionar un objeto de configuraciones diferente durante las pruebas al crear una sobrescritura de dependencia para `get_settings`:
|
||||
|
||||
{* ../../docs_src/settings/app02_an_py39/test_main.py hl[9:10,13,21] *}
|
||||
{* ../../docs_src/settings/app02_an_py310/test_main.py hl[9:10,13,21] *}
|
||||
|
||||
En la sobrescritura de dependencia establecemos un nuevo valor para el `admin_email` al crear el nuevo objeto `Settings`, y luego devolvemos ese nuevo objeto.
|
||||
|
||||
|
|
@ -193,7 +193,7 @@ APP_NAME="ChimichangApp"
|
|||
|
||||
Y luego actualizar tu `config.py` con:
|
||||
|
||||
{* ../../docs_src/settings/app03_an_py39/config.py hl[9] *}
|
||||
{* ../../docs_src/settings/app03_an_py310/config.py hl[9] *}
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
|
|
@ -226,7 +226,7 @@ crearíamos ese objeto para cada request, y estaríamos leyendo el archivo `.env
|
|||
|
||||
Pero como estamos usando el decorador `@lru_cache` encima, el objeto `Settings` se creará solo una vez, la primera vez que se llame. ✔️
|
||||
|
||||
{* ../../docs_src/settings/app03_an_py39/main.py hl[1,11] *}
|
||||
{* ../../docs_src/settings/app03_an_py310/main.py hl[1,11] *}
|
||||
|
||||
Entonces, para cualquier llamada subsiguiente de `get_settings()` en las dependencias de los próximos requests, en lugar de ejecutar el código interno de `get_settings()` y crear un nuevo objeto `Settings`, devolverá el mismo objeto que fue devuelto en la primera llamada, una y otra vez.
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ Si necesitas tener dos aplicaciones de **FastAPI** independientes, cada una con
|
|||
|
||||
Primero, crea la aplicación principal de nivel superior de **FastAPI**, y sus *path operations*:
|
||||
|
||||
{* ../../docs_src/sub_applications/tutorial001_py39.py hl[3, 6:8] *}
|
||||
{* ../../docs_src/sub_applications/tutorial001_py310.py hl[3, 6:8] *}
|
||||
|
||||
### Sub-aplicación { #sub-application }
|
||||
|
||||
|
|
@ -18,7 +18,7 @@ Luego, crea tu sub-aplicación, y sus *path operations*.
|
|||
|
||||
Esta sub-aplicación es solo otra aplicación estándar de FastAPI, pero es la que se "montará":
|
||||
|
||||
{* ../../docs_src/sub_applications/tutorial001_py39.py hl[11, 14:16] *}
|
||||
{* ../../docs_src/sub_applications/tutorial001_py310.py hl[11, 14:16] *}
|
||||
|
||||
### Montar la sub-aplicación { #mount-the-sub-application }
|
||||
|
||||
|
|
@ -26,7 +26,7 @@ En tu aplicación de nivel superior, `app`, monta la sub-aplicación, `subapi`.
|
|||
|
||||
En este caso, se montará en el path `/subapi`:
|
||||
|
||||
{* ../../docs_src/sub_applications/tutorial001_py39.py hl[11, 19] *}
|
||||
{* ../../docs_src/sub_applications/tutorial001_py310.py hl[11, 19] *}
|
||||
|
||||
### Revisa la documentación automática de la API { #check-the-automatic-api-docs }
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ $ pip install jinja2
|
|||
* Declara un parámetro `Request` en la *path operation* que devolverá una plantilla.
|
||||
* Usa los `templates` que creaste para renderizar y devolver un `TemplateResponse`, pasa el nombre de la plantilla, el objeto de request, y un diccionario "context" con pares clave-valor que se usarán dentro de la plantilla Jinja2.
|
||||
|
||||
{* ../../docs_src/templates/tutorial001_py39.py hl[4,11,15:18] *}
|
||||
{* ../../docs_src/templates/tutorial001_py310.py hl[4,11,15:18] *}
|
||||
|
||||
/// note | Nota
|
||||
|
||||
|
|
@ -43,7 +43,7 @@ Al declarar `response_class=HTMLResponse`, la interfaz de usuario de la document
|
|||
|
||||
///
|
||||
|
||||
/// note | Nota Técnica
|
||||
/// note | Detalles técnicos
|
||||
|
||||
También podrías usar `from starlette.templating import Jinja2Templates`.
|
||||
|
||||
|
|
|
|||
|
|
@ -14,11 +14,11 @@ Un ejemplo podría ser que tienes un proveedor de autenticación externo al que
|
|||
|
||||
Le envías un token y te devuelve un usuario autenticado.
|
||||
|
||||
Este proveedor podría estar cobrándote por cada request, y llamarlo podría tomar más tiempo adicional que si tuvieras un usuario de prueba fijo para los tests.
|
||||
Este proveedor podría estar cobrándote por cada request, y llamarlo podría tomar más tiempo adicional que si tuvieras un usuario mock fijo para los tests.
|
||||
|
||||
Probablemente quieras probar el proveedor externo una vez, pero no necesariamente llamarlo para cada test que se realice.
|
||||
|
||||
En este caso, puedes sobrescribir la dependencia que llama a ese proveedor y usar una dependencia personalizada que devuelva un usuario de prueba, solo para tus tests.
|
||||
En este caso, puedes sobrescribir la dependencia que llama a ese proveedor y usar una dependencia personalizada que devuelva un usuario mock, solo para tus tests.
|
||||
|
||||
### Usa el atributo `app.dependency_overrides` { #use-the-app-dependency-overrides-attribute }
|
||||
|
||||
|
|
@ -40,7 +40,7 @@ FastAPI todavía podrá sobrescribirla.
|
|||
|
||||
///
|
||||
|
||||
Entonces puedes restablecer las dependencias sobreescritas configurando `app.dependency_overrides` para que sea un `dict` vacío:
|
||||
Entonces puedes restablecer las dependencias sobreescritas (eliminarlas) configurando `app.dependency_overrides` para que sea un `dict` vacío:
|
||||
|
||||
```Python
|
||||
app.dependency_overrides = {}
|
||||
|
|
|
|||
|
|
@ -2,11 +2,11 @@
|
|||
|
||||
Cuando necesitas que `lifespan` se ejecute en tus tests, puedes usar el `TestClient` con un statement `with`:
|
||||
|
||||
{* ../../docs_src/app_testing/tutorial004_py39.py hl[9:15,18,27:28,30:32,41:43] *}
|
||||
{* ../../docs_src/app_testing/tutorial004_py310.py hl[9:15,18,27:28,30:32,41:43] *}
|
||||
|
||||
|
||||
Puedes leer más detalles sobre ["Ejecutar lifespan en tests en el sitio oficial de documentación de Starlette."](https://www.starlette.dev/lifespan/#running-lifespan-in-tests)
|
||||
|
||||
Para los eventos obsoletos `startup` y `shutdown`, puedes usar el `TestClient` así:
|
||||
|
||||
{* ../../docs_src/app_testing/tutorial003_py39.py hl[9:12,20:24] *}
|
||||
{* ../../docs_src/app_testing/tutorial003_py310.py hl[9:12,20:24] *}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ Puedes usar el mismo `TestClient` para probar WebSockets.
|
|||
|
||||
Para esto, usas el `TestClient` en un statement `with`, conectándote al WebSocket:
|
||||
|
||||
{* ../../docs_src/app_testing/tutorial002_py39.py hl[27:31] *}
|
||||
{* ../../docs_src/app_testing/tutorial002_py310.py hl[27:31] *}
|
||||
|
||||
/// note | Nota
|
||||
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ Imaginemos que quieres obtener la dirección IP/host del cliente dentro de tu *p
|
|||
|
||||
Para eso necesitas acceder al request directamente.
|
||||
|
||||
{* ../../docs_src/using_request_directly/tutorial001_py39.py hl[1,7:8] *}
|
||||
{* ../../docs_src/using_request_directly/tutorial001_py310.py hl[1,7:8] *}
|
||||
|
||||
Al declarar un parámetro de *path operation function* con el tipo siendo `Request`, **FastAPI** sabrá pasar el `Request` en ese parámetro.
|
||||
|
||||
|
|
|
|||
|
|
@ -38,13 +38,13 @@ En producción tendrías una de las opciones anteriores.
|
|||
|
||||
Pero es la forma más sencilla de enfocarse en el lado del servidor de WebSockets y tener un ejemplo funcional:
|
||||
|
||||
{* ../../docs_src/websockets/tutorial001_py39.py hl[2,6:38,41:43] *}
|
||||
{* ../../docs_src/websockets/tutorial001_py310.py hl[2,6:38,41:43] *}
|
||||
|
||||
## Crear un `websocket` { #create-a-websocket }
|
||||
|
||||
En tu aplicación de **FastAPI**, crea un `websocket`:
|
||||
|
||||
{* ../../docs_src/websockets/tutorial001_py39.py hl[1,46:47] *}
|
||||
{* ../../docs_src/websockets/tutorial001_py310.py hl[1,46:47] *}
|
||||
|
||||
/// note | Detalles Técnicos
|
||||
|
||||
|
|
@ -58,7 +58,7 @@ También podrías usar `from starlette.websockets import WebSocket`.
|
|||
|
||||
En tu ruta de WebSocket puedes `await` para recibir mensajes y enviar mensajes.
|
||||
|
||||
{* ../../docs_src/websockets/tutorial001_py39.py hl[48:52] *}
|
||||
{* ../../docs_src/websockets/tutorial001_py310.py hl[48:52] *}
|
||||
|
||||
Puedes recibir y enviar datos binarios, de texto y JSON.
|
||||
|
||||
|
|
@ -154,7 +154,7 @@ Con eso puedes conectar el WebSocket y luego enviar y recibir mensajes:
|
|||
|
||||
Cuando una conexión de WebSocket se cierra, el `await websocket.receive_text()` lanzará una excepción `WebSocketDisconnect`, que puedes capturar y manejar como en este ejemplo.
|
||||
|
||||
{* ../../docs_src/websockets/tutorial003_py39.py hl[79:81] *}
|
||||
{* ../../docs_src/websockets/tutorial003_py310.py hl[79:81] *}
|
||||
|
||||
Para probarlo:
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ Luego envuelve la aplicación WSGI (p. ej., Flask) con el middleware.
|
|||
|
||||
Y luego móntala bajo un path.
|
||||
|
||||
{* ../../docs_src/wsgi/tutorial001_py39.py hl[1,3,23] *}
|
||||
{* ../../docs_src/wsgi/tutorial001_py310.py hl[1,3,23] *}
|
||||
|
||||
/// note | Nota
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ Es el framework más popular de Python y es ampliamente confiable. Se utiliza pa
|
|||
|
||||
Está relativamente acoplado con bases de datos relacionales (como MySQL o PostgreSQL), por lo que tener una base de datos NoSQL (como Couchbase, MongoDB, Cassandra, etc) como motor de almacenamiento principal no es muy fácil.
|
||||
|
||||
Fue creado para generar el HTML en el backend, no para crear APIs utilizadas por un frontend moderno (como React, Vue.js y Angular) o por otros sistemas (como dispositivos del <abbr title="Internet of Things – Internet de las cosas">IoT</abbr>) comunicándose con él.
|
||||
Fue creado para generar el HTML en el backend, no para crear APIs utilizadas por un frontend moderno (como React, Vue.js y Angular) o por otros sistemas (como dispositivos del <abbr title="Internet of Things - Internet de las cosas">IoT</abbr>) comunicándose con él.
|
||||
|
||||
### <a href="https://www.django-rest-framework.org/" class="external-link" target="_blank">Django REST Framework</a> { #django-rest-framework }
|
||||
|
||||
|
|
@ -76,7 +76,7 @@ Aun así, FastAPI se inspiró bastante en Requests.
|
|||
|
||||
Están, más o menos, en extremos opuestos, complementándose entre sí.
|
||||
|
||||
Requests tiene un diseño muy simple e intuitivo, es muy fácil de usar, con valores predeterminados sensatos. Pero al mismo tiempo, es muy poderoso y personalizable.
|
||||
Requests tiene un diseño muy simple e intuitivo, es muy fácil de usar, con valores por defecto sensatos. Pero al mismo tiempo, es muy poderoso y personalizable.
|
||||
|
||||
Por eso, como se dice en el sitio web oficial:
|
||||
|
||||
|
|
@ -102,7 +102,7 @@ Mira las similitudes entre `requests.get(...)` y `@app.get(...)`.
|
|||
|
||||
* Tener un API simple e intuitivo.
|
||||
* Usar nombres de métodos HTTP (operaciones) directamente, de una manera sencilla e intuitiva.
|
||||
* Tener valores predeterminados sensatos, pero personalizaciones poderosas.
|
||||
* Tener valores por defecto sensatos, pero personalizaciones poderosas.
|
||||
|
||||
///
|
||||
|
||||
|
|
@ -137,7 +137,7 @@ Existen varios frameworks REST para Flask, pero después de invertir tiempo y tr
|
|||
|
||||
### <a href="https://marshmallow.readthedocs.io/en/stable/" class="external-link" target="_blank">Marshmallow</a> { #marshmallow }
|
||||
|
||||
Una de las principales funcionalidades necesitadas por los sistemas API es la "<abbr title="también llamada marshalling, conversión">serialización</abbr>" de datos, que consiste en tomar datos del código (Python) y convertirlos en algo que pueda ser enviado a través de la red. Por ejemplo, convertir un objeto que contiene datos de una base de datos en un objeto JSON. Convertir objetos `datetime` en strings, etc.
|
||||
Una de las principales funcionalidades necesitadas por los sistemas API es la "<dfn title="también llamado marshalling, conversión">serialización</dfn>" de datos, que consiste en tomar datos del código (Python) y convertirlos en algo que pueda ser enviado a través de la red. Por ejemplo, convertir un objeto que contiene datos de una base de datos en un objeto JSON. Convertir objetos `datetime` en strings, etc.
|
||||
|
||||
Otra gran funcionalidad necesaria por las APIs es la validación de datos, asegurarse de que los datos sean válidos, dados ciertos parámetros. Por ejemplo, que algún campo sea un `int`, y no algún string aleatorio. Esto es especialmente útil para los datos entrantes.
|
||||
|
||||
|
|
@ -145,7 +145,7 @@ Sin un sistema de validación de datos, tendrías que hacer todas las comprobaci
|
|||
|
||||
Estas funcionalidades son para lo que fue creado Marshmallow. Es un gran paquete, y lo he usado mucho antes.
|
||||
|
||||
Pero fue creado antes de que existieran las anotaciones de tipos en Python. Así que, para definir cada <abbr title="la definición de cómo deberían formarse los datos">esquema</abbr> necesitas usar utilidades y clases específicas proporcionadas por Marshmallow.
|
||||
Pero fue creado antes de que existieran las anotaciones de tipos en Python. Así que, para definir cada <dfn title="la definición de cómo deberían formarse los datos">esquema</dfn> necesitas usar utilidades y clases específicas proporcionadas por Marshmallow.
|
||||
|
||||
/// check | Inspiró a **FastAPI** a
|
||||
|
||||
|
|
@ -155,7 +155,7 @@ Usar código para definir "esquemas" que proporcionen tipos de datos y validaci
|
|||
|
||||
### <a href="https://webargs.readthedocs.io/en/latest/" class="external-link" target="_blank">Webargs</a> { #webargs }
|
||||
|
||||
Otra gran funcionalidad requerida por las APIs es el <abbr title="lectura y conversión a datos de Python">parse</abbr> de datos de las requests entrantes.
|
||||
Otra gran funcionalidad requerida por las APIs es el <dfn title="lectura y conversión a datos de Python">parsing</dfn> de datos de las requests entrantes.
|
||||
|
||||
Webargs es una herramienta que fue creada para proporcionar esa funcionalidad sobre varios frameworks, incluido Flask.
|
||||
|
||||
|
|
@ -177,7 +177,7 @@ Tener validación automática de datos entrantes en una request.
|
|||
|
||||
### <a href="https://apispec.readthedocs.io/en/stable/" class="external-link" target="_blank">APISpec</a> { #apispec }
|
||||
|
||||
Marshmallow y Webargs proporcionan validación, parse y serialización como plug-ins.
|
||||
Marshmallow y Webargs proporcionan validación, parsing y serialización como plug-ins.
|
||||
|
||||
Pero la documentación todavía falta. Entonces APISpec fue creado.
|
||||
|
||||
|
|
@ -419,7 +419,7 @@ Manejar toda la validación de datos, serialización de datos y documentación a
|
|||
|
||||
### <a href="https://www.starlette.dev/" class="external-link" target="_blank">Starlette</a> { #starlette }
|
||||
|
||||
Starlette es un framework/toolkit <abbr title="The new standard for building asynchronous Python web applications – El nuevo estándar para construir aplicaciones web asíncronas en Python">ASGI</abbr> liviano, ideal para construir servicios asyncio de alto rendimiento.
|
||||
Starlette es un framework/toolkit <dfn title="El nuevo estándar para construir aplicaciones web asíncronas en Python">ASGI</dfn> liviano, ideal para construir servicios asyncio de alto rendimiento.
|
||||
|
||||
Es muy simple e intuitivo. Está diseñado para ser fácilmente extensible y tener componentes modulares.
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ Detalles sobre la sintaxis `async def` para *path operation functions* y algunos
|
|||
|
||||
## ¿Con prisa? { #in-a-hurry }
|
||||
|
||||
<abbr title="too long; didn't read"><strong>TL;DR:</strong></abbr>
|
||||
<abbr title="too long; didn't read - demasiado largo; no lo leí"><strong>TL;DR:</strong></abbr>
|
||||
|
||||
Si estás usando paquetes de terceros que te dicen que los llames con `await`, como:
|
||||
|
||||
|
|
@ -74,7 +74,7 @@ Luego la computadora / programa 🤖 volverá cada vez que tenga una oportunidad
|
|||
|
||||
Después, 🤖 toma la primera tarea que termine (digamos, nuestro "archivo-lento" 📝) y continúa con lo que tenía que hacer con ella.
|
||||
|
||||
Ese "esperar otra cosa" normalmente se refiere a las operaciones de <abbr title="Input and Output – Entrada y salida">I/O</abbr> que son relativamente "lentas" (comparadas con la velocidad del procesador y la memoria RAM), como esperar:
|
||||
Ese "esperar otra cosa" normalmente se refiere a las operaciones de <abbr title="Input and Output - Entrada y salida">I/O</abbr> que son relativamente "lentas" (comparadas con la velocidad del procesador y la memoria RAM), como esperar:
|
||||
|
||||
* que los datos del cliente se envíen a través de la red
|
||||
* que los datos enviados por tu programa sean recibidos por el cliente a través de la red
|
||||
|
|
@ -85,7 +85,7 @@ Ese "esperar otra cosa" normalmente se refiere a las operaciones de <abbr title=
|
|||
* que una query de base de datos devuelva los resultados
|
||||
* etc.
|
||||
|
||||
Como el tiempo de ejecución se consume principalmente esperando operaciones de <abbr title="Input and Output – Entrada y salida">I/O</abbr>, las llaman operaciones "I/O bound".
|
||||
Como el tiempo de ejecución se consume principalmente esperando operaciones de <abbr title="Input and Output - Entrada y salida">I/O</abbr>, las llaman operaciones "I/O bound".
|
||||
|
||||
Se llama "asíncrono" porque la computadora / programa no tiene que estar "sincronizado" con la tarea lenta, esperando el momento exacto en que la tarea termine, sin hacer nada, para poder tomar el resultado de la tarea y continuar el trabajo.
|
||||
|
||||
|
|
@ -277,7 +277,7 @@ Pero en este caso, si pudieras traer a los 8 ex-cajeros/cocineros/ahora-limpiado
|
|||
|
||||
En este escenario, cada uno de los limpiadores (incluyéndote) sería un procesador, haciendo su parte del trabajo.
|
||||
|
||||
Y como la mayor parte del tiempo de ejecución se dedica al trabajo real (en lugar de esperar), y el trabajo en una computadora lo realiza una <abbr title="Central Processing Unit – Unidad Central de Procesamiento">CPU</abbr>, llaman a estos problemas "CPU bound".
|
||||
Y como la mayor parte del tiempo de ejecución se dedica al trabajo real (en lugar de esperar), y el trabajo en una computadora lo realiza una <abbr title="Central Processing Unit - Unidad Central de Procesamiento">CPU</abbr>, llaman a estos problemas "CPU bound".
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -417,7 +417,7 @@ Si tienes bastante conocimiento técnico (coroutines, hilos, bloqueo, etc.) y ti
|
|||
|
||||
Cuando declaras una *path operation function* con `def` normal en lugar de `async def`, se ejecuta en un threadpool externo que luego es esperado, en lugar de ser llamado directamente (ya que bloquearía el servidor).
|
||||
|
||||
Si vienes de otro framework async que no funciona de la manera descrita anteriormente y estás acostumbrado a definir funciones de *path operation* solo de cómputo trivial con `def` normal para una pequeña ganancia de rendimiento (alrededor de 100 nanosegundos), ten en cuenta que en **FastAPI** el efecto sería bastante opuesto. En estos casos, es mejor usar `async def` a menos que tus *path operation functions* usen código que realice <abbr title="Input/Output – Entrada/Salida: lectura o escritura en disco, comunicaciones de red.">I/O</abbr> de bloqueo.
|
||||
Si vienes de otro framework async que no funciona de la manera descrita anteriormente y estás acostumbrado a definir funciones de *path operation* solo de cómputo trivial con `def` normal para una pequeña ganancia de rendimiento (alrededor de 100 nanosegundos), ten en cuenta que en **FastAPI** el efecto sería bastante opuesto. En estos casos, es mejor usar `async def` a menos que tus *path operation functions* usen código que realice <abbr title="Input/Output - Entrada/Salida: lectura o escritura en disco, comunicaciones de red.">I/O</abbr> de bloqueo.
|
||||
|
||||
Aun así, en ambas situaciones, es probable que **FastAPI** [siga siendo más rápida](index.md#performance){.internal-link target=_blank} que (o al menos comparable a) tu framework anterior.
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ En la mayoría de los casos, los principales proveedores de nube tienen guías p
|
|||
|
||||
Simplifica el proceso de **construir**, **desplegar** y **acceder** a una API con un esfuerzo mínimo.
|
||||
|
||||
Trae la misma experiencia de desarrollador de construir aplicaciones con FastAPI a desplegarlas en la nube. 🎉
|
||||
Trae la misma **experiencia de desarrollador** de construir aplicaciones con FastAPI a **desplegarlas** en la nube. 🎉
|
||||
|
||||
FastAPI Cloud es el sponsor principal y proveedor de financiamiento de los proyectos open source *FastAPI and friends*. ✨
|
||||
|
||||
|
|
|
|||
|
|
@ -318,4 +318,4 @@ Has estado leyendo aquí algunos de los conceptos principales que probablemente
|
|||
|
||||
Comprender estas ideas y cómo aplicarlas debería darte la intuición necesaria para tomar decisiones al configurar y ajustar tus implementaciones. 🤓
|
||||
|
||||
En las próximas secciones, te daré ejemplos más concretos de posibles estrategias que puedes seguir. 🚀
|
||||
En las próximas secciones, te daré más ejemplos concretos de posibles estrategias que puedes seguir. 🚀
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ Usar contenedores de Linux tiene varias ventajas, incluyendo **seguridad**, **re
|
|||
<summary>Vista previa del Dockerfile 👀</summary>
|
||||
|
||||
```Dockerfile
|
||||
FROM python:3.9
|
||||
FROM python:3.14
|
||||
|
||||
WORKDIR /code
|
||||
|
||||
|
|
@ -166,7 +166,7 @@ Ahora, en el mismo directorio del proyecto, crea un archivo `Dockerfile` con:
|
|||
|
||||
```{ .dockerfile .annotate }
|
||||
# (1)!
|
||||
FROM python:3.9
|
||||
FROM python:3.14
|
||||
|
||||
# (2)!
|
||||
WORKDIR /code
|
||||
|
|
@ -390,7 +390,7 @@ Si tu FastAPI es un solo archivo, por ejemplo, `main.py` sin un directorio `./ap
|
|||
Entonces solo tendrías que cambiar las rutas correspondientes para copiar el archivo dentro del `Dockerfile`:
|
||||
|
||||
```{ .dockerfile .annotate hl_lines="10 13" }
|
||||
FROM python:3.9
|
||||
FROM python:3.14
|
||||
|
||||
WORKDIR /code
|
||||
|
||||
|
|
@ -454,7 +454,7 @@ Sin usar contenedores, hacer que las aplicaciones se ejecuten al inicio y con re
|
|||
|
||||
## Replicación - Número de Procesos { #replication-number-of-processes }
|
||||
|
||||
Si tienes un <abbr title="Un grupo de máquinas que están configuradas para estar conectadas y trabajar juntas de alguna manera.">cluster</abbr> de máquinas con **Kubernetes**, Docker Swarm Mode, Nomad, u otro sistema complejo similar para gestionar contenedores distribuidos en varias máquinas, entonces probablemente querrás manejar la **replicación** a nivel de **cluster** en lugar de usar un **gestor de procesos** (como Uvicorn con workers) en cada contenedor.
|
||||
Si tienes un <dfn title="Un grupo de máquinas que están configuradas para estar conectadas y trabajar juntas de alguna manera.">clúster</dfn> de máquinas con **Kubernetes**, Docker Swarm Mode, Nomad, u otro sistema complejo similar para gestionar contenedores distribuidos en varias máquinas, entonces probablemente querrás manejar la **replicación** a nivel de **cluster** en lugar de usar un **gestor de procesos** (como Uvicorn con workers) en cada contenedor.
|
||||
|
||||
Uno de esos sistemas de gestión de contenedores distribuidos como Kubernetes normalmente tiene alguna forma integrada de manejar la **replicación de contenedores** mientras aún soporta el **load balancing** para las requests entrantes. Todo a nivel de **cluster**.
|
||||
|
||||
|
|
@ -499,7 +499,7 @@ Por supuesto, hay **casos especiales** donde podrías querer tener **un contened
|
|||
En esos casos, puedes usar la opción de línea de comandos `--workers` para establecer el número de workers que deseas ejecutar:
|
||||
|
||||
```{ .dockerfile .annotate }
|
||||
FROM python:3.9
|
||||
FROM python:3.14
|
||||
|
||||
WORKDIR /code
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# FastAPI Cloud { #fastapi-cloud }
|
||||
|
||||
Puedes desplegar tu app de FastAPI en <a href="https://fastapicloud.com" class="external-link" target="_blank">FastAPI Cloud</a> con un solo comando; ve y únete a la lista de espera si aún no lo has hecho. 🚀
|
||||
Puedes desplegar tu app de FastAPI en <a href="https://fastapicloud.com" class="external-link" target="_blank">FastAPI Cloud</a> con **un solo comando**; ve y únete a la lista de espera si aún no lo has hecho. 🚀
|
||||
|
||||
## Iniciar sesión { #login }
|
||||
|
||||
|
|
@ -20,7 +20,7 @@ You are logged in to FastAPI Cloud 🚀
|
|||
|
||||
## Desplegar { #deploy }
|
||||
|
||||
Ahora despliega tu app, con un solo comando:
|
||||
Ahora despliega tu app, con **un solo comando**:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ Aquí tienes un ejemplo de cómo podría ser una API HTTPS, paso a paso, prestan
|
|||
|
||||
Probablemente todo comenzaría adquiriendo un **nombre de dominio**. Luego, lo configurarías en un servidor DNS (posiblemente tu mismo proveedor de la nube).
|
||||
|
||||
Probablemente conseguirías un servidor en la nube (una máquina virtual) o algo similar, y tendría una **dirección IP pública** <abbr title="Que no cambia">fija</abbr>.
|
||||
Probablemente conseguirías un servidor en la nube (una máquina virtual) o algo similar, y tendría una **dirección IP pública** <dfn title="No cambia con el tiempo. No dinámica.">fija</dfn>.
|
||||
|
||||
En el/los servidor(es) DNS configurarías un registro (un "`A record`") para apuntar **tu dominio** a la **dirección IP pública de tu servidor**.
|
||||
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ Podrías usar ese comando, por ejemplo, para iniciar tu app **FastAPI** en un co
|
|||
|
||||
Vamos a profundizar un poquito en los detalles.
|
||||
|
||||
FastAPI usa un estándar para construir frameworks de web y servidores de Python llamado <abbr title="Asynchronous Server Gateway Interface – Interfaz de puerta de enlace de servidor asíncrona">ASGI</abbr>. FastAPI es un framework web ASGI.
|
||||
FastAPI usa un estándar para construir frameworks de web y servidores de Python llamado <abbr title="Asynchronous Server Gateway Interface - Interfaz de puerta de enlace de servidor asíncrona">ASGI</abbr>. FastAPI es un framework web ASGI.
|
||||
|
||||
Lo principal que necesitas para ejecutar una aplicación **FastAPI** (o cualquier otra aplicación ASGI) en una máquina de servidor remota es un programa de servidor ASGI como **Uvicorn**, que es el que viene por defecto en el comando `fastapi`.
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
### Basado en estándares abiertos { #based-on-open-standards }
|
||||
|
||||
* <a href="https://github.com/OAI/OpenAPI-Specification" class="external-link" target="_blank"><strong>OpenAPI</strong></a> para la creación de APIs, incluyendo declaraciones de <abbr title="también conocido como: endpoints, rutas">path</abbr> <abbr title="también conocido como métodos HTTP, como POST, GET, PUT, DELETE">operations</abbr>, parámetros, request bodies, seguridad, etc.
|
||||
* <a href="https://github.com/OAI/OpenAPI-Specification" class="external-link" target="_blank"><strong>OpenAPI</strong></a> para la creación de APIs, incluyendo declaraciones de <dfn title="también conocido como: endpoints, rutas">path</dfn> <dfn title="también conocido como métodos HTTP, como POST, GET, PUT, DELETE">operations</dfn>, parámetros, request bodies, seguridad, etc.
|
||||
* Documentación automática de modelos de datos con <a href="https://json-schema.org/" class="external-link" target="_blank"><strong>JSON Schema</strong></a> (ya que OpenAPI en sí mismo está basado en JSON Schema).
|
||||
* Diseñado alrededor de estos estándares, tras un estudio meticuloso. En lugar de ser una capa adicional.
|
||||
* Esto también permite el uso de **generación de código cliente automática** en muchos idiomas.
|
||||
|
|
@ -105,8 +105,8 @@ Pero por defecto, todo **"simplemente funciona"**.
|
|||
|
||||
* Validación para la mayoría (¿o todas?) de los **tipos de datos** de Python, incluyendo:
|
||||
* Objetos JSON (`dict`).
|
||||
* Array JSON (`list`) definiendo tipos de elementos.
|
||||
* Campos de cadena de caracteres (`str`), definiendo longitudes mínimas y máximas.
|
||||
* array JSON (`list`) definiendo tipos de elementos.
|
||||
* Campos de string (`str`), definiendo longitudes mínimas y máximas.
|
||||
* Números (`int`, `float`) con valores mínimos y máximos, etc.
|
||||
|
||||
* Validación para tipos más exóticos, como:
|
||||
|
|
@ -136,7 +136,7 @@ Todo construido como herramientas y componentes reutilizables que son fáciles d
|
|||
|
||||
### Inyección de dependencias { #dependency-injection }
|
||||
|
||||
FastAPI incluye un sistema de <abbr title='también conocido como "componentes", "recursos", "servicios", "proveedores"'><strong>Inyección de Dependencias</strong></abbr> extremadamente fácil de usar, pero extremadamente potente.
|
||||
FastAPI incluye un sistema de <dfn title='también conocido como "componentes", "recursos", "servicios", "proveedores"'><strong>Inyección de Dependencias</strong></dfn> extremadamente fácil de usar, pero extremadamente potente.
|
||||
|
||||
* Incluso las dependencias pueden tener dependencias, creando una jerarquía o **"grafo de dependencias"**.
|
||||
* Todo **manejado automáticamente** por el framework.
|
||||
|
|
@ -153,8 +153,8 @@ Cualquier integración está diseñada para ser tan simple de usar (con dependen
|
|||
|
||||
### Probado { #tested }
|
||||
|
||||
* 100% de <abbr title="La cantidad de código que se prueba automáticamente">cobertura de tests</abbr>.
|
||||
* 100% <abbr title="Anotaciones de tipos en Python, con esto tu editor y herramientas externas pueden ofrecerte mejor soporte">anotada con tipos</abbr> code base.
|
||||
* 100% de <dfn title="La cantidad de código que se prueba automáticamente">cobertura de tests</dfn>.
|
||||
* 100% <dfn title="Anotaciones de tipos en Python, con esto tu editor y herramientas externas pueden ofrecerte mejor soporte">anotada con tipos</dfn> code base.
|
||||
* Usado en aplicaciones en producción.
|
||||
|
||||
## Funcionalidades de Starlette { #starlette-features }
|
||||
|
|
@ -173,7 +173,7 @@ Con **FastAPI** obtienes todas las funcionalidades de **Starlette** (ya que Fast
|
|||
* **CORS**, GZip, archivos estáticos, responses en streaming.
|
||||
* Soporte para **Session y Cookie**.
|
||||
* Cobertura de tests del 100%.
|
||||
* code base completamente anotada con tipos.
|
||||
* code base 100% anotada con tipos.
|
||||
|
||||
## Funcionalidades de Pydantic { #pydantic-features }
|
||||
|
||||
|
|
@ -190,7 +190,7 @@ Con **FastAPI** obtienes todas las funcionalidades de **Pydantic** (ya que FastA
|
|||
* **Sin complicaciones**:
|
||||
* Sin micro-lenguaje de definición de esquemas nuevo que aprender.
|
||||
* Si conoces los tipos en Python sabes cómo usar Pydantic.
|
||||
* Se lleva bien con tu **<abbr title="Integrated Development Environment – Entorno de Desarrollo Integrado: similar a un editor de código">IDE</abbr>/<abbr title="Un programa que verifica errores de código">linter</abbr>/cerebro**:
|
||||
* Se lleva bien con tu **<abbr title="Integrated Development Environment – Entorno de Desarrollo Integrado: similar a un editor de código">IDE</abbr>/<dfn title="Un programa que verifica errores de código">linter</dfn>/cerebro**:
|
||||
* Porque las estructuras de datos de pydantic son solo instances de clases que defines; autocompletado, linting, mypy y tu intuición deberían funcionar correctamente con tus datos validados.
|
||||
* Valida **estructuras complejas**:
|
||||
* Uso de modelos jerárquicos de Pydantic, `List` y `Dict` de `typing` de Python, etc.
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@ En muchos casos solo copiarán un fragmento del código, pero eso no es suficien
|
|||
|
||||
### Sugerir soluciones { #suggest-solutions }
|
||||
|
||||
* Después de poder entender la pregunta, puedes darles un posible **respuesta**.
|
||||
* Después de poder entender la pregunta, puedes darles una posible **respuesta**.
|
||||
|
||||
* En muchos casos, es mejor entender su **problema subyacente o caso de uso**, porque podría haber una mejor manera de resolverlo que lo que están intentando hacer.
|
||||
|
||||
|
|
@ -247,9 +247,9 @@ Las conversaciones en los sistemas de chat tampoco son tan fácilmente buscables
|
|||
|
||||
Por otro lado, hay miles de usuarios en los sistemas de chat, por lo que hay muchas posibilidades de que encuentres a alguien con quien hablar allí, casi todo el tiempo. 😄
|
||||
|
||||
## Patrocina al autor { #sponsor-the-author }
|
||||
## Hazte sponsor del autor { #sponsor-the-author }
|
||||
|
||||
Si tu **producto/empresa** depende de o está relacionado con **FastAPI** y quieres llegar a sus usuarios, puedes patrocinar al autor (a mí) a través de <a href="https://github.com/sponsors/tiangolo" class="external-link" target="_blank">GitHub sponsors</a>. Según el nivel, podrías obtener algunos beneficios extra, como una insignia en la documentación. 🎁
|
||||
Si tu **producto/empresa** depende de o está relacionado con **FastAPI** y quieres llegar a sus usuarios, puedes hacerte sponsor del autor (de mí) a través de <a href="https://github.com/sponsors/tiangolo" class="external-link" target="_blank">GitHub sponsors</a>. Según el nivel, podrías obtener algunos beneficios extra, como una insignia en la documentación. 🎁
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ Pero si por alguna razón tus clientes dependen del comportamiento anterior, pue
|
|||
|
||||
Por ejemplo, puedes crear una subclase de `HTTPBearer` que devuelva un error `403 Forbidden` en lugar del `401 Unauthorized` por defecto:
|
||||
|
||||
{* ../../docs_src/authentication_error_status_code/tutorial001_an_py39.py hl[9:13] *}
|
||||
{* ../../docs_src/authentication_error_status_code/tutorial001_an_py310.py hl[9:13] *}
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ Puedes usar fácilmente las mismas configuraciones de Pydantic para configurar t
|
|||
|
||||
Por ejemplo:
|
||||
|
||||
{* ../../docs_src/conditional_openapi/tutorial001_py39.py hl[6,11] *}
|
||||
{* ../../docs_src/conditional_openapi/tutorial001_py310.py hl[6,11] *}
|
||||
|
||||
Aquí declaramos la configuración `openapi_url` con el mismo valor por defecto de `"/openapi.json"`.
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ Sin cambiar la configuración, el resaltado de sintaxis está activado por defec
|
|||
|
||||
Pero puedes desactivarlo estableciendo `syntaxHighlight` en `False`:
|
||||
|
||||
{* ../../docs_src/configure_swagger_ui/tutorial001_py39.py hl[3] *}
|
||||
{* ../../docs_src/configure_swagger_ui/tutorial001_py310.py hl[3] *}
|
||||
|
||||
...y entonces Swagger UI ya no mostrará el resaltado de sintaxis:
|
||||
|
||||
|
|
@ -28,17 +28,17 @@ Pero puedes desactivarlo estableciendo `syntaxHighlight` en `False`:
|
|||
|
||||
De la misma manera, podrías configurar el tema del resaltado de sintaxis con la clave `"syntaxHighlight.theme"` (ten en cuenta que tiene un punto en el medio):
|
||||
|
||||
{* ../../docs_src/configure_swagger_ui/tutorial002_py39.py hl[3] *}
|
||||
{* ../../docs_src/configure_swagger_ui/tutorial002_py310.py hl[3] *}
|
||||
|
||||
Esa configuración cambiaría el tema de color del resaltado de sintaxis:
|
||||
|
||||
<img src="/img/tutorial/extending-openapi/image04.png">
|
||||
|
||||
## Cambiar los parámetros predeterminados de Swagger UI { #change-default-swagger-ui-parameters }
|
||||
## Cambiar los parámetros por defecto de Swagger UI { #change-default-swagger-ui-parameters }
|
||||
|
||||
FastAPI incluye algunos parámetros de configuración predeterminados apropiados para la mayoría de los casos de uso.
|
||||
FastAPI incluye algunos parámetros de configuración por defecto apropiados para la mayoría de los casos de uso.
|
||||
|
||||
Incluye estas configuraciones predeterminadas:
|
||||
Incluye estas configuraciones por defecto:
|
||||
|
||||
{* ../../fastapi/openapi/docs.py ln[9:24] hl[18:24] *}
|
||||
|
||||
|
|
@ -46,7 +46,7 @@ Puedes sobrescribir cualquiera de ellos estableciendo un valor diferente en el a
|
|||
|
||||
Por ejemplo, para desactivar `deepLinking` podrías pasar estas configuraciones a `swagger_ui_parameters`:
|
||||
|
||||
{* ../../docs_src/configure_swagger_ui/tutorial003_py39.py hl[3] *}
|
||||
{* ../../docs_src/configure_swagger_ui/tutorial003_py310.py hl[3] *}
|
||||
|
||||
## Otros parámetros de Swagger UI { #other-swagger-ui-parameters }
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ El primer paso es desactivar la documentación automática, ya que por defecto,
|
|||
|
||||
Para desactivarlos, establece sus URLs en `None` cuando crees tu aplicación de `FastAPI`:
|
||||
|
||||
{* ../../docs_src/custom_docs_ui/tutorial001_py39.py hl[8] *}
|
||||
{* ../../docs_src/custom_docs_ui/tutorial001_py310.py hl[8] *}
|
||||
|
||||
### Incluye la documentación personalizada { #include-the-custom-docs }
|
||||
|
||||
|
|
@ -34,7 +34,7 @@ Puedes reutilizar las funciones internas de FastAPI para crear las páginas HTML
|
|||
|
||||
Y de manera similar para ReDoc...
|
||||
|
||||
{* ../../docs_src/custom_docs_ui/tutorial001_py39.py hl[2:6,11:19,22:24,27:33] *}
|
||||
{* ../../docs_src/custom_docs_ui/tutorial001_py310.py hl[2:6,11:19,22:24,27:33] *}
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
|
|
@ -50,7 +50,7 @@ Swagger UI lo manejará detrás de escena para ti, pero necesita este auxiliar d
|
|||
|
||||
Ahora, para poder probar que todo funciona, crea una *path operation*:
|
||||
|
||||
{* ../../docs_src/custom_docs_ui/tutorial001_py39.py hl[36:38] *}
|
||||
{* ../../docs_src/custom_docs_ui/tutorial001_py310.py hl[36:38] *}
|
||||
|
||||
### Pruébalo { #test-it }
|
||||
|
||||
|
|
@ -118,7 +118,7 @@ Después de eso, tu estructura de archivos podría verse así:
|
|||
* Importa `StaticFiles`.
|
||||
* "Monta" una instance de `StaticFiles()` en un path específico.
|
||||
|
||||
{* ../../docs_src/custom_docs_ui/tutorial002_py39.py hl[7,11] *}
|
||||
{* ../../docs_src/custom_docs_ui/tutorial002_py310.py hl[7,11] *}
|
||||
|
||||
### Prueba los archivos estáticos { #test-the-static-files }
|
||||
|
||||
|
|
@ -144,7 +144,7 @@ Igual que cuando usas un CDN personalizado, el primer paso es desactivar la docu
|
|||
|
||||
Para desactivarlos, establece sus URLs en `None` cuando crees tu aplicación de `FastAPI`:
|
||||
|
||||
{* ../../docs_src/custom_docs_ui/tutorial002_py39.py hl[9] *}
|
||||
{* ../../docs_src/custom_docs_ui/tutorial002_py310.py hl[9] *}
|
||||
|
||||
### Incluye la documentación personalizada para archivos estáticos { #include-the-custom-docs-for-static-files }
|
||||
|
||||
|
|
@ -160,7 +160,7 @@ Nuevamente, puedes reutilizar las funciones internas de FastAPI para crear las p
|
|||
|
||||
Y de manera similar para ReDoc...
|
||||
|
||||
{* ../../docs_src/custom_docs_ui/tutorial002_py39.py hl[2:6,14:22,25:27,30:36] *}
|
||||
{* ../../docs_src/custom_docs_ui/tutorial002_py310.py hl[2:6,14:22,25:27,30:36] *}
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
|
|
@ -176,7 +176,7 @@ Swagger UI lo manejará detrás de escena para ti, pero necesita este auxiliar d
|
|||
|
||||
Ahora, para poder probar que todo funciona, crea una *path operation*:
|
||||
|
||||
{* ../../docs_src/custom_docs_ui/tutorial002_py39.py hl[39:41] *}
|
||||
{* ../../docs_src/custom_docs_ui/tutorial002_py310.py hl[39:41] *}
|
||||
|
||||
### Prueba la UI de Archivos Estáticos { #test-static-files-ui }
|
||||
|
||||
|
|
|
|||
|
|
@ -43,19 +43,19 @@ Por ejemplo, vamos a añadir <a href="https://github.com/Rebilly/ReDoc/blob/mast
|
|||
|
||||
Primero, escribe toda tu aplicación **FastAPI** como normalmente:
|
||||
|
||||
{* ../../docs_src/extending_openapi/tutorial001_py39.py hl[1,4,7:9] *}
|
||||
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[1,4,7:9] *}
|
||||
|
||||
### Generar el esquema de OpenAPI { #generate-the-openapi-schema }
|
||||
|
||||
Luego, usa la misma función de utilidad para generar el esquema de OpenAPI, dentro de una función `custom_openapi()`:
|
||||
|
||||
{* ../../docs_src/extending_openapi/tutorial001_py39.py hl[2,15:21] *}
|
||||
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[2,15:21] *}
|
||||
|
||||
### Modificar el esquema de OpenAPI { #modify-the-openapi-schema }
|
||||
|
||||
Ahora puedes añadir la extensión de ReDoc, agregando un `x-logo` personalizado al "objeto" `info` en el esquema de OpenAPI:
|
||||
|
||||
{* ../../docs_src/extending_openapi/tutorial001_py39.py hl[22:24] *}
|
||||
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[22:24] *}
|
||||
|
||||
### Cachear el esquema de OpenAPI { #cache-the-openapi-schema }
|
||||
|
||||
|
|
@ -65,13 +65,13 @@ De esa forma, tu aplicación no tendrá que generar el esquema cada vez que un u
|
|||
|
||||
Se generará solo una vez, y luego se usará el mismo esquema cacheado para las siguientes requests.
|
||||
|
||||
{* ../../docs_src/extending_openapi/tutorial001_py39.py hl[13:14,25:26] *}
|
||||
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[13:14,25:26] *}
|
||||
|
||||
### Sobrescribir el método { #override-the-method }
|
||||
|
||||
Ahora puedes reemplazar el método `.openapi()` por tu nueva función.
|
||||
|
||||
{* ../../docs_src/extending_openapi/tutorial001_py39.py hl[29] *}
|
||||
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[29] *}
|
||||
|
||||
### Revisa { #check-it }
|
||||
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ Dependiendo de tu caso de uso, podrías preferir usar un paquete diferente, pero
|
|||
|
||||
Aquí tienes una pequeña vista previa de cómo podrías integrar Strawberry con FastAPI:
|
||||
|
||||
{* ../../docs_src/graphql_/tutorial001_py39.py hl[3,22,25] *}
|
||||
{* ../../docs_src/graphql_/tutorial001_py310.py hl[3,22,25] *}
|
||||
|
||||
Puedes aprender más sobre Strawberry en la <a href="https://strawberry.rocks/" class="external-link" target="_blank">documentación de Strawberry</a>.
|
||||
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ Probablemente el caso principal para esto es si ya tienes algún código cliente
|
|||
|
||||
En ese caso, puedes desactivar esta funcionalidad en **FastAPI**, con el parámetro `separate_input_output_schemas=False`.
|
||||
|
||||
/// info
|
||||
/// info | Información
|
||||
|
||||
El soporte para `separate_input_output_schemas` fue agregado en FastAPI `0.102.0`. 🤓
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# Probando una Base de Datos { #testing-a-database }
|
||||
# Escribiendo pruebas para una base de datos { #testing-a-database }
|
||||
|
||||
Puedes estudiar sobre bases de datos, SQL y SQLModel en la <a href="https://sqlmodel.tiangolo.com/" class="external-link" target="_blank">documentación de SQLModel</a>. 🤓
|
||||
|
||||
Hay un mini <a href="https://sqlmodel.tiangolo.com/tutorial/fastapi/" class="external-link" target="_blank">tutorial sobre el uso de SQLModel con FastAPI</a>. ✨
|
||||
|
||||
Ese tutorial incluye una sección sobre <a href="https://sqlmodel.tiangolo.com/tutorial/fastapi/tests/" class="external-link" target="_blank">cómo probar bases de datos SQL</a>. 😎
|
||||
Ese tutorial incluye una sección sobre <a href="https://sqlmodel.tiangolo.com/tutorial/fastapi/tests/" class="external-link" target="_blank">escribir pruebas para bases de datos SQL</a>. 😎
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ Las funcionalidades clave son:
|
|||
* **Rápido**: Muy alto rendimiento, a la par con **NodeJS** y **Go** (gracias a Starlette y Pydantic). [Uno de los frameworks Python más rápidos disponibles](#performance).
|
||||
* **Rápido de programar**: Aumenta la velocidad para desarrollar funcionalidades en aproximadamente un 200% a 300%. *
|
||||
* **Menos bugs**: Reduce en aproximadamente un 40% los errores inducidos por humanos (desarrolladores). *
|
||||
* **Intuitivo**: Gran soporte para editores. <abbr title="también conocido como auto-complete, autocompletado, IntelliSense">Autocompletado</abbr> en todas partes. Menos tiempo depurando.
|
||||
* **Intuitivo**: Gran soporte para editores. <dfn title="también conocido como: autocompletado, IntelliSense">Autocompletado</dfn> en todas partes. Menos tiempo depurando.
|
||||
* **Fácil**: Diseñado para ser fácil de usar y aprender. Menos tiempo leyendo documentación.
|
||||
* **Corto**: Minimiza la duplicación de código. Múltiples funcionalidades desde cada declaración de parámetro. Menos bugs.
|
||||
* **Robusto**: Obtén código listo para producción. Con documentación interactiva automática.
|
||||
|
|
@ -368,7 +368,7 @@ item: Item
|
|||
* Validación de datos:
|
||||
* Errores automáticos y claros cuando los datos son inválidos.
|
||||
* Validación incluso para objetos JSON profundamente anidados.
|
||||
* <abbr title="también conocido como: serialización, parsing, marshalling">Conversión</abbr> de datos de entrada: de la red a los datos y tipos de Python. Leyendo desde:
|
||||
* <dfn title="también conocido como: serialización, parsing, marshalling">Conversión</dfn> de datos de entrada: de la red a los datos y tipos de Python. Leyendo desde:
|
||||
* JSON.
|
||||
* Parámetros de path.
|
||||
* Parámetros de query.
|
||||
|
|
@ -376,7 +376,7 @@ item: Item
|
|||
* Headers.
|
||||
* Forms.
|
||||
* Archivos.
|
||||
* <abbr title="también conocido como: serialización, parsing, marshalling">Conversión</abbr> de datos de salida: convirtiendo de datos y tipos de Python a datos de red (como JSON):
|
||||
* <dfn title="también conocido como: serialización, parsing, marshalling">Conversión</dfn> de datos de salida: convirtiendo de datos y tipos de Python a datos de red (como JSON):
|
||||
* Convertir tipos de Python (`str`, `int`, `float`, `bool`, `list`, etc).
|
||||
* Objetos `datetime`.
|
||||
* Objetos `UUID`.
|
||||
|
|
@ -439,7 +439,7 @@ Para un ejemplo más completo incluyendo más funcionalidades, ve al <a href="ht
|
|||
|
||||
* Declaración de **parámetros** desde otros lugares diferentes como: **headers**, **cookies**, **campos de formulario** y **archivos**.
|
||||
* Cómo establecer **restricciones de validación** como `maximum_length` o `regex`.
|
||||
* Un sistema de **<abbr title="también conocido como componentes, recursos, proveedores, servicios, inyectables">Inyección de Dependencias</abbr>** muy poderoso y fácil de usar.
|
||||
* Un sistema de **<dfn title="también conocido como: componentes, recursos, proveedores, servicios, inyectables">Inyección de Dependencias</dfn>** muy poderoso y fácil de usar.
|
||||
* Seguridad y autenticación, incluyendo soporte para **OAuth2** con **tokens JWT** y autenticación **HTTP Basic**.
|
||||
* Técnicas más avanzadas (pero igualmente fáciles) para declarar **modelos JSON profundamente anidados** (gracias a Pydantic).
|
||||
* Integración con **GraphQL** usando <a href="https://strawberry.rocks" class="external-link" target="_blank">Strawberry</a> y otros paquetes.
|
||||
|
|
@ -524,7 +524,7 @@ Usadas por Starlette:
|
|||
|
||||
* <a href="https://www.python-httpx.org" target="_blank"><code>httpx</code></a> - Requerido si deseas usar el `TestClient`.
|
||||
* <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - Requerido si deseas usar la configuración de plantilla por defecto.
|
||||
* <a href="https://github.com/Kludex/python-multipart" target="_blank"><code>python-multipart</code></a> - Requerido si deseas soportar <abbr title="convertir el string que viene de un request HTTP en datos de Python">"parsing"</abbr> de forms, con `request.form()`.
|
||||
* <a href="https://github.com/Kludex/python-multipart" target="_blank"><code>python-multipart</code></a> - Requerido si deseas soportar form <dfn title="convertir el string que viene de un request HTTP en datos de Python">"parsing"</dfn>, con `request.form()`.
|
||||
|
||||
Usadas por FastAPI:
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ Puedes usar esta plantilla para comenzar, ya que incluye gran parte de la config
|
|||
|
||||
Repositorio de GitHub: <a href="https://github.com/tiangolo/full-stack-fastapi-template" class="external-link" target="_blank">Plantilla Full Stack FastAPI</a>
|
||||
|
||||
## Plantilla Full Stack FastAPI - Tecnología y Funcionalidades { #full-stack-fastapi-template-technology-stack-and-features }
|
||||
## Plantilla Full Stack FastAPI - Stack de tecnología y funcionalidades { #full-stack-fastapi-template-technology-stack-and-features }
|
||||
|
||||
- ⚡ [**FastAPI**](https://fastapi.tiangolo.com/es) para la API del backend en Python.
|
||||
- 🧰 [SQLModel](https://sqlmodel.tiangolo.com) para las interacciones con bases de datos SQL en Python (ORM).
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
Python tiene soporte para "anotaciones de tipos" opcionales (también llamadas "type hints").
|
||||
|
||||
Estas **"anotaciones de tipos"** o type hints son una sintaxis especial que permite declarar el <abbr title="por ejemplo: str, int, float, bool">tipo</abbr> de una variable.
|
||||
Estas **"anotaciones de tipos"** o type hints son una sintaxis especial que permite declarar el <dfn title="por ejemplo: str, int, float, bool">tipo</dfn> de una variable.
|
||||
|
||||
Al declarar tipos para tus variables, los editores y herramientas te pueden proporcionar un mejor soporte.
|
||||
|
||||
|
|
@ -22,7 +22,7 @@ Si eres un experto en Python, y ya sabes todo sobre las anotaciones de tipos, sa
|
|||
|
||||
Comencemos con un ejemplo simple:
|
||||
|
||||
{* ../../docs_src/python_types/tutorial001_py39.py *}
|
||||
{* ../../docs_src/python_types/tutorial001_py310.py *}
|
||||
|
||||
Llamar a este programa genera:
|
||||
|
||||
|
|
@ -34,9 +34,9 @@ La función hace lo siguiente:
|
|||
|
||||
* Toma un `first_name` y `last_name`.
|
||||
* Convierte la primera letra de cada uno a mayúsculas con `title()`.
|
||||
* <abbr title="Los une, como uno. Con el contenido de uno después del otro.">Concatena</abbr> ambos con un espacio en el medio.
|
||||
* <dfn title="Los une, como uno. Con el contenido de uno después del otro.">Concatena</dfn> ambos con un espacio en el medio.
|
||||
|
||||
{* ../../docs_src/python_types/tutorial001_py39.py hl[2] *}
|
||||
{* ../../docs_src/python_types/tutorial001_py310.py hl[2] *}
|
||||
|
||||
### Edítalo { #edit-it }
|
||||
|
||||
|
|
@ -78,7 +78,7 @@ Eso es todo.
|
|||
|
||||
Esas son las "anotaciones de tipos":
|
||||
|
||||
{* ../../docs_src/python_types/tutorial002_py39.py hl[1] *}
|
||||
{* ../../docs_src/python_types/tutorial002_py310.py hl[1] *}
|
||||
|
||||
Eso no es lo mismo que declarar valores predeterminados como sería con:
|
||||
|
||||
|
|
@ -106,7 +106,7 @@ Con eso, puedes desplazarte, viendo las opciones, hasta que encuentres la que "t
|
|||
|
||||
Revisa esta función, ya tiene anotaciones de tipos:
|
||||
|
||||
{* ../../docs_src/python_types/tutorial003_py39.py hl[1] *}
|
||||
{* ../../docs_src/python_types/tutorial003_py310.py hl[1] *}
|
||||
|
||||
Porque el editor conoce los tipos de las variables, no solo obtienes autocompletado, también obtienes chequeo de errores:
|
||||
|
||||
|
|
@ -114,7 +114,7 @@ Porque el editor conoce los tipos de las variables, no solo obtienes autocomplet
|
|||
|
||||
Ahora sabes que debes corregirlo, convertir `age` a un string con `str(age)`:
|
||||
|
||||
{* ../../docs_src/python_types/tutorial004_py39.py hl[2] *}
|
||||
{* ../../docs_src/python_types/tutorial004_py310.py hl[2] *}
|
||||
|
||||
## Declaración de tipos { #declaring-types }
|
||||
|
||||
|
|
@ -133,29 +133,32 @@ Puedes usar, por ejemplo:
|
|||
* `bool`
|
||||
* `bytes`
|
||||
|
||||
{* ../../docs_src/python_types/tutorial005_py39.py hl[1] *}
|
||||
{* ../../docs_src/python_types/tutorial005_py310.py hl[1] *}
|
||||
|
||||
### Tipos genéricos con parámetros de tipo { #generic-types-with-type-parameters }
|
||||
### Módulo `typing` { #typing-module }
|
||||
|
||||
Hay algunas estructuras de datos que pueden contener otros valores, como `dict`, `list`, `set` y `tuple`. Y los valores internos también pueden tener su propio tipo.
|
||||
Para algunos casos adicionales, podrías necesitar importar algunas cosas del módulo `typing` de la standard library, por ejemplo cuando quieres declarar que algo tiene "cualquier tipo", puedes usar `Any` de `typing`:
|
||||
|
||||
Estos tipos que tienen tipos internos se denominan tipos "**genéricos**". Y es posible declararlos, incluso con sus tipos internos.
|
||||
```python
|
||||
from typing import Any
|
||||
|
||||
Para declarar esos tipos y los tipos internos, puedes usar el módulo estándar de Python `typing`. Existe específicamente para soportar estas anotaciones de tipos.
|
||||
|
||||
#### Versiones más recientes de Python { #newer-versions-of-python }
|
||||
def some_function(data: Any):
|
||||
print(data)
|
||||
```
|
||||
|
||||
La sintaxis que utiliza `typing` es **compatible** con todas las versiones, desde Python 3.6 hasta las versiones más recientes, incluyendo Python 3.9, Python 3.10, etc.
|
||||
### Tipos genéricos { #generic-types }
|
||||
|
||||
A medida que avanza Python, las **versiones más recientes** vienen con soporte mejorado para estas anotaciones de tipos y en muchos casos ni siquiera necesitarás importar y usar el módulo `typing` para declarar las anotaciones de tipos.
|
||||
Algunos tipos pueden tomar "parámetros de tipo" entre corchetes, para definir sus tipos internos, por ejemplo una "lista de strings" se declararía `list[str]`.
|
||||
|
||||
Si puedes elegir una versión más reciente de Python para tu proyecto, podrás aprovechar esa simplicidad adicional.
|
||||
Estos tipos que pueden tomar parámetros de tipo se llaman **Tipos Genéricos** o **Genéricos**.
|
||||
|
||||
En toda la documentación hay ejemplos compatibles con cada versión de Python (cuando hay una diferencia).
|
||||
Puedes usar los mismos tipos integrados como genéricos (con corchetes y tipos dentro):
|
||||
|
||||
Por ejemplo, "**Python 3.6+**" significa que es compatible con Python 3.6 o superior (incluyendo 3.7, 3.8, 3.9, 3.10, etc). Y "**Python 3.9+**" significa que es compatible con Python 3.9 o superior (incluyendo 3.10, etc).
|
||||
|
||||
Si puedes usar las **últimas versiones de Python**, utiliza los ejemplos para la última versión, esos tendrán la **mejor y más simple sintaxis**, por ejemplo, "**Python 3.10+**".
|
||||
* `list`
|
||||
* `tuple`
|
||||
* `set`
|
||||
* `dict`
|
||||
|
||||
#### Lista { #list }
|
||||
|
||||
|
|
@ -167,7 +170,7 @@ Como tipo, pon `list`.
|
|||
|
||||
Como la lista es un tipo que contiene algunos tipos internos, los pones entre corchetes:
|
||||
|
||||
{* ../../docs_src/python_types/tutorial006_py39.py hl[1] *}
|
||||
{* ../../docs_src/python_types/tutorial006_py310.py hl[1] *}
|
||||
|
||||
/// info | Información
|
||||
|
||||
|
|
@ -193,7 +196,7 @@ Y aún así, el editor sabe que es un `str` y proporciona soporte para eso.
|
|||
|
||||
Harías lo mismo para declarar `tuple`s y `set`s:
|
||||
|
||||
{* ../../docs_src/python_types/tutorial007_py39.py hl[1] *}
|
||||
{* ../../docs_src/python_types/tutorial007_py310.py hl[1] *}
|
||||
|
||||
Esto significa:
|
||||
|
||||
|
|
@ -208,7 +211,7 @@ El primer parámetro de tipo es para las claves del `dict`.
|
|||
|
||||
El segundo parámetro de tipo es para los valores del `dict`:
|
||||
|
||||
{* ../../docs_src/python_types/tutorial008_py39.py hl[1] *}
|
||||
{* ../../docs_src/python_types/tutorial008_py310.py hl[1] *}
|
||||
|
||||
Esto significa:
|
||||
|
||||
|
|
@ -218,46 +221,22 @@ Esto significa:
|
|||
|
||||
#### Union { #union }
|
||||
|
||||
Puedes declarar que una variable puede ser cualquier de **varios tipos**, por ejemplo, un `int` o un `str`.
|
||||
Puedes declarar que una variable puede ser cualquiera de **varios tipos**, por ejemplo, un `int` o un `str`.
|
||||
|
||||
En Python 3.6 y posterior (incluyendo Python 3.10) puedes usar el tipo `Union` de `typing` y poner dentro de los corchetes los posibles tipos a aceptar.
|
||||
Para definirlo usas la <dfn title='también llamado "operador OR a nivel de bits", pero ese significado no es relevante aquí'>barra vertical (`|`)</dfn> para separar ambos tipos.
|
||||
|
||||
En Python 3.10 también hay una **nueva sintaxis** donde puedes poner los posibles tipos separados por una <abbr title='también llamado "operador OR a nivel de bits", pero ese significado no es relevante aquí'>barra vertical (`|`)</abbr>.
|
||||
|
||||
//// tab | Python 3.10+
|
||||
Esto se llama una "unión", porque la variable puede ser cualquiera en la unión de esos dos conjuntos de tipos.
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!> ../../docs_src/python_types/tutorial008b_py310.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!> ../../docs_src/python_types/tutorial008b_py39.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
En ambos casos, esto significa que `item` podría ser un `int` o un `str`.
|
||||
Esto significa que `item` podría ser un `int` o un `str`.
|
||||
|
||||
#### Posiblemente `None` { #possibly-none }
|
||||
|
||||
Puedes declarar que un valor podría tener un tipo, como `str`, pero que también podría ser `None`.
|
||||
|
||||
En Python 3.6 y posteriores (incluyendo Python 3.10) puedes declararlo importando y usando `Optional` del módulo `typing`.
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!../../docs_src/python_types/tutorial009_py39.py!}
|
||||
```
|
||||
|
||||
Usar `Optional[str]` en lugar de solo `str` te permitirá al editor ayudarte a detectar errores donde podrías estar asumiendo que un valor siempre es un `str`, cuando en realidad también podría ser `None`.
|
||||
|
||||
`Optional[Something]` es realmente un atajo para `Union[Something, None]`, son equivalentes.
|
||||
|
||||
Esto también significa que en Python 3.10, puedes usar `Something | None`:
|
||||
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python hl_lines="1"
|
||||
|
|
@ -266,96 +245,7 @@ Esto también significa que en Python 3.10, puedes usar `Something | None`:
|
|||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!> ../../docs_src/python_types/tutorial009_py39.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+ alternativa
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!> ../../docs_src/python_types/tutorial009b_py39.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
#### Uso de `Union` u `Optional` { #using-union-or-optional }
|
||||
|
||||
Si estás usando una versión de Python inferior a 3.10, aquí tienes un consejo desde mi punto de vista muy **subjetivo**:
|
||||
|
||||
* 🚨 Evita usar `Optional[SomeType]`
|
||||
* En su lugar ✨ **usa `Union[SomeType, None]`** ✨.
|
||||
|
||||
Ambos son equivalentes y debajo son lo mismo, pero recomendaría `Union` en lugar de `Optional` porque la palabra "**opcional**" parecería implicar que el valor es opcional, y en realidad significa "puede ser `None`", incluso si no es opcional y aún es requerido.
|
||||
|
||||
Creo que `Union[SomeType, None]` es más explícito sobre lo que significa.
|
||||
|
||||
Se trata solo de las palabras y nombres. Pero esas palabras pueden afectar cómo tú y tus compañeros de equipo piensan sobre el código.
|
||||
|
||||
Como ejemplo, tomemos esta función:
|
||||
|
||||
{* ../../docs_src/python_types/tutorial009c_py39.py hl[1,4] *}
|
||||
|
||||
El parámetro `name` está definido como `Optional[str]`, pero **no es opcional**, no puedes llamar a la función sin el parámetro:
|
||||
|
||||
```Python
|
||||
say_hi() # ¡Oh, no, esto lanza un error! 😱
|
||||
```
|
||||
|
||||
El parámetro `name` sigue siendo **requerido** (no *opcional*) porque no tiene un valor predeterminado. Aún así, `name` acepta `None` como valor:
|
||||
|
||||
```Python
|
||||
say_hi(name=None) # Esto funciona, None es válido 🎉
|
||||
```
|
||||
|
||||
La buena noticia es que, una vez que estés en Python 3.10, no tendrás que preocuparte por eso, ya que podrás simplemente usar `|` para definir uniones de tipos:
|
||||
|
||||
{* ../../docs_src/python_types/tutorial009c_py310.py hl[1,4] *}
|
||||
|
||||
Y entonces no tendrás que preocuparte por nombres como `Optional` y `Union`. 😎
|
||||
|
||||
#### Tipos genéricos { #generic-types }
|
||||
|
||||
Estos tipos que toman parámetros de tipo en corchetes se llaman **Tipos Genéricos** o **Genéricos**, por ejemplo:
|
||||
|
||||
//// tab | Python 3.10+
|
||||
|
||||
Puedes usar los mismos tipos integrados como genéricos (con corchetes y tipos dentro):
|
||||
|
||||
* `list`
|
||||
* `tuple`
|
||||
* `set`
|
||||
* `dict`
|
||||
|
||||
Y, como con versiones anteriores de Python, desde el módulo `typing`:
|
||||
|
||||
* `Union`
|
||||
* `Optional`
|
||||
* ...y otros.
|
||||
|
||||
En Python 3.10, como alternativa a usar los genéricos `Union` y `Optional`, puedes usar la <abbr title='también llamado "operador OR a nivel de bits", pero ese significado no es relevante aquí'>barra vertical (`|`)</abbr> para declarar uniones de tipos, eso es mucho mejor y más simple.
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+
|
||||
|
||||
Puedes usar los mismos tipos integrados como genéricos (con corchetes y tipos dentro):
|
||||
|
||||
* `list`
|
||||
* `tuple`
|
||||
* `set`
|
||||
* `dict`
|
||||
|
||||
Y generics desde el módulo `typing`:
|
||||
|
||||
* `Union`
|
||||
* `Optional`
|
||||
* ...y otros.
|
||||
|
||||
////
|
||||
Usar `str | None` en lugar de solo `str` te permitirá al editor ayudarte a detectar errores donde podrías estar asumiendo que un valor siempre es un `str`, cuando en realidad también podría ser `None`.
|
||||
|
||||
### Clases como tipos { #classes-as-types }
|
||||
|
||||
|
|
@ -363,11 +253,11 @@ También puedes declarar una clase como el tipo de una variable.
|
|||
|
||||
Digamos que tienes una clase `Person`, con un nombre:
|
||||
|
||||
{* ../../docs_src/python_types/tutorial010_py39.py hl[1:3] *}
|
||||
{* ../../docs_src/python_types/tutorial010_py310.py hl[1:3] *}
|
||||
|
||||
Luego puedes declarar una variable para que sea de tipo `Person`:
|
||||
|
||||
{* ../../docs_src/python_types/tutorial010_py39.py hl[6] *}
|
||||
{* ../../docs_src/python_types/tutorial010_py310.py hl[6] *}
|
||||
|
||||
Y luego, nuevamente, obtienes todo el soporte del editor:
|
||||
|
||||
|
|
@ -403,19 +293,13 @@ Para saber más sobre <a href="https://docs.pydantic.dev/" class="external-link"
|
|||
|
||||
Verás mucho más de todo esto en práctica en el [Tutorial - Guía del Usuario](tutorial/index.md){.internal-link target=_blank}.
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
Pydantic tiene un comportamiento especial cuando utilizas `Optional` o `Union[Something, None]` sin un valor por defecto, puedes leer más sobre ello en la documentación de Pydantic sobre <a href="https://docs.pydantic.dev/2.3/usage/models/#required-fields" class="external-link" target="_blank">Required Optional fields</a>.
|
||||
|
||||
///
|
||||
|
||||
## Anotaciones de tipos con metadata { #type-hints-with-metadata-annotations }
|
||||
|
||||
Python también tiene una funcionalidad que permite poner **<abbr title="Datos sobre los datos, en este caso, información sobre el tipo, por ejemplo, una descripción.">metadatos</abbr> adicional** en estas anotaciones de tipos usando `Annotated`.
|
||||
Python también tiene una funcionalidad que permite poner **<dfn title="Datos sobre los datos, en este caso, información sobre el tipo, por ejemplo, una descripción.">metadata</dfn> adicional** en estas anotaciones de tipos usando `Annotated`.
|
||||
|
||||
Desde Python 3.9, `Annotated` es parte de la standard library, así que puedes importarlo desde `typing`.
|
||||
Puedes importar `Annotated` desde `typing`.
|
||||
|
||||
{* ../../docs_src/python_types/tutorial013_py39.py hl[1,4] *}
|
||||
{* ../../docs_src/python_types/tutorial013_py310.py hl[1,4] *}
|
||||
|
||||
Python en sí no hace nada con este `Annotated`. Y para los editores y otras herramientas, el tipo sigue siendo `str`.
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
/// details | 🌐 Traducción por IA y humanos
|
||||
|
||||
Esta traducción fue hecha por IA guiada por humanos. 🤝
|
||||
|
||||
Podría tener errores al interpretar el significado original, o sonar poco natural, etc. 🤖
|
||||
|
||||
Puedes mejorar esta traducción [ayudándonos a guiar mejor al LLM de IA](https://fastapi.tiangolo.com/es/contributing/#translations).
|
||||
|
||||
[Versión en inglés](ENGLISH_VERSION_URL)
|
||||
|
||||
///
|
||||
|
|
@ -15,7 +15,7 @@ Esto incluye, por ejemplo:
|
|||
|
||||
Primero, importa `BackgroundTasks` y define un parámetro en tu *path operation function* con una declaración de tipo de `BackgroundTasks`:
|
||||
|
||||
{* ../../docs_src/background_tasks/tutorial001_py39.py hl[1,13] *}
|
||||
{* ../../docs_src/background_tasks/tutorial001_py310.py hl[1,13] *}
|
||||
|
||||
**FastAPI** creará el objeto de tipo `BackgroundTasks` por ti y lo pasará como ese parámetro.
|
||||
|
||||
|
|
@ -31,13 +31,13 @@ En este caso, la función de tarea escribirá en un archivo (simulando el envío
|
|||
|
||||
Y como la operación de escritura no usa `async` y `await`, definimos la función con un `def` normal:
|
||||
|
||||
{* ../../docs_src/background_tasks/tutorial001_py39.py hl[6:9] *}
|
||||
{* ../../docs_src/background_tasks/tutorial001_py310.py hl[6:9] *}
|
||||
|
||||
## Agregar la tarea en segundo plano { #add-the-background-task }
|
||||
|
||||
Dentro de tu *path operation function*, pasa tu función de tarea al objeto de *background tasks* con el método `.add_task()`:
|
||||
|
||||
{* ../../docs_src/background_tasks/tutorial001_py39.py hl[14] *}
|
||||
{* ../../docs_src/background_tasks/tutorial001_py310.py hl[14] *}
|
||||
|
||||
`.add_task()` recibe como argumentos:
|
||||
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ Puedes crear las *path operations* para ese módulo usando `APIRouter`.
|
|||
|
||||
Lo importas y creas una "instance" de la misma manera que lo harías con la clase `FastAPI`:
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/routers/users.py hl[1,3] title["app/routers/users.py"] *}
|
||||
{* ../../docs_src/bigger_applications/app_an_py310/routers/users.py hl[1,3] title["app/routers/users.py"] *}
|
||||
|
||||
### *Path operations* con `APIRouter` { #path-operations-with-apirouter }
|
||||
|
||||
|
|
@ -93,7 +93,7 @@ Y luego lo usas para declarar tus *path operations*.
|
|||
|
||||
Úsalo de la misma manera que usarías la clase `FastAPI`:
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/routers/users.py hl[6,11,16] title["app/routers/users.py"] *}
|
||||
{* ../../docs_src/bigger_applications/app_an_py310/routers/users.py hl[6,11,16] title["app/routers/users.py"] *}
|
||||
|
||||
Puedes pensar en `APIRouter` como una clase "mini `FastAPI`".
|
||||
|
||||
|
|
@ -117,7 +117,7 @@ Así que las ponemos en su propio módulo `dependencies` (`app/dependencies.py`)
|
|||
|
||||
Ahora utilizaremos una dependencia simple para leer un header `X-Token` personalizado:
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/dependencies.py hl[3,6:8] title["app/dependencies.py"] *}
|
||||
{* ../../docs_src/bigger_applications/app_an_py310/dependencies.py hl[3,6:8] title["app/dependencies.py"] *}
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
|
|
@ -149,7 +149,7 @@ Sabemos que todas las *path operations* en este módulo tienen el mismo:
|
|||
|
||||
Entonces, en lugar de agregar todo eso a cada *path operation*, podemos agregarlo al `APIRouter`.
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/routers/items.py hl[5:10,16,21] title["app/routers/items.py"] *}
|
||||
{* ../../docs_src/bigger_applications/app_an_py310/routers/items.py hl[5:10,16,21] title["app/routers/items.py"] *}
|
||||
|
||||
Como el path de cada *path operation* tiene que empezar con `/`, como en:
|
||||
|
||||
|
|
@ -208,7 +208,7 @@ Y necesitamos obtener la función de dependencia del módulo `app.dependencies`,
|
|||
|
||||
Así que usamos un import relativo con `..` para las dependencias:
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/routers/items.py hl[3] title["app/routers/items.py"] *}
|
||||
{* ../../docs_src/bigger_applications/app_an_py310/routers/items.py hl[3] title["app/routers/items.py"] *}
|
||||
|
||||
#### Cómo funcionan los imports relativos { #how-relative-imports-work }
|
||||
|
||||
|
|
@ -279,7 +279,7 @@ No estamos agregando el prefijo `/items` ni los `tags=["items"]` a cada *path op
|
|||
|
||||
Pero aún podemos agregar _más_ `tags` que se aplicarán a una *path operation* específica, y también algunas `responses` extra específicas para esa *path operation*:
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/routers/items.py hl[30:31] title["app/routers/items.py"] *}
|
||||
{* ../../docs_src/bigger_applications/app_an_py310/routers/items.py hl[30:31] title["app/routers/items.py"] *}
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
|
|
@ -305,13 +305,13 @@ Importas y creas una clase `FastAPI` como normalmente.
|
|||
|
||||
Y podemos incluso declarar [dependencias globales](dependencies/global-dependencies.md){.internal-link target=_blank} que se combinarán con las dependencias para cada `APIRouter`:
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/main.py hl[1,3,7] title["app/main.py"] *}
|
||||
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[1,3,7] title["app/main.py"] *}
|
||||
|
||||
### Importar el `APIRouter` { #import-the-apirouter }
|
||||
|
||||
Ahora importamos los otros submódulos que tienen `APIRouter`s:
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/main.py hl[4:5] title["app/main.py"] *}
|
||||
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[4:5] title["app/main.py"] *}
|
||||
|
||||
Como los archivos `app/routers/users.py` y `app/routers/items.py` son submódulos que son parte del mismo paquete de Python `app`, podemos usar un solo punto `.` para importarlos usando "imports relativos".
|
||||
|
||||
|
|
@ -374,13 +374,13 @@ el `router` de `users` sobrescribiría el de `items` y no podríamos usarlos al
|
|||
|
||||
Así que, para poder usar ambos en el mismo archivo, importamos los submódulos directamente:
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/main.py hl[5] title["app/main.py"] *}
|
||||
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[5] title["app/main.py"] *}
|
||||
|
||||
### Incluir los `APIRouter`s para `users` y `items` { #include-the-apirouters-for-users-and-items }
|
||||
|
||||
Ahora, incluyamos los `router`s de los submódulos `users` y `items`:
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/main.py hl[10:11] title["app/main.py"] *}
|
||||
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[10:11] title["app/main.py"] *}
|
||||
|
||||
/// info | Información
|
||||
|
||||
|
|
@ -420,13 +420,13 @@ Contiene un `APIRouter` con algunas *path operations* de administración que tu
|
|||
|
||||
Para este ejemplo será súper simple. Pero digamos que porque está compartido con otros proyectos en la organización, no podemos modificarlo y agregar un `prefix`, `dependencies`, `tags`, etc. directamente al `APIRouter`:
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/internal/admin.py hl[3] title["app/internal/admin.py"] *}
|
||||
{* ../../docs_src/bigger_applications/app_an_py310/internal/admin.py hl[3] title["app/internal/admin.py"] *}
|
||||
|
||||
Pero aún queremos configurar un `prefix` personalizado al incluir el `APIRouter` para que todas sus *path operations* comiencen con `/admin`, queremos asegurarlo con las `dependencies` que ya tenemos para este proyecto, y queremos incluir `tags` y `responses`.
|
||||
|
||||
Podemos declarar todo eso sin tener que modificar el `APIRouter` original pasando esos parámetros a `app.include_router()`:
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/main.py hl[14:17] title["app/main.py"] *}
|
||||
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[14:17] title["app/main.py"] *}
|
||||
|
||||
De esa manera, el `APIRouter` original permanecerá sin modificar, por lo que aún podemos compartir ese mismo archivo `app/internal/admin.py` con otros proyectos en la organización.
|
||||
|
||||
|
|
@ -447,7 +447,7 @@ También podemos agregar *path operations* directamente a la app de `FastAPI`.
|
|||
|
||||
Aquí lo hacemos... solo para mostrar que podemos 🤷:
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/main.py hl[21:23] title["app/main.py"] *}
|
||||
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[21:23] title["app/main.py"] *}
|
||||
|
||||
y funcionará correctamente, junto con todas las otras *path operations* añadidas con `app.include_router()`.
|
||||
|
||||
|
|
|
|||
|
|
@ -104,12 +104,6 @@ Como, por defecto, los valores singulares se interpretan como parámetros de que
|
|||
q: str | None = None
|
||||
```
|
||||
|
||||
O en Python 3.9:
|
||||
|
||||
```Python
|
||||
q: Union[str, None] = None
|
||||
```
|
||||
|
||||
Por ejemplo:
|
||||
|
||||
{* ../../docs_src/body_multiple_params/tutorial004_an_py310.py hl[28] *}
|
||||
|
|
|
|||
|
|
@ -164,7 +164,7 @@ images: list[Image]
|
|||
|
||||
como en:
|
||||
|
||||
{* ../../docs_src/body_nested_models/tutorial008_py39.py hl[13] *}
|
||||
{* ../../docs_src/body_nested_models/tutorial008_py310.py hl[13] *}
|
||||
|
||||
## Soporte de editor en todas partes { #editor-support-everywhere }
|
||||
|
||||
|
|
@ -194,7 +194,7 @@ Eso es lo que vamos a ver aquí.
|
|||
|
||||
En este caso, aceptarías cualquier `dict` siempre que tenga claves `int` con valores `float`:
|
||||
|
||||
{* ../../docs_src/body_nested_models/tutorial009_py39.py hl[7] *}
|
||||
{* ../../docs_src/body_nested_models/tutorial009_py310.py hl[7] *}
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ Con solo esa declaración de tipo en Python, **FastAPI** hará lo siguiente:
|
|||
* Proporcionar los datos recibidos en el parámetro `item`.
|
||||
* Como lo declaraste en la función como de tipo `Item`, también tendrás todo el soporte del editor (autocompletado, etc.) para todos los atributos y sus tipos.
|
||||
* Generar definiciones de <a href="https://json-schema.org" class="external-link" target="_blank">JSON Schema</a> para tu modelo, que también puedes usar en cualquier otro lugar si tiene sentido para tu proyecto.
|
||||
* Esos esquemas serán parte del esquema de OpenAPI generado y usados por las <abbr title="User Interfaces – Interfaces de usuario">UIs</abbr> de documentación automática.
|
||||
* Esos esquemas serán parte del esquema de OpenAPI generado y usados por las <abbr title="User Interfaces - Interfaces de usuario">UIs</abbr> de documentación automática.
|
||||
|
||||
## Documentación automática { #automatic-docs }
|
||||
|
||||
|
|
@ -155,7 +155,7 @@ Los parámetros de la función se reconocerán de la siguiente manera:
|
|||
|
||||
FastAPI sabrá que el valor de `q` no es requerido debido al valor por defecto `= None`.
|
||||
|
||||
El `str | None` (Python 3.10+) o `Union` en `Union[str, None]` (Python 3.9+) no es utilizado por FastAPI para determinar que el valor no es requerido, sabrá que no es requerido porque tiene un valor por defecto de `= None`.
|
||||
El `str | None` no es utilizado por FastAPI para determinar que el valor no es requerido, sabrá que no es requerido porque tiene un valor por defecto de `= None`.
|
||||
|
||||
Pero agregar las anotaciones de tipos permitirá que tu editor te brinde un mejor soporte y detecte errores.
|
||||
|
||||
|
|
@ -163,4 +163,4 @@ Pero agregar las anotaciones de tipos permitirá que tu editor te brinde un mejo
|
|||
|
||||
## Sin Pydantic { #without-pydantic }
|
||||
|
||||
Si no quieres usar modelos de Pydantic, también puedes usar parámetros **Body**. Consulta la documentación para [Body - Multiple Parameters: Singular values in body](body-multiple-params.md#singular-values-in-body){.internal-link target=_blank}.
|
||||
Si no quieres usar modelos de Pydantic, también puedes usar parámetros **Body**. Consulta la documentación para [Body - Múltiples parámetros: Valores singulares en el body](body-multiple-params.md#singular-values-in-body){.internal-link target=_blank}.
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ Pero incluso si **rellenas los datos** y haces clic en "Execute", como la UI de
|
|||
|
||||
En algunos casos de uso especiales (probablemente no muy comunes), podrías querer **restringir** las cookies que deseas recibir.
|
||||
|
||||
Tu API ahora tiene el poder de controlar su propio <abbr title="Esto es una broma, por si acaso. No tiene nada que ver con los consentimientos de cookies, pero es gracioso que incluso la API ahora pueda rechazar las pobres cookies. Toma una cookie. 🍪">consentimiento de cookies</abbr>. 🤪🍪
|
||||
Tu API ahora tiene el poder de controlar su propio <dfn title="Esto es una broma, por si acaso. No tiene nada que ver con los consentimientos de cookies, pero es gracioso que incluso la API ahora pueda rechazar las pobres cookies. Toma una cookie. 🍪">consentimiento de cookies</dfn>. 🤪🍪
|
||||
|
||||
Puedes usar la configuración del modelo de Pydantic para `prohibir` cualquier campo `extra`:
|
||||
|
||||
|
|
@ -54,9 +54,9 @@ Puedes usar la configuración del modelo de Pydantic para `prohibir` cualquier c
|
|||
|
||||
Si un cliente intenta enviar algunas **cookies extra**, recibirán un response de **error**.
|
||||
|
||||
Pobres banners de cookies con todo su esfuerzo para obtener tu consentimiento para que la <abbr title="Esta es otra broma. No me prestes atención. Toma un café para tu cookie. ☕">API lo rechace</abbr>. 🍪
|
||||
Pobres banners de cookies con todo su esfuerzo para obtener tu consentimiento para que la <dfn title="Esta es otra broma. No me prestes atención. Toma un café para tu cookie. ☕">API lo rechace</dfn>. 🍪
|
||||
|
||||
Por ejemplo, si el cliente intenta enviar una cookie `santa_tracker` con un valor de `good-list-please`, el cliente recibirá un response de **error** que le informa que la cookie `santa_tracker` <abbr title="Santa desaprueba la falta de cookies. 🎅 Está bien, no más bromas de cookies.">no está permitida</abbr>:
|
||||
Por ejemplo, si el cliente intenta enviar una cookie `santa_tracker` con un valor de `good-list-please`, el cliente recibirá un response de **error** que le informa que la `santa_tracker` <dfn title="Santa desaprueba la falta de cookies. 🎅 Está bien, no más bromas de cookies.">cookie no está permitida</dfn>:
|
||||
|
||||
```json
|
||||
{
|
||||
|
|
@ -73,4 +73,4 @@ Por ejemplo, si el cliente intenta enviar una cookie `santa_tracker` con un valo
|
|||
|
||||
## Resumen { #summary }
|
||||
|
||||
Puedes usar **modelos de Pydantic** para declarar <abbr title="Toma una última cookie antes de irte. 🍪">**cookies**</abbr> en **FastAPI**. 😎
|
||||
Puedes usar **modelos de Pydantic** para declarar <dfn title="Toma una última cookie antes de irte. 🍪">**cookies**</dfn> en **FastAPI**. 😎
|
||||
|
|
|
|||
|
|
@ -46,7 +46,8 @@ También puedes especificar si tu backend permite:
|
|||
* Métodos HTTP específicos (`POST`, `PUT`) o todos ellos con el comodín `"*"`.
|
||||
* Headers HTTP específicos o todos ellos con el comodín `"*"`.
|
||||
|
||||
{* ../../docs_src/cors/tutorial001_py39.py hl[2,6:11,13:19] *}
|
||||
{* ../../docs_src/cors/tutorial001_py310.py hl[2,6:11,13:19] *}
|
||||
|
||||
|
||||
Los parámetros predeterminados utilizados por la implementación de `CORSMiddleware` son restrictivos por defecto, por lo que necesitarás habilitar explícitamente orígenes, métodos o headers particulares para que los navegadores estén permitidos de usarlos en un contexto de Cross-Domain.
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ Puedes conectar el depurador en tu editor, por ejemplo con Visual Studio Code o
|
|||
|
||||
En tu aplicación de FastAPI, importa y ejecuta `uvicorn` directamente:
|
||||
|
||||
{* ../../docs_src/debugging/tutorial001_py39.py hl[1,15] *}
|
||||
{* ../../docs_src/debugging/tutorial001_py310.py hl[1,15] *}
|
||||
|
||||
### Acerca de `__name__ == "__main__"` { #about-name-main }
|
||||
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ Ahora puedes declarar tu dependencia usando esta clase.
|
|||
|
||||
Nota cómo escribimos `CommonQueryParams` dos veces en el código anterior:
|
||||
|
||||
//// tab | Python 3.9+
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python
|
||||
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
|
||||
|
|
@ -109,7 +109,7 @@ commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
|
|||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+ sin `Annotated`
|
||||
//// tab | Python 3.10+ sin `Annotated`
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
|
|
@ -137,7 +137,7 @@ Es a partir de este que **FastAPI** extraerá los parámetros declarados y es lo
|
|||
|
||||
En este caso, el primer `CommonQueryParams`, en:
|
||||
|
||||
//// tab | Python 3.9+
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python
|
||||
commons: Annotated[CommonQueryParams, ...
|
||||
|
|
@ -145,7 +145,7 @@ commons: Annotated[CommonQueryParams, ...
|
|||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+ sin `Annotated`
|
||||
//// tab | Python 3.10+ sin `Annotated`
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
|
|
@ -163,7 +163,7 @@ commons: CommonQueryParams ...
|
|||
|
||||
De hecho, podrías escribir simplemente:
|
||||
|
||||
//// tab | Python 3.9+
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python
|
||||
commons: Annotated[Any, Depends(CommonQueryParams)]
|
||||
|
|
@ -171,7 +171,7 @@ commons: Annotated[Any, Depends(CommonQueryParams)]
|
|||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+ sin `Annotated`
|
||||
//// tab | Python 3.10+ sin `Annotated`
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
|
|
@ -197,7 +197,7 @@ Pero declarar el tipo es recomendable, ya que de esa manera tu editor sabrá lo
|
|||
|
||||
Pero ves que estamos teniendo algo de repetición de código aquí, escribiendo `CommonQueryParams` dos veces:
|
||||
|
||||
//// tab | Python 3.9+
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python
|
||||
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
|
||||
|
|
@ -205,7 +205,7 @@ commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
|
|||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+ sin `Annotated`
|
||||
//// tab | Python 3.10+ sin `Annotated`
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
|
|
@ -225,7 +225,7 @@ Para esos casos específicos, puedes hacer lo siguiente:
|
|||
|
||||
En lugar de escribir:
|
||||
|
||||
//// tab | Python 3.9+
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python
|
||||
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
|
||||
|
|
@ -233,7 +233,7 @@ commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
|
|||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+ sin `Annotated`
|
||||
//// tab | Python 3.10+ sin `Annotated`
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
|
|
@ -249,7 +249,7 @@ commons: CommonQueryParams = Depends(CommonQueryParams)
|
|||
|
||||
...escribes:
|
||||
|
||||
//// tab | Python 3.9+
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python
|
||||
commons: Annotated[CommonQueryParams, Depends()]
|
||||
|
|
@ -257,7 +257,7 @@ commons: Annotated[CommonQueryParams, Depends()]
|
|||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+ sin `Annotated`
|
||||
//// tab | Python 3.10+ sin `Annotated`
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ El decorador de *path operation* recibe un argumento opcional `dependencies`.
|
|||
|
||||
Debe ser una `list` de `Depends()`:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial006_an_py39.py hl[19] *}
|
||||
{* ../../docs_src/dependencies/tutorial006_an_py310.py hl[19] *}
|
||||
|
||||
Estas dependencias serán ejecutadas/resueltas de la misma manera que las dependencias normales. Pero su valor (si devuelven alguno) no será pasado a tu *path operation function*.
|
||||
|
||||
|
|
@ -44,13 +44,13 @@ Puedes usar las mismas *funciones* de dependencia que usas normalmente.
|
|||
|
||||
Pueden declarar requisitos de request (como headers) u otras sub-dependencias:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial006_an_py39.py hl[8,13] *}
|
||||
{* ../../docs_src/dependencies/tutorial006_an_py310.py hl[8,13] *}
|
||||
|
||||
### Lanzar excepciones { #raise-exceptions }
|
||||
|
||||
Estas dependencias pueden `raise` excepciones, igual que las dependencias normales:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial006_an_py39.py hl[10,15] *}
|
||||
{* ../../docs_src/dependencies/tutorial006_an_py310.py hl[10,15] *}
|
||||
|
||||
### Valores de retorno { #return-values }
|
||||
|
||||
|
|
@ -58,7 +58,7 @@ Y pueden devolver valores o no, los valores no serán usados.
|
|||
|
||||
Así que, puedes reutilizar una dependencia normal (que devuelve un valor) que ya uses en otro lugar, y aunque el valor no se use, la dependencia será ejecutada:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial006_an_py39.py hl[11,16] *}
|
||||
{* ../../docs_src/dependencies/tutorial006_an_py310.py hl[11,16] *}
|
||||
|
||||
## Dependencias para un grupo de *path operations* { #dependencies-for-a-group-of-path-operations }
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# Dependencias con yield { #dependencies-with-yield }
|
||||
|
||||
FastAPI admite dependencias que realizan algunos <abbr title='a veces también llamado "código de salida", "código de limpieza", "código de teardown", "código de cierre", "código de salida del context manager", etc.'>pasos adicionales después de finalizar</abbr>.
|
||||
FastAPI admite dependencias que realizan algunos <dfn title='a veces también llamado "código de salida", "código de limpieza", "código de teardown", "código de cierre", "código de salida del context manager", etc.'>pasos adicionales después de finalizar</dfn>.
|
||||
|
||||
Para hacer esto, usa `yield` en lugar de `return`, y escribe los pasos adicionales (código) después.
|
||||
|
||||
|
|
@ -29,15 +29,15 @@ Por ejemplo, podrías usar esto para crear una sesión de base de datos y cerrar
|
|||
|
||||
Solo el código anterior e incluyendo la declaración `yield` se ejecuta antes de crear un response:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial007_py39.py hl[2:4] *}
|
||||
{* ../../docs_src/dependencies/tutorial007_py310.py hl[2:4] *}
|
||||
|
||||
El valor generado es lo que se inyecta en *path operations* y otras dependencias:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial007_py39.py hl[4] *}
|
||||
{* ../../docs_src/dependencies/tutorial007_py310.py hl[4] *}
|
||||
|
||||
El código posterior a la declaración `yield` se ejecuta después del response:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial007_py39.py hl[5:6] *}
|
||||
{* ../../docs_src/dependencies/tutorial007_py310.py hl[5:6] *}
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
|
|
@ -57,7 +57,7 @@ Por lo tanto, puedes buscar esa excepción específica dentro de la dependencia
|
|||
|
||||
Del mismo modo, puedes usar `finally` para asegurarte de que los pasos de salida se ejecuten, sin importar si hubo una excepción o no.
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial007_py39.py hl[3,5] *}
|
||||
{* ../../docs_src/dependencies/tutorial007_py310.py hl[3,5] *}
|
||||
|
||||
## Sub-dependencias con `yield` { #sub-dependencies-with-yield }
|
||||
|
||||
|
|
@ -67,7 +67,7 @@ Puedes tener sub-dependencias y "árboles" de sub-dependencias de cualquier tama
|
|||
|
||||
Por ejemplo, `dependency_c` puede tener una dependencia de `dependency_b`, y `dependency_b` de `dependency_a`:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial008_an_py39.py hl[6,14,22] *}
|
||||
{* ../../docs_src/dependencies/tutorial008_an_py310.py hl[6,14,22] *}
|
||||
|
||||
Y todas ellas pueden usar `yield`.
|
||||
|
||||
|
|
@ -75,7 +75,7 @@ En este caso, `dependency_c`, para ejecutar su código de salida, necesita que e
|
|||
|
||||
Y, a su vez, `dependency_b` necesita que el valor de `dependency_a` (aquí llamado `dep_a`) esté disponible para su código de salida.
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial008_an_py39.py hl[18:19,26:27] *}
|
||||
{* ../../docs_src/dependencies/tutorial008_an_py310.py hl[18:19,26:27] *}
|
||||
|
||||
De la misma manera, podrías tener algunas dependencias con `yield` y otras dependencias con `return`, y hacer que algunas de esas dependan de algunas de las otras.
|
||||
|
||||
|
|
@ -109,7 +109,7 @@ Pero está ahí para ti si la necesitas. 🤓
|
|||
|
||||
///
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial008b_an_py39.py hl[18:22,31] *}
|
||||
{* ../../docs_src/dependencies/tutorial008b_an_py310.py hl[18:22,31] *}
|
||||
|
||||
Si quieres capturar excepciones y crear un response personalizado en base a eso, crea un [Manejador de Excepciones Personalizado](../handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank}.
|
||||
|
||||
|
|
@ -117,7 +117,7 @@ Si quieres capturar excepciones y crear un response personalizado en base a eso,
|
|||
|
||||
Si capturas una excepción usando `except` en una dependencia con `yield` y no la lanzas nuevamente (o lanzas una nueva excepción), FastAPI no podrá notar que hubo una excepción, al igual que sucedería con Python normal:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial008c_an_py39.py hl[15:16] *}
|
||||
{* ../../docs_src/dependencies/tutorial008c_an_py310.py hl[15:16] *}
|
||||
|
||||
En este caso, el cliente verá un response *HTTP 500 Internal Server Error* como debería, dado que no estamos lanzando una `HTTPException` o similar, pero el servidor **no tendrá ningún registro** ni ninguna otra indicación de cuál fue el error. 😱
|
||||
|
||||
|
|
@ -127,7 +127,7 @@ Si capturas una excepción en una dependencia con `yield`, a menos que estés la
|
|||
|
||||
Puedes volver a lanzar la misma excepción usando `raise`:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial008d_an_py39.py hl[17] *}
|
||||
{* ../../docs_src/dependencies/tutorial008d_an_py310.py hl[17] *}
|
||||
|
||||
Ahora el cliente obtendrá el mismo response *HTTP 500 Internal Server Error*, pero el servidor tendrá nuestro `InternalError` personalizado en los registros. 😎
|
||||
|
||||
|
|
@ -190,7 +190,7 @@ Normalmente, el código de salida de las dependencias con `yield` se ejecuta **d
|
|||
|
||||
Pero si sabes que no necesitarás usar la dependencia después de regresar de la *path operation function*, puedes usar `Depends(scope="function")` para decirle a FastAPI que debe cerrar la dependencia después de que la *path operation function* regrese, pero **antes** de que se envíe el **response**.
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial008e_an_py39.py hl[12,16] *}
|
||||
{* ../../docs_src/dependencies/tutorial008e_an_py310.py hl[12,16] *}
|
||||
|
||||
`Depends()` recibe un parámetro `scope` que puede ser:
|
||||
|
||||
|
|
@ -234,7 +234,6 @@ participant operation as Path Operation
|
|||
Las dependencias con `yield` han evolucionado con el tiempo para cubrir diferentes casos de uso y corregir algunos problemas.
|
||||
|
||||
Si quieres ver qué ha cambiado en diferentes versiones de FastAPI, puedes leer más al respecto en la guía avanzada, en [Dependencias avanzadas - Dependencias con `yield`, `HTTPException`, `except` y Tareas en Background](../../advanced/advanced-dependencies.md#dependencies-with-yield-httpexception-except-and-background-tasks){.internal-link target=_blank}.
|
||||
|
||||
## Context Managers { #context-managers }
|
||||
|
||||
### Qué son los "Context Managers" { #what-are-context-managers }
|
||||
|
|
@ -270,7 +269,7 @@ En Python, puedes crear Context Managers <a href="https://docs.python.org/3/refe
|
|||
También puedes usarlos dentro de las dependencias de **FastAPI** con `yield` usando
|
||||
`with` o `async with` en la función de dependencia:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial010_py39.py hl[1:9,13] *}
|
||||
{* ../../docs_src/dependencies/tutorial010_py310.py hl[1:9,13] *}
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ Similar a como puedes [agregar `dependencies` a los *path operation decorators*]
|
|||
|
||||
En ese caso, se aplicarán a todas las *path operations* en la aplicación:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial012_an_py39.py hl[17] *}
|
||||
{* ../../docs_src/dependencies/tutorial012_an_py310.py hl[17] *}
|
||||
|
||||
Y todas las ideas en la sección sobre [agregar `dependencies` a los *path operation decorators*](dependencies-in-path-operation-decorators.md){.internal-link target=_blank} siguen aplicándose, pero en este caso, a todas las *path operations* en la app.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# Dependencias { #dependencies }
|
||||
|
||||
**FastAPI** tiene un sistema de **<abbr title="también conocido como componentes, recursos, proveedores, servicios, inyectables">Inyección de Dependencias</abbr>** muy poderoso pero intuitivo.
|
||||
**FastAPI** tiene un sistema de **<dfn title="también conocido como componentes, recursos, proveedores, servicios, inyectables">Inyección de Dependencias</dfn>** muy poderoso pero intuitivo.
|
||||
|
||||
Está diseñado para ser muy simple de usar, y para hacer que cualquier desarrollador integre otros componentes con **FastAPI** de forma muy sencilla.
|
||||
|
||||
|
|
|
|||
|
|
@ -58,11 +58,11 @@ query_extractor --> query_or_cookie_extractor --> read_query
|
|||
|
||||
Si una de tus dependencias se declara varias veces para la misma *path operation*, por ejemplo, múltiples dependencias tienen una sub-dependencia común, **FastAPI** sabrá llamar a esa sub-dependencia solo una vez por request.
|
||||
|
||||
Y guardará el valor devuelto en un <abbr title="Una utilidad/sistema para almacenar valores calculados/generados, para reutilizarlos en lugar de calcularlos nuevamente.">"cache"</abbr> y lo pasará a todos los "dependants" que lo necesiten en ese request específico, en lugar de llamar a la dependencia varias veces para el mismo request.
|
||||
Y guardará el valor devuelto en un <dfn title="Una utilidad/sistema para almacenar valores calculados/generados, para reutilizarlos en lugar de calcularlos nuevamente.">"caché"</dfn> y lo pasará a todos los "dependants" que lo necesiten en ese request específico, en lugar de llamar a la dependencia varias veces para el mismo request.
|
||||
|
||||
En un escenario avanzado donde sabes que necesitas que la dependencia se llame en cada paso (posiblemente varias veces) en el mismo request en lugar de usar el valor "cache", puedes establecer el parámetro `use_cache=False` al usar `Depends`:
|
||||
En un escenario avanzado donde sabes que necesitas que la dependencia se llame en cada paso (posiblemente varias veces) en el mismo request en lugar de usar el valor "en caché", puedes establecer el parámetro `use_cache=False` al usar `Depends`:
|
||||
|
||||
//// tab | Python 3.9+
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python hl_lines="1"
|
||||
async def needy_dependency(fresh_value: Annotated[str, Depends(get_value, use_cache=False)]):
|
||||
|
|
@ -71,7 +71,7 @@ async def needy_dependency(fresh_value: Annotated[str, Depends(get_value, use_ca
|
|||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+ sin Anotaciones
|
||||
//// tab | Python 3.10+ sin Anotaciones
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ En este ejemplo, convertiría el modelo de Pydantic a un `dict`, y el `datetime`
|
|||
|
||||
El resultado de llamarlo es algo que puede ser codificado con la función estándar de Python <a href="https://docs.python.org/3/library/json.html#json.dumps" class="external-link" target="_blank">`json.dumps()`</a>.
|
||||
|
||||
No devuelve un gran `str` que contenga los datos en formato JSON (como una cadena de texto). Devuelve una estructura de datos estándar de Python (por ejemplo, un `dict`) con valores y sub-valores que son todos compatibles con JSON.
|
||||
No devuelve un gran `str` que contenga los datos en formato JSON (como un string). Devuelve una estructura de datos estándar de Python (por ejemplo, un `dict`) con valores y sub-valores que son todos compatibles con JSON.
|
||||
|
||||
/// note | Nota
|
||||
|
||||
|
|
|
|||
|
|
@ -190,9 +190,9 @@ Pero si ponemos eso en la asignación `response_model=PlaneItem | CarItem` obten
|
|||
|
||||
De la misma manera, puedes declarar responses de listas de objetos.
|
||||
|
||||
Para eso, usa el `typing.List` estándar de Python (o simplemente `list` en Python 3.9 y posteriores):
|
||||
Para eso, usa la `list` estándar de Python:
|
||||
|
||||
{* ../../docs_src/extra_models/tutorial004_py39.py hl[18] *}
|
||||
{* ../../docs_src/extra_models/tutorial004_py310.py hl[18] *}
|
||||
|
||||
## Response con `dict` arbitrario { #response-with-arbitrary-dict }
|
||||
|
||||
|
|
@ -200,9 +200,9 @@ También puedes declarar un response usando un `dict` arbitrario plano, declaran
|
|||
|
||||
Esto es útil si no conoces los nombres de los campos/atributos válidos (que serían necesarios para un modelo Pydantic) de antemano.
|
||||
|
||||
En este caso, puedes usar `typing.Dict` (o solo `dict` en Python 3.9 y posteriores):
|
||||
En este caso, puedes usar `dict`:
|
||||
|
||||
{* ../../docs_src/extra_models/tutorial005_py39.py hl[6] *}
|
||||
{* ../../docs_src/extra_models/tutorial005_py310.py hl[6] *}
|
||||
|
||||
## Recapitulación { #recap }
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
El archivo FastAPI más simple podría verse así:
|
||||
|
||||
{* ../../docs_src/first_steps/tutorial001_py39.py *}
|
||||
{* ../../docs_src/first_steps/tutorial001_py310.py *}
|
||||
|
||||
Copia eso en un archivo `main.py`.
|
||||
|
||||
|
|
@ -56,7 +56,7 @@ INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
|
|||
|
||||
Esa línea muestra la URL donde tu aplicación está siendo servida, en tu máquina local.
|
||||
|
||||
### Compruébalo { #check-it }
|
||||
### Revisa { #check-it }
|
||||
|
||||
Abre tu navegador en <a href="http://127.0.0.1:8000" class="external-link" target="_blank">http://127.0.0.1:8000</a>.
|
||||
|
||||
|
|
@ -183,7 +183,7 @@ Deploying to FastAPI Cloud...
|
|||
|
||||
### Paso 1: importa `FastAPI` { #step-1-import-fastapi }
|
||||
|
||||
{* ../../docs_src/first_steps/tutorial001_py39.py hl[1] *}
|
||||
{* ../../docs_src/first_steps/tutorial001_py310.py hl[1] *}
|
||||
|
||||
`FastAPI` es una clase de Python que proporciona toda la funcionalidad para tu API.
|
||||
|
||||
|
|
@ -197,7 +197,7 @@ Puedes usar toda la funcionalidad de <a href="https://www.starlette.dev/" class=
|
|||
|
||||
### Paso 2: crea una "instance" de `FastAPI` { #step-2-create-a-fastapi-instance }
|
||||
|
||||
{* ../../docs_src/first_steps/tutorial001_py39.py hl[3] *}
|
||||
{* ../../docs_src/first_steps/tutorial001_py310.py hl[3] *}
|
||||
|
||||
Aquí la variable `app` será una "instance" de la clase `FastAPI`.
|
||||
|
||||
|
|
@ -266,12 +266,12 @@ Vamos a llamarlas "**operaciones**" también.
|
|||
|
||||
#### Define un *path operation decorator* { #define-a-path-operation-decorator }
|
||||
|
||||
{* ../../docs_src/first_steps/tutorial001_py39.py hl[6] *}
|
||||
{* ../../docs_src/first_steps/tutorial001_py310.py hl[6] *}
|
||||
|
||||
El `@app.get("/")` le dice a **FastAPI** que la función justo debajo se encarga de manejar requests que vayan a:
|
||||
|
||||
* el path `/`
|
||||
* usando una <abbr title="un método HTTP GET"><code>get</code> operation</abbr>
|
||||
* usando una <dfn title="un método HTTP GET"><code>get</code> operación</dfn>
|
||||
|
||||
/// info | Información sobre `@decorator`
|
||||
|
||||
|
|
@ -320,7 +320,7 @@ Esta es nuestra "**path operation function**":
|
|||
* **operation**: es `get`.
|
||||
* **function**: es la función debajo del "decorador" (debajo de `@app.get("/")`).
|
||||
|
||||
{* ../../docs_src/first_steps/tutorial001_py39.py hl[7] *}
|
||||
{* ../../docs_src/first_steps/tutorial001_py310.py hl[7] *}
|
||||
|
||||
Esta es una función de Python.
|
||||
|
||||
|
|
@ -332,7 +332,7 @@ En este caso, es una función `async`.
|
|||
|
||||
También podrías definirla como una función normal en lugar de `async def`:
|
||||
|
||||
{* ../../docs_src/first_steps/tutorial003_py39.py hl[7] *}
|
||||
{* ../../docs_src/first_steps/tutorial003_py310.py hl[7] *}
|
||||
|
||||
/// note | Nota
|
||||
|
||||
|
|
@ -342,7 +342,7 @@ Si no sabes la diferencia, Revisa la sección [Async: *"¿Tienes prisa?"*](../as
|
|||
|
||||
### Paso 5: retorna el contenido { #step-5-return-the-content }
|
||||
|
||||
{* ../../docs_src/first_steps/tutorial001_py39.py hl[8] *}
|
||||
{* ../../docs_src/first_steps/tutorial001_py310.py hl[8] *}
|
||||
|
||||
Puedes retornar un `dict`, `list`, valores singulares como `str`, `int`, etc.
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ Para devolver responses HTTP con errores al cliente, usa `HTTPException`.
|
|||
|
||||
### Importa `HTTPException` { #import-httpexception }
|
||||
|
||||
{* ../../docs_src/handling_errors/tutorial001_py39.py hl[1] *}
|
||||
{* ../../docs_src/handling_errors/tutorial001_py310.py hl[1] *}
|
||||
|
||||
### Lanza un `HTTPException` en tu código { #raise-an-httpexception-in-your-code }
|
||||
|
||||
|
|
@ -39,7 +39,7 @@ El beneficio de lanzar una excepción en lugar de `return`ar un valor será más
|
|||
|
||||
En este ejemplo, cuando el cliente solicita un ítem por un ID que no existe, lanza una excepción con un código de estado de `404`:
|
||||
|
||||
{* ../../docs_src/handling_errors/tutorial001_py39.py hl[11] *}
|
||||
{* ../../docs_src/handling_errors/tutorial001_py310.py hl[11] *}
|
||||
|
||||
### El response resultante { #the-resulting-response }
|
||||
|
||||
|
|
@ -77,7 +77,7 @@ Probablemente no necesitarás usarlos directamente en tu código.
|
|||
|
||||
Pero en caso de que los necesites para un escenario avanzado, puedes agregar headers personalizados:
|
||||
|
||||
{* ../../docs_src/handling_errors/tutorial002_py39.py hl[14] *}
|
||||
{* ../../docs_src/handling_errors/tutorial002_py310.py hl[14] *}
|
||||
|
||||
## Instalar manejadores de excepciones personalizados { #install-custom-exception-handlers }
|
||||
|
||||
|
|
@ -89,7 +89,7 @@ Y quieres manejar esta excepción globalmente con FastAPI.
|
|||
|
||||
Podrías agregar un manejador de excepciones personalizado con `@app.exception_handler()`:
|
||||
|
||||
{* ../../docs_src/handling_errors/tutorial003_py39.py hl[5:7,13:18,24] *}
|
||||
{* ../../docs_src/handling_errors/tutorial003_py310.py hl[5:7,13:18,24] *}
|
||||
|
||||
Aquí, si solicitas `/unicorns/yolo`, la *path operation* lanzará un `UnicornException`.
|
||||
|
||||
|
|
@ -127,7 +127,7 @@ Para sobrescribirlo, importa el `RequestValidationError` y úsalo con `@app.exce
|
|||
|
||||
El manejador de excepciones recibirá un `Request` y la excepción.
|
||||
|
||||
{* ../../docs_src/handling_errors/tutorial004_py39.py hl[2,14:19] *}
|
||||
{* ../../docs_src/handling_errors/tutorial004_py310.py hl[2,14:19] *}
|
||||
|
||||
Ahora, si vas a `/items/foo`, en lugar de obtener el error JSON por defecto con:
|
||||
|
||||
|
|
@ -159,7 +159,7 @@ De la misma manera, puedes sobrescribir el manejador de `HTTPException`.
|
|||
|
||||
Por ejemplo, podrías querer devolver un response de texto plano en lugar de JSON para estos errores:
|
||||
|
||||
{* ../../docs_src/handling_errors/tutorial004_py39.py hl[3:4,9:11,25] *}
|
||||
{* ../../docs_src/handling_errors/tutorial004_py310.py hl[3:4,9:11,25] *}
|
||||
|
||||
/// note | Nota Técnica
|
||||
|
||||
|
|
@ -183,7 +183,7 @@ El `RequestValidationError` contiene el `body` que recibió con datos inválidos
|
|||
|
||||
Podrías usarlo mientras desarrollas tu aplicación para registrar el body y depurarlo, devolverlo al usuario, etc.
|
||||
|
||||
{* ../../docs_src/handling_errors/tutorial005_py39.py hl[14] *}
|
||||
{* ../../docs_src/handling_errors/tutorial005_py310.py hl[14] *}
|
||||
|
||||
Ahora intenta enviar un ítem inválido como:
|
||||
|
||||
|
|
@ -239,6 +239,6 @@ from starlette.exceptions import HTTPException as StarletteHTTPException
|
|||
|
||||
Si quieres usar la excepción junto con los mismos manejadores de excepciones predeterminados de **FastAPI**, puedes importar y reutilizar los manejadores de excepciones predeterminados de `fastapi.exception_handlers`:
|
||||
|
||||
{* ../../docs_src/handling_errors/tutorial006_py39.py hl[2:5,15,21] *}
|
||||
{* ../../docs_src/handling_errors/tutorial006_py310.py hl[2:5,15,21] *}
|
||||
|
||||
En este ejemplo solo estás `print`eando el error con un mensaje muy expresivo, pero te haces una idea. Puedes usar la excepción y luego simplemente reutilizar los manejadores de excepciones predeterminados.
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ Puedes establecer los siguientes campos que se usan en la especificación OpenAP
|
|||
|
||||
Puedes configurarlos de la siguiente manera:
|
||||
|
||||
{* ../../docs_src/metadata/tutorial001_py39.py hl[3:16, 19:32] *}
|
||||
{* ../../docs_src/metadata/tutorial001_py310.py hl[3:16, 19:32] *}
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
|
|
@ -36,7 +36,7 @@ Desde OpenAPI 3.1.0 y FastAPI 0.99.0, también puedes establecer la `license_inf
|
|||
|
||||
Por ejemplo:
|
||||
|
||||
{* ../../docs_src/metadata/tutorial001_1_py39.py hl[31] *}
|
||||
{* ../../docs_src/metadata/tutorial001_1_py310.py hl[31] *}
|
||||
|
||||
## Metadata para etiquetas { #metadata-for-tags }
|
||||
|
||||
|
|
@ -58,7 +58,7 @@ Probemos eso en un ejemplo con etiquetas para `users` y `items`.
|
|||
|
||||
Crea metadata para tus etiquetas y pásala al parámetro `openapi_tags`:
|
||||
|
||||
{* ../../docs_src/metadata/tutorial004_py39.py hl[3:16,18] *}
|
||||
{* ../../docs_src/metadata/tutorial004_py310.py hl[3:16,18] *}
|
||||
|
||||
Nota que puedes utilizar Markdown dentro de las descripciones, por ejemplo "login" se mostrará en negrita (**login**) y "fancy" se mostrará en cursiva (_fancy_).
|
||||
|
||||
|
|
@ -72,7 +72,7 @@ No tienes que agregar metadata para todas las etiquetas que uses.
|
|||
|
||||
Usa el parámetro `tags` con tus *path operations* (y `APIRouter`s) para asignarlas a diferentes etiquetas:
|
||||
|
||||
{* ../../docs_src/metadata/tutorial004_py39.py hl[21,26] *}
|
||||
{* ../../docs_src/metadata/tutorial004_py310.py hl[21,26] *}
|
||||
|
||||
/// info | Información
|
||||
|
||||
|
|
@ -100,7 +100,7 @@ Pero puedes configurarlo con el parámetro `openapi_url`.
|
|||
|
||||
Por ejemplo, para configurarlo para que se sirva en `/api/v1/openapi.json`:
|
||||
|
||||
{* ../../docs_src/metadata/tutorial002_py39.py hl[3] *}
|
||||
{* ../../docs_src/metadata/tutorial002_py310.py hl[3] *}
|
||||
|
||||
Si quieres deshabilitar el esquema OpenAPI completamente, puedes establecer `openapi_url=None`, eso también deshabilitará las interfaces de usuario de documentación que lo usan.
|
||||
|
||||
|
|
@ -117,4 +117,4 @@ Puedes configurar las dos interfaces de usuario de documentación incluidas:
|
|||
|
||||
Por ejemplo, para configurar Swagger UI para que se sirva en `/documentation` y deshabilitar ReDoc:
|
||||
|
||||
{* ../../docs_src/metadata/tutorial003_py39.py hl[3] *}
|
||||
{* ../../docs_src/metadata/tutorial003_py310.py hl[3] *}
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ La función middleware recibe:
|
|||
* Luego devuelve la `response` generada por la correspondiente *path operation*.
|
||||
* Puedes entonces modificar aún más la `response` antes de devolverla.
|
||||
|
||||
{* ../../docs_src/middleware/tutorial001_py39.py hl[8:9,11,14] *}
|
||||
{* ../../docs_src/middleware/tutorial001_py310.py hl[8:9,11,14] *}
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
|
|
@ -57,7 +57,7 @@ Y también después de que se genere la `response`, antes de devolverla.
|
|||
|
||||
Por ejemplo, podrías añadir un custom header `X-Process-Time` que contenga el tiempo en segundos que tomó procesar la request y generar una response:
|
||||
|
||||
{* ../../docs_src/middleware/tutorial001_py39.py hl[10,12:13] *}
|
||||
{* ../../docs_src/middleware/tutorial001_py310.py hl[10,12:13] *}
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ En estos casos, podría tener sentido almacenar las tags en un `Enum`.
|
|||
|
||||
**FastAPI** soporta eso de la misma manera que con strings normales:
|
||||
|
||||
{* ../../docs_src/path_operation_configuration/tutorial002b_py39.py hl[1,8:10,13,18] *}
|
||||
{* ../../docs_src/path_operation_configuration/tutorial002b_py310.py hl[1,8:10,13,18] *}
|
||||
|
||||
## Resumen y Descripción { #summary-and-description }
|
||||
|
||||
|
|
@ -56,7 +56,7 @@ Puedes añadir un `summary` y `description`:
|
|||
|
||||
## Descripción desde docstring { #description-from-docstring }
|
||||
|
||||
Como las descripciones tienden a ser largas y cubrir múltiples líneas, puedes declarar la descripción de la *path operation* en la <abbr title="un string de múltiples líneas como la primera expresión dentro de una función (no asignada a ninguna variable) usada para documentación">docstring</abbr> de la función y **FastAPI** la leerá desde allí.
|
||||
Como las descripciones tienden a ser largas y cubrir múltiples líneas, puedes declarar la descripción de la *path operation* en la <dfn title="un string de múltiples líneas como la primera expresión dentro de una función (no asignada a ninguna variable) usada para documentación">docstring</dfn> de la función y **FastAPI** la leerá desde allí.
|
||||
|
||||
Puedes escribir <a href="https://en.wikipedia.org/wiki/Markdown" class="external-link" target="_blank">Markdown</a> en el docstring, se interpretará y mostrará correctamente (teniendo en cuenta la indentación del docstring).
|
||||
|
||||
|
|
@ -90,9 +90,9 @@ Entonces, si no proporcionas una, **FastAPI** generará automáticamente una de
|
|||
|
||||
## Deprecar una *path operation* { #deprecate-a-path-operation }
|
||||
|
||||
Si necesitas marcar una *path operation* como <abbr title="obsoleta, se recomienda no usarla">deprecated</abbr>, pero sin eliminarla, pasa el parámetro `deprecated`:
|
||||
Si necesitas marcar una *path operation* como <dfn title="obsoleta, se recomienda no usarla">deprecated</dfn>, pero sin eliminarla, pasa el parámetro `deprecated`:
|
||||
|
||||
{* ../../docs_src/path_operation_configuration/tutorial006_py39.py hl[16] *}
|
||||
{* ../../docs_src/path_operation_configuration/tutorial006_py310.py hl[16] *}
|
||||
|
||||
Se marcará claramente como deprecado en la documentación interactiva:
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
De la misma manera que puedes declarar más validaciones y metadatos para los parámetros de query con `Query`, puedes declarar el mismo tipo de validaciones y metadatos para los parámetros de path con `Path`.
|
||||
|
||||
## Importar Path { #import-path }
|
||||
## Importar `Path` { #import-path }
|
||||
|
||||
Primero, importa `Path` de `fastapi`, e importa `Annotated`:
|
||||
|
||||
|
|
@ -46,19 +46,19 @@ Y no necesitas declarar nada más para ese parámetro, así que realmente no nec
|
|||
|
||||
Pero aún necesitas usar `Path` para el parámetro de path `item_id`. Y no quieres usar `Annotated` por alguna razón.
|
||||
|
||||
Python se quejará si pones un valor con un "default" antes de un valor que no tenga un "default".
|
||||
Python se quejará si pones un valor con "por defecto" antes de un valor que no tenga "por defecto".
|
||||
|
||||
Pero puedes reordenarlos y poner el valor sin un default (el parámetro de query `q`) primero.
|
||||
Pero puedes reordenarlos y poner el valor sin un valor por defecto (el parámetro de query `q`) primero.
|
||||
|
||||
No importa para **FastAPI**. Detectará los parámetros por sus nombres, tipos y declaraciones por defecto (`Query`, `Path`, etc.), no le importa el orden.
|
||||
No importa para **FastAPI**. Detectará los parámetros por sus nombres, tipos y declaraciones por defecto (`Query`, `Path`, etc), no le importa el orden.
|
||||
|
||||
Así que puedes declarar tu función como:
|
||||
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial002_py39.py hl[7] *}
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial002_py310.py hl[7] *}
|
||||
|
||||
Pero ten en cuenta que si usas `Annotated`, no tendrás este problema, no importará ya que no estás usando los valores por defecto de los parámetros de la función para `Query()` o `Path()`.
|
||||
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial002_an_py39.py *}
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial002_an_py310.py *}
|
||||
|
||||
## Ordena los parámetros como necesites, trucos { #order-the-parameters-as-you-need-tricks }
|
||||
|
||||
|
|
@ -83,13 +83,13 @@ Pasa `*`, como el primer parámetro de la función.
|
|||
|
||||
Python no hará nada con ese `*`, pero sabrá que todos los parámetros siguientes deben ser llamados como argumentos de palabras clave (parejas key-value), también conocidos como <abbr title="De: K-ey W-ord Arg-uments"><code>kwargs</code></abbr>. Incluso si no tienen un valor por defecto.
|
||||
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial003_py39.py hl[7] *}
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial003_py310.py hl[7] *}
|
||||
|
||||
### Mejor con `Annotated` { #better-with-annotated }
|
||||
|
||||
Ten en cuenta que si usas `Annotated`, como no estás usando valores por defecto de los parámetros de la función, no tendrás este problema y probablemente no necesitarás usar `*`.
|
||||
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial003_an_py39.py hl[10] *}
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial003_an_py310.py hl[10] *}
|
||||
|
||||
## Validaciones numéricas: mayor o igual { #number-validations-greater-than-or-equal }
|
||||
|
||||
|
|
@ -97,7 +97,7 @@ Con `Query` y `Path` (y otros que verás más adelante) puedes declarar restricc
|
|||
|
||||
Aquí, con `ge=1`, `item_id` necesitará ser un número entero "`g`reater than or `e`qual" a `1`.
|
||||
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial004_an_py39.py hl[10] *}
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial004_an_py310.py hl[10] *}
|
||||
|
||||
## Validaciones numéricas: mayor que y menor o igual { #number-validations-greater-than-and-less-than-or-equal }
|
||||
|
||||
|
|
@ -106,19 +106,19 @@ Lo mismo aplica para:
|
|||
* `gt`: `g`reater `t`han
|
||||
* `le`: `l`ess than or `e`qual
|
||||
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial005_an_py39.py hl[10] *}
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial005_an_py310.py hl[10] *}
|
||||
|
||||
## Validaciones numéricas: flotantes, mayor y menor { #number-validations-floats-greater-than-and-less-than }
|
||||
|
||||
Las validaciones numéricas también funcionan para valores `float`.
|
||||
|
||||
Aquí es donde se convierte en importante poder declarar <abbr title="greater than – mayor que"><code>gt</code></abbr> y no solo <abbr title="greater than or equal – mayor o igual que"><code>ge</code></abbr>. Ya que con esto puedes requerir, por ejemplo, que un valor sea mayor que `0`, incluso si es menor que `1`.
|
||||
Aquí es donde se convierte en importante poder declarar <abbr title="greater than - mayor que"><code>gt</code></abbr> y no solo <abbr title="greater than or equal - mayor o igual que"><code>ge</code></abbr>. Ya que con esto puedes requerir, por ejemplo, que un valor sea mayor que `0`, incluso si es menor que `1`.
|
||||
|
||||
Así, `0.5` sería un valor válido. Pero `0.0` o `0` no lo serían.
|
||||
|
||||
Y lo mismo para <abbr title="less than – menor que"><code>lt</code></abbr>.
|
||||
Y lo mismo para <abbr title="less than - menor que"><code>lt</code></abbr>.
|
||||
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial006_an_py39.py hl[13] *}
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial006_an_py310.py hl[13] *}
|
||||
|
||||
## Resumen { #recap }
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
Puedes declarar "parámetros" o "variables" de path con la misma sintaxis que se usa en los format strings de Python:
|
||||
|
||||
{* ../../docs_src/path_params/tutorial001_py39.py hl[6:7] *}
|
||||
{* ../../docs_src/path_params/tutorial001_py310.py hl[6:7] *}
|
||||
|
||||
El valor del parámetro de path `item_id` se pasará a tu función como el argumento `item_id`.
|
||||
|
||||
|
|
@ -16,7 +16,7 @@ Así que, si ejecutas este ejemplo y vas a <a href="http://127.0.0.1:8000/items/
|
|||
|
||||
Puedes declarar el tipo de un parámetro de path en la función, usando anotaciones de tipos estándar de Python:
|
||||
|
||||
{* ../../docs_src/path_params/tutorial002_py39.py hl[7] *}
|
||||
{* ../../docs_src/path_params/tutorial002_py310.py hl[7] *}
|
||||
|
||||
En este caso, `item_id` se declara como un `int`.
|
||||
|
||||
|
|
@ -26,7 +26,7 @@ Esto te dará soporte del editor dentro de tu función, con chequeo de errores,
|
|||
|
||||
///
|
||||
|
||||
## <abbr title="también conocido como: serialización, parsing, marshalling">Conversión</abbr> de datos { #data-conversion }
|
||||
## <dfn title="también conocido como: serialización, parsing, marshalling">Conversión</dfn> de datos { #data-conversion }
|
||||
|
||||
Si ejecutas este ejemplo y abres tu navegador en <a href="http://127.0.0.1:8000/items/3" class="external-link" target="_blank">http://127.0.0.1:8000/items/3</a>, verás un response de:
|
||||
|
||||
|
|
@ -38,7 +38,7 @@ Si ejecutas este ejemplo y abres tu navegador en <a href="http://127.0.0.1:8000/
|
|||
|
||||
Nota que el valor que tu función recibió (y devolvió) es `3`, como un `int` de Python, no un string `"3"`.
|
||||
|
||||
Entonces, con esa declaración de tipo, **FastAPI** te ofrece <abbr title="convertir el string que viene de un request HTTP en datos de Python">"parsing"</abbr> automático de request.
|
||||
Entonces, con esa declaración de tipo, **FastAPI** te ofrece <dfn title="convertir el string que viene de un request HTTP en datos de Python">"parsing"</dfn> automático de request.
|
||||
|
||||
///
|
||||
|
||||
|
|
@ -118,13 +118,13 @@ Y luego también puedes tener un path `/users/{user_id}` para obtener datos sobr
|
|||
|
||||
Debido a que las *path operations* se evalúan en orden, necesitas asegurarte de que el path para `/users/me` se declara antes que el de `/users/{user_id}`:
|
||||
|
||||
{* ../../docs_src/path_params/tutorial003_py39.py hl[6,11] *}
|
||||
{* ../../docs_src/path_params/tutorial003_py310.py hl[6,11] *}
|
||||
|
||||
De lo contrario, el path para `/users/{user_id}` también coincidiría para `/users/me`, "pensando" que está recibiendo un parámetro `user_id` con un valor de `"me"`.
|
||||
|
||||
De manera similar, no puedes redefinir una path operation:
|
||||
|
||||
{* ../../docs_src/path_params/tutorial003b_py39.py hl[6,11] *}
|
||||
{* ../../docs_src/path_params/tutorial003b_py310.py hl[6,11] *}
|
||||
|
||||
La primera siempre será utilizada ya que el path coincide primero.
|
||||
|
||||
|
|
@ -140,11 +140,11 @@ Al heredar de `str`, la documentación de la API podrá saber que los valores de
|
|||
|
||||
Luego crea atributos de clase con valores fijos, que serán los valores válidos disponibles:
|
||||
|
||||
{* ../../docs_src/path_params/tutorial005_py39.py hl[1,6:9] *}
|
||||
{* ../../docs_src/path_params/tutorial005_py310.py hl[1,6:9] *}
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
Si te estás preguntando, "AlexNet", "ResNet" y "LeNet" son solo nombres de <abbr title="Técnicamente, arquitecturas de modelos de Deep Learning">modelos</abbr> de Machine Learning.
|
||||
Si te estás preguntando, "AlexNet", "ResNet" y "LeNet" son solo nombres de <dfn title="Técnicamente, arquitecturas de modelos de Deep Learning">modelos</dfn> de Machine Learning.
|
||||
|
||||
///
|
||||
|
||||
|
|
@ -152,7 +152,7 @@ Si te estás preguntando, "AlexNet", "ResNet" y "LeNet" son solo nombres de <abb
|
|||
|
||||
Luego crea un *path parameter* con una anotación de tipo usando la clase enum que creaste (`ModelName`):
|
||||
|
||||
{* ../../docs_src/path_params/tutorial005_py39.py hl[16] *}
|
||||
{* ../../docs_src/path_params/tutorial005_py310.py hl[16] *}
|
||||
|
||||
### Revisa la documentación { #check-the-docs }
|
||||
|
||||
|
|
@ -168,13 +168,13 @@ El valor del *path parameter* será un *miembro* de enumeración.
|
|||
|
||||
Puedes compararlo con el *miembro* de enumeración en tu enum creada `ModelName`:
|
||||
|
||||
{* ../../docs_src/path_params/tutorial005_py39.py hl[17] *}
|
||||
{* ../../docs_src/path_params/tutorial005_py310.py hl[17] *}
|
||||
|
||||
#### Obtener el valor de *enumeración* { #get-the-enumeration-value }
|
||||
|
||||
Puedes obtener el valor actual (un `str` en este caso) usando `model_name.value`, o en general, `your_enum_member.value`:
|
||||
|
||||
{* ../../docs_src/path_params/tutorial005_py39.py hl[20] *}
|
||||
{* ../../docs_src/path_params/tutorial005_py310.py hl[20] *}
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
|
|
@ -188,7 +188,7 @@ Puedes devolver *miembros de enum* desde tu *path operation*, incluso anidados e
|
|||
|
||||
Serán convertidos a sus valores correspondientes (cadenas en este caso) antes de devolverlos al cliente:
|
||||
|
||||
{* ../../docs_src/path_params/tutorial005_py39.py hl[18,21,23] *}
|
||||
{* ../../docs_src/path_params/tutorial005_py310.py hl[18,21,23] *}
|
||||
|
||||
En tu cliente recibirás un response JSON como:
|
||||
|
||||
|
|
@ -227,7 +227,7 @@ En este caso, el nombre del parámetro es `file_path`, y la última parte, `:pat
|
|||
|
||||
Así que, puedes usarlo con:
|
||||
|
||||
{* ../../docs_src/path_params/tutorial004_py39.py hl[6] *}
|
||||
{* ../../docs_src/path_params/tutorial004_py310.py hl[6] *}
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
|
|
@ -242,7 +242,7 @@ En ese caso, la URL sería: `/files//home/johndoe/myfile.txt`, con una doble bar
|
|||
Con **FastAPI**, al usar declaraciones de tipo estándar de Python, cortas e intuitivas, obtienes:
|
||||
|
||||
* Soporte del editor: chequeo de errores, autocompletado, etc.
|
||||
* "<abbr title="convertir el string que viene de un request HTTP en datos de Python">parsing</abbr>" de datos
|
||||
* " <dfn title="convertir el string que viene de un request HTTP en datos de Python">parsing</dfn> " de datos
|
||||
* Validación de datos
|
||||
* Anotación de API y documentación automática
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ Tener `str | None` permitirá que tu editor te dé un mejor soporte y detecte er
|
|||
|
||||
## Validaciones adicionales { #additional-validation }
|
||||
|
||||
Vamos a hacer que, aunque `q` sea opcional, siempre que se proporcione, **su longitud no exceda los 50 caracteres**.
|
||||
Vamos a hacer que, aunque `q` sea opcional, siempre que se proporcione, su longitud no exceda los 50 caracteres.
|
||||
|
||||
### Importar `Query` y `Annotated` { #import-query-and-annotated }
|
||||
|
||||
|
|
@ -47,40 +47,16 @@ Ahora es el momento de usarlo con FastAPI. 🚀
|
|||
|
||||
Teníamos esta anotación de tipo:
|
||||
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python
|
||||
q: str | None = None
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python
|
||||
q: Union[str, None] = None
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
Lo que haremos es envolver eso con `Annotated`, para que se convierta en:
|
||||
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python
|
||||
q: Annotated[str | None] = None
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python
|
||||
q: Annotated[Union[str, None]] = None
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
Ambas versiones significan lo mismo, `q` es un parámetro que puede ser un `str` o `None`, y por defecto, es `None`.
|
||||
|
||||
Ahora vamos a lo divertido. 🎉
|
||||
|
|
@ -93,7 +69,7 @@ Ahora que tenemos este `Annotated` donde podemos poner más información (en est
|
|||
|
||||
Nota que el valor por defecto sigue siendo `None`, por lo que el parámetro sigue siendo opcional.
|
||||
|
||||
Pero ahora, al tener `Query(max_length=50)` dentro de `Annotated`, le estamos diciendo a FastAPI que queremos que tenga **validación adicional** para este valor, queremos que tenga un máximo de 50 caracteres. 😎
|
||||
Pero ahora, al tener `Query(max_length=50)` dentro de `Annotated`, le estamos diciendo a FastAPI que queremos que tenga validación adicional para este valor, queremos que tenga un máximo de 50 caracteres. 😎
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
|
|
@ -103,13 +79,13 @@ Aquí estamos usando `Query()` porque este es un **parámetro de query**. Más a
|
|||
|
||||
FastAPI ahora:
|
||||
|
||||
* **Validará** los datos asegurándose de que la longitud máxima sea de 50 caracteres
|
||||
* Mostrará un **error claro** para el cliente cuando los datos no sean válidos
|
||||
* **Documentará** el parámetro en el OpenAPI esquema *path operation* (así aparecerá en la **UI de documentación automática**)
|
||||
* Validará los datos asegurándose de que la longitud máxima sea de 50 caracteres
|
||||
* Mostrará un error claro para el cliente cuando los datos no sean válidos
|
||||
* Documentará el parámetro en el OpenAPI esquema *path operation* (así aparecerá en la UI de documentación automática)
|
||||
|
||||
## Alternativa (antigua): `Query` como valor por defecto { #alternative-old-query-as-the-default-value }
|
||||
|
||||
Versiones anteriores de FastAPI (antes de <abbr title="before 2023-03 - antes de 2023-03">0.95.0</abbr>) requerían que usaras `Query` como el valor por defecto de tu parámetro, en lugar de ponerlo en `Annotated`, hay una alta probabilidad de que veas código usándolo alrededor, así que te lo explicaré.
|
||||
Versiones anteriores de FastAPI (antes de <dfn title="antes de 2023-03">0.95.0</dfn>) requerían que usaras `Query` como el valor por defecto de tu parámetro, en lugar de ponerlo en `Annotated`, hay una alta probabilidad de que veas código usándolo alrededor, así que te lo explicaré.
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
|
|
@ -174,13 +150,13 @@ q: str = Query(default="rick")
|
|||
|
||||
### Ventajas de `Annotated` { #advantages-of-annotated }
|
||||
|
||||
**Usar `Annotated` es recomendado** en lugar del valor por defecto en los parámetros de función, es **mejor** por múltiples razones. 🤓
|
||||
Usar `Annotated` es recomendado en lugar del valor por defecto en los parámetros de función, es mejor por múltiples razones. 🤓
|
||||
|
||||
El valor **por defecto** del **parámetro de función** es el valor **real por defecto**, eso es más intuitivo con Python en general. 😌
|
||||
El valor por defecto del parámetro de función es el valor real por defecto, eso es más intuitivo con Python en general. 😌
|
||||
|
||||
Podrías **llamar** a esa misma función en **otros lugares** sin FastAPI, y **funcionaría como se espera**. Si hay un parámetro **requerido** (sin un valor por defecto), tu **editor** te avisará con un error, **Python** también se quejará si lo ejecutas sin pasar el parámetro requerido.
|
||||
Podrías llamar a esa misma función en otros lugares sin FastAPI, y funcionaría como se espera. Si hay un parámetro requerido (sin un valor por defecto), tu editor te avisará con un error, Python también se quejará si lo ejecutas sin pasar el parámetro requerido.
|
||||
|
||||
Cuando no usas `Annotated` y en su lugar usas el estilo de valor por defecto **(antiguo)**, si llamas a esa función sin FastAPI en **otros lugares**, tienes que **recordar** pasar los argumentos a la función para que funcione correctamente, de lo contrario, los valores serán diferentes de lo que esperas (por ejemplo, `QueryInfo` o algo similar en lugar de `str`). Y tu editor no se quejará, y Python no se quejará al ejecutar esa función, solo cuando los errores dentro de las operaciones hagan que funcione incorrectamente.
|
||||
Cuando no usas `Annotated` y en su lugar usas el estilo de valor por defecto (antiguo), si llamas a esa función sin FastAPI en otros lugares, tienes que recordar pasar los argumentos a la función para que funcione correctamente, de lo contrario, los valores serán diferentes de lo que esperas (por ejemplo, `QueryInfo` o algo similar en lugar de `str`). Y tu editor no se quejará, y Python no se quejará al ejecutar esa función, solo cuando los errores dentro de las operaciones hagan que funcione incorrectamente.
|
||||
|
||||
Dado que `Annotated` puede tener más de una anotación de metadato, ahora podrías incluso usar la misma función con otras herramientas, como <a href="https://typer.tiangolo.com/" class="external-link" target="_blank">Typer</a>. 🚀
|
||||
|
||||
|
|
@ -192,7 +168,7 @@ También puedes agregar un parámetro `min_length`:
|
|||
|
||||
## Agregar expresiones regulares { #add-regular-expressions }
|
||||
|
||||
Puedes definir una <abbr title="Una expresión regular, regex o regexp es una secuencia de caracteres que define un patrón de búsqueda para strings.">expresión regular</abbr> `pattern` que el parámetro debe coincidir:
|
||||
Puedes definir una <dfn title="Una expresión regular, regex o regexp es una secuencia de caracteres que define un patrón de búsqueda para strings.">expresión regular</dfn> `pattern` que el parámetro debe coincidir:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial004_an_py310.py hl[11] *}
|
||||
|
||||
|
|
@ -202,7 +178,7 @@ Este patrón específico de expresión regular comprueba que el valor recibido d
|
|||
* `fixedquery`: tiene el valor exacto `fixedquery`.
|
||||
* `$`: termina allí, no tiene más caracteres después de `fixedquery`.
|
||||
|
||||
Si te sientes perdido con todas estas ideas de **"expresión regular"**, no te preocupes. Son un tema difícil para muchas personas. Aún puedes hacer muchas cosas sin necesitar expresiones regulares todavía.
|
||||
Si te sientes perdido con todas estas ideas de "expresión regular", no te preocupes. Son un tema difícil para muchas personas. Aún puedes hacer muchas cosas sin necesitar expresiones regulares todavía.
|
||||
|
||||
Ahora sabes que cuando las necesites puedes usarlas en **FastAPI**.
|
||||
|
||||
|
|
@ -212,7 +188,7 @@ Puedes, por supuesto, usar valores por defecto diferentes de `None`.
|
|||
|
||||
Digamos que quieres declarar el parámetro de query `q` para que tenga un `min_length` de `3`, y para que tenga un valor por defecto de `"fixedquery"`:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial005_an_py39.py hl[9] *}
|
||||
{* ../../docs_src/query_params_str_validations/tutorial005_an_py310.py hl[9] *}
|
||||
|
||||
/// note | Nota
|
||||
|
||||
|
|
@ -242,7 +218,7 @@ q: Annotated[str | None, Query(min_length=3)] = None
|
|||
|
||||
Así que, cuando necesites declarar un valor como requerido mientras usas `Query`, simplemente puedes no declarar un valor por defecto:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial006_an_py39.py hl[9] *}
|
||||
{* ../../docs_src/query_params_str_validations/tutorial006_an_py310.py hl[9] *}
|
||||
|
||||
### Requerido, puede ser `None` { #required-can-be-none }
|
||||
|
||||
|
|
@ -293,7 +269,7 @@ La documentación interactiva de API se actualizará en consecuencia, para permi
|
|||
|
||||
También puedes definir un valor por defecto `list` de valores si no se proporciona ninguno:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial012_an_py39.py hl[9] *}
|
||||
{* ../../docs_src/query_params_str_validations/tutorial012_an_py310.py hl[9] *}
|
||||
|
||||
Si vas a:
|
||||
|
||||
|
|
@ -316,7 +292,7 @@ el valor por defecto de `q` será: `["foo", "bar"]` y tu response será:
|
|||
|
||||
También puedes usar `list` directamente en lugar de `list[str]`:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial013_an_py39.py hl[9] *}
|
||||
{* ../../docs_src/query_params_str_validations/tutorial013_an_py310.py hl[9] *}
|
||||
|
||||
/// note | Nota
|
||||
|
||||
|
|
@ -372,7 +348,7 @@ Entonces puedes declarar un `alias`, y ese alias será usado para encontrar el v
|
|||
|
||||
Ahora digamos que ya no te gusta este parámetro.
|
||||
|
||||
Tienes que dejarlo allí por un tiempo porque hay clientes usándolo, pero quieres que la documentación lo muestre claramente como <abbr title="obsolete, recommended not to use it - obsoleto, se recomienda no usarlo">deprecated</abbr>.
|
||||
Tienes que dejarlo allí por un tiempo porque hay clientes usándolo, pero quieres que la documentación lo muestre claramente como <dfn title="obsoleto, se recomienda no usarlo">obsoleto</dfn>.
|
||||
|
||||
Luego pasa el parámetro `deprecated=True` a `Query`:
|
||||
|
||||
|
|
@ -390,9 +366,9 @@ Para excluir un parámetro de query del esquema de OpenAPI generado (y por lo ta
|
|||
|
||||
## Validación personalizada { #custom-validation }
|
||||
|
||||
Podría haber casos donde necesites hacer alguna **validación personalizada** que no puede hacerse con los parámetros mostrados arriba.
|
||||
Podría haber casos donde necesites hacer alguna validación personalizada que no puede hacerse con los parámetros mostrados arriba.
|
||||
|
||||
En esos casos, puedes usar una **función validadora personalizada** que se aplique después de la validación normal (por ejemplo, después de validar que el valor es un `str`).
|
||||
En esos casos, puedes usar una función validadora personalizada que se aplique después de la validación normal (por ejemplo, después de validar que el valor es un `str`).
|
||||
|
||||
Puedes lograr eso usando <a href="https://docs.pydantic.dev/latest/concepts/validators/#field-after-validator" class="external-link" target="_blank">`AfterValidator` de Pydantic</a> dentro de `Annotated`.
|
||||
|
||||
|
|
@ -402,7 +378,7 @@ Pydantic también tiene <a href="https://docs.pydantic.dev/latest/concepts/valid
|
|||
|
||||
///
|
||||
|
||||
Por ejemplo, este validador personalizado comprueba que el ID del ítem empiece con `isbn-` para un número de libro <abbr title="ISBN means International Standard Book Number - ISBN significa International Standard Book Number">ISBN</abbr> o con `imdb-` para un ID de URL de película de <abbr title="IMDB (Internet Movie Database) is a website with information about movies - IMDB (Internet Movie Database) es un sitio web con información sobre películas">IMDB</abbr>:
|
||||
Por ejemplo, este validador personalizado comprueba que el ID del ítem empiece con `isbn-` para un número de libro <abbr title="International Standard Book Number - Número Estándar Internacional de Libros">ISBN</abbr> o con `imdb-` para un ID de URL de película de <abbr title="Internet Movie Database - Base de datos de películas en Internet: un sitio web con información sobre películas">IMDB</abbr>:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial015_an_py310.py hl[5,16:19,24] *}
|
||||
|
||||
|
|
@ -414,15 +390,15 @@ Esto está disponible con Pydantic versión 2 o superior. 😎
|
|||
|
||||
/// tip | Consejo
|
||||
|
||||
Si necesitas hacer cualquier tipo de validación que requiera comunicarte con algún **componente externo**, como una base de datos u otra API, deberías usar **Dependencias de FastAPI**, las aprenderás más adelante.
|
||||
Si necesitas hacer cualquier tipo de validación que requiera comunicarte con algún componente externo, como una base de datos u otra API, deberías usar Dependencias de FastAPI, las aprenderás más adelante.
|
||||
|
||||
Estos validadores personalizados son para cosas que pueden comprobarse **solo** con los **mismos datos** provistos en el request.
|
||||
Estos validadores personalizados son para cosas que pueden comprobarse solo con los mismos datos provistos en el request.
|
||||
|
||||
///
|
||||
|
||||
### Entiende ese código { #understand-that-code }
|
||||
|
||||
El punto importante es solo usar **`AfterValidator` con una función dentro de `Annotated`**. Si quieres, sáltate esta parte. 🤸
|
||||
El punto importante es solo usar `AfterValidator` con una función dentro de `Annotated`. Si quieres, sáltate esta parte. 🤸
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -436,17 +412,17 @@ Pero si te da curiosidad este ejemplo de código específico y sigues entretenid
|
|||
|
||||
#### Un ítem aleatorio { #a-random-item }
|
||||
|
||||
Con `data.items()` obtenemos un <abbr title="Algo que podemos iterar con un for loop, como una list, set, etc.">objeto iterable</abbr> con tuplas que contienen la clave y el valor para cada elemento del diccionario.
|
||||
Con `data.items()` obtenemos un <dfn title="Algo que podemos iterar con un for loop, como una list, set, etc.">objeto iterable</dfn> con tuplas que contienen la clave y el valor para cada elemento del diccionario.
|
||||
|
||||
Convertimos este objeto iterable en una `list` propiamente dicha con `list(data.items())`.
|
||||
|
||||
Luego con `random.choice()` podemos obtener un **valor aleatorio** de la lista, así que obtenemos una tupla con `(id, name)`. Será algo como `("imdb-tt0371724", "The Hitchhiker's Guide to the Galaxy")`.
|
||||
Luego con `random.choice()` podemos obtener un valor aleatorio de la lista, así que obtenemos una tupla con `(id, name)`. Será algo como `("imdb-tt0371724", "The Hitchhiker's Guide to the Galaxy")`.
|
||||
|
||||
Luego **asignamos esos dos valores** de la tupla a las variables `id` y `name`.
|
||||
Luego asignamos esos dos valores de la tupla a las variables `id` y `name`.
|
||||
|
||||
Así, si el usuario no proporcionó un ID de ítem, aún recibirá una sugerencia aleatoria.
|
||||
|
||||
...hacemos todo esto en una **sola línea simple**. 🤯 ¿No te encanta Python? 🐍
|
||||
...hacemos todo esto en una sola línea simple. 🤯 ¿No te encanta Python? 🐍
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial015_an_py310.py ln[22:30] hl[29] *}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
Cuando declaras otros parámetros de función que no son parte de los parámetros de path, son automáticamente interpretados como parámetros de "query".
|
||||
|
||||
{* ../../docs_src/query_params/tutorial001_py39.py hl[9] *}
|
||||
{* ../../docs_src/query_params/tutorial001_py310.py hl[9] *}
|
||||
|
||||
La query es el conjunto de pares clave-valor que van después del `?` en una URL, separados por caracteres `&`.
|
||||
|
||||
|
|
@ -24,7 +24,7 @@ Pero cuando los declaras con tipos de Python (en el ejemplo anterior, como `int`
|
|||
Todo el mismo proceso que se aplica para los parámetros de path también se aplica para los parámetros de query:
|
||||
|
||||
* Soporte del editor (obviamente)
|
||||
* <abbr title="convirtiendo el string que viene de un request HTTP en datos de Python">"parsing"</abbr> de datos
|
||||
* <dfn title="convirtiendo el string que viene de un request HTTP en datos de Python">"parsing"</dfn> de datos
|
||||
* Validación de datos
|
||||
* Documentación automática
|
||||
|
||||
|
|
@ -127,7 +127,7 @@ Si no quieres agregar un valor específico pero solo hacer que sea opcional, est
|
|||
|
||||
Pero cuando quieres hacer un parámetro de query requerido, simplemente no declares ningún valor por defecto:
|
||||
|
||||
{* ../../docs_src/query_params/tutorial005_py39.py hl[6:7] *}
|
||||
{* ../../docs_src/query_params/tutorial005_py310.py hl[6:7] *}
|
||||
|
||||
Aquí el parámetro de query `needy` es un parámetro de query requerido de tipo `str`.
|
||||
|
||||
|
|
|
|||
|
|
@ -20,13 +20,13 @@ Esto es porque los archivos subidos se envían como "form data".
|
|||
|
||||
Importa `File` y `UploadFile` desde `fastapi`:
|
||||
|
||||
{* ../../docs_src/request_files/tutorial001_an_py39.py hl[3] *}
|
||||
{* ../../docs_src/request_files/tutorial001_an_py310.py hl[3] *}
|
||||
|
||||
## Definir Parámetros `File` { #define-file-parameters }
|
||||
|
||||
Crea parámetros de archivo de la misma manera que lo harías para `Body` o `Form`:
|
||||
|
||||
{* ../../docs_src/request_files/tutorial001_an_py39.py hl[9] *}
|
||||
{* ../../docs_src/request_files/tutorial001_an_py310.py hl[9] *}
|
||||
|
||||
/// info | Información
|
||||
|
||||
|
|
@ -54,7 +54,7 @@ Pero hay varios casos en los que podrías beneficiarte de usar `UploadFile`.
|
|||
|
||||
Define un parámetro de archivo con un tipo de `UploadFile`:
|
||||
|
||||
{* ../../docs_src/request_files/tutorial001_an_py39.py hl[14] *}
|
||||
{* ../../docs_src/request_files/tutorial001_an_py310.py hl[14] *}
|
||||
|
||||
Usar `UploadFile` tiene varias ventajas sobre `bytes`:
|
||||
|
||||
|
|
@ -121,7 +121,7 @@ Los datos de los forms normalmente se codifican usando el "media type" `applicat
|
|||
|
||||
Pero cuando el formulario incluye archivos, se codifica como `multipart/form-data`. Si usas `File`, **FastAPI** sabrá que tiene que obtener los archivos de la parte correcta del cuerpo.
|
||||
|
||||
Si deseas leer más sobre estas codificaciones y campos de formularios, dirígete a la <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST" class="external-link" target="_blank"><abbr title="Mozilla Developer Network – Red de Desarrolladores de Mozilla">MDN</abbr> web docs para <code>POST</code></a>.
|
||||
Si deseas leer más sobre estas codificaciones y campos de formularios, dirígete a la <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST" class="external-link" target="_blank"><abbr title="Mozilla Developer Network - Red de Desarrolladores de Mozilla">MDN</abbr> web docs para <code>POST</code></a>.
|
||||
|
||||
///
|
||||
|
||||
|
|
@ -143,7 +143,7 @@ Puedes hacer un archivo opcional utilizando anotaciones de tipos estándar y est
|
|||
|
||||
También puedes usar `File()` con `UploadFile`, por ejemplo, para establecer metadatos adicionales:
|
||||
|
||||
{* ../../docs_src/request_files/tutorial001_03_an_py39.py hl[9,15] *}
|
||||
{* ../../docs_src/request_files/tutorial001_03_an_py310.py hl[9,15] *}
|
||||
|
||||
## Subidas de Múltiples Archivos { #multiple-file-uploads }
|
||||
|
||||
|
|
@ -151,9 +151,9 @@ Es posible subir varios archivos al mismo tiempo.
|
|||
|
||||
Estarían asociados al mismo "campo de formulario" enviado usando "form data".
|
||||
|
||||
Para usar eso, declara una lista de `bytes` o `UploadFile`:
|
||||
Para usar eso, declara una `list` de `bytes` o `UploadFile`:
|
||||
|
||||
{* ../../docs_src/request_files/tutorial002_an_py39.py hl[10,15] *}
|
||||
{* ../../docs_src/request_files/tutorial002_an_py310.py hl[10,15] *}
|
||||
|
||||
Recibirás, como se declaró, una `list` de `bytes` o `UploadFile`s.
|
||||
|
||||
|
|
@ -169,7 +169,7 @@ También podrías usar `from starlette.responses import HTMLResponse`.
|
|||
|
||||
Y de la misma manera que antes, puedes usar `File()` para establecer parámetros adicionales, incluso para `UploadFile`:
|
||||
|
||||
{* ../../docs_src/request_files/tutorial003_an_py39.py hl[11,18:20] *}
|
||||
{* ../../docs_src/request_files/tutorial003_an_py310.py hl[11,18:20] *}
|
||||
|
||||
## Recapitulación { #recap }
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ Esto es compatible desde la versión `0.113.0` de FastAPI. 🤓
|
|||
|
||||
Solo necesitas declarar un **modelo de Pydantic** con los campos que quieres recibir como **campos de formulario**, y luego declarar el parámetro como `Form`:
|
||||
|
||||
{* ../../docs_src/request_form_models/tutorial001_an_py39.py hl[9:11,15] *}
|
||||
{* ../../docs_src/request_form_models/tutorial001_an_py310.py hl[9:11,15] *}
|
||||
|
||||
**FastAPI** **extraerá** los datos de **cada campo** de los **form data** en el request y te dará el modelo de Pydantic que definiste.
|
||||
|
||||
|
|
@ -48,7 +48,7 @@ Esto es compatible desde la versión `0.114.0` de FastAPI. 🤓
|
|||
|
||||
Puedes usar la configuración del modelo de Pydantic para `forbid` cualquier campo `extra`:
|
||||
|
||||
{* ../../docs_src/request_form_models/tutorial002_an_py39.py hl[12] *}
|
||||
{* ../../docs_src/request_form_models/tutorial002_an_py310.py hl[12] *}
|
||||
|
||||
Si un cliente intenta enviar datos extra, recibirá un response de **error**.
|
||||
|
||||
|
|
|
|||
|
|
@ -16,13 +16,13 @@ $ pip install python-multipart
|
|||
|
||||
## Importa `File` y `Form` { #import-file-and-form }
|
||||
|
||||
{* ../../docs_src/request_forms_and_files/tutorial001_an_py39.py hl[3] *}
|
||||
{* ../../docs_src/request_forms_and_files/tutorial001_an_py310.py hl[3] *}
|
||||
|
||||
## Define parámetros `File` y `Form` { #define-file-and-form-parameters }
|
||||
|
||||
Crea parámetros de archivo y formulario de la misma manera que lo harías para `Body` o `Query`:
|
||||
|
||||
{* ../../docs_src/request_forms_and_files/tutorial001_an_py39.py hl[10:12] *}
|
||||
{* ../../docs_src/request_forms_and_files/tutorial001_an_py310.py hl[10:12] *}
|
||||
|
||||
Los archivos y campos de formulario se subirán como form data y recibirás los archivos y campos de formulario.
|
||||
|
||||
|
|
|
|||
|
|
@ -18,17 +18,17 @@ $ pip install python-multipart
|
|||
|
||||
Importar `Form` desde `fastapi`:
|
||||
|
||||
{* ../../docs_src/request_forms/tutorial001_an_py39.py hl[3] *}
|
||||
{* ../../docs_src/request_forms/tutorial001_an_py310.py hl[3] *}
|
||||
|
||||
## Definir parámetros de `Form` { #define-form-parameters }
|
||||
|
||||
Crea parámetros de formulario de la misma manera que lo harías para `Body` o `Query`:
|
||||
|
||||
{* ../../docs_src/request_forms/tutorial001_an_py39.py hl[9] *}
|
||||
{* ../../docs_src/request_forms/tutorial001_an_py310.py hl[9] *}
|
||||
|
||||
Por ejemplo, en una de las formas en las que se puede usar la especificación OAuth2 (llamada "password flow") se requiere enviar un `username` y `password` como campos de formulario.
|
||||
|
||||
La <abbr title="specification – especificación">spec</abbr> requiere que los campos se llamen exactamente `username` y `password`, y que se envíen como campos de formulario, no JSON.
|
||||
La <dfn title="especificación">especificación</dfn> requiere que los campos se llamen exactamente `username` y `password`, y que se envíen como campos de formulario, no JSON.
|
||||
|
||||
Con `Form` puedes declarar las mismas configuraciones que con `Body` (y `Query`, `Path`, `Cookie`), incluyendo validación, ejemplos, un alias (por ejemplo, `user-name` en lugar de `username`), etc.
|
||||
|
||||
|
|
@ -56,7 +56,7 @@ Los datos de formularios normalmente se codifican usando el "media type" `applic
|
|||
|
||||
Pero cuando el formulario incluye archivos, se codifica como `multipart/form-data`. Leerás sobre la gestión de archivos en el próximo capítulo.
|
||||
|
||||
Si quieres leer más sobre estas codificaciones y campos de formulario, dirígete a la <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST" class="external-link" target="_blank"><abbr title="Mozilla Developer Network – Red de Desarrolladores de Mozilla">MDN</abbr> web docs para <code>POST</code></a>.
|
||||
Si quieres leer más sobre estas codificaciones y campos de formulario, dirígete a la <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST" class="external-link" target="_blank"><abbr title="Mozilla Developer Network - Red de Desarrolladores de Mozilla">MDN</abbr> web docs para <code>POST</code></a>.
|
||||
|
||||
///
|
||||
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ FastAPI usará este `response_model` para hacer toda la documentación de datos,
|
|||
|
||||
/// tip | Consejo
|
||||
|
||||
Si tienes chequeos estrictos de tipos en tu editor, mypy, etc., puedes declarar el tipo de retorno de la función como `Any`.
|
||||
Si tienes chequeo de tipos estricto en tu editor, mypy, etc., puedes declarar el tipo de retorno de la función como `Any`.
|
||||
|
||||
De esa manera le dices al editor que intencionalmente estás devolviendo cualquier cosa. Pero FastAPI todavía hará la documentación de datos, validación, filtrado, etc. con `response_model`.
|
||||
|
||||
|
|
@ -183,7 +183,7 @@ Podría haber casos en los que devuelvas algo que no es un campo válido de Pyda
|
|||
|
||||
El caso más común sería [devolver un Response directamente como se explica más adelante en la documentación avanzada](../advanced/response-directly.md){.internal-link target=_blank}.
|
||||
|
||||
{* ../../docs_src/response_model/tutorial003_02_py39.py hl[8,10:11] *}
|
||||
{* ../../docs_src/response_model/tutorial003_02_py310.py hl[8,10:11] *}
|
||||
|
||||
Este caso simple es manejado automáticamente por FastAPI porque la anotación del tipo de retorno es la clase (o una subclase de) `Response`.
|
||||
|
||||
|
|
@ -193,7 +193,7 @@ Y las herramientas también estarán felices porque tanto `RedirectResponse` com
|
|||
|
||||
También puedes usar una subclase de `Response` en la anotación del tipo:
|
||||
|
||||
{* ../../docs_src/response_model/tutorial003_03_py39.py hl[8:9] *}
|
||||
{* ../../docs_src/response_model/tutorial003_03_py310.py hl[8:9] *}
|
||||
|
||||
Esto también funcionará porque `RedirectResponse` es una subclase de `Response`, y FastAPI manejará automáticamente este caso simple.
|
||||
|
||||
|
|
@ -201,7 +201,7 @@ Esto también funcionará porque `RedirectResponse` es una subclase de `Response
|
|||
|
||||
Pero cuando devuelves algún otro objeto arbitrario que no es un tipo válido de Pydantic (por ejemplo, un objeto de base de datos) y lo anotas así en la función, FastAPI intentará crear un modelo de response de Pydantic a partir de esa anotación de tipo, y fallará.
|
||||
|
||||
Lo mismo sucedería si tuvieras algo como un <abbr title='Una unión entre múltiples tipos significa "cualquiera de estos tipos".'>union</abbr> entre diferentes tipos donde uno o más de ellos no son tipos válidos de Pydantic, por ejemplo esto fallaría 💥:
|
||||
Lo mismo sucedería si tuvieras algo como una <dfn title='una unión entre múltiples tipos significa "cualquiera de estos tipos".'>unión</dfn> entre diferentes tipos donde uno o más de ellos no son tipos válidos de Pydantic, por ejemplo esto fallaría 💥:
|
||||
|
||||
{* ../../docs_src/response_model/tutorial003_04_py310.py hl[8] *}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ De la misma manera que puedes especificar un modelo de response, también puedes
|
|||
* `@app.delete()`
|
||||
* etc.
|
||||
|
||||
{* ../../docs_src/response_status_code/tutorial001_py39.py hl[6] *}
|
||||
{* ../../docs_src/response_status_code/tutorial001_py310.py hl[6] *}
|
||||
|
||||
/// note | Nota
|
||||
|
||||
|
|
@ -66,7 +66,7 @@ En breve:
|
|||
|
||||
/// tip | Consejo
|
||||
|
||||
Para saber más sobre cada código de estado y qué código es para qué, revisa la <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Status" class="external-link" target="_blank">documentación de <abbr title="Mozilla Developer Network – Red de Desarrolladores de Mozilla">MDN</abbr> sobre códigos de estado HTTP</a>.
|
||||
Para saber más sobre cada código de estado y qué código es para qué, revisa la <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Status" class="external-link" target="_blank">documentación de <abbr title="Mozilla Developer Network - Red de Desarrolladores de Mozilla">MDN</abbr> sobre códigos de estado HTTP</a>.
|
||||
|
||||
///
|
||||
|
||||
|
|
@ -74,7 +74,7 @@ Para saber más sobre cada código de estado y qué código es para qué, revisa
|
|||
|
||||
Veamos de nuevo el ejemplo anterior:
|
||||
|
||||
{* ../../docs_src/response_status_code/tutorial001_py39.py hl[6] *}
|
||||
{* ../../docs_src/response_status_code/tutorial001_py310.py hl[6] *}
|
||||
|
||||
`201` es el código de estado para "Created".
|
||||
|
||||
|
|
@ -82,7 +82,7 @@ Pero no tienes que memorizar lo que significa cada uno de estos códigos.
|
|||
|
||||
Puedes usar las variables de conveniencia de `fastapi.status`.
|
||||
|
||||
{* ../../docs_src/response_status_code/tutorial002_py39.py hl[1,6] *}
|
||||
{* ../../docs_src/response_status_code/tutorial002_py310.py hl[1,6] *}
|
||||
|
||||
Son solo una conveniencia, mantienen el mismo número, pero de esa manera puedes usar el autocompletado del editor para encontrarlos:
|
||||
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ Por supuesto, también puedes pasar múltiples `examples`:
|
|||
|
||||
Cuando haces esto, los ejemplos serán parte del **JSON Schema** interno para esos datos del body.
|
||||
|
||||
Sin embargo, al <abbr title="2023-08-26">momento de escribir esto</abbr>, Swagger UI, la herramienta encargada de mostrar la interfaz de documentación, no soporta mostrar múltiples ejemplos para los datos en **JSON Schema**. Pero lee más abajo para una solución alternativa.
|
||||
Sin embargo, al <dfn title="2023-08-26">momento de escribir esto</dfn>, Swagger UI, la herramienta encargada de mostrar la interfaz de documentación, no soporta mostrar múltiples ejemplos para los datos en **JSON Schema**. Pero lee más abajo para una solución alternativa.
|
||||
|
||||
### `examples` específicos de OpenAPI { #openapi-specific-examples }
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ Primero solo usemos el código y veamos cómo funciona, y luego volveremos para
|
|||
|
||||
Copia el ejemplo en un archivo `main.py`:
|
||||
|
||||
{* ../../docs_src/security/tutorial001_an_py39.py *}
|
||||
{* ../../docs_src/security/tutorial001_an_py310.py *}
|
||||
|
||||
## Ejecútalo { #run-it }
|
||||
|
||||
|
|
@ -132,7 +132,7 @@ En ese caso, **FastAPI** también te proporciona las herramientas para construir
|
|||
|
||||
Cuando creamos una instance de la clase `OAuth2PasswordBearer` pasamos el parámetro `tokenUrl`. Este parámetro contiene la URL que el cliente (el frontend corriendo en el navegador del usuario) usará para enviar el `username` y `password` a fin de obtener un token.
|
||||
|
||||
{* ../../docs_src/security/tutorial001_an_py39.py hl[8] *}
|
||||
{* ../../docs_src/security/tutorial001_an_py310.py hl[8] *}
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
|
|
@ -170,7 +170,7 @@ Así que, puede usarse con `Depends`.
|
|||
|
||||
Ahora puedes pasar ese `oauth2_scheme` en una dependencia con `Depends`.
|
||||
|
||||
{* ../../docs_src/security/tutorial001_an_py39.py hl[12] *}
|
||||
{* ../../docs_src/security/tutorial001_an_py310.py hl[12] *}
|
||||
|
||||
Esta dependencia proporcionará un `str` que se asigna al parámetro `token` de la *path operation function*.
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
En el capítulo anterior, el sistema de seguridad (que se basa en el sistema de inyección de dependencias) le estaba dando a la *path operation function* un `token` como un `str`:
|
||||
|
||||
{* ../../docs_src/security/tutorial001_an_py39.py hl[12] *}
|
||||
{* ../../docs_src/security/tutorial001_an_py310.py hl[12] *}
|
||||
|
||||
Pero eso aún no es tan útil. Vamos a hacer que nos dé el usuario actual.
|
||||
|
||||
|
|
|
|||
|
|
@ -116,7 +116,11 @@ Y otra utilidad para verificar si una contraseña recibida coincide con el hash
|
|||
|
||||
Y otra más para autenticar y devolver un usuario.
|
||||
|
||||
{* ../../docs_src/security/tutorial004_an_py310.py hl[8,49,56:57,60:61,70:76] *}
|
||||
{* ../../docs_src/security/tutorial004_an_py310.py hl[8,49,51,58:59,62:63,72:79] *}
|
||||
|
||||
Cuando `authenticate_user` se llama con un nombre de usuario que no existe en la base de datos, aun así ejecutamos `verify_password` contra un hash ficticio.
|
||||
|
||||
Esto asegura que el endpoint tarda aproximadamente la misma cantidad de tiempo en responder tanto si el nombre de usuario es válido como si no, previniendo los **timing attacks** que podrían usarse para enumerar nombres de usuario existentes.
|
||||
|
||||
/// note | Nota
|
||||
|
||||
|
|
@ -152,7 +156,7 @@ Define un Modelo de Pydantic que se usará en el endpoint de token para el respo
|
|||
|
||||
Crea una función de utilidad para generar un nuevo token de acceso.
|
||||
|
||||
{* ../../docs_src/security/tutorial004_an_py310.py hl[4,7,13:15,29:31,79:87] *}
|
||||
{* ../../docs_src/security/tutorial004_an_py310.py hl[4,7,13:15,29:31,82:90] *}
|
||||
|
||||
## Actualizar las dependencias { #update-the-dependencies }
|
||||
|
||||
|
|
@ -162,7 +166,7 @@ Decodifica el token recibido, verifícalo y devuelve el usuario actual.
|
|||
|
||||
Si el token es inválido, devuelve un error HTTP de inmediato.
|
||||
|
||||
{* ../../docs_src/security/tutorial004_an_py310.py hl[90:107] *}
|
||||
{* ../../docs_src/security/tutorial004_an_py310.py hl[93:110] *}
|
||||
|
||||
## Actualizar la *path operation* `/token` { #update-the-token-path-operation }
|
||||
|
||||
|
|
@ -170,7 +174,7 @@ Crea un `timedelta` con el tiempo de expiración del token.
|
|||
|
||||
Crea un verdadero token de acceso JWT y devuélvelo.
|
||||
|
||||
{* ../../docs_src/security/tutorial004_an_py310.py hl[118:133] *}
|
||||
{* ../../docs_src/security/tutorial004_an_py310.py hl[121:136] *}
|
||||
|
||||
### Detalles técnicos sobre el "sujeto" `sub` de JWT { #technical-details-about-the-jwt-subject-sub }
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ Aquí veremos un ejemplo usando <a href="https://sqlmodel.tiangolo.com/" class="
|
|||
|
||||
/// tip | Consejo
|
||||
|
||||
Puedes usar cualquier otro paquete de bases de datos SQL o NoSQL que quieras (en algunos casos llamadas <abbr title="Object Relational Mapper – Mapeador Objeto-Relacional: un término elegante para un paquete donde algunas clases representan tablas SQL y las instances representan filas en esas tablas">"ORMs"</abbr>), FastAPI no te obliga a usar nada. 😎
|
||||
Puedes usar cualquier otro paquete de bases de datos SQL o NoSQL que quieras (en algunos casos llamadas <abbr title="Object Relational Mapper - Mapeador Objeto-Relacional: un término elegante para un paquete donde algunas clases representan tablas SQL y las instances representan filas en esas tablas">"ORMs"</abbr>), FastAPI no te obliga a usar nada. 😎
|
||||
|
||||
///
|
||||
|
||||
|
|
@ -354,4 +354,4 @@ Si vas a la interfaz de `/docs` de la API, verás que ahora está actualizada, y
|
|||
|
||||
Puedes usar <a href="https://sqlmodel.tiangolo.com/" class="external-link" target="_blank">**SQLModel**</a> para interactuar con una base de datos SQL y simplificar el código con *modelos de datos* y *modelos de tablas*.
|
||||
|
||||
Puedes aprender mucho más en la documentación de **SQLModel**, hay un mini <a href="https://sqlmodel.tiangolo.com/tutorial/fastapi/" class="external-link" target="_blank">tutorial sobre el uso de SQLModel con **FastAPI**</a>. 🚀
|
||||
Puedes aprender mucho más en la documentación de **SQLModel**, hay un mini <a href="https://sqlmodel.tiangolo.com/tutorial/fastapi/" class="external-link" target="_blank">tutorial más largo sobre el uso de SQLModel con **FastAPI**</a>. 🚀
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ Puedes servir archivos estáticos automáticamente desde un directorio utilizand
|
|||
* Importa `StaticFiles`.
|
||||
* "Monta" una instance de `StaticFiles()` en un path específico.
|
||||
|
||||
{* ../../docs_src/static_files/tutorial001_py39.py hl[2,6] *}
|
||||
{* ../../docs_src/static_files/tutorial001_py310.py hl[2,6] *}
|
||||
|
||||
/// note | Detalles Técnicos
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ Usa el objeto `TestClient` de la misma manera que con `httpx`.
|
|||
|
||||
Escribe declaraciones `assert` simples con las expresiones estándar de Python que necesites revisar (otra vez, estándar de `pytest`).
|
||||
|
||||
{* ../../docs_src/app_testing/tutorial001_py39.py hl[2,12,15:18] *}
|
||||
{* ../../docs_src/app_testing/tutorial001_py310.py hl[2,12,15:18] *}
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
|
|
@ -75,7 +75,7 @@ Digamos que tienes una estructura de archivos como se describe en [Aplicaciones
|
|||
|
||||
En el archivo `main.py` tienes tu aplicación de **FastAPI**:
|
||||
|
||||
{* ../../docs_src/app_testing/app_a_py39/main.py *}
|
||||
{* ../../docs_src/app_testing/app_a_py310/main.py *}
|
||||
|
||||
### Archivo de prueba { #testing-file }
|
||||
|
||||
|
|
@ -91,7 +91,7 @@ Entonces podrías tener un archivo `test_main.py` con tus pruebas. Podría estar
|
|||
|
||||
Debido a que este archivo está en el mismo paquete, puedes usar importaciones relativas para importar el objeto `app` desde el módulo `main` (`main.py`):
|
||||
|
||||
{* ../../docs_src/app_testing/app_a_py39/test_main.py hl[3] *}
|
||||
{* ../../docs_src/app_testing/app_a_py310/test_main.py hl[3] *}
|
||||
|
||||
...y tener el código para las pruebas tal como antes.
|
||||
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ $ cd awesome-project
|
|||
|
||||
## Crea un Entorno Virtual { #create-a-virtual-environment }
|
||||
|
||||
Cuando empiezas a trabajar en un proyecto de Python **por primera vez**, crea un entorno virtual **<abbr title="hay otras opciones, esto es solo una guía sencilla">dentro de tu proyecto</abbr>**.
|
||||
Cuando empiezas a trabajar en un proyecto de Python **por primera vez**, crea un entorno virtual **<dfn title="hay otras opciones, esto es solo una guía sencilla">dentro de tu proyecto</dfn>**.
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
|
|
@ -170,9 +170,9 @@ Esto asegura que si usas un programa de **terminal (<abbr title="command line in
|
|||
|
||||
///
|
||||
|
||||
## Verifica que el Entorno Virtual esté Activo { #check-the-virtual-environment-is-active }
|
||||
## Revisa que el Entorno Virtual esté Activo { #check-the-virtual-environment-is-active }
|
||||
|
||||
Verifica que el entorno virtual esté activo (el comando anterior funcionó).
|
||||
Revisa que el entorno virtual esté activo (el comando anterior funcionó).
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
|
|
@ -732,7 +732,7 @@ Un detalle importante es que pondrá el path del entorno virtual al **comienzo**
|
|||
|
||||
Activar un entorno virtual también cambia un par de otras cosas, pero esta es una de las cosas más importantes que hace.
|
||||
|
||||
## Verificando un Entorno Virtual { #checking-a-virtual-environment }
|
||||
## Revisando un Entorno Virtual { #checking-a-virtual-environment }
|
||||
|
||||
Cuando revisas si un entorno virtual está activo, por ejemplo con:
|
||||
|
||||
|
|
|
|||
|
|
@ -104,8 +104,8 @@ $ cd code
|
|||
...e um exemplo de código Python...
|
||||
|
||||
```Python
|
||||
wont_work() # This won't work 😱
|
||||
works(foo="bar") # This works 🎉
|
||||
wont_work() # Isto não vai funcionar 😱
|
||||
works(foo="bar") # Isto funciona 🎉
|
||||
```
|
||||
|
||||
...e é isso.
|
||||
|
|
@ -204,8 +204,8 @@ Aqui estão algumas coisas envolvidas em elementos HTML "abbr" (algumas são inv
|
|||
|
||||
### O abbr fornece uma frase completa e uma explicação { #the-abbr-gives-a-full-phrase-and-an-explanation }
|
||||
|
||||
* <abbr title="Mozilla Developer Network: documentação para desenvolvedores, escrita pelo pessoal do Firefox">MDN</abbr>
|
||||
* <abbr title="Input/Output: leitura ou escrita em disco, comunicações de rede.">I/O</abbr>.
|
||||
* <abbr title="Mozilla Developer Network – Rede de Desenvolvedores da Mozilla: documentação para desenvolvedores, escrita pelo pessoal do Firefox">MDN</abbr>
|
||||
* <abbr title="Input/Output – Entrada/Saída: leitura ou escrita em disco, comunicações de rede.">I/O</abbr>.
|
||||
|
||||
////
|
||||
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ O **FastAPI** pegará este modelo, gerará o esquema JSON dele e incluirá no lo
|
|||
|
||||
Por exemplo, para declarar um outro retorno com o status code `404` e um modelo do Pydantic chamado `Message`, você pode escrever:
|
||||
|
||||
{* ../../docs_src/additional_responses/tutorial001_py39.py hl[18,22] *}
|
||||
{* ../../docs_src/additional_responses/tutorial001_py310.py hl[18,22] *}
|
||||
|
||||
/// note | Nota
|
||||
|
||||
|
|
@ -203,7 +203,7 @@ Por exemplo, você pode declarar um retorno com o código de status `404` que ut
|
|||
|
||||
E um retorno com o código de status `200` que utiliza o seu `response_model`, porém inclui um `example` customizado:
|
||||
|
||||
{* ../../docs_src/additional_responses/tutorial003_py39.py hl[20:31] *}
|
||||
{* ../../docs_src/additional_responses/tutorial003_py310.py hl[20:31] *}
|
||||
|
||||
Isso será combinado e incluído em seu OpenAPI, e disponibilizado na documentação da sua API:
|
||||
|
||||
|
|
@ -243,5 +243,5 @@ Por exemplo:
|
|||
|
||||
Para verificar exatamente o que você pode incluir nos retornos, você pode conferir estas seções na especificação do OpenAPI:
|
||||
|
||||
* <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#responses-object" class="external-link" target="_blank">Objeto de Retorno OpenAPI</a>, inclui o `Response Object`.
|
||||
* <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#response-object" class="external-link" target="_blank">Objeto de Retorno OpenAPI</a>, você pode incluir qualquer coisa dele diretamente em cada retorno dentro do seu parâmetro `responses`. Incluindo `description`, `headers`, `content` (dentro dele que você declara diferentes media types e esquemas JSON), e `links`.
|
||||
* <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#responses-object" class="external-link" target="_blank">Objeto de Retornos do OpenAPI</a>, inclui o `Response Object`.
|
||||
* <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#response-object" class="external-link" target="_blank">Objeto de Retorno do OpenAPI</a>, você pode incluir qualquer coisa dele diretamente em cada retorno dentro do seu parâmetro `responses`. Incluindo `description`, `headers`, `content` (dentro dele que você declara diferentes media types e esquemas JSON), e `links`.
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ Não propriamente a classe (que já é um chamável), mas a instância desta cla
|
|||
|
||||
Para fazer isso, nós declaramos o método `__call__`:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial011_an_py39.py hl[12] *}
|
||||
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[12] *}
|
||||
|
||||
Neste caso, o `__call__` é o que o **FastAPI** utilizará para verificar parâmetros adicionais e sub dependências, e isso é o que será chamado para passar o valor ao parâmetro na sua *função de operação de rota* posteriormente.
|
||||
|
||||
|
|
@ -26,7 +26,7 @@ Neste caso, o `__call__` é o que o **FastAPI** utilizará para verificar parâm
|
|||
|
||||
E agora, nós podemos utilizar o `__init__` para declarar os parâmetros da instância que podemos utilizar para "parametrizar" a dependência:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial011_an_py39.py hl[9] *}
|
||||
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[9] *}
|
||||
|
||||
Neste caso, o **FastAPI** nunca tocará ou se importará com o `__init__`, nós vamos utilizar diretamente em nosso código.
|
||||
|
||||
|
|
@ -34,7 +34,7 @@ Neste caso, o **FastAPI** nunca tocará ou se importará com o `__init__`, nós
|
|||
|
||||
Nós poderíamos criar uma instância desta classe com:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial011_an_py39.py hl[18] *}
|
||||
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[18] *}
|
||||
|
||||
E deste modo nós podemos "parametrizar" a nossa dependência, que agora possui `"bar"` dentro dele, como o atributo `checker.fixed_content`.
|
||||
|
||||
|
|
@ -50,7 +50,7 @@ checker(q="somequery")
|
|||
|
||||
...e passar o que quer que isso retorne como valor da dependência em nossa *função de operação de rota* como o parâmetro `fixed_content_included`:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial011_an_py39.py hl[22] *}
|
||||
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[22] *}
|
||||
|
||||
/// tip | Dica
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,61 @@
|
|||
# Tipos Avançados de Python { #advanced-python-types }
|
||||
|
||||
Aqui estão algumas ideias adicionais que podem ser úteis ao trabalhar com tipos em Python.
|
||||
|
||||
## Usando `Union` ou `Optional` { #using-union-or-optional }
|
||||
|
||||
Se, por algum motivo, seu código não puder usar `|`, por exemplo, se não for em uma anotação de tipo, mas em algo como `response_model=`, em vez de usar a barra vertical (`|`) você pode usar `Union` do `typing`.
|
||||
|
||||
Por exemplo, você poderia declarar que algo pode ser `str` ou `None`:
|
||||
|
||||
```python
|
||||
from typing import Union
|
||||
|
||||
|
||||
def say_hi(name: Union[str, None]):
|
||||
print(f"Hi {name}!")
|
||||
```
|
||||
|
||||
O `typing` também tem um atalho para declarar que algo pode ser `None`, com `Optional`.
|
||||
|
||||
Aqui vai uma dica do meu ponto de vista bem subjetivo:
|
||||
|
||||
* 🚨 Evite usar `Optional[SomeType]`
|
||||
* Em vez disso ✨ use **`Union[SomeType, None]`** ✨.
|
||||
|
||||
Ambos são equivalentes e, por baixo, são a mesma coisa, mas eu recomendaria `Union` em vez de `Optional` porque a palavra "opcional" sugere que o valor é opcional; na verdade, significa "pode ser `None`", mesmo quando não é opcional e continua sendo obrigatório.
|
||||
|
||||
Acho que `Union[SomeType, None]` é mais explícito quanto ao significado.
|
||||
|
||||
É apenas uma questão de palavras e nomes. Mas essas palavras podem influenciar como você e sua equipe pensam sobre o código.
|
||||
|
||||
Como exemplo, veja esta função:
|
||||
|
||||
```python
|
||||
from typing import Optional
|
||||
|
||||
|
||||
def say_hi(name: Optional[str]):
|
||||
print(f"Hey {name}!")
|
||||
```
|
||||
|
||||
O parâmetro `name` é definido como `Optional[str]`, mas não é opcional; não é possível chamar a função sem o parâmetro:
|
||||
|
||||
```Python
|
||||
say_hi() # Ah, não, isso gera um erro! 😱
|
||||
```
|
||||
|
||||
O parâmetro `name` continua obrigatório (não é opcional) porque não tem valor padrão. Ainda assim, `name` aceita `None` como valor:
|
||||
|
||||
```Python
|
||||
say_hi(name=None) # Isso funciona, None é válido 🎉
|
||||
```
|
||||
|
||||
A boa notícia é que, na maioria dos casos, você poderá simplesmente usar `|` para definir uniões de tipos:
|
||||
|
||||
```python
|
||||
def say_hi(name: str | None):
|
||||
print(f"Hey {name}!")
|
||||
```
|
||||
|
||||
Então, normalmente você não precisa se preocupar com nomes como `Optional` e `Union`. 😎
|
||||
|
|
@ -32,11 +32,11 @@ Para um exemplos simples, vamos considerar uma estrutura de arquivos semelhante
|
|||
|
||||
O arquivo `main.py` teria:
|
||||
|
||||
{* ../../docs_src/async_tests/app_a_py39/main.py *}
|
||||
{* ../../docs_src/async_tests/app_a_py310/main.py *}
|
||||
|
||||
O arquivo `test_main.py` teria os testes para para o arquivo `main.py`, ele poderia ficar assim:
|
||||
|
||||
{* ../../docs_src/async_tests/app_a_py39/test_main.py *}
|
||||
{* ../../docs_src/async_tests/app_a_py310/test_main.py *}
|
||||
|
||||
## Executá-lo { #run-it }
|
||||
|
||||
|
|
@ -56,7 +56,7 @@ $ pytest
|
|||
|
||||
O marcador `@pytest.mark.anyio` informa ao pytest que esta função de teste deve ser invocada de maneira assíncrona:
|
||||
|
||||
{* ../../docs_src/async_tests/app_a_py39/test_main.py hl[7] *}
|
||||
{* ../../docs_src/async_tests/app_a_py310/test_main.py hl[7] *}
|
||||
|
||||
/// tip | Dica
|
||||
|
||||
|
|
@ -66,7 +66,7 @@ Note que a função de teste é `async def` agora, no lugar de apenas `def` como
|
|||
|
||||
Então podemos criar um `AsyncClient` com a aplicação, e enviar requisições assíncronas para ela utilizando `await`.
|
||||
|
||||
{* ../../docs_src/async_tests/app_a_py39/test_main.py hl[9:12] *}
|
||||
{* ../../docs_src/async_tests/app_a_py310/test_main.py hl[9:12] *}
|
||||
|
||||
Isso é equivalente a:
|
||||
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ $ fastapi run --forwarded-allow-ips="*"
|
|||
|
||||
Por exemplo, suponha que você defina uma *operação de rota* `/items/`:
|
||||
|
||||
{* ../../docs_src/behind_a_proxy/tutorial001_01_py39.py hl[6] *}
|
||||
{* ../../docs_src/behind_a_proxy/tutorial001_01_py310.py hl[6] *}
|
||||
|
||||
Se o cliente tentar ir para `/items`, por padrão, ele seria redirecionado para `/items/`.
|
||||
|
||||
|
|
@ -91,9 +91,9 @@ O **proxy** intercepta a requisição original do cliente e adiciona os headers
|
|||
|
||||
Esses headers preservam informações sobre a requisição original que, de outra forma, seriam perdidas:
|
||||
|
||||
* X-Forwarded-For: o endereço IP original do cliente
|
||||
* X-Forwarded-Proto: o protocolo original (`https`)
|
||||
* X-Forwarded-Host: o host original (`mysuperapp.com`)
|
||||
* **X-Forwarded-For**: o endereço IP original do cliente
|
||||
* **X-Forwarded-Proto**: o protocolo original (`https`)
|
||||
* **X-Forwarded-Host**: o host original (`mysuperapp.com`)
|
||||
|
||||
Quando a **CLI do FastAPI** é configurada com `--forwarded-allow-ips`, ela confia nesses headers e os utiliza, por exemplo, para gerar as URLs corretas em redirecionamentos.
|
||||
|
||||
|
|
@ -115,7 +115,7 @@ Nesse caso, o path original `/app` seria servido em `/api/v1/app`.
|
|||
|
||||
Embora todo o seu código esteja escrito assumindo que existe apenas `/app`.
|
||||
|
||||
{* ../../docs_src/behind_a_proxy/tutorial001_py39.py hl[6] *}
|
||||
{* ../../docs_src/behind_a_proxy/tutorial001_py310.py hl[6] *}
|
||||
|
||||
E o proxy estaria **"removendo"** o **prefixo de path** dinamicamente antes de transmitir a solicitação para o servidor da aplicação (provavelmente Uvicorn via CLI do FastAPI), mantendo sua aplicação convencida de que está sendo servida em `/app`, para que você não precise atualizar todo o seu código para incluir o prefixo `/api/v1`.
|
||||
|
||||
|
|
@ -193,7 +193,7 @@ Você pode obter o `root_path` atual usado pela sua aplicação para cada solici
|
|||
|
||||
Aqui estamos incluindo-o na mensagem apenas para fins de demonstração.
|
||||
|
||||
{* ../../docs_src/behind_a_proxy/tutorial001_py39.py hl[8] *}
|
||||
{* ../../docs_src/behind_a_proxy/tutorial001_py310.py hl[8] *}
|
||||
|
||||
Então, se você iniciar o Uvicorn com:
|
||||
|
||||
|
|
@ -220,7 +220,7 @@ A resposta seria algo como:
|
|||
|
||||
Alternativamente, se você não tiver uma maneira de fornecer uma opção de linha de comando como `--root-path` ou equivalente, você pode definir o parâmetro `root_path` ao criar sua aplicação FastAPI:
|
||||
|
||||
{* ../../docs_src/behind_a_proxy/tutorial002_py39.py hl[3] *}
|
||||
{* ../../docs_src/behind_a_proxy/tutorial002_py310.py hl[3] *}
|
||||
|
||||
Passar o `root_path` para `FastAPI` seria o equivalente a passar a opção de linha de comando `--root-path` para Uvicorn ou Hypercorn.
|
||||
|
||||
|
|
@ -400,7 +400,7 @@ Se você passar uma lista personalizada de `servers` e houver um `root_path` (po
|
|||
|
||||
Por exemplo:
|
||||
|
||||
{* ../../docs_src/behind_a_proxy/tutorial003_py39.py hl[4:7] *}
|
||||
{* ../../docs_src/behind_a_proxy/tutorial003_py310.py hl[4:7] *}
|
||||
|
||||
Gerará um OpenAPI schema como:
|
||||
|
||||
|
|
@ -455,7 +455,7 @@ Se você não especificar o parâmetro `servers` e `root_path` for igual a `/`,
|
|||
|
||||
Se você não quiser que o **FastAPI** inclua um servidor automático usando o `root_path`, você pode usar o parâmetro `root_path_in_servers=False`:
|
||||
|
||||
{* ../../docs_src/behind_a_proxy/tutorial004_py39.py hl[9] *}
|
||||
{* ../../docs_src/behind_a_proxy/tutorial004_py310.py hl[9] *}
|
||||
|
||||
e então ele não será incluído no OpenAPI schema.
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ Isso ocorre por que, por padrão, o FastAPI irá verificar cada item dentro do d
|
|||
|
||||
Mas se você tem certeza que o conteúdo que você está retornando é **serializável com JSON**, você pode passá-lo diretamente para a classe de resposta e evitar o trabalho extra que o FastAPI teria ao passar o conteúdo pelo `jsonable_encoder` antes de passar para a classe de resposta.
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial001b_py39.py hl[2,7] *}
|
||||
{* ../../docs_src/custom_response/tutorial001b_py310.py hl[2,7] *}
|
||||
|
||||
/// info | Informação
|
||||
|
||||
|
|
@ -55,7 +55,7 @@ Para retornar uma resposta com HTML diretamente do **FastAPI**, utilize `HTMLRes
|
|||
* Importe `HTMLResponse`
|
||||
* Passe `HTMLResponse` como o parâmetro de `response_class` do seu *decorador de operação de rota*.
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial002_py39.py hl[2,7] *}
|
||||
{* ../../docs_src/custom_response/tutorial002_py310.py hl[2,7] *}
|
||||
|
||||
/// info | Informação
|
||||
|
||||
|
|
@ -73,7 +73,7 @@ Como visto em [Retornando uma Resposta Diretamente](response-directly.md){.inter
|
|||
|
||||
O mesmo exemplo de antes, retornando uma `HTMLResponse`, poderia parecer com:
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial003_py39.py hl[2,7,19] *}
|
||||
{* ../../docs_src/custom_response/tutorial003_py310.py hl[2,7,19] *}
|
||||
|
||||
/// warning | Atenção
|
||||
|
||||
|
|
@ -97,7 +97,7 @@ A `response_class` será usada apenas para documentar o OpenAPI da *operação d
|
|||
|
||||
Por exemplo, poderia ser algo como:
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial004_py39.py hl[7,21,23] *}
|
||||
{* ../../docs_src/custom_response/tutorial004_py310.py hl[7,21,23] *}
|
||||
|
||||
Neste exemplo, a função `generate_html_response()` já cria e retorna uma `Response` em vez de retornar o HTML em uma `str`.
|
||||
|
||||
|
|
@ -136,7 +136,7 @@ Ela aceita os seguintes parâmetros:
|
|||
|
||||
O FastAPI (Starlette, na verdade) irá incluir o cabeçalho Content-Length automaticamente. Ele também irá incluir o cabeçalho Content-Type, baseado no `media_type` e acrescentando uma codificação para tipos textuais.
|
||||
|
||||
{* ../../docs_src/response_directly/tutorial002_py39.py hl[1,18] *}
|
||||
{* ../../docs_src/response_directly/tutorial002_py310.py hl[1,18] *}
|
||||
|
||||
### `HTMLResponse` { #htmlresponse }
|
||||
|
||||
|
|
@ -146,7 +146,7 @@ Usa algum texto ou sequência de bytes e retorna uma resposta HTML. Como você l
|
|||
|
||||
Usa algum texto ou sequência de bytes para retornar uma resposta de texto não formatado.
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial005_py39.py hl[2,7,9] *}
|
||||
{* ../../docs_src/custom_response/tutorial005_py310.py hl[2,7,9] *}
|
||||
|
||||
### `JSONResponse` { #jsonresponse }
|
||||
|
||||
|
|
@ -180,7 +180,7 @@ Essa resposta requer a instalação do pacote `ujson`, com o comando `pip instal
|
|||
|
||||
///
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial001_py39.py hl[2,7] *}
|
||||
{* ../../docs_src/custom_response/tutorial001_py310.py hl[2,7] *}
|
||||
|
||||
/// tip | Dica
|
||||
|
||||
|
|
@ -194,13 +194,13 @@ Retorna um redirecionamento HTTP. Utiliza o código de status 307 (Redirecioname
|
|||
|
||||
Você pode retornar uma `RedirectResponse` diretamente:
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial006_py39.py hl[2,9] *}
|
||||
{* ../../docs_src/custom_response/tutorial006_py310.py hl[2,9] *}
|
||||
|
||||
---
|
||||
|
||||
Ou você pode utilizá-la no parâmetro `response_class`:
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial006b_py39.py hl[2,7,9] *}
|
||||
{* ../../docs_src/custom_response/tutorial006b_py310.py hl[2,7,9] *}
|
||||
|
||||
Se você fizer isso, então você pode retornar a URL diretamente da sua *função de operação de rota*
|
||||
|
||||
|
|
@ -210,13 +210,13 @@ Neste caso, o `status_code` utilizada será o padrão de `RedirectResponse`, que
|
|||
|
||||
Você também pode utilizar o parâmetro `status_code` combinado com o parâmetro `response_class`:
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial006c_py39.py hl[2,7,9] *}
|
||||
{* ../../docs_src/custom_response/tutorial006c_py310.py hl[2,7,9] *}
|
||||
|
||||
### `StreamingResponse` { #streamingresponse }
|
||||
|
||||
Recebe um gerador assíncrono ou um gerador/iterador comum e retorna o corpo da resposta de forma contínua (stream).
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial007_py39.py hl[2,14] *}
|
||||
{* ../../docs_src/custom_response/tutorial007_py310.py hl[2,14] *}
|
||||
|
||||
#### Utilizando `StreamingResponse` com objetos semelhantes a arquivos { #using-streamingresponse-with-file-like-objects }
|
||||
|
||||
|
|
@ -226,7 +226,7 @@ Dessa forma, você não precisa ler todo o arquivo na memória primeiro, e você
|
|||
|
||||
Isso inclui muitas bibliotecas que interagem com armazenamento em nuvem, processamento de vídeos, entre outras.
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial008_py39.py hl[2,10:12,14] *}
|
||||
{* ../../docs_src/custom_response/tutorial008_py310.py hl[2,10:12,14] *}
|
||||
|
||||
1. Essa é a função geradora. É definida como "função geradora" porque contém declarações `yield` nela.
|
||||
2. Ao utilizar o bloco `with`, nós garantimos que o objeto semelhante a um arquivo é fechado após a função geradora ser finalizada. Isto é, após a resposta terminar de ser enviada.
|
||||
|
|
@ -255,11 +255,11 @@ Recebe um conjunto de argumentos do construtor diferente dos outros tipos de res
|
|||
|
||||
Respostas de Arquivos incluem o tamanho do arquivo, data da última modificação e ETags apropriados, nos cabeçalhos `Content-Length`, `Last-Modified` e `ETag`, respectivamente.
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial009_py39.py hl[2,10] *}
|
||||
{* ../../docs_src/custom_response/tutorial009_py310.py hl[2,10] *}
|
||||
|
||||
Você também pode usar o parâmetro `response_class`:
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial009b_py39.py hl[2,8,10] *}
|
||||
{* ../../docs_src/custom_response/tutorial009b_py310.py hl[2,8,10] *}
|
||||
|
||||
Nesse caso, você pode retornar o caminho do arquivo diretamente da sua *função de operação de rota*.
|
||||
|
||||
|
|
@ -273,7 +273,7 @@ Vamos supor também que você queira retornar um JSON indentado e formatado, ent
|
|||
|
||||
Você poderia criar uma classe `CustomORJSONResponse`. A principal coisa a ser feita é sobrecarregar o método render da classe Response, `Response.render(content)`, que retorna o conteúdo em bytes:
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial009c_py39.py hl[9:14,17] *}
|
||||
{* ../../docs_src/custom_response/tutorial009c_py310.py hl[9:14,17] *}
|
||||
|
||||
Agora em vez de retornar:
|
||||
|
||||
|
|
@ -299,7 +299,7 @@ O padrão que define isso é o `default_response_class`.
|
|||
|
||||
No exemplo abaixo, o **FastAPI** irá utilizar `ORJSONResponse` por padrão, em todas as *operações de rota*, em vez de `JSONResponse`.
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial010_py39.py hl[2,4] *}
|
||||
{* ../../docs_src/custom_response/tutorial010_py310.py hl[2,4] *}
|
||||
|
||||
/// tip | Dica
|
||||
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ Nesse caso, você pode simplesmente trocar as `dataclasses` padrão por `pydanti
|
|||
|
||||
6. Aqui estamos retornando um dicionário que contém `items`, que é uma lista de dataclasses.
|
||||
|
||||
O FastAPI ainda é capaz de <abbr title="converter os dados para um formato que pode ser transmitido">serializar</abbr> os dados para JSON.
|
||||
O FastAPI ainda é capaz de <dfn title="converter os dados para um formato que pode ser transmitido">serializar</dfn> os dados para JSON.
|
||||
|
||||
7. Aqui o `response_model` está usando uma anotação de tipo de uma lista de dataclasses `Author`.
|
||||
|
||||
|
|
|
|||
|
|
@ -6,13 +6,13 @@ Da mesma forma, você pode definir a lógica (código) que deve ser executada qu
|
|||
|
||||
Como esse código é executado antes de a aplicação **começar** a receber requisições e logo depois que ela **termina** de lidar com as requisições, ele cobre todo o **lifespan** da aplicação (a palavra "lifespan" será importante em um segundo 😉).
|
||||
|
||||
Isso pode ser muito útil para configurar **recursos** que você precisa usar por toda a aplicação, e que são **compartilhados** entre as requisições e/ou que você precisa **limpar** depois. Por exemplo, um pool de conexões com o banco de dados ou o carregamento de um modelo de machine learning compartilhado.
|
||||
Isso pode ser muito útil para configurar **recursos** que você precisa usar por toda a aplicação, e que são **compartilhados** entre as requisições e/ou que você precisa **limpar** depois. Por exemplo, um pool de conexões com o banco de dados ou o carregamento de um modelo de Aprendizado de Máquina compartilhado.
|
||||
|
||||
## Caso de uso { #use-case }
|
||||
|
||||
Vamos começar com um exemplo de **caso de uso** e então ver como resolvê-lo com isso.
|
||||
|
||||
Vamos imaginar que você tem alguns **modelos de machine learning** que deseja usar para lidar com as requisições. 🤖
|
||||
Vamos imaginar que você tem alguns **modelos de Aprendizado de Máquina** que deseja usar para lidar com as requisições. 🤖
|
||||
|
||||
Os mesmos modelos são compartilhados entre as requisições, então não é um modelo por requisição, ou um por usuário, ou algo parecido.
|
||||
|
||||
|
|
@ -30,9 +30,9 @@ Vamos começar com um exemplo e depois ver em detalhes.
|
|||
|
||||
Nós criamos uma função assíncrona `lifespan()` com `yield` assim:
|
||||
|
||||
{* ../../docs_src/events/tutorial003_py39.py hl[16,19] *}
|
||||
{* ../../docs_src/events/tutorial003_py310.py hl[16,19] *}
|
||||
|
||||
Aqui estamos simulando a operação de *inicialização* custosa de carregar o modelo, colocando a (falsa) função do modelo no dicionário com modelos de machine learning antes do `yield`. Esse código será executado **antes** de a aplicação **começar a receber requisições**, durante a *inicialização*.
|
||||
Aqui estamos simulando a operação de *inicialização* custosa de carregar o modelo, colocando a (falsa) função do modelo no dicionário com modelos de Aprendizado de Máquina antes do `yield`. Esse código será executado **antes** de a aplicação **começar a receber requisições**, durante a *inicialização*.
|
||||
|
||||
E então, logo após o `yield`, descarregamos o modelo. Esse código será executado **depois** de a aplicação **terminar de lidar com as requisições**, pouco antes do *encerramento*. Isso poderia, por exemplo, liberar recursos como memória ou uma GPU.
|
||||
|
||||
|
|
@ -48,7 +48,7 @@ Talvez você precise iniciar uma nova versão, ou apenas cansou de executá-la.
|
|||
|
||||
A primeira coisa a notar é que estamos definindo uma função assíncrona com `yield`. Isso é muito semelhante a Dependências com `yield`.
|
||||
|
||||
{* ../../docs_src/events/tutorial003_py39.py hl[14:19] *}
|
||||
{* ../../docs_src/events/tutorial003_py310.py hl[14:19] *}
|
||||
|
||||
A primeira parte da função, antes do `yield`, será executada **antes** de a aplicação iniciar.
|
||||
|
||||
|
|
@ -60,7 +60,7 @@ Se você verificar, a função está decorada com um `@asynccontextmanager`.
|
|||
|
||||
Isso converte a função em algo chamado "**gerenciador de contexto assíncrono**".
|
||||
|
||||
{* ../../docs_src/events/tutorial003_py39.py hl[1,13] *}
|
||||
{* ../../docs_src/events/tutorial003_py310.py hl[1,13] *}
|
||||
|
||||
Um **gerenciador de contexto** em Python é algo que você pode usar em uma declaração `with`, por exemplo, `open()` pode ser usado como um gerenciador de contexto:
|
||||
|
||||
|
|
@ -82,7 +82,7 @@ No nosso exemplo de código acima, não o usamos diretamente, mas passamos para
|
|||
|
||||
O parâmetro `lifespan` da aplicação `FastAPI` aceita um **gerenciador de contexto assíncrono**, então podemos passar para ele nosso novo gerenciador de contexto assíncrono `lifespan`.
|
||||
|
||||
{* ../../docs_src/events/tutorial003_py39.py hl[22] *}
|
||||
{* ../../docs_src/events/tutorial003_py310.py hl[22] *}
|
||||
|
||||
## Eventos alternativos (descontinuados) { #alternative-events-deprecated }
|
||||
|
||||
|
|
@ -104,7 +104,7 @@ Essas funções podem ser declaradas com `async def` ou `def` normal.
|
|||
|
||||
Para adicionar uma função que deve rodar antes de a aplicação iniciar, declare-a com o evento `"startup"`:
|
||||
|
||||
{* ../../docs_src/events/tutorial001_py39.py hl[8] *}
|
||||
{* ../../docs_src/events/tutorial001_py310.py hl[8] *}
|
||||
|
||||
Nesse caso, a função de manipulador do evento `startup` inicializará os itens do "banco de dados" (apenas um `dict`) com alguns valores.
|
||||
|
||||
|
|
@ -116,7 +116,7 @@ E sua aplicação não começará a receber requisições até que todos os mani
|
|||
|
||||
Para adicionar uma função que deve ser executada quando a aplicação estiver encerrando, declare-a com o evento `"shutdown"`:
|
||||
|
||||
{* ../../docs_src/events/tutorial002_py39.py hl[6] *}
|
||||
{* ../../docs_src/events/tutorial002_py310.py hl[6] *}
|
||||
|
||||
Aqui, a função de manipulador do evento `shutdown` escreverá uma linha de texto `"Application shutdown"` no arquivo `log.txt`.
|
||||
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ Algumas dessas soluções também podem ser open source ou oferecer planos gratu
|
|||
|
||||
Vamos começar com uma aplicação FastAPI simples:
|
||||
|
||||
{* ../../docs_src/generate_clients/tutorial001_py39.py hl[7:9,12:13,16:17,21] *}
|
||||
{* ../../docs_src/generate_clients/tutorial001_py310.py hl[7:9,12:13,16:17,21] *}
|
||||
|
||||
Note que as *operações de rota* definem os modelos que usam para o corpo da requisição e o corpo da resposta, usando os modelos `Item` e `ResponseMessage`.
|
||||
|
||||
|
|
@ -98,7 +98,7 @@ Em muitos casos, sua aplicação FastAPI será maior, e você provavelmente usar
|
|||
|
||||
Por exemplo, você poderia ter uma seção para **items** e outra seção para **users**, e elas poderiam ser separadas por tags:
|
||||
|
||||
{* ../../docs_src/generate_clients/tutorial002_py39.py hl[21,26,34] *}
|
||||
{* ../../docs_src/generate_clients/tutorial002_py310.py hl[21,26,34] *}
|
||||
|
||||
### Gere um cliente TypeScript com Tags { #generate-a-typescript-client-with-tags }
|
||||
|
||||
|
|
@ -141,11 +141,11 @@ O FastAPI usa um **ID exclusivo** para cada *operação de rota*, ele é usado p
|
|||
|
||||
Você pode personalizar essa função. Ela recebe uma `APIRoute` e retorna uma string.
|
||||
|
||||
Por exemplo, aqui está usando a primeira tag (você provavelmente terá apenas uma tag) e o nome da *operação de rota* (o nome da função).
|
||||
Por exemplo, aqui está usando a primeira tag (Você provavelmente terá apenas uma tag) e o nome da *operação de rota* (o nome da função).
|
||||
|
||||
Você pode então passar essa função personalizada para o **FastAPI** como o parâmetro `generate_unique_id_function`:
|
||||
|
||||
{* ../../docs_src/generate_clients/tutorial003_py39.py hl[6:7,10] *}
|
||||
{* ../../docs_src/generate_clients/tutorial003_py310.py hl[6:7,10] *}
|
||||
|
||||
### Gere um cliente TypeScript com IDs de operação personalizados { #generate-a-typescript-client-with-custom-operation-ids }
|
||||
|
||||
|
|
@ -167,7 +167,7 @@ Mas para o cliente gerado, poderíamos **modificar** os IDs de operação do Ope
|
|||
|
||||
Poderíamos baixar o JSON do OpenAPI para um arquivo `openapi.json` e então poderíamos **remover essa tag prefixada** com um script como este:
|
||||
|
||||
{* ../../docs_src/generate_clients/tutorial004_py39.py *}
|
||||
{* ../../docs_src/generate_clients/tutorial004_py310.py *}
|
||||
|
||||
//// tab | Node.js
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ Nas próximas seções você verá outras opções, configurações, e recursos
|
|||
|
||||
/// tip | Dica
|
||||
|
||||
As próximas seções **não são necessáriamente "avançadas"**
|
||||
As próximas seções **não são necessariamente "avançadas"**.
|
||||
|
||||
E é possível que para seu caso de uso, a solução esteja em uma delas.
|
||||
|
||||
|
|
|
|||
|
|
@ -57,13 +57,13 @@ Garante que todas as requisições devem ser `https` ou `wss`.
|
|||
|
||||
Qualquer requisição para `http` ou `ws` será redirecionada para o esquema seguro.
|
||||
|
||||
{* ../../docs_src/advanced_middleware/tutorial001_py39.py hl[2,6] *}
|
||||
{* ../../docs_src/advanced_middleware/tutorial001_py310.py hl[2,6] *}
|
||||
|
||||
## `TrustedHostMiddleware` { #trustedhostmiddleware }
|
||||
|
||||
Garante que todas as requisições recebidas tenham um cabeçalho `Host` corretamente configurado, a fim de proteger contra ataques de cabeçalho de host HTTP.
|
||||
|
||||
{* ../../docs_src/advanced_middleware/tutorial002_py39.py hl[2,6:8] *}
|
||||
{* ../../docs_src/advanced_middleware/tutorial002_py310.py hl[2,6:8] *}
|
||||
|
||||
Os seguintes argumentos são suportados:
|
||||
|
||||
|
|
@ -78,7 +78,7 @@ Gerencia respostas GZip para qualquer requisição que inclua `"gzip"` no cabeç
|
|||
|
||||
O middleware lidará com respostas padrão e de streaming.
|
||||
|
||||
{* ../../docs_src/advanced_middleware/tutorial003_py39.py hl[2,6] *}
|
||||
{* ../../docs_src/advanced_middleware/tutorial003_py310.py hl[2,6] *}
|
||||
|
||||
Os seguintes argumentos são suportados:
|
||||
|
||||
|
|
@ -91,7 +91,7 @@ Há muitos outros middlewares ASGI.
|
|||
|
||||
Por exemplo:
|
||||
|
||||
* <a href="https://github.com/encode/uvicorn/blob/master/uvicorn/middleware/proxy_headers.py" class="external-link" target="_blank">Uvicorn's `ProxyHeadersMiddleware`</a>
|
||||
* <a href="https://github.com/encode/uvicorn/blob/master/uvicorn/middleware/proxy_headers.py" class="external-link" target="_blank">`ProxyHeadersMiddleware` do Uvicorn</a>
|
||||
* <a href="https://github.com/florimondmanca/msgpack-asgi" class="external-link" target="_blank">MessagePack</a>
|
||||
|
||||
Para checar outros middlewares disponíveis, confira <a href="https://www.starlette.dev/middleware/" class="external-link" target="_blank">Documentação de Middlewares do Starlette</a> e a <a href="https://github.com/florimondmanca/awesome-asgi" class="external-link" target="_blank">Lista Incrível do ASGI</a>.
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue