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:
# 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`. 😎
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.
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:
@ -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.
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.
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`.
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`:
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`.
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.
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 (<abbrtitle="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 (<abbrtitle="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
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:
Como **FastAPI** está basado en Starlette e implementa la especificación <abbrtitle="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 <abbrtitle="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.
@ -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()`.
@ -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`:
## 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):
En este ejemplo, no declaramos ningún modelo Pydantic. De hecho, el request body ni siquiera se <abbrtitle="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 <dfntitle="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:
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:
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:
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"`.
@ -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:
@ -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()`.
### 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`:
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.
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.
* 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.
@ -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 }
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í:
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.
@ -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 <abbrtitle="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 <abbrtitle="Internet of Things - Internet de las cosas">IoT</abbr>) comunicándose con él.
@ -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
Una de las principales funcionalidades necesitadas por los sistemas API es la "<abbrtitle="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 "<dfntitle="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 <abbrtitle="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 <dfntitle="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
Otra gran funcionalidad requerida por las APIs es el <abbrtitle="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 <dfntitle="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.
Starlette es un framework/toolkit <abbrtitle="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 <dfntitle="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.
<abbrtitle="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 <abbrtitle="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 <abbrtitle="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 <abbrtitle="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 <abbrtitle="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 <abbrtitle="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 <abbrtitle="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 <abbrtitle="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 <abbrtitle="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.
@ -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 <abbrtitle="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 <dfntitle="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:
Puedes desplegar tu app de FastAPI en <ahref="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 <ahref="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 🚀
@ -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**<abbrtitle="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**<dfntitle="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 <abbrtitle="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 <abbrtitle="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`.
### Basado en estándares abiertos { #based-on-open-standards }
* <ahref="https://github.com/OAI/OpenAPI-Specification"class="external-link"target="_blank"><strong>OpenAPI</strong></a> para la creación de APIs, incluyendo declaraciones de <abbrtitle="también conocido como: endpoints, rutas">path</abbr><abbrtitle="también conocido como métodos HTTP, como POST, GET, PUT, DELETE">operations</abbr>, parámetros, request bodies, seguridad, etc.
* <ahref="https://github.com/OAI/OpenAPI-Specification"class="external-link"target="_blank"><strong>OpenAPI</strong></a> para la creación de APIs, incluyendo declaraciones de <dfntitle="también conocido como: endpoints, rutas">path</dfn><dfntitle="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 <ahref="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 <abbrtitle='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 <dfntitle='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 <abbrtitle="La cantidad de código que se prueba automáticamente">cobertura de tests</abbr>.
* 100% <abbrtitle="Anotaciones de tipos en Python, con esto tu editor y herramientas externas pueden ofrecerte mejor soporte">anotada con tipos</abbr> code base.
* 100% de <dfntitle="La cantidad de código que se prueba automáticamente">cobertura de tests</dfn>.
* 100% <dfntitle="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 **<abbrtitle="Integrated Development Environment – Entorno de Desarrollo Integrado: similar a un editor de código">IDE</abbr>/<abbrtitle="Un programa que verifica errores de código">linter</abbr>/cerebro**:
* Se lleva bien con tu **<abbrtitle="Integrated Development Environment – Entorno de Desarrollo Integrado: similar a un editor de código">IDE</abbr>/<dfntitle="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 <ahref="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 <ahref="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. 🎁
...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):
# 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 <ahref="https://sqlmodel.tiangolo.com/"class="external-link"target="_blank">documentación de SQLModel</a>. 🤓
Hay un mini <ahref="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 <ahref="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 <ahref="https://sqlmodel.tiangolo.com/tutorial/fastapi/tests/"class="external-link"target="_blank">escribir pruebas para bases de datos SQL</a>. 😎
* **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. <abbrtitle="también conocido como auto-complete, autocompletado, IntelliSense">Autocompletado</abbr> en todas partes. Menos tiempo depurando.
* **Intuitivo**: Gran soporte para editores. <dfntitle="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.
* <abbrtitle="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:
* <dfntitle="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.
* <abbrtitle="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):
* <dfntitle="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):
@ -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 **<abbrtitle="también conocido como componentes, recursos, proveedores, servicios, inyectables">Inyección de Dependencias</abbr>** muy poderoso y fácil de usar.
* Un sistema de **<dfntitle="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 <ahref="https://strawberry.rocks"class="external-link"target="_blank">Strawberry</a> y otros paquetes.
@ -524,7 +524,7 @@ Usadas por Starlette:
* <ahref="https://www.python-httpx.org"target="_blank"><code>httpx</code></a> - Requerido si deseas usar el `TestClient`.
* <ahref="https://jinja.palletsprojects.com"target="_blank"><code>jinja2</code></a> - Requerido si deseas usar la configuración de plantilla por defecto.
* <ahref="https://github.com/Kludex/python-multipart"target="_blank"><code>python-multipart</code></a> - Requerido si deseas soportar <abbrtitle="convertir el string que viene de un request HTTP en datos de Python">"parsing"</abbr> de forms, con `request.form()`.
* <ahref="https://github.com/Kludex/python-multipart"target="_blank"><code>python-multipart</code></a> - Requerido si deseas soportar form <dfntitle="convertir el string que viene de un request HTTP en datos de Python">"parsing"</dfn>, con `request.form()`.
@ -6,7 +6,7 @@ Puedes usar esta plantilla para comenzar, ya que incluye gran parte de la config
Repositorio de GitHub: <ahref="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).
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 <abbrtitle="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 <dfntitle="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
### 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 }
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.
def some_function(data: Any):
print(data)
```
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.
### Tipos genéricos { #generic-types }
Si puedes elegir una versión más reciente de Python para tu proyecto, podrás aprovechar esa simplicidad adicional.
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]`.
En toda la documentación hay ejemplos compatibles con cada versión de Python (cuando hay una diferencia).
Estos tipos que pueden tomar parámetros de tipo se llaman **Tipos Genéricos** o **Genéricos**.
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).
Puedes usar los mismos tipos integrados como genéricos (con corchetes y tipos dentro):
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:
Puedes declarar que una variable puede ser cualquier 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.
Puedes declarar que una variable puede ser cualquiera de **varios tipos**, por ejemplo, un `int` o un `str`.
En Python 3.10 también hay una **nueva sintaxis** donde puedes poner los posibles tipos separados por una <abbrtitle='también llamado "operador OR a nivel de bits", pero ese significado no es relevante aquí'>barra vertical (`|`)</abbr>.
Para definirlo usas la <dfntitle='también llamado "operador OR a nivel de bits", pero ese significado no es relevante aquí'>barra vertical (`|`)</dfn> para separar ambos tipos.
//// 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.
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`:
#### 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.
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:
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 <abbrtitle='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:
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 <ahref="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 **<abbrtitle="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 **<dfntitle="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`.
#### 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*:
@ -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`:
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:
@ -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`:
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()`:
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 🤷:
@ -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 <ahref="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 <abbrtitle="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 <abbrtitle="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 <abbrtitle="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 <dfntitle="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 <abbrtitle="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 <dfntitle="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`<abbrtitle="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`<dfntitle="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 <abbrtitle="Toma una última cookie antes de irte. 🍪">**cookies**</abbr> en **FastAPI**. 😎
Puedes usar **modelos de Pydantic** para declarar <dfntitle="Toma una última cookie antes de irte. 🍪">**cookies**</dfn> en **FastAPI**. 😎
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.
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:
@ -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:
# Dependencias con yield { #dependencies-with-yield }
FastAPI admite dependencias que realizan algunos <abbrtitle='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 <dfntitle='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:
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. 🤓
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:
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`:
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**.
`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 }
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.
**FastAPI** tiene un sistema de **<abbrtitle="también conocido como componentes, recursos, proveedores, servicios, inyectables">Inyección de Dependencias</abbr>** muy poderoso pero intuitivo.
**FastAPI** tiene un sistema de **<dfntitle="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.
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 <abbrtitle="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 <dfntitle="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`:
@ -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 <ahref="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.
@ -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`:
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.
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:
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:
## 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 <abbrtitle="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 <dfntitle="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 <ahref="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 <abbrtitle="obsoleta, se recomienda no usarla">deprecated</abbr>, pero sin eliminarla, pasa el parámetro `deprecated`:
Si necesitas marcar una *path operation* como <dfntitle="obsoleta, se recomienda no usarla">deprecated</dfn>, pero sin eliminarla, pasa el parámetro `deprecated`:
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.
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()`.
## 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 <abbrtitle="De: K-ey W-ord Arg-uments"><code>kwargs</code></abbr>. Incluso si no tienen un valor por defecto.
### 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 `*`.
## 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 <abbrtitle="greater than – mayor que"><code>gt</code></abbr> y no solo <abbrtitle="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 <abbrtitle="greater than - mayor que"><code>gt</code></abbr> y no solo <abbrtitle="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 <abbrtitle="less than – menor que"><code>lt</code></abbr>.
Y lo mismo para <abbrtitle="less than - menor que"><code>lt</code></abbr>.
@ -26,7 +26,7 @@ Esto te dará soporte del editor dentro de tu función, con chequeo de errores,
///
## <abbrtitle="también conocido como: serialización, parsing, marshalling">Conversión</abbr> de datos { #data-conversion }
## <dfntitle="también conocido como: serialización, parsing, marshalling">Conversión</dfn> de datos { #data-conversion }
Si ejecutas este ejemplo y abres tu navegador en <ahref="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 <abbrtitle="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 <dfntitle="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}`:
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:
Si te estás preguntando, "AlexNet", "ResNet" y "LeNet" son solo nombres de <abbrtitle="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 <dfntitle="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`):
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 <abbrtitle="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 <dfntitle="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é.
### 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 <ahref="https://typer.tiangolo.com/"class="external-link"target="_blank">Typer</a>. 🚀
@ -192,7 +168,7 @@ También puedes agregar un parámetro `min_length`:
Puedes definir una <abbrtitle="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 <dfntitle="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:
@ -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"`:
@ -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 <abbrtitle="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 <dfntitle="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
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 <ahref="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 <abbrtitle="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 <abbrtitle="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 <abbrtitle="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 <abbrtitle="Internet Movie Database - Base de datos de películas en Internet: un sitio web con información sobre películas">IMDB</abbr>:
@ -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 <abbrtitle="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 <dfntitle="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? 🐍
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 <ahref="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST"class="external-link"target="_blank"><abbrtitle="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 <ahref="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST"class="external-link"target="_blank"><abbrtitle="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:
@ -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`:
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 <abbrtitle="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 <dfntitle="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 <ahref="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST"class="external-link"target="_blank"><abbrtitle="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 <ahref="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST"class="external-link"target="_blank"><abbrtitle="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}.
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<abbrtitle='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 <dfntitle='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 💥:
Para saber más sobre cada código de estado y qué código es para qué, revisa la <ahref="https://developer.mozilla.org/en-US/docs/Web/HTTP/Status"class="external-link"target="_blank">documentación de <abbrtitle="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 <ahref="https://developer.mozilla.org/en-US/docs/Web/HTTP/Status"class="external-link"target="_blank">documentación de <abbrtitle="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
@ -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 <abbrtitle="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 <dfntitle="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 }
@ -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.
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`:
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.
Puedes usar cualquier otro paquete de bases de datos SQL o NoSQL que quieras (en algunos casos llamadas <abbrtitle="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 <abbrtitle="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 <ahref="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 <ahref="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 <ahref="https://sqlmodel.tiangolo.com/tutorial/fastapi/"class="external-link"target="_blank">tutorial más largo sobre el uso de SQLModel con **FastAPI**</a>. 🚀
@ -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`):
## Crea un Entorno Virtual { #create-a-virtual-environment }
Cuando empiezas a trabajar en un proyecto de Python **por primera vez**, crea un entorno virtual **<abbrtitle="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 **<dfntitle="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: