Sync fastapi docs from 50fa3f7c on 2026-01-11
This commit is contained in:
@@ -0,0 +1,503 @@
|
||||
# Archivo de prueba de LLM { #llm-test-file }
|
||||
|
||||
Este documento prueba si el <abbr title="Large Language Model - Modelo de lenguaje grande">LLM</abbr>, que traduce la documentación, entiende el `general_prompt` en `scripts/translate.py` y el prompt específico del idioma en `docs/{language code}/llm-prompt.md`. El prompt específico del idioma se agrega al final de `general_prompt`.
|
||||
|
||||
Las pruebas añadidas aquí serán vistas por todas las personas que diseñan prompts específicos del idioma.
|
||||
|
||||
Úsalo de la siguiente manera:
|
||||
|
||||
* Ten un prompt específico del idioma - `docs/{language code}/llm-prompt.md`.
|
||||
* Haz una traducción fresca de este documento a tu idioma destino (mira, por ejemplo, el comando `translate-page` de `translate.py`). Esto creará la traducción en `docs/{language code}/docs/_llm-test.md`.
|
||||
* Revisa si las cosas están bien en la traducción.
|
||||
* Si es necesario, mejora tu prompt específico del idioma, el prompt general, o el documento en inglés.
|
||||
* Luego corrige manualmente los problemas restantes en la traducción para que sea una buena traducción.
|
||||
* Vuelve a traducir, teniendo la buena traducción en su lugar. El resultado ideal sería que el LLM ya no hiciera cambios a la traducción. Eso significa que el prompt general y tu prompt específico del idioma están tan bien como pueden estar (A veces hará algunos cambios aparentemente aleatorios; la razón es que <a href="https://doublespeak.chat/#/handbook#deterministic-output" class="external-link" target="_blank">los LLMs no son algoritmos deterministas</a>).
|
||||
|
||||
Las pruebas:
|
||||
|
||||
## Fragmentos de código { #code-snippets }
|
||||
|
||||
//// tab | Prueba
|
||||
|
||||
Este es un fragmento de código: `foo`. Y este es otro fragmento de código: `bar`. Y otro más: `baz quux`.
|
||||
|
||||
////
|
||||
|
||||
//// tab | Info
|
||||
|
||||
El contenido de los fragmentos de código debe dejarse tal cual.
|
||||
|
||||
Consulta la sección `### Content of code snippets` en el prompt general en `scripts/translate.py`.
|
||||
|
||||
////
|
||||
|
||||
## Comillas { #quotes }
|
||||
|
||||
//// tab | Prueba
|
||||
|
||||
Ayer, mi amigo escribió: "Si escribes 'incorrectly' correctamente, lo habrás escrito incorrectamente". A lo que respondí: "Correcto, pero 'incorrectly' está incorrecto, no '"incorrectly"'".
|
||||
|
||||
/// note | Nota
|
||||
|
||||
El LLM probablemente traducirá esto mal. Lo interesante es si mantiene la traducción corregida al volver a traducir.
|
||||
|
||||
///
|
||||
|
||||
////
|
||||
|
||||
//// tab | Info
|
||||
|
||||
La persona que diseña el prompt puede elegir si quiere convertir comillas neutras a comillas tipográficas. También está bien dejarlas como están.
|
||||
|
||||
Consulta por ejemplo la sección `### Quotes` en `docs/de/llm-prompt.md`.
|
||||
|
||||
////
|
||||
|
||||
## Comillas en fragmentos de código { #quotes-in-code-snippets }
|
||||
|
||||
//// tab | Prueba
|
||||
|
||||
`pip install "foo[bar]"`
|
||||
|
||||
Ejemplos de literales de string en fragmentos de código: `"this"`, `'that'`.
|
||||
|
||||
Un ejemplo difícil de literales de string en fragmentos de código: `f"I like {'oranges' if orange else "apples"}"`
|
||||
|
||||
Hardcore: `Yesterday, my friend wrote: "If you spell incorrectly correctly, you have spelled it incorrectly". To which I answered: "Correct, but 'incorrectly' is incorrectly not '"incorrectly"'"`
|
||||
|
||||
////
|
||||
|
||||
//// tab | Info
|
||||
|
||||
... Sin embargo, las comillas dentro de fragmentos de código deben quedarse tal cual.
|
||||
|
||||
////
|
||||
|
||||
## bloques de código { #code-blocks }
|
||||
|
||||
//// tab | Prueba
|
||||
|
||||
Un ejemplo de código Bash...
|
||||
|
||||
```bash
|
||||
# Imprime un saludo al universo
|
||||
echo "Hello universe"
|
||||
```
|
||||
|
||||
...y un ejemplo de código de consola...
|
||||
|
||||
```console
|
||||
$ <font color="#4E9A06">fastapi</font> run <u style="text-decoration-style:solid">main.py</u>
|
||||
<span style="background-color:#009485"><font color="#D3D7CF"> FastAPI </font></span> Starting server
|
||||
Searching for package file structure
|
||||
```
|
||||
|
||||
...y otro ejemplo de código de consola...
|
||||
|
||||
```console
|
||||
// Crea un directorio "Code"
|
||||
$ mkdir code
|
||||
// Cambia a ese directorio
|
||||
$ cd code
|
||||
```
|
||||
|
||||
...y un ejemplo de código Python...
|
||||
|
||||
```Python
|
||||
wont_work() # Esto no va a funcionar 😱
|
||||
works(foo="bar") # Esto funciona 🎉
|
||||
```
|
||||
|
||||
...y eso es todo.
|
||||
|
||||
////
|
||||
|
||||
//// tab | Info
|
||||
|
||||
El código en bloques de código no debe modificarse, con la excepción de los comentarios.
|
||||
|
||||
Consulta la sección `### Content of code blocks` en el prompt general en `scripts/translate.py`.
|
||||
|
||||
////
|
||||
|
||||
## Pestañas y cajas coloreadas { #tabs-and-colored-boxes }
|
||||
|
||||
//// tab | Prueba
|
||||
|
||||
/// info | Información
|
||||
Algo de texto
|
||||
///
|
||||
|
||||
/// note | Nota
|
||||
Algo de texto
|
||||
///
|
||||
|
||||
/// note | Detalles técnicos
|
||||
Algo de texto
|
||||
///
|
||||
|
||||
/// check | Revisa
|
||||
Algo de texto
|
||||
///
|
||||
|
||||
/// tip | Consejo
|
||||
Algo de texto
|
||||
///
|
||||
|
||||
/// warning | Advertencia
|
||||
Algo de texto
|
||||
///
|
||||
|
||||
/// danger | Peligro
|
||||
Algo de texto
|
||||
///
|
||||
|
||||
////
|
||||
|
||||
//// tab | Info
|
||||
|
||||
Las pestañas y los bloques `Info`/`Note`/`Warning`/etc. deben tener la traducción de su título añadida después de una barra vertical (`|`).
|
||||
|
||||
Consulta las secciones `### Special blocks` y `### Tab blocks` en el prompt general en `scripts/translate.py`.
|
||||
|
||||
////
|
||||
|
||||
## Enlaces web e internos { #web-and-internal-links }
|
||||
|
||||
//// tab | Prueba
|
||||
|
||||
El texto del enlace debe traducirse, la dirección del enlace debe permanecer sin cambios:
|
||||
|
||||
* [Enlace al encabezado de arriba](#code-snippets)
|
||||
* [Enlace interno](index.md#installation){.internal-link target=_blank}
|
||||
* <a href="https://sqlmodel.tiangolo.com/" class="external-link" target="_blank">Enlace externo</a>
|
||||
* <a href="https://fastapi.tiangolo.com/css/styles.css" class="external-link" target="_blank">Enlace a un estilo</a>
|
||||
* <a href="https://fastapi.tiangolo.com/js/logic.js" class="external-link" target="_blank">Enlace a un script</a>
|
||||
* <a href="https://fastapi.tiangolo.com/img/foo.jpg" class="external-link" target="_blank">Enlace a una imagen</a>
|
||||
|
||||
El texto del enlace debe traducirse, la dirección del enlace debe apuntar a la traducción:
|
||||
|
||||
* <a href="https://fastapi.tiangolo.com/es/" class="external-link" target="_blank">Enlace a FastAPI</a>
|
||||
|
||||
////
|
||||
|
||||
//// tab | Info
|
||||
|
||||
Los enlaces deben traducirse, pero su dirección debe permanecer sin cambios. Una excepción son los enlaces absolutos a páginas de la documentación de FastAPI. En ese caso deben enlazar a la traducción.
|
||||
|
||||
Consulta la sección `### Links` en el prompt general en `scripts/translate.py`.
|
||||
|
||||
////
|
||||
|
||||
## Elementos HTML "abbr" { #html-abbr-elements }
|
||||
|
||||
//// tab | Prueba
|
||||
|
||||
Aquí algunas cosas envueltas en elementos HTML "abbr" (algunas son inventadas):
|
||||
|
||||
### El abbr da una frase completa { #the-abbr-gives-a-full-phrase }
|
||||
|
||||
* <abbr title="Getting Things Done - Hacer las cosas">GTD</abbr>
|
||||
* <abbr title="less than - menor que"><code>lt</code></abbr>
|
||||
* <abbr title="XML Web Token - Token web XML">XWT</abbr>
|
||||
* <abbr title="Parallel Server Gateway Interface - Interfaz de pasarela de servidor paralela">PSGI</abbr>
|
||||
|
||||
### El abbr da una explicación { #the-abbr-gives-an-explanation }
|
||||
|
||||
* <abbr title="Un grupo de máquinas configuradas para estar conectadas y trabajar juntas de alguna manera.">clúster</abbr>
|
||||
* <abbr title="Un método de machine learning que usa redes neuronales artificiales con numerosas capas ocultas entre las capas de entrada y salida, desarrollando así una estructura interna completa">Deep Learning</abbr>
|
||||
|
||||
### El abbr da una frase completa y una explicación { #the-abbr-gives-a-full-phrase-and-an-explanation }
|
||||
|
||||
* <abbr title="Mozilla Developer Network - Red de Desarrolladores de Mozilla: documentación para desarrolladores, escrita por la gente de Firefox">MDN</abbr>
|
||||
* <abbr title="Input/Output: lectura o escritura de disco, comunicaciones de red.">I/O</abbr>.
|
||||
|
||||
////
|
||||
|
||||
//// tab | Info
|
||||
|
||||
Los atributos "title" de los elementos "abbr" se traducen siguiendo instrucciones específicas.
|
||||
|
||||
Las traducciones pueden añadir sus propios elementos "abbr" que el LLM no debe eliminar. P. ej., para explicar palabras en inglés.
|
||||
|
||||
Consulta la sección `### HTML abbr elements` en el prompt general en `scripts/translate.py`.
|
||||
|
||||
////
|
||||
|
||||
## Encabezados { #headings }
|
||||
|
||||
//// tab | Prueba
|
||||
|
||||
### Desarrolla una webapp - un tutorial { #develop-a-webapp-a-tutorial }
|
||||
|
||||
Hola.
|
||||
|
||||
### Anotaciones de tipos y -anotaciones { #type-hints-and-annotations }
|
||||
|
||||
Hola de nuevo.
|
||||
|
||||
### Superclases y subclases { #super-and-subclasses }
|
||||
|
||||
Hola de nuevo.
|
||||
|
||||
////
|
||||
|
||||
//// tab | Info
|
||||
|
||||
La única regla estricta para los encabezados es que el LLM deje la parte del hash dentro de llaves sin cambios, lo que asegura que los enlaces no se rompan.
|
||||
|
||||
Consulta la sección `### Headings` en el prompt general en `scripts/translate.py`.
|
||||
|
||||
Para instrucciones específicas del idioma, mira p. ej. la sección `### Headings` en `docs/de/llm-prompt.md`.
|
||||
|
||||
////
|
||||
|
||||
## Términos usados en la documentación { #terms-used-in-the-docs }
|
||||
|
||||
//// tab | Prueba
|
||||
|
||||
* tú
|
||||
* tu
|
||||
|
||||
* p. ej.
|
||||
* etc.
|
||||
|
||||
* `foo` como un `int`
|
||||
* `bar` como un `str`
|
||||
* `baz` como una `list`
|
||||
|
||||
* el Tutorial - Guía de usuario
|
||||
* la Guía de usuario avanzada
|
||||
* la documentación de SQLModel
|
||||
* la documentación de la API
|
||||
* la documentación automática
|
||||
|
||||
* Ciencia de datos
|
||||
* Deep Learning
|
||||
* Machine Learning
|
||||
* Inyección de dependencias
|
||||
* autenticación HTTP Basic
|
||||
* HTTP Digest
|
||||
* formato ISO
|
||||
* el estándar JSON Schema
|
||||
* el JSON Schema
|
||||
* la definición del esquema
|
||||
* Flujo de contraseña
|
||||
* Móvil
|
||||
|
||||
* obsoleto
|
||||
* diseñado
|
||||
* inválido
|
||||
* sobre la marcha
|
||||
* estándar
|
||||
* por defecto
|
||||
* sensible a mayúsculas/minúsculas
|
||||
* insensible a mayúsculas/minúsculas
|
||||
|
||||
* servir la aplicación
|
||||
* servir la página
|
||||
|
||||
* la app
|
||||
* la aplicación
|
||||
|
||||
* la request
|
||||
* la response
|
||||
* la response de error
|
||||
|
||||
* la path operation
|
||||
* el decorador de path operation
|
||||
* la path operation function
|
||||
|
||||
* el body
|
||||
* el request body
|
||||
* el response body
|
||||
* el body JSON
|
||||
* el body del formulario
|
||||
* el body de archivo
|
||||
* el cuerpo de la función
|
||||
|
||||
* el parámetro
|
||||
* el parámetro del body
|
||||
* el parámetro del path
|
||||
* el parámetro de query
|
||||
* el parámetro de cookie
|
||||
* el parámetro de header
|
||||
* el parámetro del formulario
|
||||
* el parámetro de la función
|
||||
|
||||
* el evento
|
||||
* el evento de inicio
|
||||
* el inicio del servidor
|
||||
* el evento de apagado
|
||||
* el evento de lifespan
|
||||
|
||||
* el manejador
|
||||
* el manejador de eventos
|
||||
* el manejador de excepciones
|
||||
* manejar
|
||||
|
||||
* el modelo
|
||||
* el modelo de Pydantic
|
||||
* el modelo de datos
|
||||
* el modelo de base de datos
|
||||
* el modelo de formulario
|
||||
* el objeto del modelo
|
||||
|
||||
* la clase
|
||||
* la clase base
|
||||
* la clase padre
|
||||
* la subclase
|
||||
* la clase hija
|
||||
* la clase hermana
|
||||
* el método de clase
|
||||
|
||||
* el header
|
||||
* los headers
|
||||
* el header de autorización
|
||||
* el header `Authorization`
|
||||
* el header forwarded
|
||||
|
||||
* el sistema de inyección de dependencias
|
||||
* la dependencia
|
||||
* el dependable
|
||||
* el dependiente
|
||||
|
||||
* limitado por I/O
|
||||
* limitado por CPU
|
||||
* concurrencia
|
||||
* paralelismo
|
||||
* multiprocesamiento
|
||||
|
||||
* la env var
|
||||
* la variable de entorno
|
||||
* el `PATH`
|
||||
* la variable `PATH`
|
||||
|
||||
* la autenticación
|
||||
* el proveedor de autenticación
|
||||
* la autorización
|
||||
* el formulario de autorización
|
||||
* el proveedor de autorización
|
||||
* el usuario se autentica
|
||||
* el sistema autentica al usuario
|
||||
|
||||
* la CLI
|
||||
* la interfaz de línea de comandos
|
||||
|
||||
* el servidor
|
||||
* el cliente
|
||||
|
||||
* el proveedor en la nube
|
||||
* el servicio en la nube
|
||||
|
||||
* el desarrollo
|
||||
* las etapas de desarrollo
|
||||
|
||||
* el dict
|
||||
* el diccionario
|
||||
* la enumeración
|
||||
* el enum
|
||||
* el miembro del enum
|
||||
|
||||
* el codificador
|
||||
* el decodificador
|
||||
* codificar
|
||||
* decodificar
|
||||
|
||||
* la excepción
|
||||
* lanzar
|
||||
|
||||
* la expresión
|
||||
* el statement
|
||||
|
||||
* el frontend
|
||||
* el backend
|
||||
|
||||
* la discusión de GitHub
|
||||
* el issue de GitHub
|
||||
|
||||
* el rendimiento
|
||||
* la optimización de rendimiento
|
||||
|
||||
* el tipo de retorno
|
||||
* el valor de retorno
|
||||
|
||||
* la seguridad
|
||||
* el esquema de seguridad
|
||||
|
||||
* la tarea
|
||||
* la tarea en segundo plano
|
||||
* la función de tarea
|
||||
|
||||
* la plantilla
|
||||
* el motor de plantillas
|
||||
|
||||
* la anotación de tipos
|
||||
* las anotaciones de tipos
|
||||
|
||||
* el worker del servidor
|
||||
* el worker de Uvicorn
|
||||
* el Gunicorn Worker
|
||||
* el worker process
|
||||
* la worker class
|
||||
* la carga de trabajo
|
||||
|
||||
* el despliegue
|
||||
* desplegar
|
||||
|
||||
* el SDK
|
||||
* el kit de desarrollo de software
|
||||
|
||||
* el `APIRouter`
|
||||
* el `requirements.txt`
|
||||
* el Bearer Token
|
||||
* el cambio incompatible
|
||||
* el bug
|
||||
* el botón
|
||||
* el invocable
|
||||
* el código
|
||||
* el commit
|
||||
* el context manager
|
||||
* la corrutina
|
||||
* la sesión de base de datos
|
||||
* el disco
|
||||
* el dominio
|
||||
* el motor
|
||||
* el X falso
|
||||
* el método HTTP GET
|
||||
* el ítem
|
||||
* el paquete
|
||||
* el lifespan
|
||||
* el lock
|
||||
* el middleware
|
||||
* la aplicación móvil
|
||||
* el módulo
|
||||
* el montaje
|
||||
* la red
|
||||
* el origen
|
||||
* el override
|
||||
* el payload
|
||||
* el procesador
|
||||
* la propiedad
|
||||
* el proxy
|
||||
* el pull request
|
||||
* la query
|
||||
* la RAM
|
||||
* la máquina remota
|
||||
* el código de estado
|
||||
* el string
|
||||
* la etiqueta
|
||||
* el framework web
|
||||
* el comodín
|
||||
* devolver
|
||||
* validar
|
||||
|
||||
////
|
||||
|
||||
//// tab | Info
|
||||
|
||||
Esta es una lista no completa y no normativa de términos (mayormente) técnicos vistos en la documentación. Puede ayudar a la persona que diseña el prompt a identificar para qué términos el LLM necesita una mano. Por ejemplo cuando sigue revirtiendo una buena traducción a una traducción subóptima. O cuando tiene problemas conjugando/declinando un término en tu idioma.
|
||||
|
||||
Mira p. ej. la sección `### List of English terms and their preferred German translations` en `docs/de/llm-prompt.md`.
|
||||
|
||||
////
|
||||
@@ -1,3 +1,3 @@
|
||||
# Acerca de
|
||||
# Acerca de { #about }
|
||||
|
||||
Acerca de FastAPI, su diseño, inspiración y más. 🤓
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Responses Adicionales en OpenAPI
|
||||
# Responses Adicionales en OpenAPI { #additional-responses-in-openapi }
|
||||
|
||||
/// warning | Advertencia
|
||||
|
||||
@@ -14,7 +14,7 @@ Esos responses adicionales se incluirán en el esquema de OpenAPI, por lo que ta
|
||||
|
||||
Pero para esos responses adicionales tienes que asegurarte de devolver un `Response` como `JSONResponse` directamente, con tu código de estado y contenido.
|
||||
|
||||
## Response Adicional con `model`
|
||||
## Response Adicional con `model` { #additional-response-with-model }
|
||||
|
||||
Puedes pasar a tus *decoradores de path operation* un parámetro `responses`.
|
||||
|
||||
@@ -26,7 +26,7 @@ Cada uno de esos `dict`s de response puede tener una clave `model`, conteniendo
|
||||
|
||||
Por ejemplo, para declarar otro response con un código de estado `404` y un modelo Pydantic `Message`, puedes escribir:
|
||||
|
||||
{* ../../docs_src/additional_responses/tutorial001.py hl[18,22] *}
|
||||
{* ../../docs_src/additional_responses/tutorial001_py39.py hl[18,22] *}
|
||||
|
||||
/// note | Nota
|
||||
|
||||
@@ -169,13 +169,13 @@ Los esquemas se referencian a otro lugar dentro del esquema de OpenAPI:
|
||||
}
|
||||
```
|
||||
|
||||
## Media types adicionales para el response principal
|
||||
## Media types adicionales para el response principal { #additional-media-types-for-the-main-response }
|
||||
|
||||
Puedes usar este mismo parámetro `responses` para agregar diferentes media type para el mismo response principal.
|
||||
|
||||
Por ejemplo, puedes agregar un media type adicional de `image/png`, declarando que tu *path operation* puede devolver un objeto JSON (con media type `application/json`) o una imagen PNG:
|
||||
|
||||
{* ../../docs_src/additional_responses/tutorial002.py hl[19:24,28] *}
|
||||
{* ../../docs_src/additional_responses/tutorial002_py310.py hl[17:22,26] *}
|
||||
|
||||
/// note | Nota
|
||||
|
||||
@@ -191,25 +191,25 @@ Pero si has especificado una clase de response personalizada con `None` como su
|
||||
|
||||
///
|
||||
|
||||
## Combinando información
|
||||
## Combinando información { #combining-information }
|
||||
|
||||
También puedes combinar información de response de múltiples lugares, incluyendo los parámetros `response_model`, `status_code`, y `responses`.
|
||||
|
||||
Puedes declarar un `response_model`, usando el código de estado predeterminado `200` (o uno personalizado si lo necesitas), y luego declarar información adicional para ese mismo response en `responses`, directamente en el esquema de OpenAPI.
|
||||
Puedes declarar un `response_model`, usando el código de estado por defecto `200` (o uno personalizado si lo necesitas), y luego declarar información adicional para ese mismo response en `responses`, directamente en el esquema de OpenAPI.
|
||||
|
||||
**FastAPI** manterá la información adicional de `responses` y la combinará con el JSON Schema de tu modelo.
|
||||
**FastAPI** mantendrá la información adicional de `responses` y la combinará con el JSON Schema de tu modelo.
|
||||
|
||||
Por ejemplo, puedes declarar un response con un código de estado `404` que usa un modelo Pydantic y tiene una `description` personalizada.
|
||||
|
||||
Y un response con un código de estado `200` que usa tu `response_model`, pero incluye un `example` personalizado:
|
||||
|
||||
{* ../../docs_src/additional_responses/tutorial003.py hl[20:31] *}
|
||||
{* ../../docs_src/additional_responses/tutorial003_py39.py hl[20:31] *}
|
||||
|
||||
Todo se combinará e incluirá en tu OpenAPI, y se mostrará en la documentación de la API:
|
||||
|
||||
<img src="/img/tutorial/additional-responses/image01.png">
|
||||
|
||||
## Combina responses predefinidos y personalizados
|
||||
## Combina responses predefinidos y personalizados { #combine-predefined-responses-and-custom-ones }
|
||||
|
||||
Es posible que desees tener algunos responses predefinidos que se apliquen a muchas *path operations*, pero que quieras combinarlos con responses personalizados necesarios por cada *path operation*.
|
||||
|
||||
@@ -237,9 +237,9 @@ Puedes usar esa técnica para reutilizar algunos responses predefinidos en tus *
|
||||
|
||||
Por ejemplo:
|
||||
|
||||
{* ../../docs_src/additional_responses/tutorial004.py hl[13:17,26] *}
|
||||
{* ../../docs_src/additional_responses/tutorial004_py310.py hl[11:15,24] *}
|
||||
|
||||
## Más información sobre responses OpenAPI
|
||||
## Más información sobre responses OpenAPI { #more-information-about-openapi-responses }
|
||||
|
||||
Para ver exactamente qué puedes incluir en los responses, puedes revisar estas secciones en la especificación OpenAPI:
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# Códigos de Estado Adicionales
|
||||
# Códigos de Estado Adicionales { #additional-status-codes }
|
||||
|
||||
Por defecto, **FastAPI** devolverá los responses usando un `JSONResponse`, colocando el contenido que devuelves desde tu *path operation* dentro de ese `JSONResponse`.
|
||||
|
||||
Usará el código de estado por defecto o el que configures en tu *path operation*.
|
||||
|
||||
## Códigos de estado adicionales
|
||||
## Códigos de estado adicionales { #additional-status-codes_1 }
|
||||
|
||||
Si quieres devolver códigos de estado adicionales aparte del principal, puedes hacerlo devolviendo un `Response` directamente, como un `JSONResponse`, y configurando el código de estado adicional directamente.
|
||||
|
||||
@@ -34,7 +34,7 @@ También podrías usar `from starlette.responses import JSONResponse`.
|
||||
|
||||
///
|
||||
|
||||
## OpenAPI y documentación de API
|
||||
## OpenAPI y documentación de API { #openapi-and-api-docs }
|
||||
|
||||
Si devuelves códigos de estado adicionales y responses directamente, no se incluirán en el esquema de OpenAPI (la documentación de la API), porque FastAPI no tiene una forma de saber de antemano qué vas a devolver.
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Dependencias Avanzadas
|
||||
# Dependencias Avanzadas { #advanced-dependencies }
|
||||
|
||||
## Dependencias con parámetros
|
||||
## Dependencias con parámetros { #parameterized-dependencies }
|
||||
|
||||
Todas las dependencias que hemos visto son una función o clase fija.
|
||||
|
||||
@@ -10,7 +10,7 @@ Imaginemos que queremos tener una dependencia que revise si el parámetro de que
|
||||
|
||||
Pero queremos poder parametrizar ese contenido fijo.
|
||||
|
||||
## Una *instance* "callable"
|
||||
## Una *instance* "callable" { #a-callable-instance }
|
||||
|
||||
En Python hay una forma de hacer que una instance de una clase sea un "callable".
|
||||
|
||||
@@ -22,7 +22,7 @@ Para hacer eso, declaramos un método `__call__`:
|
||||
|
||||
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.
|
||||
|
||||
## Parametrizar la instance
|
||||
## Parametrizar la instance { #parameterize-the-instance }
|
||||
|
||||
Y ahora, podemos usar `__init__` para declarar los parámetros de la instance que podemos usar para "parametrizar" la dependencia:
|
||||
|
||||
@@ -30,7 +30,7 @@ Y ahora, podemos usar `__init__` para declarar los parámetros de la instance qu
|
||||
|
||||
En este caso, **FastAPI** nunca tocará ni se preocupará por `__init__`, lo usaremos directamente en nuestro código.
|
||||
|
||||
## Crear una instance
|
||||
## Crear una instance { #create-an-instance }
|
||||
|
||||
Podríamos crear una instance de esta clase con:
|
||||
|
||||
@@ -38,7 +38,7 @@ Podríamos crear una instance de esta clase con:
|
||||
|
||||
Y de esa manera podemos "parametrizar" nuestra dependencia, que ahora tiene `"bar"` dentro de ella, como el atributo `checker.fixed_content`.
|
||||
|
||||
## Usar la instance como una dependencia
|
||||
## Usar la instance como una dependencia { #use-the-instance-as-a-dependency }
|
||||
|
||||
Luego, podríamos usar este `checker` en un `Depends(checker)`, en lugar de `Depends(FixedContentQueryChecker)`, porque la dependencia es la instance, `checker`, no la clase en sí.
|
||||
|
||||
@@ -63,3 +63,101 @@ En los capítulos sobre seguridad, hay funciones utilitarias que se implementan
|
||||
Si entendiste todo esto, ya sabes cómo funcionan por debajo esas herramientas de utilidad para seguridad.
|
||||
|
||||
///
|
||||
|
||||
## Dependencias con `yield`, `HTTPException`, `except` y Tareas en segundo plano { #dependencies-with-yield-httpexception-except-and-background-tasks }
|
||||
|
||||
/// warning | Advertencia
|
||||
|
||||
Muy probablemente no necesites estos detalles técnicos.
|
||||
|
||||
Estos detalles son útiles principalmente si tenías una aplicación de FastAPI anterior a la 0.121.0 y estás enfrentando problemas con dependencias con `yield`.
|
||||
|
||||
///
|
||||
|
||||
Las dependencias con `yield` han evolucionado con el tiempo para cubrir diferentes casos de uso y arreglar algunos problemas; aquí tienes un resumen de lo que ha cambiado.
|
||||
|
||||
### Dependencias con `yield` y `scope` { #dependencies-with-yield-and-scope }
|
||||
|
||||
En la versión 0.121.0, FastAPI agregó soporte para `Depends(scope="function")` para dependencias con `yield`.
|
||||
|
||||
Usando `Depends(scope="function")`, el código de salida después de `yield` se ejecuta justo después de que la *path operation function* termina, antes de que la response se envíe de vuelta al cliente.
|
||||
|
||||
Y al usar `Depends(scope="request")` (el valor por defecto), el código de salida después de `yield` se ejecuta después de que la response es enviada.
|
||||
|
||||
Puedes leer más al respecto en la documentación de [Dependencias con `yield` - Salida temprana y `scope`](../tutorial/dependencies/dependencies-with-yield.md#early-exit-and-scope).
|
||||
|
||||
### Dependencias con `yield` y `StreamingResponse`, detalles técnicos { #dependencies-with-yield-and-streamingresponse-technical-details }
|
||||
|
||||
Antes de FastAPI 0.118.0, si usabas una dependencia con `yield`, ejecutaba el código de salida después de que la *path operation function* retornaba pero justo antes de enviar la response.
|
||||
|
||||
La intención era evitar retener recursos por más tiempo del necesario, esperando a que la response viajara por la red.
|
||||
|
||||
Este cambio también significaba que si retornabas un `StreamingResponse`, el código de salida de la dependencia con `yield` ya se habría ejecutado.
|
||||
|
||||
Por ejemplo, si tenías una sesión de base de datos en una dependencia con `yield`, el `StreamingResponse` no podría usar esa sesión mientras hace streaming de datos porque la sesión ya se habría cerrado en el código de salida después de `yield`.
|
||||
|
||||
Este comportamiento se revirtió en la 0.118.0, para hacer que el código de salida después de `yield` se ejecute después de que la response sea enviada.
|
||||
|
||||
/// info | Información
|
||||
|
||||
Como verás abajo, esto es muy similar al comportamiento anterior a la versión 0.106.0, pero con varias mejoras y arreglos de bugs para casos límite.
|
||||
|
||||
///
|
||||
|
||||
#### Casos de uso con salida temprana del código { #use-cases-with-early-exit-code }
|
||||
|
||||
Hay algunos casos de uso con condiciones específicas que podrían beneficiarse del comportamiento antiguo de ejecutar el código de salida de dependencias con `yield` antes de enviar la response.
|
||||
|
||||
Por ejemplo, imagina que tienes código que usa una sesión de base de datos en una dependencia con `yield` solo para verificar un usuario, pero la sesión de base de datos no se vuelve a usar en la *path operation function*, solo en la dependencia, y la response tarda mucho en enviarse, como un `StreamingResponse` que envía datos lentamente, pero que por alguna razón no usa la base de datos.
|
||||
|
||||
En este caso, la sesión de base de datos se mantendría hasta que la response termine de enviarse, pero si no la usas, entonces no sería necesario mantenerla.
|
||||
|
||||
Así es como se vería:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial013_an_py310.py *}
|
||||
|
||||
El código de salida, el cierre automático de la `Session` en:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial013_an_py310.py ln[19:21] *}
|
||||
|
||||
...se ejecutaría después de que la response termine de enviar los datos lentos:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial013_an_py310.py ln[30:38] hl[31:33] *}
|
||||
|
||||
Pero como `generate_stream()` no usa la sesión de base de datos, no es realmente necesario mantener la sesión abierta mientras se envía la response.
|
||||
|
||||
Si tienes este caso de uso específico usando SQLModel (o SQLAlchemy), podrías cerrar explícitamente la sesión después de que ya no la necesites:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial014_an_py310.py ln[24:28] hl[28] *}
|
||||
|
||||
De esa manera la sesión liberaría la conexión a la base de datos, para que otras requests puedan usarla.
|
||||
|
||||
Si tienes un caso de uso diferente que necesite salir temprano desde una dependencia con `yield`, por favor crea una <a href="https://github.com/fastapi/fastapi/discussions/new?category=questions" class="external-link" target="_blank">Pregunta de Discusión en GitHub</a> con tu caso de uso específico y por qué te beneficiaría tener cierre temprano para dependencias con `yield`.
|
||||
|
||||
Si hay casos de uso convincentes para el cierre temprano en dependencias con `yield`, consideraría agregar una nueva forma de optar por el cierre temprano.
|
||||
|
||||
### Dependencias con `yield` y `except`, detalles técnicos { #dependencies-with-yield-and-except-technical-details }
|
||||
|
||||
Antes de FastAPI 0.110.0, si usabas una dependencia con `yield`, y luego capturabas una excepción con `except` en esa dependencia, y no volvías a elevar la excepción, la excepción se elevaría/remitiría automáticamente a cualquier manejador de excepciones o al manejador de error interno del servidor.
|
||||
|
||||
Esto cambió en la versión 0.110.0 para arreglar consumo de memoria no manejado por excepciones reenviadas sin un manejador (errores internos del servidor), y para hacerlo consistente con el comportamiento del código Python normal.
|
||||
|
||||
### Tareas en segundo plano y dependencias con `yield`, detalles técnicos { #background-tasks-and-dependencies-with-yield-technical-details }
|
||||
|
||||
Antes de FastAPI 0.106.0, elevar excepciones después de `yield` no era posible, el código de salida en dependencias con `yield` se ejecutaba después de que la response era enviada, por lo que [Manejadores de Excepciones](../tutorial/handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank} ya habrían corrido.
|
||||
|
||||
Esto se diseñó así principalmente para permitir usar los mismos objetos devueltos con `yield` por las dependencias dentro de tareas en segundo plano, porque el código de salida se ejecutaría después de que las tareas en segundo plano terminaran.
|
||||
|
||||
Esto cambió en FastAPI 0.106.0 con la intención de no retener recursos mientras se espera a que la response viaje por la red.
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
Adicionalmente, una tarea en segundo plano normalmente es un conjunto independiente de lógica que debería manejarse por separado, con sus propios recursos (por ejemplo, su propia conexión a la base de datos).
|
||||
|
||||
Así, probablemente tendrás un código más limpio.
|
||||
|
||||
///
|
||||
|
||||
Si solías depender de este comportamiento, ahora deberías crear los recursos para las tareas en segundo plano dentro de la propia tarea en segundo plano, y usar internamente solo datos que no dependan de los recursos de dependencias con `yield`.
|
||||
|
||||
Por ejemplo, en lugar de usar la misma sesión de base de datos, crearías una nueva sesión de base de datos dentro de la tarea en segundo plano, y obtendrías los objetos de la base de datos usando esta nueva sesión. Y entonces, en lugar de pasar el objeto de la base de datos como parámetro a la función de la tarea en segundo plano, pasarías el ID de ese objeto y luego obtendrías el objeto de nuevo dentro de la función de la tarea en segundo plano.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Tests Asíncronos
|
||||
# Tests Asíncronos { #async-tests }
|
||||
|
||||
Ya has visto cómo probar tus aplicaciones de **FastAPI** usando el `TestClient` proporcionado. Hasta ahora, solo has visto cómo escribir tests sincrónicos, sin usar funciones `async`.
|
||||
|
||||
@@ -6,11 +6,11 @@ Poder usar funciones asíncronas en tus tests puede ser útil, por ejemplo, cuan
|
||||
|
||||
Veamos cómo podemos hacer que esto funcione.
|
||||
|
||||
## pytest.mark.anyio
|
||||
## pytest.mark.anyio { #pytest-mark-anyio }
|
||||
|
||||
Si queremos llamar funciones asíncronas en nuestros tests, nuestras funciones de test tienen que ser asíncronas. AnyIO proporciona un plugin útil para esto, que nos permite especificar que algunas funciones de test deben ser llamadas de manera asíncrona.
|
||||
|
||||
## HTTPX
|
||||
## HTTPX { #httpx }
|
||||
|
||||
Incluso si tu aplicación de **FastAPI** usa funciones `def` normales en lugar de `async def`, sigue siendo una aplicación `async` por debajo.
|
||||
|
||||
@@ -18,7 +18,7 @@ El `TestClient` hace algo de magia interna para llamar a la aplicación FastAPI
|
||||
|
||||
El `TestClient` está basado en <a href="https://www.python-httpx.org" class="external-link" target="_blank">HTTPX</a>, y afortunadamente, podemos usarlo directamente para probar la API.
|
||||
|
||||
## Ejemplo
|
||||
## Ejemplo { #example }
|
||||
|
||||
Para un ejemplo simple, consideremos una estructura de archivos similar a la descrita en [Aplicaciones Más Grandes](../tutorial/bigger-applications.md){.internal-link target=_blank} y [Testing](../tutorial/testing.md){.internal-link target=_blank}:
|
||||
|
||||
@@ -32,13 +32,13 @@ Para un ejemplo simple, consideremos una estructura de archivos similar a la des
|
||||
|
||||
El archivo `main.py` tendría:
|
||||
|
||||
{* ../../docs_src/async_tests/main.py *}
|
||||
{* ../../docs_src/async_tests/app_a_py39/main.py *}
|
||||
|
||||
El archivo `test_main.py` tendría los tests para `main.py`, podría verse así ahora:
|
||||
|
||||
{* ../../docs_src/async_tests/test_main.py *}
|
||||
{* ../../docs_src/async_tests/app_a_py39/test_main.py *}
|
||||
|
||||
## Ejecútalo
|
||||
## Ejecútalo { #run-it }
|
||||
|
||||
Puedes ejecutar tus tests como de costumbre vía:
|
||||
|
||||
@@ -52,21 +52,21 @@ $ pytest
|
||||
|
||||
</div>
|
||||
|
||||
## En Detalle
|
||||
## En Detalle { #in-detail }
|
||||
|
||||
El marcador `@pytest.mark.anyio` le dice a pytest que esta función de test debe ser llamada asíncronamente:
|
||||
|
||||
{* ../../docs_src/async_tests/test_main.py hl[7] *}
|
||||
{* ../../docs_src/async_tests/app_a_py39/test_main.py hl[7] *}
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
Note que la función de test ahora es `async def` en lugar de solo `def` como antes al usar el `TestClient`.
|
||||
Nota que la función de test ahora es `async def` en lugar de solo `def` como antes al usar el `TestClient`.
|
||||
|
||||
///
|
||||
|
||||
Luego podemos crear un `AsyncClient` con la app y enviar requests asíncronos a ella, usando `await`.
|
||||
|
||||
{* ../../docs_src/async_tests/test_main.py hl[9:12] *}
|
||||
{* ../../docs_src/async_tests/app_a_py39/test_main.py hl[9:12] *}
|
||||
|
||||
Esto es equivalente a:
|
||||
|
||||
@@ -88,12 +88,12 @@ Si tu aplicación depende de eventos de lifespan, el `AsyncClient` no activará
|
||||
|
||||
///
|
||||
|
||||
## Otras Llamadas a Funciones Asíncronas
|
||||
## Otras Llamadas a Funciones Asíncronas { #other-asynchronous-function-calls }
|
||||
|
||||
Al ser la función de test asíncrona, ahora también puedes llamar (y `await`) otras funciones `async` además de enviar requests a tu aplicación FastAPI en tus tests, exactamente como las llamarías en cualquier otro lugar de tu código.
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
Si encuentras un `RuntimeError: Task attached to a different loop` al integrar llamadas a funciones asíncronas en tus tests (por ejemplo, cuando usas <a href="https://stackoverflow.com/questions/41584243/runtimeerror-task-attached-to-a-different-loop" class="external-link" target="_blank">MotorClient de MongoDB</a>), recuerda crear instances de objetos que necesiten un loop de eventos solo dentro de funciones async, por ejemplo, en un callback `'@app.on_event("startup")`.
|
||||
Si encuentras un `RuntimeError: Task attached to a different loop` al integrar llamadas a funciones asíncronas en tus tests (por ejemplo, cuando usas <a href="https://stackoverflow.com/questions/41584243/runtimeerror-task-attached-to-a-different-loop" class="external-link" target="_blank">MotorClient de MongoDB</a>), recuerda crear instances de objetos que necesiten un loop de eventos solo dentro de funciones async, por ejemplo, en un callback `@app.on_event("startup")`.
|
||||
|
||||
///
|
||||
|
||||
@@ -1,6 +1,105 @@
|
||||
# Detrás de un Proxy
|
||||
# Detrás de un Proxy { #behind-a-proxy }
|
||||
|
||||
En algunas situaciones, podrías necesitar usar un **proxy** como Traefik o Nginx con una configuración que añade un prefijo de path extra que no es visto por tu aplicación.
|
||||
En muchas situaciones, usarías un **proxy** como Traefik o Nginx delante de tu app de FastAPI.
|
||||
|
||||
Estos proxies podrían manejar certificados HTTPS y otras cosas.
|
||||
|
||||
## Headers reenviados por el Proxy { #proxy-forwarded-headers }
|
||||
|
||||
Un **proxy** delante de tu aplicación normalmente establecería algunos headers sobre la marcha antes de enviar los requests a tu **server** para que el servidor sepa que el request fue **reenviado** por el proxy, informándole la URL original (pública), incluyendo el dominio, que está usando HTTPS, etc.
|
||||
|
||||
El programa **server** (por ejemplo **Uvicorn** a través de **FastAPI CLI**) es capaz de interpretar esos headers, y luego pasar esa información a tu aplicación.
|
||||
|
||||
Pero por seguridad, como el server no sabe que está detrás de un proxy confiable, no interpretará esos headers.
|
||||
|
||||
/// note | Detalles Técnicos
|
||||
|
||||
Los headers del proxy son:
|
||||
|
||||
* <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-For" class="external-link" target="_blank">X-Forwarded-For</a>
|
||||
* <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Proto" class="external-link" target="_blank">X-Forwarded-Proto</a>
|
||||
* <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Host" class="external-link" target="_blank">X-Forwarded-Host</a>
|
||||
|
||||
///
|
||||
|
||||
### Habilitar headers reenviados por el Proxy { #enable-proxy-forwarded-headers }
|
||||
|
||||
Puedes iniciar FastAPI CLI con la *Opción de CLI* `--forwarded-allow-ips` y pasar las direcciones IP que deberían ser confiables para leer esos headers reenviados.
|
||||
|
||||
Si lo estableces a `--forwarded-allow-ips="*"`, confiaría en todas las IPs entrantes.
|
||||
|
||||
Si tu **server** está detrás de un **proxy** confiable y solo el proxy le habla, esto haría que acepte cualquiera que sea la IP de ese **proxy**.
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ fastapi run --forwarded-allow-ips="*"
|
||||
|
||||
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
### Redirecciones con HTTPS { #redirects-with-https }
|
||||
|
||||
Por ejemplo, digamos que defines una *path operation* `/items/`:
|
||||
|
||||
{* ../../docs_src/behind_a_proxy/tutorial001_01_py39.py hl[6] *}
|
||||
|
||||
Si el cliente intenta ir a `/items`, por defecto, sería redirigido a `/items/`.
|
||||
|
||||
Pero antes de configurar la *Opción de CLI* `--forwarded-allow-ips` podría redirigir a `http://localhost:8000/items/`.
|
||||
|
||||
Pero quizá tu aplicación está alojada en `https://mysuperapp.com`, y la redirección debería ser a `https://mysuperapp.com/items/`.
|
||||
|
||||
Al configurar `--proxy-headers` ahora FastAPI podrá redirigir a la ubicación correcta. 😎
|
||||
|
||||
```
|
||||
https://mysuperapp.com/items/
|
||||
```
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
Si quieres aprender más sobre HTTPS, revisa la guía [Acerca de HTTPS](../deployment/https.md){.internal-link target=_blank}.
|
||||
|
||||
///
|
||||
|
||||
### Cómo funcionan los headers reenviados por el Proxy { #how-proxy-forwarded-headers-work }
|
||||
|
||||
Aquí tienes una representación visual de cómo el **proxy** añade headers reenviados entre el cliente y el **application server**:
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Client as Cliente
|
||||
participant Proxy as Proxy/Load Balancer
|
||||
participant Server as Servidor de FastAPI
|
||||
|
||||
Client->>Proxy: HTTPS Request<br/>Host: mysuperapp.com<br/>Path: /items
|
||||
|
||||
Note over Proxy: El proxy añade headers reenviados
|
||||
|
||||
Proxy->>Server: HTTP Request<br/>X-Forwarded-For: [client IP]<br/>X-Forwarded-Proto: https<br/>X-Forwarded-Host: mysuperapp.com<br/>Path: /items
|
||||
|
||||
Note over Server: El servidor interpreta los headers<br/>(si --forwarded-allow-ips está configurado)
|
||||
|
||||
Server->>Proxy: HTTP Response<br/>con URLs HTTPS correctas
|
||||
|
||||
Proxy->>Client: HTTPS Response
|
||||
```
|
||||
|
||||
El **proxy** intercepta el request original del cliente y añade los *headers* especiales de reenvío (`X-Forwarded-*`) antes de pasar el request al **application server**.
|
||||
|
||||
Estos headers preservan información sobre el request original que de otro modo se perdería:
|
||||
|
||||
* **X-Forwarded-For**: La IP original del cliente
|
||||
* **X-Forwarded-Proto**: El protocolo original (`https`)
|
||||
* **X-Forwarded-Host**: El host original (`mysuperapp.com`)
|
||||
|
||||
Cuando **FastAPI CLI** está configurado con `--forwarded-allow-ips`, confía en estos headers y los usa, por ejemplo para generar las URLs correctas en redirecciones.
|
||||
|
||||
## Proxy con un prefijo de path eliminado { #proxy-with-a-stripped-path-prefix }
|
||||
|
||||
Podrías tener un proxy que añada un prefijo de path a tu aplicación.
|
||||
|
||||
En estos casos, puedes usar `root_path` para configurar tu aplicación.
|
||||
|
||||
@@ -10,15 +109,13 @@ El `root_path` se usa para manejar estos casos específicos.
|
||||
|
||||
Y también se usa internamente al montar subaplicaciones.
|
||||
|
||||
## Proxy con un prefijo de path eliminado
|
||||
|
||||
Tener un proxy con un prefijo de path eliminado, en este caso, significa que podrías declarar un path en `/app` en tu código, pero luego añades una capa encima (el proxy) que situaría tu aplicación **FastAPI** bajo un path como `/api/v1`.
|
||||
|
||||
En este caso, el path original `/app` realmente sería servido en `/api/v1/app`.
|
||||
|
||||
Aunque todo tu código esté escrito asumiendo que solo existe `/app`.
|
||||
|
||||
{* ../../docs_src/behind_a_proxy/tutorial001.py hl[6] *}
|
||||
{* ../../docs_src/behind_a_proxy/tutorial001_py39.py hl[6] *}
|
||||
|
||||
Y el proxy estaría **"eliminando"** el **prefijo del path** sobre la marcha antes de transmitir el request al servidor de aplicaciones (probablemente Uvicorn a través de FastAPI CLI), manteniendo a tu aplicación convencida de que está siendo servida en `/app`, así que no tienes que actualizar todo tu código para incluir el prefijo `/api/v1`.
|
||||
|
||||
@@ -66,14 +163,14 @@ La UI de los docs también necesitaría el esquema de OpenAPI para declarar que
|
||||
|
||||
En este ejemplo, el "Proxy" podría ser algo como **Traefik**. Y el servidor sería algo como FastAPI CLI con **Uvicorn**, ejecutando tu aplicación de FastAPI.
|
||||
|
||||
### Proporcionando el `root_path`
|
||||
### Proporcionando el `root_path` { #providing-the-root-path }
|
||||
|
||||
Para lograr esto, puedes usar la opción de línea de comandos `--root-path` como:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ fastapi run main.py --root-path /api/v1
|
||||
$ fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1
|
||||
|
||||
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
|
||||
```
|
||||
@@ -90,20 +187,20 @@ Y la opción de línea de comandos `--root-path` proporciona ese `root_path`.
|
||||
|
||||
///
|
||||
|
||||
### Revisar el `root_path` actual
|
||||
### Revisar el `root_path` actual { #checking-the-current-root-path }
|
||||
|
||||
Puedes obtener el `root_path` actual utilizado por tu aplicación para cada request, es parte del diccionario `scope` (que es parte de la especificación ASGI).
|
||||
|
||||
Aquí lo estamos incluyendo en el mensaje solo con fines de demostración.
|
||||
|
||||
{* ../../docs_src/behind_a_proxy/tutorial001.py hl[8] *}
|
||||
{* ../../docs_src/behind_a_proxy/tutorial001_py39.py hl[8] *}
|
||||
|
||||
Luego, si inicias Uvicorn con:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ fastapi run main.py --root-path /api/v1
|
||||
$ fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1
|
||||
|
||||
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
|
||||
```
|
||||
@@ -119,19 +216,19 @@ El response sería algo como:
|
||||
}
|
||||
```
|
||||
|
||||
### Configurar el `root_path` en la app de FastAPI
|
||||
### Configurar el `root_path` en la app de FastAPI { #setting-the-root-path-in-the-fastapi-app }
|
||||
|
||||
Alternativamente, si no tienes una forma de proporcionar una opción de línea de comandos como `--root-path` o su equivalente, puedes configurar el parámetro `root_path` al crear tu app de FastAPI:
|
||||
|
||||
{* ../../docs_src/behind_a_proxy/tutorial002.py hl[3] *}
|
||||
{* ../../docs_src/behind_a_proxy/tutorial002_py39.py hl[3] *}
|
||||
|
||||
Pasar el `root_path` a `FastAPI` sería el equivalente a pasar la opción de línea de comandos `--root-path` a Uvicorn o Hypercorn.
|
||||
|
||||
### Acerca de `root_path`
|
||||
### Acerca de `root_path` { #about-root-path }
|
||||
|
||||
Ten en cuenta que el servidor (Uvicorn) no usará ese `root_path` para nada, a excepción de pasárselo a la app.
|
||||
|
||||
Pero si vas con tu navegador a <a href="http://127.0.0.1:8000" class="external-link" target="_blank">http://127.0.0.1:8000/app</a> verás el response normal:
|
||||
Pero si vas con tu navegador a <a href="http://127.0.0.1:8000/app" class="external-link" target="_blank">http://127.0.0.1:8000/app</a> verás el response normal:
|
||||
|
||||
```JSON
|
||||
{
|
||||
@@ -144,15 +241,15 @@ Así que no se esperará que sea accedido en `http://127.0.0.1:8000/api/v1/app`.
|
||||
|
||||
Uvicorn esperará que el proxy acceda a Uvicorn en `http://127.0.0.1:8000/app`, y luego será responsabilidad del proxy añadir el prefijo extra `/api/v1` encima.
|
||||
|
||||
## Sobre proxies con un prefijo de path eliminado
|
||||
## Sobre proxies con un prefijo de path eliminado { #about-proxies-with-a-stripped-path-prefix }
|
||||
|
||||
Ten en cuenta que un proxy con prefijo de path eliminado es solo una de las formas de configurarlo.
|
||||
|
||||
Probablemente en muchos casos, el valor predeterminado será que el proxy no tenga un prefijo de path eliminado.
|
||||
Probablemente en muchos casos, el valor por defecto será que el proxy no tenga un prefijo de path eliminado.
|
||||
|
||||
En un caso así (sin un prefijo de path eliminado), el proxy escucharía algo como `https://myawesomeapp.com`, y luego si el navegador va a `https://myawesomeapp.com/api/v1/app` y tu servidor (por ejemplo, Uvicorn) escucha en `http://127.0.0.1:8000`, el proxy (sin un prefijo de path eliminado) accedería a Uvicorn en el mismo path: `http://127.0.0.1:8000/api/v1/app`.
|
||||
|
||||
## Probando localmente con Traefik
|
||||
## Probando localmente con Traefik { #testing-locally-with-traefik }
|
||||
|
||||
Puedes ejecutar fácilmente el experimento localmente con un prefijo de path eliminado usando <a href="https://docs.traefik.io/" class="external-link" target="_blank">Traefik</a>.
|
||||
|
||||
@@ -224,14 +321,14 @@ Y ahora inicia tu app, utilizando la opción `--root-path`:
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ fastapi run main.py --root-path /api/v1
|
||||
$ fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1
|
||||
|
||||
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
### Revisa los responses
|
||||
### Revisa los responses { #check-the-responses }
|
||||
|
||||
Ahora, si vas a la URL con el puerto para Uvicorn: <a href="http://127.0.0.1:8000/app" class="external-link" target="_blank">http://127.0.0.1:8000/app</a>, verás el response normal:
|
||||
|
||||
@@ -267,7 +364,7 @@ Y la versión sin el prefijo de path (`http://127.0.0.1:8000/app`), proporcionad
|
||||
|
||||
Eso demuestra cómo el Proxy (Traefik) usa el prefijo de path y cómo el servidor (Uvicorn) usa el `root_path` de la opción `--root-path`.
|
||||
|
||||
### Revisa la UI de los docs
|
||||
### Revisa la UI de los docs { #check-the-docs-ui }
|
||||
|
||||
Pero aquí está la parte divertida. ✨
|
||||
|
||||
@@ -287,7 +384,7 @@ Justo como queríamos. ✔️
|
||||
|
||||
Esto es porque FastAPI usa este `root_path` para crear el `server` por defecto en OpenAPI con la URL proporcionada por `root_path`.
|
||||
|
||||
## Servidores adicionales
|
||||
## Servidores adicionales { #additional-servers }
|
||||
|
||||
/// warning | Advertencia
|
||||
|
||||
@@ -303,7 +400,7 @@ Si pasas una lista personalizada de `servers` y hay un `root_path` (porque tu AP
|
||||
|
||||
Por ejemplo:
|
||||
|
||||
{* ../../docs_src/behind_a_proxy/tutorial003.py hl[4:7] *}
|
||||
{* ../../docs_src/behind_a_proxy/tutorial003_py39.py hl[4:7] *}
|
||||
|
||||
Generará un esquema de OpenAPI como:
|
||||
|
||||
@@ -317,11 +414,11 @@ Generará un esquema de OpenAPI como:
|
||||
},
|
||||
{
|
||||
"url": "https://stag.example.com",
|
||||
"description": "Entorno de pruebas"
|
||||
"description": "Staging environment"
|
||||
},
|
||||
{
|
||||
"url": "https://prod.example.com",
|
||||
"description": "Entorno de producción"
|
||||
"description": "Production environment"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
@@ -346,15 +443,23 @@ La UI de los docs interactuará con el server que selecciones.
|
||||
|
||||
///
|
||||
|
||||
### Desactivar el server automático de `root_path`
|
||||
/// note | Detalles Técnicos
|
||||
|
||||
La propiedad `servers` en la especificación de OpenAPI es opcional.
|
||||
|
||||
Si no especificas el parámetro `servers` y `root_path` es igual a `/`, la propiedad `servers` en el esquema de OpenAPI generado se omitirá por completo por defecto, lo cual es equivalente a un único server con un valor `url` de `/`.
|
||||
|
||||
///
|
||||
|
||||
### Desactivar el server automático de `root_path` { #disable-automatic-server-from-root-path }
|
||||
|
||||
Si no quieres que **FastAPI** incluya un server automático usando el `root_path`, puedes usar el parámetro `root_path_in_servers=False`:
|
||||
|
||||
{* ../../docs_src/behind_a_proxy/tutorial004.py hl[9] *}
|
||||
{* ../../docs_src/behind_a_proxy/tutorial004_py39.py hl[9] *}
|
||||
|
||||
y entonces no lo incluirá en el esquema de OpenAPI.
|
||||
|
||||
## Montando una sub-aplicación
|
||||
## Montando una sub-aplicación { #mounting-a-sub-application }
|
||||
|
||||
Si necesitas montar una sub-aplicación (como se describe en [Aplicaciones secundarias - Monturas](sub-applications.md){.internal-link target=_blank}) mientras usas un proxy con `root_path`, puedes hacerlo normalmente, como esperarías.
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Response Personalizado - HTML, Stream, Archivo, otros
|
||||
# Response Personalizado - HTML, Stream, Archivo, otros { #custom-response-html-stream-file-others }
|
||||
|
||||
Por defecto, **FastAPI** devolverá los responses usando `JSONResponse`.
|
||||
|
||||
@@ -18,7 +18,7 @@ Si usas una clase de response sin media type, FastAPI esperará que tu response
|
||||
|
||||
///
|
||||
|
||||
## Usa `ORJSONResponse`
|
||||
## Usa `ORJSONResponse` { #use-orjsonresponse }
|
||||
|
||||
Por ejemplo, si estás exprimendo el rendimiento, puedes instalar y usar <a href="https://github.com/ijl/orjson" class="external-link" target="_blank">`orjson`</a> y establecer el response como `ORJSONResponse`.
|
||||
|
||||
@@ -30,7 +30,7 @@ Esto se debe a que, por defecto, FastAPI inspeccionará cada elemento dentro y s
|
||||
|
||||
Pero si estás seguro de que el contenido que estás devolviendo es **serializable con JSON**, puedes pasarlo directamente a la clase de response y evitar la sobrecarga extra que FastAPI tendría al pasar tu contenido de retorno a través de `jsonable_encoder` antes de pasarlo a la clase de response.
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial001b.py hl[2,7] *}
|
||||
{* ../../docs_src/custom_response/tutorial001b_py39.py hl[2,7] *}
|
||||
|
||||
/// info | Información
|
||||
|
||||
@@ -48,14 +48,14 @@ El `ORJSONResponse` solo está disponible en FastAPI, no en Starlette.
|
||||
|
||||
///
|
||||
|
||||
## Response HTML
|
||||
## Response HTML { #html-response }
|
||||
|
||||
Para devolver un response con HTML directamente desde **FastAPI**, usa `HTMLResponse`.
|
||||
|
||||
* Importa `HTMLResponse`.
|
||||
* Pasa `HTMLResponse` como parámetro `response_class` de tu *path operation decorator*.
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial002.py hl[2,7] *}
|
||||
{* ../../docs_src/custom_response/tutorial002_py39.py hl[2,7] *}
|
||||
|
||||
/// info | Información
|
||||
|
||||
@@ -67,13 +67,13 @@ Y se documentará así en OpenAPI.
|
||||
|
||||
///
|
||||
|
||||
### Devuelve una `Response`
|
||||
### Devuelve una `Response` { #return-a-response }
|
||||
|
||||
Como se ve en [Devolver una Response directamente](response-directly.md){.internal-link target=_blank}, también puedes sobrescribir el response directamente en tu *path operation*, devolviéndolo.
|
||||
|
||||
El mismo ejemplo de arriba, devolviendo una `HTMLResponse`, podría verse así:
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial003.py hl[2,7,19] *}
|
||||
{* ../../docs_src/custom_response/tutorial003_py39.py hl[2,7,19] *}
|
||||
|
||||
/// warning | Advertencia
|
||||
|
||||
@@ -87,27 +87,27 @@ Por supuesto, el `Content-Type` header real, el código de estado, etc., provend
|
||||
|
||||
///
|
||||
|
||||
### Documenta en OpenAPI y sobrescribe `Response`
|
||||
### Documenta en OpenAPI y sobrescribe `Response` { #document-in-openapi-and-override-response }
|
||||
|
||||
Si quieres sobrescribir el response desde dentro de la función pero al mismo tiempo documentar el "media type" en OpenAPI, puedes usar el parámetro `response_class` Y devolver un objeto `Response`.
|
||||
|
||||
El `response_class` solo se usará para documentar el OpenAPI *path operation*, pero tu `Response` se usará tal cual.
|
||||
|
||||
#### Devuelve un `HTMLResponse` directamente
|
||||
#### Devuelve un `HTMLResponse` directamente { #return-an-htmlresponse-directly }
|
||||
|
||||
Por ejemplo, podría ser algo así:
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial004.py hl[7,21,23] *}
|
||||
{* ../../docs_src/custom_response/tutorial004_py39.py hl[7,21,23] *}
|
||||
|
||||
En este ejemplo, la función `generate_html_response()` ya genera y devuelve una `Response` en lugar de devolver el HTML en un `str`.
|
||||
|
||||
Al devolver el resultado de llamar a `generate_html_response()`, ya estás devolviendo una `Response` que sobrescribirá el comportamiento predeterminado de **FastAPI**.
|
||||
Al devolver el resultado de llamar a `generate_html_response()`, ya estás devolviendo una `Response` que sobrescribirá el comportamiento por defecto de **FastAPI**.
|
||||
|
||||
Pero como pasaste `HTMLResponse` en el `response_class` también, **FastAPI** sabrá cómo documentarlo en OpenAPI y la documentación interactiva como HTML con `text/html`:
|
||||
|
||||
<img src="/img/tutorial/custom-response/image01.png">
|
||||
|
||||
## Responses disponibles
|
||||
## Responses disponibles { #available-responses }
|
||||
|
||||
Aquí hay algunos de los responses disponibles.
|
||||
|
||||
@@ -121,7 +121,7 @@ También podrías usar `from starlette.responses import HTMLResponse`.
|
||||
|
||||
///
|
||||
|
||||
### `Response`
|
||||
### `Response` { #response }
|
||||
|
||||
La clase principal `Response`, todos los otros responses heredan de ella.
|
||||
|
||||
@@ -136,25 +136,25 @@ Acepta los siguientes parámetros:
|
||||
|
||||
FastAPI (de hecho Starlette) incluirá automáticamente un header Content-Length. También incluirá un header Content-Type, basado en el `media_type` y añadiendo un conjunto de caracteres para tipos de texto.
|
||||
|
||||
{* ../../docs_src/response_directly/tutorial002.py hl[1,18] *}
|
||||
{* ../../docs_src/response_directly/tutorial002_py39.py hl[1,18] *}
|
||||
|
||||
### `HTMLResponse`
|
||||
### `HTMLResponse` { #htmlresponse }
|
||||
|
||||
Toma algún texto o bytes y devuelve un response HTML, como leíste arriba.
|
||||
|
||||
### `PlainTextResponse`
|
||||
### `PlainTextResponse` { #plaintextresponse }
|
||||
|
||||
Toma algún texto o bytes y devuelve un response de texto plano.
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial005.py hl[2,7,9] *}
|
||||
{* ../../docs_src/custom_response/tutorial005_py39.py hl[2,7,9] *}
|
||||
|
||||
### `JSONResponse`
|
||||
### `JSONResponse` { #jsonresponse }
|
||||
|
||||
Toma algunos datos y devuelve un response codificado como `application/json`.
|
||||
|
||||
Este es el response predeterminado usado en **FastAPI**, como leíste arriba.
|
||||
Este es el response usado por defecto en **FastAPI**, como leíste arriba.
|
||||
|
||||
### `ORJSONResponse`
|
||||
### `ORJSONResponse` { #orjsonresponse }
|
||||
|
||||
Un response JSON rápido alternativo usando <a href="https://github.com/ijl/orjson" class="external-link" target="_blank">`orjson`</a>, como leíste arriba.
|
||||
|
||||
@@ -164,7 +164,7 @@ Esto requiere instalar `orjson`, por ejemplo, con `pip install orjson`.
|
||||
|
||||
///
|
||||
|
||||
### `UJSONResponse`
|
||||
### `UJSONResponse` { #ujsonresponse }
|
||||
|
||||
Un response JSON alternativo usando <a href="https://github.com/ultrajson/ultrajson" class="external-link" target="_blank">`ujson`</a>.
|
||||
|
||||
@@ -180,7 +180,7 @@ Esto requiere instalar `ujson`, por ejemplo, con `pip install ujson`.
|
||||
|
||||
///
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial001.py hl[2,7] *}
|
||||
{* ../../docs_src/custom_response/tutorial001_py39.py hl[2,7] *}
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
@@ -188,45 +188,45 @@ Es posible que `ORJSONResponse` sea una alternativa más rápida.
|
||||
|
||||
///
|
||||
|
||||
### `RedirectResponse`
|
||||
### `RedirectResponse` { #redirectresponse }
|
||||
|
||||
Devuelve una redirección HTTP. Usa un código de estado 307 (Redirección Temporal) por defecto.
|
||||
|
||||
Puedes devolver un `RedirectResponse` directamente:
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial006.py hl[2,9] *}
|
||||
{* ../../docs_src/custom_response/tutorial006_py39.py hl[2,9] *}
|
||||
|
||||
---
|
||||
|
||||
O puedes usarlo en el parámetro `response_class`:
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial006b.py hl[2,7,9] *}
|
||||
{* ../../docs_src/custom_response/tutorial006b_py39.py hl[2,7,9] *}
|
||||
|
||||
Si haces eso, entonces puedes devolver la URL directamente desde tu *path operation function*.
|
||||
|
||||
En este caso, el `status_code` utilizado será el predeterminado para `RedirectResponse`, que es `307`.
|
||||
En este caso, el `status_code` utilizado será el por defecto para `RedirectResponse`, que es `307`.
|
||||
|
||||
---
|
||||
|
||||
También puedes usar el parámetro `status_code` combinado con el parámetro `response_class`:
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial006c.py hl[2,7,9] *}
|
||||
{* ../../docs_src/custom_response/tutorial006c_py39.py hl[2,7,9] *}
|
||||
|
||||
### `StreamingResponse`
|
||||
### `StreamingResponse` { #streamingresponse }
|
||||
|
||||
Toma un generador `async` o un generador/iterador normal y transmite el cuerpo del response.
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial007.py hl[2,14] *}
|
||||
{* ../../docs_src/custom_response/tutorial007_py39.py hl[2,14] *}
|
||||
|
||||
#### Usando `StreamingResponse` con objetos similares a archivos
|
||||
#### Usando `StreamingResponse` con objetos similares a archivos { #using-streamingresponse-with-file-like-objects }
|
||||
|
||||
Si tienes un objeto similar a un archivo (por ejemplo, el objeto devuelto por `open()`), puedes crear una función generadora para iterar sobre ese objeto similar a un archivo.
|
||||
Si tienes un <a href="https://docs.python.org/3/glossary.html#term-file-like-object" class="external-link" target="_blank">objeto similar a un archivo</a> (por ejemplo, el objeto devuelto por `open()`), puedes crear una función generadora para iterar sobre ese objeto similar a un archivo.
|
||||
|
||||
De esa manera, no tienes que leerlo todo primero en memoria, y puedes pasar esa función generadora al `StreamingResponse`, y devolverlo.
|
||||
|
||||
Esto incluye muchos paquetes para interactuar con almacenamiento en la nube, procesamiento de video y otros.
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial008.py hl[2,10:12,14] *}
|
||||
{* ../../docs_src/custom_response/tutorial008_py39.py hl[2,10:12,14] *}
|
||||
|
||||
1. Esta es la función generadora. Es una "función generadora" porque contiene declaraciones `yield` dentro.
|
||||
2. Al usar un bloque `with`, nos aseguramos de que el objeto similar a un archivo se cierre después de que la función generadora termine. Así, después de que termina de enviar el response.
|
||||
@@ -242,7 +242,7 @@ Nota que aquí como estamos usando `open()` estándar que no admite `async` y `a
|
||||
|
||||
///
|
||||
|
||||
### `FileResponse`
|
||||
### `FileResponse` { #fileresponse }
|
||||
|
||||
Transmite un archivo asincrónicamente como response.
|
||||
|
||||
@@ -255,15 +255,15 @@ Toma un conjunto diferente de argumentos para crear un instance que los otros ti
|
||||
|
||||
Los responses de archivos incluirán los headers apropiados `Content-Length`, `Last-Modified` y `ETag`.
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial009.py hl[2,10] *}
|
||||
{* ../../docs_src/custom_response/tutorial009_py39.py hl[2,10] *}
|
||||
|
||||
También puedes usar el parámetro `response_class`:
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial009b.py hl[2,8,10] *}
|
||||
{* ../../docs_src/custom_response/tutorial009b_py39.py hl[2,8,10] *}
|
||||
|
||||
En este caso, puedes devolver la path del archivo directamente desde tu *path operation* function.
|
||||
|
||||
## Clase de response personalizada
|
||||
## Clase de response personalizada { #custom-response-class }
|
||||
|
||||
Puedes crear tu propia clase de response personalizada, heredando de `Response` y usándola.
|
||||
|
||||
@@ -273,7 +273,7 @@ Digamos que quieres que devuelva JSON con sangría y formato, por lo que quieres
|
||||
|
||||
Podrías crear un `CustomORJSONResponse`. Lo principal que tienes que hacer es crear un método `Response.render(content)` que devuelva el contenido como `bytes`:
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial009c.py hl[9:14,17] *}
|
||||
{* ../../docs_src/custom_response/tutorial009c_py39.py hl[9:14,17] *}
|
||||
|
||||
Ahora en lugar de devolver:
|
||||
|
||||
@@ -291,7 +291,7 @@ Ahora en lugar de devolver:
|
||||
|
||||
Por supuesto, probablemente encontrarás formas mucho mejores de aprovechar esto que formatear JSON. 😉
|
||||
|
||||
## Clase de response predeterminada
|
||||
## Clase de response por defecto { #default-response-class }
|
||||
|
||||
Al crear una instance de la clase **FastAPI** o un `APIRouter`, puedes especificar qué clase de response usar por defecto.
|
||||
|
||||
@@ -299,7 +299,7 @@ El parámetro que define esto es `default_response_class`.
|
||||
|
||||
En el ejemplo a continuación, **FastAPI** usará `ORJSONResponse` por defecto, en todas las *path operations*, en lugar de `JSONResponse`.
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial010.py hl[2,4] *}
|
||||
{* ../../docs_src/custom_response/tutorial010_py39.py hl[2,4] *}
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
@@ -307,6 +307,6 @@ Todavía puedes sobrescribir `response_class` en *path operations* como antes.
|
||||
|
||||
///
|
||||
|
||||
## Documentación adicional
|
||||
## Documentación adicional { #additional-documentation }
|
||||
|
||||
También puedes declarar el media type y muchos otros detalles en OpenAPI usando `responses`: [Responses Adicionales en OpenAPI](additional-responses.md){.internal-link target=_blank}.
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# Usando Dataclasses
|
||||
# Usando Dataclasses { #using-dataclasses }
|
||||
|
||||
FastAPI está construido sobre **Pydantic**, y te he estado mostrando cómo usar modelos de Pydantic para declarar requests y responses.
|
||||
|
||||
Pero FastAPI también soporta el uso de <a href="https://docs.python.org/3/library/dataclasses.html" class="external-link" target="_blank">`dataclasses`</a> de la misma manera:
|
||||
|
||||
{* ../../docs_src/dataclasses/tutorial001.py hl[1,7:12,19:20] *}
|
||||
{* ../../docs_src/dataclasses_/tutorial001_py310.py hl[1,6:11,18:19] *}
|
||||
|
||||
Esto sigue siendo soportado gracias a **Pydantic**, ya que tiene <a href="https://docs.pydantic.dev/latest/concepts/dataclasses/#use-of-stdlib-dataclasses-with-basemodel" class="external-link" target="_blank">soporte interno para `dataclasses`</a>.
|
||||
|
||||
@@ -28,11 +28,11 @@ Pero si tienes un montón de dataclasses por ahí, este es un buen truco para us
|
||||
|
||||
///
|
||||
|
||||
## Dataclasses en `response_model`
|
||||
## Dataclasses en `response_model` { #dataclasses-in-response-model }
|
||||
|
||||
También puedes usar `dataclasses` en el parámetro `response_model`:
|
||||
|
||||
{* ../../docs_src/dataclasses/tutorial002.py hl[1,7:13,19] *}
|
||||
{* ../../docs_src/dataclasses_/tutorial002_py310.py hl[1,6:12,18] *}
|
||||
|
||||
El dataclass será automáticamente convertido a un dataclass de Pydantic.
|
||||
|
||||
@@ -40,7 +40,7 @@ De esta manera, su esquema aparecerá en la interfaz de usuario de la documentac
|
||||
|
||||
<img src="/img/tutorial/dataclasses/image01.png">
|
||||
|
||||
## Dataclasses en Estructuras de Datos Anidadas
|
||||
## Dataclasses en Estructuras de Datos Anidadas { #dataclasses-in-nested-data-structures }
|
||||
|
||||
También puedes combinar `dataclasses` con otras anotaciones de tipos para crear estructuras de datos anidadas.
|
||||
|
||||
@@ -48,7 +48,7 @@ En algunos casos, todavía podrías tener que usar la versión de `dataclasses`
|
||||
|
||||
En ese caso, simplemente puedes intercambiar los `dataclasses` estándar con `pydantic.dataclasses`, que es un reemplazo directo:
|
||||
|
||||
{* ../../docs_src/dataclasses/tutorial003.py hl[1,5,8:11,14:17,23:25,28] *}
|
||||
{* ../../docs_src/dataclasses_/tutorial003_py310.py hl[1,4,7:10,13:16,22:24,27] *}
|
||||
|
||||
1. Todavía importamos `field` de los `dataclasses` estándar.
|
||||
|
||||
@@ -64,7 +64,7 @@ En ese caso, simplemente puedes intercambiar los `dataclasses` estándar con `py
|
||||
|
||||
6. Aquí estamos regresando un diccionario que contiene `items`, que es una lista de dataclasses.
|
||||
|
||||
FastAPI todavía es capaz de <abbr title="converting the data to a format that can be transmitted">serializar</abbr> los datos a JSON.
|
||||
FastAPI todavía es capaz de <abbr title="convertir los datos a un formato que pueda transmitirse">serializar</abbr> los datos a JSON.
|
||||
|
||||
7. Aquí el `response_model` está usando una anotación de tipo de una lista de dataclasses `Author`.
|
||||
|
||||
@@ -84,12 +84,12 @@ Puedes combinar `dataclasses` con otras anotaciones de tipos en muchas combinaci
|
||||
|
||||
Revisa las anotaciones en el código arriba para ver más detalles específicos.
|
||||
|
||||
## Aprende Más
|
||||
## Aprende Más { #learn-more }
|
||||
|
||||
También puedes combinar `dataclasses` con otros modelos de Pydantic, heredar de ellos, incluirlos en tus propios modelos, etc.
|
||||
|
||||
Para saber más, revisa la <a href="https://docs.pydantic.dev/latest/concepts/dataclasses/" class="external-link" target="_blank">documentación de Pydantic sobre dataclasses</a>.
|
||||
|
||||
## Versión
|
||||
## Versión { #version }
|
||||
|
||||
Esto está disponible desde la versión `0.67.0` de FastAPI. 🔖
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Eventos de Lifespan
|
||||
# Eventos de Lifespan { #lifespan-events }
|
||||
|
||||
Puedes definir lógica (código) que debería ser ejecutada antes de que la aplicación **inicie**. Esto significa que este código será ejecutado **una vez**, **antes** de que la aplicación **comience a recibir requests**.
|
||||
|
||||
@@ -8,7 +8,7 @@ Debido a que este código se ejecuta antes de que la aplicación **comience** a
|
||||
|
||||
Esto puede ser muy útil para configurar **recursos** que necesitas usar para toda la app, y que son **compartidos** entre requests, y/o que necesitas **limpiar** después. Por ejemplo, un pool de conexiones a una base de datos, o cargando un modelo de machine learning compartido.
|
||||
|
||||
## Caso de Uso
|
||||
## Caso de Uso { #use-case }
|
||||
|
||||
Empecemos con un ejemplo de **caso de uso** y luego veamos cómo resolverlo con esto.
|
||||
|
||||
@@ -22,7 +22,7 @@ Podrías cargarlo en el nivel superior del módulo/archivo, pero eso también si
|
||||
|
||||
Eso es lo que resolveremos, vamos a cargar el modelo antes de que los requests sean manejados, pero solo justo antes de que la aplicación comience a recibir requests, no mientras el código se está cargando.
|
||||
|
||||
## Lifespan
|
||||
## Lifespan { #lifespan }
|
||||
|
||||
Puedes definir esta lógica de *startup* y *shutdown* usando el parámetro `lifespan` de la app de `FastAPI`, y un "context manager" (te mostraré lo que es en un momento).
|
||||
|
||||
@@ -30,7 +30,7 @@ Comencemos con un ejemplo y luego veámoslo en detalle.
|
||||
|
||||
Creamos una función asíncrona `lifespan()` con `yield` así:
|
||||
|
||||
{* ../../docs_src/events/tutorial003.py hl[16,19] *}
|
||||
{* ../../docs_src/events/tutorial003_py39.py hl[16,19] *}
|
||||
|
||||
Aquí estamos simulando la operación costosa de *startup* de cargar el modelo poniendo la función del (falso) modelo en el diccionario con modelos de machine learning antes del `yield`. Este código será ejecutado **antes** de que la aplicación **comience a tomar requests**, durante el *startup*.
|
||||
|
||||
@@ -44,25 +44,25 @@ Quizás necesites iniciar una nueva versión, o simplemente te cansaste de ejecu
|
||||
|
||||
///
|
||||
|
||||
### Función de Lifespan
|
||||
### Función de Lifespan { #lifespan-function }
|
||||
|
||||
Lo primero que hay que notar es que estamos definiendo una función asíncrona con `yield`. Esto es muy similar a las Dependencias con `yield`.
|
||||
|
||||
{* ../../docs_src/events/tutorial003.py hl[14:19] *}
|
||||
{* ../../docs_src/events/tutorial003_py39.py hl[14:19] *}
|
||||
|
||||
La primera parte de la función, antes del `yield`, será ejecutada **antes** de que la aplicación comience.
|
||||
|
||||
Y la parte después del `yield` será ejecutada **después** de que la aplicación haya terminado.
|
||||
|
||||
### Async Context Manager
|
||||
### Async Context Manager { #async-context-manager }
|
||||
|
||||
Si revisas, la función está decorada con un `@asynccontextmanager`.
|
||||
|
||||
Eso convierte a la función en algo llamado un "**async context manager**".
|
||||
|
||||
{* ../../docs_src/events/tutorial003.py hl[1,13] *}
|
||||
{* ../../docs_src/events/tutorial003_py39.py hl[1,13] *}
|
||||
|
||||
Un **context manager** en Python es algo que puedes usar en una declaración `with`, por ejemplo, `open()` puede ser usado como un context manager:
|
||||
Un **context manager** en Python es algo que puedes usar en un statement `with`, por ejemplo, `open()` puede ser usado como un context manager:
|
||||
|
||||
```Python
|
||||
with open("file.txt") as file:
|
||||
@@ -82,9 +82,9 @@ En nuestro ejemplo de código arriba, no lo usamos directamente, pero se lo pasa
|
||||
|
||||
El parámetro `lifespan` de la app de `FastAPI` toma un **async context manager**, por lo que podemos pasar nuestro nuevo `lifespan` async context manager a él.
|
||||
|
||||
{* ../../docs_src/events/tutorial003.py hl[22] *}
|
||||
{* ../../docs_src/events/tutorial003_py39.py hl[22] *}
|
||||
|
||||
## Eventos Alternativos (obsoleto)
|
||||
## Eventos Alternativos (obsoleto) { #alternative-events-deprecated }
|
||||
|
||||
/// warning | Advertencia
|
||||
|
||||
@@ -100,11 +100,11 @@ Puedes definir manejadores de eventos (funciones) que necesitan ser ejecutadas a
|
||||
|
||||
Estas funciones pueden ser declaradas con `async def` o `def` normal.
|
||||
|
||||
### Evento `startup`
|
||||
### Evento `startup` { #startup-event }
|
||||
|
||||
Para añadir una función que debería ejecutarse antes de que la aplicación inicie, declárala con el evento `"startup"`:
|
||||
|
||||
{* ../../docs_src/events/tutorial001.py hl[8] *}
|
||||
{* ../../docs_src/events/tutorial001_py39.py hl[8] *}
|
||||
|
||||
En este caso, la función manejadora del evento `startup` inicializará los ítems de la "base de datos" (solo un `dict`) con algunos valores.
|
||||
|
||||
@@ -112,11 +112,11 @@ Puedes añadir más de un manejador de eventos.
|
||||
|
||||
Y tu aplicación no comenzará a recibir requests hasta que todos los manejadores de eventos `startup` hayan completado.
|
||||
|
||||
### Evento `shutdown`
|
||||
### Evento `shutdown` { #shutdown-event }
|
||||
|
||||
Para añadir una función que debería ejecutarse cuando la aplicación se esté cerrando, declárala con el evento `"shutdown"`:
|
||||
|
||||
{* ../../docs_src/events/tutorial002.py hl[6] *}
|
||||
{* ../../docs_src/events/tutorial002_py39.py hl[6] *}
|
||||
|
||||
Aquí, la función manejadora del evento `shutdown` escribirá una línea de texto `"Application shutdown"` a un archivo `log.txt`.
|
||||
|
||||
@@ -138,7 +138,7 @@ Por eso, declaramos la función manejadora del evento con `def` estándar en vez
|
||||
|
||||
///
|
||||
|
||||
### `startup` y `shutdown` juntos
|
||||
### `startup` y `shutdown` juntos { #startup-and-shutdown-together }
|
||||
|
||||
Hay una gran posibilidad de que la lógica para tu *startup* y *shutdown* esté conectada, podrías querer iniciar algo y luego finalizarlo, adquirir un recurso y luego liberarlo, etc.
|
||||
|
||||
@@ -146,7 +146,7 @@ Hacer eso en funciones separadas que no comparten lógica o variables juntas es
|
||||
|
||||
Debido a eso, ahora se recomienda en su lugar usar el `lifespan` como se explicó arriba.
|
||||
|
||||
## Detalles Técnicos
|
||||
## Detalles Técnicos { #technical-details }
|
||||
|
||||
Solo un detalle técnico para los nerds curiosos. 🤓
|
||||
|
||||
@@ -160,6 +160,6 @@ Incluyendo cómo manejar el estado de lifespan que puede ser usado en otras áre
|
||||
|
||||
///
|
||||
|
||||
## Sub Aplicaciones
|
||||
## Sub Aplicaciones { #sub-applications }
|
||||
|
||||
🚨 Ten en cuenta que estos eventos de lifespan (startup y shutdown) solo serán ejecutados para la aplicación principal, no para [Sub Aplicaciones - Mounts](sub-applications.md){.internal-link target=_blank}.
|
||||
|
||||
@@ -1,115 +1,76 @@
|
||||
# Genera Clientes
|
||||
# Generando SDKs { #generating-sdks }
|
||||
|
||||
Como **FastAPI** está basado en la especificación OpenAPI, obtienes compatibilidad automática con muchas herramientas, incluyendo la documentación automática de la API (proporcionada por Swagger UI).
|
||||
Como **FastAPI** está basado en la especificación **OpenAPI**, sus APIs se pueden describir en un formato estándar que muchas herramientas entienden.
|
||||
|
||||
Una ventaja particular que no es necesariamente obvia es que puedes **generar clientes** (a veces llamados <abbr title="Software Development Kits">**SDKs**</abbr> ) para tu API, para muchos **lenguajes de programación** diferentes.
|
||||
Esto facilita generar **documentación** actualizada, paquetes de cliente (<abbr title="Software Development Kits – Kits de Desarrollo de Software">**SDKs**</abbr>) en múltiples lenguajes y **escribir pruebas** o **flujos de automatización** que se mantengan sincronizados con tu código.
|
||||
|
||||
## Generadores de Clientes OpenAPI
|
||||
En esta guía, aprenderás a generar un **SDK de TypeScript** para tu backend con FastAPI.
|
||||
|
||||
Hay muchas herramientas para generar clientes desde **OpenAPI**.
|
||||
## Generadores de SDKs de código abierto { #open-source-sdk-generators }
|
||||
|
||||
Una herramienta común es <a href="https://openapi-generator.tech/" class="external-link" target="_blank">OpenAPI Generator</a>.
|
||||
Una opción versátil es el <a href="https://openapi-generator.tech/" class="external-link" target="_blank">OpenAPI Generator</a>, que soporta **muchos lenguajes de programación** y puede generar SDKs a partir de tu especificación OpenAPI.
|
||||
|
||||
Si estás construyendo un **frontend**, una alternativa muy interesante es <a href="https://github.com/hey-api/openapi-ts" class="external-link" target="_blank">openapi-ts</a>.
|
||||
Para **clientes de TypeScript**, <a href="https://heyapi.dev/" class="external-link" target="_blank">Hey API</a> es una solución diseñada específicamente, que ofrece una experiencia optimizada para el ecosistema de TypeScript.
|
||||
|
||||
## Generadores de Clientes y SDKs - Sponsor
|
||||
Puedes descubrir más generadores de SDK en <a href="https://openapi.tools/#sdk" class="external-link" target="_blank">OpenAPI.Tools</a>.
|
||||
|
||||
También hay algunos generadores de Clientes y SDKs **respaldados por empresas** basados en OpenAPI (FastAPI), en algunos casos pueden ofrecerte **funcionalidades adicionales** además de SDKs/clientes generados de alta calidad.
|
||||
/// tip | Consejo
|
||||
|
||||
Algunos de ellos también ✨ [**sponsorean FastAPI**](../help-fastapi.md#sponsor-the-author){.internal-link target=_blank} ✨, esto asegura el **desarrollo** continuo y saludable de FastAPI y su **ecosistema**.
|
||||
FastAPI genera automáticamente especificaciones **OpenAPI 3.1**, así que cualquier herramienta que uses debe soportar esta versión.
|
||||
|
||||
Y muestra su verdadero compromiso con FastAPI y su **comunidad** (tú), ya que no solo quieren proporcionarte un **buen servicio** sino también asegurarse de que tengas un **buen y saludable framework**, FastAPI. 🙇
|
||||
///
|
||||
|
||||
## Generadores de SDKs de sponsors de FastAPI { #sdk-generators-from-fastapi-sponsors }
|
||||
|
||||
Esta sección destaca soluciones **respaldadas por empresas** y **venture-backed** de compañías que sponsorean FastAPI. Estos productos ofrecen **funcionalidades adicionales** e **integraciones** además de SDKs generados de alta calidad.
|
||||
|
||||
Al ✨ [**sponsorear FastAPI**](../help-fastapi.md#sponsor-the-author){.internal-link target=_blank} ✨, estas compañías ayudan a asegurar que el framework y su **ecosistema** se mantengan saludables y **sustentables**.
|
||||
|
||||
Su sponsorship también demuestra un fuerte compromiso con la **comunidad** de FastAPI (tú), mostrando que no solo les importa ofrecer un **gran servicio**, sino también apoyar un **framework robusto y próspero**, FastAPI. 🙇
|
||||
|
||||
Por ejemplo, podrías querer probar:
|
||||
|
||||
* <a href="https://speakeasy.com/editor?utm_source=fastapi+repo&utm_medium=github+sponsorship" class="external-link" target="_blank">Speakeasy</a>
|
||||
* <a href="https://www.stainlessapi.com/?utm_source=fastapi&utm_medium=referral" class="external-link" target="_blank">Stainless</a>
|
||||
* <a href="https://developers.liblab.com/tutorials/sdk-for-fastapi/?utm_source=fastapi" class="external-link" target="_blank">liblab</a>
|
||||
* <a href="https://www.stainless.com/?utm_source=fastapi&utm_medium=referral" class="external-link" target="_blank">Stainless</a>
|
||||
* <a href="https://developers.liblab.com/tutorials/sdk-for-fastapi?utm_source=fastapi" class="external-link" target="_blank">liblab</a>
|
||||
|
||||
También hay varias otras empresas que ofrecen servicios similares que puedes buscar y encontrar en línea. 🤓
|
||||
Algunas de estas soluciones también pueden ser open source u ofrecer niveles gratuitos, así que puedes probarlas sin un compromiso financiero. Hay otros generadores de SDK comerciales disponibles y se pueden encontrar en línea. 🤓
|
||||
|
||||
## Genera un Cliente Frontend en TypeScript
|
||||
## Crea un SDK de TypeScript { #create-a-typescript-sdk }
|
||||
|
||||
Empecemos con una aplicación simple de FastAPI:
|
||||
|
||||
{* ../../docs_src/generate_clients/tutorial001_py39.py hl[7:9,12:13,16:17,21] *}
|
||||
|
||||
Nota que las *path operations* definen los modelos que usan para el payload de la petición y el payload del response, usando los modelos `Item` y `ResponseMessage`.
|
||||
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`.
|
||||
|
||||
### Documentación de la API
|
||||
### Documentación de la API { #api-docs }
|
||||
|
||||
Si vas a la documentación de la API, verás que tiene los **esquemas** para los datos que se enviarán en las peticiones y se recibirán en los responses:
|
||||
Si vas a `/docs`, verás que tiene los **esquemas** para los datos a enviar en requests y recibir en responses:
|
||||
|
||||
<img src="/img/tutorial/generate-clients/image01.png">
|
||||
|
||||
Puedes ver esos esquemas porque fueron declarados con los modelos en la aplicación.
|
||||
Puedes ver esos esquemas porque fueron declarados con los modelos en la app.
|
||||
|
||||
Esa información está disponible en el **JSON Schema** de OpenAPI de la aplicación, y luego se muestra en la documentación de la API (por Swagger UI).
|
||||
Esa información está disponible en el **OpenAPI schema** de la app, y luego se muestra en la documentación de la API.
|
||||
|
||||
Y esa misma información de los modelos que está incluida en OpenAPI es lo que puede usarse para **generar el código del cliente**.
|
||||
|
||||
### Genera un Cliente en TypeScript
|
||||
### Hey API { #hey-api }
|
||||
|
||||
Ahora que tenemos la aplicación con los modelos, podemos generar el código del cliente para el frontend.
|
||||
Una vez que tenemos una app de FastAPI con los modelos, podemos usar Hey API para generar un cliente de TypeScript. La forma más rápida de hacerlo es con npx.
|
||||
|
||||
#### Instalar `openapi-ts`
|
||||
|
||||
Puedes instalar `openapi-ts` en tu código de frontend con:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ npm install @hey-api/openapi-ts --save-dev
|
||||
|
||||
---> 100%
|
||||
```sh
|
||||
npx @hey-api/openapi-ts -i http://localhost:8000/openapi.json -o src/client
|
||||
```
|
||||
|
||||
</div>
|
||||
Esto generará un SDK de TypeScript en `./src/client`.
|
||||
|
||||
#### Generar el Código del Cliente
|
||||
Puedes aprender cómo <a href="https://heyapi.dev/openapi-ts/get-started" class="external-link" target="_blank">instalar `@hey-api/openapi-ts`</a> y leer sobre el <a href="https://heyapi.dev/openapi-ts/output" class="external-link" target="_blank">output generado</a> en su sitio web.
|
||||
|
||||
Para generar el código del cliente puedes usar la aplicación de línea de comandos `openapi-ts` que ahora estaría instalada.
|
||||
### Usar el SDK { #using-the-sdk }
|
||||
|
||||
Como está instalada en el proyecto local, probablemente no podrías llamar a ese comando directamente, pero podrías ponerlo en tu archivo `package.json`.
|
||||
|
||||
Podría verse como esto:
|
||||
|
||||
```JSON hl_lines="7"
|
||||
{
|
||||
"name": "frontend-app",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"generate-client": "openapi-ts --input http://localhost:8000/openapi.json --output ./src/client --client axios"
|
||||
},
|
||||
"author": "",
|
||||
"license": "",
|
||||
"devDependencies": {
|
||||
"@hey-api/openapi-ts": "^0.27.38",
|
||||
"typescript": "^4.6.2"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Después de tener ese script de NPM `generate-client` allí, puedes ejecutarlo con:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ npm run generate-client
|
||||
|
||||
frontend-app@1.0.0 generate-client /home/user/code/frontend-app
|
||||
> openapi-ts --input http://localhost:8000/openapi.json --output ./src/client --client axios
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
Ese comando generará código en `./src/client` y usará `axios` (el paquete HTTP de frontend) internamente.
|
||||
|
||||
### Prueba el Código del Cliente
|
||||
|
||||
Ahora puedes importar y usar el código del cliente, podría verse así, nota que tienes autocompletado para los métodos:
|
||||
Ahora puedes importar y usar el código del cliente. Podría verse así, nota que tienes autocompletado para los métodos:
|
||||
|
||||
<img src="/img/tutorial/generate-clients/image02.png">
|
||||
|
||||
@@ -131,17 +92,17 @@ El objeto de response también tendrá autocompletado:
|
||||
|
||||
<img src="/img/tutorial/generate-clients/image05.png">
|
||||
|
||||
## App de FastAPI con Tags
|
||||
## App de FastAPI con tags { #fastapi-app-with-tags }
|
||||
|
||||
En muchos casos tu aplicación de FastAPI será más grande, y probablemente usarás tags para separar diferentes grupos de *path operations*.
|
||||
En muchos casos tu app de FastAPI será más grande, y probablemente usarás tags para separar diferentes grupos de *path operations*.
|
||||
|
||||
Por ejemplo, podrías tener una sección para **items** y otra sección para **usuarios**, y podrían estar separadas por tags:
|
||||
Por ejemplo, podrías tener una sección para **items** y otra sección para **users**, y podrían estar separadas por tags:
|
||||
|
||||
{* ../../docs_src/generate_clients/tutorial002_py39.py hl[21,26,34] *}
|
||||
|
||||
### Genera un Cliente TypeScript con Tags
|
||||
### Genera un Cliente TypeScript con tags { #generate-a-typescript-client-with-tags }
|
||||
|
||||
Si generas un cliente para una aplicación de FastAPI usando tags, normalmente también separará el código del cliente basándose en los tags.
|
||||
Si generas un cliente para una app de FastAPI usando tags, normalmente también separará el código del cliente basándose en los tags.
|
||||
|
||||
De esta manera podrás tener las cosas ordenadas y agrupadas correctamente para el código del cliente:
|
||||
|
||||
@@ -152,7 +113,7 @@ En este caso tienes:
|
||||
* `ItemsService`
|
||||
* `UsersService`
|
||||
|
||||
### Nombres de los Métodos del Cliente
|
||||
### Nombres de los métodos del cliente { #client-method-names }
|
||||
|
||||
Ahora mismo los nombres de los métodos generados como `createItemItemsPost` no se ven muy limpios:
|
||||
|
||||
@@ -166,15 +127,15 @@ OpenAPI requiere que cada operation ID sea único a través de todas las *path o
|
||||
|
||||
Pero te mostraré cómo mejorar eso a continuación. 🤓
|
||||
|
||||
## Operation IDs Personalizados y Mejores Nombres de Métodos
|
||||
## Operation IDs personalizados y mejores nombres de métodos { #custom-operation-ids-and-better-method-names }
|
||||
|
||||
Puedes **modificar** la forma en que estos operation IDs son **generados** para hacerlos más simples y tener **nombres de métodos más simples** en los clientes.
|
||||
|
||||
En este caso tendrás que asegurarte de que cada operation ID sea **único** de alguna otra manera.
|
||||
|
||||
Por ejemplo, podrías asegurarte de que cada *path operation* tenga un tag, y luego generar el operation ID basado en el **tag** y el nombre de la *path operation* **name** (el nombre de la función).
|
||||
Por ejemplo, podrías asegurarte de que cada *path operation* tenga un tag, y luego generar el operation ID basado en el **tag** y el **name** de la *path operation* (el nombre de la función).
|
||||
|
||||
### Función Personalizada para Generar ID Único
|
||||
### Función personalizada para generar ID único { #custom-generate-unique-id-function }
|
||||
|
||||
FastAPI usa un **ID único** para cada *path operation*, se usa para el **operation ID** y también para los nombres de cualquier modelo personalizado necesario, para requests o responses.
|
||||
|
||||
@@ -186,15 +147,15 @@ Puedes entonces pasar esa función personalizada a **FastAPI** como el parámetr
|
||||
|
||||
{* ../../docs_src/generate_clients/tutorial003_py39.py hl[6:7,10] *}
|
||||
|
||||
### Generar un Cliente TypeScript con Operation IDs Personalizados
|
||||
### Genera un Cliente TypeScript con operation IDs personalizados { #generate-a-typescript-client-with-custom-operation-ids }
|
||||
|
||||
Ahora si generas el cliente de nuevo, verás que tiene los nombres de métodos mejorados:
|
||||
Ahora, si generas el cliente de nuevo, verás que tiene los nombres de métodos mejorados:
|
||||
|
||||
<img src="/img/tutorial/generate-clients/image07.png">
|
||||
|
||||
Como ves, los nombres de métodos ahora tienen el tag y luego el nombre de la función, ahora no incluyen información del path de la URL y la operación HTTP.
|
||||
|
||||
### Preprocesa la Especificación OpenAPI para el Generador de Clientes
|
||||
### Preprocesa la especificación OpenAPI para el generador de clientes { #preprocess-the-openapi-specification-for-the-client-generator }
|
||||
|
||||
El código generado aún tiene algo de **información duplicada**.
|
||||
|
||||
@@ -206,7 +167,7 @@ Pero para el cliente generado podríamos **modificar** los operation IDs de Open
|
||||
|
||||
Podríamos descargar el JSON de OpenAPI a un archivo `openapi.json` y luego podríamos **remover ese tag prefijado** con un script como este:
|
||||
|
||||
{* ../../docs_src/generate_clients/tutorial004.py *}
|
||||
{* ../../docs_src/generate_clients/tutorial004_py39.py *}
|
||||
|
||||
//// tab | Node.js
|
||||
|
||||
@@ -218,44 +179,30 @@ Podríamos descargar el JSON de OpenAPI a un archivo `openapi.json` y luego podr
|
||||
|
||||
Con eso, los operation IDs serían renombrados de cosas como `items-get_items` a solo `get_items`, de esa manera el generador del cliente puede generar nombres de métodos más simples.
|
||||
|
||||
### Generar un Cliente TypeScript con el OpenAPI Preprocesado
|
||||
### Genera un Cliente TypeScript con el OpenAPI preprocesado { #generate-a-typescript-client-with-the-preprocessed-openapi }
|
||||
|
||||
Ahora como el resultado final está en un archivo `openapi.json`, modificarías el `package.json` para usar ese archivo local, por ejemplo:
|
||||
Como el resultado final ahora está en un archivo `openapi.json`, necesitas actualizar la ubicación de la entrada:
|
||||
|
||||
```JSON hl_lines="7"
|
||||
{
|
||||
"name": "frontend-app",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"generate-client": "openapi-ts --input ./openapi.json --output ./src/client --client axios"
|
||||
},
|
||||
"author": "",
|
||||
"license": "",
|
||||
"devDependencies": {
|
||||
"@hey-api/openapi-ts": "^0.27.38",
|
||||
"typescript": "^4.6.2"
|
||||
}
|
||||
}
|
||||
```sh
|
||||
npx @hey-api/openapi-ts -i ./openapi.json -o src/client
|
||||
```
|
||||
|
||||
Después de generar el nuevo cliente, ahora tendrías nombres de métodos **limpios**, con todo el **autocompletado**, **errores en línea**, etc:
|
||||
|
||||
<img src="/img/tutorial/generate-clients/image08.png">
|
||||
|
||||
## Beneficios
|
||||
## Beneficios { #benefits }
|
||||
|
||||
Cuando usas los clientes generados automáticamente obtendrás **autocompletado** para:
|
||||
Cuando uses los clientes generados automáticamente obtendrás **autocompletado** para:
|
||||
|
||||
* Métodos.
|
||||
* Payloads de peticiones en el cuerpo, parámetros de query, etc.
|
||||
* Payloads de responses.
|
||||
* Payloads de request en el body, parámetros de query, etc.
|
||||
* Payloads de response.
|
||||
|
||||
También tendrás **errores en línea** para todo.
|
||||
|
||||
Y cada vez que actualices el código del backend, y **regeneres** el frontend, tendrás las nuevas *path operations* disponibles como métodos, las antiguas eliminadas, y cualquier otro cambio se reflejará en el código generado. 🤓
|
||||
|
||||
Esto también significa que si algo cambió será **reflejado** automáticamente en el código del cliente. Y si haces **build** del cliente, te dará error si tienes algún **desajuste** en los datos utilizados.
|
||||
Esto también significa que si algo cambió será **reflejado** automáticamente en el código del cliente. Y si haces **build** del cliente, dará error si tienes algún **desajuste** en los datos utilizados.
|
||||
|
||||
Así que, **detectarás muchos errores** muy temprano en el ciclo de desarrollo en lugar de tener que esperar a que los errores se muestren a tus usuarios finales en producción para luego intentar depurar dónde está el problema. ✨
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Guía avanzada del usuario
|
||||
# Guía avanzada del usuario { #advanced-user-guide }
|
||||
|
||||
## Funcionalidades adicionales
|
||||
## Funcionalidades adicionales { #additional-features }
|
||||
|
||||
El [Tutorial - Guía del usuario](../tutorial/index.md){.internal-link target=_blank} principal debería ser suficiente para darte un recorrido por todas las funcionalidades principales de **FastAPI**.
|
||||
|
||||
@@ -14,23 +14,8 @@ Y es posible que para tu caso de uso, la solución esté en una de ellas.
|
||||
|
||||
///
|
||||
|
||||
## Lee primero el Tutorial
|
||||
## Lee primero el Tutorial { #read-the-tutorial-first }
|
||||
|
||||
Aún podrías usar la mayoría de las funcionalidades en **FastAPI** con el conocimiento del [Tutorial - Guía del usuario](../tutorial/index.md){.internal-link target=_blank} principal.
|
||||
|
||||
Y las siguientes secciones asumen que ya lo leíste y que conoces esas ideas principales.
|
||||
|
||||
## Cursos externos
|
||||
|
||||
Aunque el [Tutorial - Guía del usuario](../tutorial/index.md){.internal-link target=_blank} y esta **Guía avanzada del usuario** están escritos como un tutorial guiado (como un libro) y deberían ser suficientes para que **aprendas FastAPI**, podrías querer complementarlo con cursos adicionales.
|
||||
|
||||
O podría ser que simplemente prefieras tomar otros cursos porque se adaptan mejor a tu estilo de aprendizaje.
|
||||
|
||||
Algunos proveedores de cursos ✨ [**sponsorean FastAPI**](../help-fastapi.md#sponsor-the-author){.internal-link target=_blank} ✨, esto asegura el desarrollo continuo y saludable de FastAPI y su **ecosistema**.
|
||||
|
||||
Y muestra su verdadero compromiso con FastAPI y su **comunidad** (tú), ya que no solo quieren brindarte una **buena experiencia de aprendizaje** sino que también quieren asegurarse de que tengas un **buen y saludable framework**, FastAPI. 🙇
|
||||
|
||||
Podrías querer probar sus cursos:
|
||||
|
||||
* <a href="https://training.talkpython.fm/fastapi-courses" class="external-link" target="_blank">Talk Python Training</a>
|
||||
* <a href="https://testdriven.io/courses/tdd-fastapi/" class="external-link" target="_blank">Desarrollo guiado por pruebas</a>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Middleware Avanzado
|
||||
# Middleware Avanzado { #advanced-middleware }
|
||||
|
||||
En el tutorial principal leíste cómo agregar [Middleware Personalizado](../tutorial/middleware.md){.internal-link target=_blank} a tu aplicación.
|
||||
|
||||
@@ -6,9 +6,9 @@ Y luego también leíste cómo manejar [CORS con el `CORSMiddleware`](../tutoria
|
||||
|
||||
En esta sección veremos cómo usar otros middlewares.
|
||||
|
||||
## Agregando middlewares ASGI
|
||||
## Agregando middlewares ASGI { #adding-asgi-middlewares }
|
||||
|
||||
Como **FastAPI** está basado en Starlette e implementa la especificación <abbr title="Asynchronous Server Gateway Interface">ASGI</abbr>, puedes usar cualquier middleware ASGI.
|
||||
Como **FastAPI** está basado en Starlette e implementa la especificación <abbr title="Asynchronous Server Gateway Interface – Interfaz de puerta de enlace de servidor asíncrona">ASGI</abbr>, puedes usar cualquier middleware ASGI.
|
||||
|
||||
Un middleware no tiene que estar hecho para FastAPI o Starlette para funcionar, siempre que siga la especificación ASGI.
|
||||
|
||||
@@ -39,7 +39,7 @@ app.add_middleware(UnicornMiddleware, some_config="rainbow")
|
||||
|
||||
`app.add_middleware()` recibe una clase de middleware como primer argumento y cualquier argumento adicional que se le quiera pasar al middleware.
|
||||
|
||||
## Middlewares integrados
|
||||
## Middlewares integrados { #integrated-middlewares }
|
||||
|
||||
**FastAPI** incluye varios middlewares para casos de uso común, veremos a continuación cómo usarlos.
|
||||
|
||||
@@ -51,40 +51,41 @@ Para los próximos ejemplos, también podrías usar `from starlette.middleware.s
|
||||
|
||||
///
|
||||
|
||||
## `HTTPSRedirectMiddleware`
|
||||
## `HTTPSRedirectMiddleware` { #httpsredirectmiddleware }
|
||||
|
||||
Impone que todas las requests entrantes deben ser `https` o `wss`.
|
||||
|
||||
Cualquier request entrante a `http` o `ws` será redirigida al esquema seguro.
|
||||
|
||||
{* ../../docs_src/advanced_middleware/tutorial001.py hl[2,6] *}
|
||||
{* ../../docs_src/advanced_middleware/tutorial001_py39.py hl[2,6] *}
|
||||
|
||||
## `TrustedHostMiddleware`
|
||||
## `TrustedHostMiddleware` { #trustedhostmiddleware }
|
||||
|
||||
Impone que todas las requests entrantes tengan correctamente configurado el header `Host`, para proteger contra ataques de HTTP Host Header.
|
||||
|
||||
{* ../../docs_src/advanced_middleware/tutorial002.py hl[2,6:8] *}
|
||||
{* ../../docs_src/advanced_middleware/tutorial002_py39.py hl[2,6:8] *}
|
||||
|
||||
Se soportan los siguientes argumentos:
|
||||
|
||||
* `allowed_hosts` - Una list de nombres de dominio que deberían ser permitidos como nombres de host. Se soportan dominios comodín como `*.example.com` para hacer coincidir subdominios. Para permitir cualquier nombre de host, usa `allowed_hosts=["*"]` u omite el middleware.
|
||||
* `www_redirect` - Si se establece en True, las requests a versiones sin www de los hosts permitidos serán redirigidas a sus equivalentes con www. Por defecto es `True`.
|
||||
|
||||
Si una request entrante no se valida correctamente, se enviará un response `400`.
|
||||
|
||||
## `GZipMiddleware`
|
||||
## `GZipMiddleware` { #gzipmiddleware }
|
||||
|
||||
Maneja responses GZip para cualquier request que incluya `"gzip"` en el header `Accept-Encoding`.
|
||||
|
||||
El middleware manejará tanto responses estándar como en streaming.
|
||||
|
||||
{* ../../docs_src/advanced_middleware/tutorial003.py hl[2,6] *}
|
||||
{* ../../docs_src/advanced_middleware/tutorial003_py39.py hl[2,6] *}
|
||||
|
||||
Se soportan los siguientes argumentos:
|
||||
|
||||
* `minimum_size` - No comprimir con GZip responses que sean más pequeñas que este tamaño mínimo en bytes. Por defecto es `500`.
|
||||
* `compresslevel` - Usado durante la compresión GZip. Es un entero que varía de 1 a 9. Por defecto es `9`. Un valor más bajo resulta en una compresión más rápida pero archivos más grandes, mientras que un valor más alto resulta en una compresión más lenta pero archivos más pequeños.
|
||||
|
||||
## Otros middlewares
|
||||
## Otros middlewares { #other-middlewares }
|
||||
|
||||
Hay muchos otros middlewares ASGI.
|
||||
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
# OpenAPI Callbacks
|
||||
# Callbacks de OpenAPI { #openapi-callbacks }
|
||||
|
||||
Podrías crear una API con una *path operation* que podría desencadenar un request a una *API externa* creada por alguien más (probablemente el mismo desarrollador que estaría *usando* tu API).
|
||||
|
||||
El proceso que ocurre cuando tu aplicación API llama a la *API externa* se llama un "callback". Porque el software que escribió el desarrollador externo envía un request a tu API y luego tu API *responde*, enviando un request a una *API externa* (que probablemente fue creada por el mismo desarrollador).
|
||||
El proceso que ocurre cuando tu aplicación API llama a la *API externa* se llama un "callback". Porque el software que escribió el desarrollador externo envía un request a tu API y luego tu API hace un *callback*, enviando un request a una *API externa* (que probablemente fue creada por el mismo desarrollador).
|
||||
|
||||
En este caso, podrías querer documentar cómo esa API externa *debería* verse. Qué *path operation* debería tener, qué cuerpo debería esperar, qué response debería devolver, etc.
|
||||
|
||||
## Una aplicación con callbacks
|
||||
## Una aplicación con callbacks { #an-app-with-callbacks }
|
||||
|
||||
Veamos todo esto con un ejemplo.
|
||||
|
||||
Imagina que desarrollas una aplicación que permite crear facturas.
|
||||
|
||||
Estas facturas tendrán un `id`, `title` (opcional), `customer`, y `total`.
|
||||
Estas facturas tendrán un `id`, `title` (opcional), `customer` y `total`.
|
||||
|
||||
El usuario de tu API (un desarrollador externo) creará una factura en tu API con un request POST.
|
||||
|
||||
@@ -23,15 +23,15 @@ Luego tu API (imaginemos):
|
||||
* Enviará una notificación de vuelta al usuario de la API (el desarrollador externo).
|
||||
* Esto se hará enviando un request POST (desde *tu API*) a alguna *API externa* proporcionada por ese desarrollador externo (este es el "callback").
|
||||
|
||||
## La aplicación normal de **FastAPI**
|
||||
## La aplicación normal de **FastAPI** { #the-normal-fastapi-app }
|
||||
|
||||
Primero veamos cómo sería la aplicación API normal antes de agregar el callback.
|
||||
Primero veamos cómo se vería la aplicación API normal antes de agregar el callback.
|
||||
|
||||
Tendrá una *path operation* que recibirá un cuerpo `Invoice`, y un parámetro de query `callback_url` que contendrá la URL para el callback.
|
||||
|
||||
Esta parte es bastante normal, probablemente ya estés familiarizado con la mayor parte del código:
|
||||
|
||||
{* ../../docs_src/openapi_callbacks/tutorial001.py hl[9:13,36:53] *}
|
||||
{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[7:11,34:51] *}
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
@@ -39,9 +39,9 @@ El parámetro de query `callback_url` utiliza un tipo <a href="https://docs.pyda
|
||||
|
||||
///
|
||||
|
||||
Lo único nuevo es el `callbacks=invoices_callback_router.routes` como un argumento para el *decorador de path operation*. Veremos qué es eso a continuación.
|
||||
Lo único nuevo es `callbacks=invoices_callback_router.routes` como un argumento para el *decorador de path operation*. Veremos qué es eso a continuación.
|
||||
|
||||
## Documentar el callback
|
||||
## Documentar el callback { #documenting-the-callback }
|
||||
|
||||
El código real del callback dependerá mucho de tu propia aplicación API.
|
||||
|
||||
@@ -56,7 +56,7 @@ httpx.post(callback_url, json={"description": "Invoice paid", "paid": True})
|
||||
|
||||
Pero posiblemente la parte más importante del callback es asegurarse de que el usuario de tu API (el desarrollador externo) implemente la *API externa* correctamente, de acuerdo con los datos que *tu API* va a enviar en el request body del callback, etc.
|
||||
|
||||
Entonces, lo que haremos a continuación es agregar el código para documentar cómo debería verse esa *API externa* para recibir el callback de *tu API*.
|
||||
Así que, lo que haremos a continuación es agregar el código para documentar cómo debería verse esa *API externa* para recibir el callback de *tu API*.
|
||||
|
||||
Esa documentación aparecerá en la Swagger UI en `/docs` en tu API, y permitirá a los desarrolladores externos saber cómo construir la *API externa*.
|
||||
|
||||
@@ -70,11 +70,11 @@ Cuando implementes el callback tú mismo, podrías usar algo como <a href="https
|
||||
|
||||
///
|
||||
|
||||
## Escribir el código de documentación del callback
|
||||
## Escribe el código de documentación del callback { #write-the-callback-documentation-code }
|
||||
|
||||
Este código no se ejecutará en tu aplicación, solo lo necesitamos para *documentar* cómo debería verse esa *API externa*.
|
||||
|
||||
Pero, ya sabes cómo crear fácilmente documentación automática para una API con **FastAPI**.
|
||||
Pero ya sabes cómo crear fácilmente documentación automática para una API con **FastAPI**.
|
||||
|
||||
Así que vamos a usar ese mismo conocimiento para documentar cómo debería verse la *API externa*... creando la(s) *path operation(s)* que la API externa debería implementar (las que tu API va a llamar).
|
||||
|
||||
@@ -86,29 +86,29 @@ Adoptar temporalmente este punto de vista (del *desarrollador externo*) puede ay
|
||||
|
||||
///
|
||||
|
||||
### Crear un `APIRouter` de callback
|
||||
### Crea un `APIRouter` de callback { #create-a-callback-apirouter }
|
||||
|
||||
Primero crea un nuevo `APIRouter` que contendrá uno o más callbacks.
|
||||
|
||||
{* ../../docs_src/openapi_callbacks/tutorial001.py hl[3,25] *}
|
||||
{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[1,23] *}
|
||||
|
||||
### Crear la *path operation* del callback
|
||||
### Crea la *path operation* del callback { #create-the-callback-path-operation }
|
||||
|
||||
Para crear la *path operation* del callback utiliza el mismo `APIRouter` que creaste anteriormente.
|
||||
Para crear la *path operation* del callback usa el mismo `APIRouter` que creaste arriba.
|
||||
|
||||
Debería verse como una *path operation* normal de FastAPI:
|
||||
|
||||
* Probablemente debería tener una declaración del body que debería recibir, por ejemplo `body: InvoiceEvent`.
|
||||
* Y también podría tener una declaración del response que debería devolver, por ejemplo `response_model=InvoiceEventReceived`.
|
||||
|
||||
{* ../../docs_src/openapi_callbacks/tutorial001.py hl[16:18,21:22,28:32] *}
|
||||
{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[14:16,19:20,26:30] *}
|
||||
|
||||
Hay 2 diferencias principales respecto a una *path operation* normal:
|
||||
|
||||
* No necesita tener ningún código real, porque tu aplicación nunca llamará a este código. Solo se usa para documentar la *API externa*. Así que, la función podría simplemente tener `pass`.
|
||||
* El *path* puede contener una <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#key-expression" class="external-link" target="_blank">expresión OpenAPI 3</a> (ver más abajo) donde puede usar variables con parámetros y partes del request original enviado a *tu API*.
|
||||
|
||||
### La expresión del path del callback
|
||||
### La expresión del path del callback { #the-callback-path-expression }
|
||||
|
||||
El *path* del callback puede tener una <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#key-expression" class="external-link" target="_blank">expresión OpenAPI 3</a> que puede contener partes del request original enviado a *tu API*.
|
||||
|
||||
@@ -134,7 +134,7 @@ con un JSON body de:
|
||||
}
|
||||
```
|
||||
|
||||
luego *tu API* procesará la factura, y en algún momento después, enviará un request de callback al `callback_url` (la *API externa*):
|
||||
luego *tu API* procesará la factura y, en algún momento después, enviará un request de callback al `callback_url` (la *API externa*):
|
||||
|
||||
```
|
||||
https://www.external.org/events/invoices/2expen51ve
|
||||
@@ -163,13 +163,13 @@ Observa cómo la URL del callback utilizada contiene la URL recibida como parám
|
||||
|
||||
///
|
||||
|
||||
### Agregar el router de callback
|
||||
### Agrega el router de callback { #add-the-callback-router }
|
||||
|
||||
En este punto tienes las *path operation(s)* del callback necesarias (las que el *desarrollador externo* debería implementar en la *API externa*) en el router de callback que creaste antes.
|
||||
En este punto tienes las *path operation(s)* del callback necesarias (las que el *desarrollador externo* debería implementar en la *API externa*) en el router de callback que creaste arriba.
|
||||
|
||||
Ahora usa el parámetro `callbacks` en el *decorador de path operation de tu API* para pasar el atributo `.routes` (que en realidad es solo un `list` de rutas/*path operations*) de ese router de callback:
|
||||
|
||||
{* ../../docs_src/openapi_callbacks/tutorial001.py hl[35] *}
|
||||
{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[33] *}
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
@@ -177,7 +177,7 @@ Observa que no estás pasando el router en sí (`invoices_callback_router`) a `c
|
||||
|
||||
///
|
||||
|
||||
### Revisa la documentación
|
||||
### Revisa la documentación { #check-the-docs }
|
||||
|
||||
Ahora puedes iniciar tu aplicación e ir a <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Webhooks de OpenAPI
|
||||
# Webhooks de OpenAPI { #openapi-webhooks }
|
||||
|
||||
Hay casos donde quieres decirle a los **usuarios** de tu API que tu aplicación podría llamar a *su* aplicación (enviando una request) con algunos datos, normalmente para **notificar** de algún tipo de **evento**.
|
||||
|
||||
@@ -6,7 +6,7 @@ Esto significa que en lugar del proceso normal de tus usuarios enviando requests
|
||||
|
||||
Esto normalmente se llama un **webhook**.
|
||||
|
||||
## Pasos de los webhooks
|
||||
## Pasos de los webhooks { #webhooks-steps }
|
||||
|
||||
El proceso normalmente es que **tú defines** en tu código cuál es el mensaje que enviarás, el **body de la request**.
|
||||
|
||||
@@ -16,7 +16,7 @@ Y **tus usuarios** definen de alguna manera (por ejemplo en un panel web en alg
|
||||
|
||||
Toda la **lógica** sobre cómo registrar los URLs para webhooks y el código para realmente enviar esas requests depende de ti. Lo escribes como quieras en **tu propio código**.
|
||||
|
||||
## Documentando webhooks con **FastAPI** y OpenAPI
|
||||
## Documentando webhooks con **FastAPI** y OpenAPI { #documenting-webhooks-with-fastapi-and-openapi }
|
||||
|
||||
Con **FastAPI**, usando OpenAPI, puedes definir los nombres de estos webhooks, los tipos de operaciones HTTP que tu aplicación puede enviar (por ejemplo, `POST`, `PUT`, etc.) y los **bodies** de las requests que tu aplicación enviaría.
|
||||
|
||||
@@ -28,11 +28,11 @@ Los webhooks están disponibles en OpenAPI 3.1.0 y superiores, soportados por Fa
|
||||
|
||||
///
|
||||
|
||||
## Una aplicación con webhooks
|
||||
## Una aplicación con webhooks { #an-app-with-webhooks }
|
||||
|
||||
Cuando creas una aplicación de **FastAPI**, hay un atributo `webhooks` que puedes usar para definir *webhooks*, de la misma manera que definirías *path operations*, por ejemplo con `@app.webhooks.post()`.
|
||||
|
||||
{* ../../docs_src/openapi_webhooks/tutorial001.py hl[9:13,36:53] *}
|
||||
{* ../../docs_src/openapi_webhooks/tutorial001_py39.py hl[9:13,36:53] *}
|
||||
|
||||
Los webhooks que defines terminarán en el esquema de **OpenAPI** y en la interfaz automática de **documentación**.
|
||||
|
||||
@@ -46,7 +46,7 @@ Nota que con los webhooks en realidad no estás declarando un *path* (como `/ite
|
||||
|
||||
Esto es porque se espera que **tus usuarios** definan el actual **URL path** donde quieren recibir la request del webhook de alguna otra manera (por ejemplo, un panel web).
|
||||
|
||||
### Revisa la documentación
|
||||
### Revisa la documentación { #check-the-docs }
|
||||
|
||||
Ahora puedes iniciar tu app e ir a <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Configuración Avanzada de Path Operation
|
||||
# Configuración Avanzada de Path Operation { #path-operation-advanced-configuration }
|
||||
|
||||
## operationId de OpenAPI
|
||||
## operationId de OpenAPI { #openapi-operationid }
|
||||
|
||||
/// warning | Advertencia
|
||||
|
||||
@@ -10,17 +10,17 @@ Si no eres un "experto" en OpenAPI, probablemente no necesites esto.
|
||||
|
||||
Puedes establecer el `operationId` de OpenAPI para ser usado en tu *path operation* con el parámetro `operation_id`.
|
||||
|
||||
Tienes que asegurarte de que sea único para cada operación.
|
||||
Tendrías que asegurarte de que sea único para cada operación.
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial001.py hl[6] *}
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial001_py39.py hl[6] *}
|
||||
|
||||
### Usar el nombre de la *función de path operation* como el operationId
|
||||
### Usar el nombre de la *path operation function* como el operationId { #using-the-path-operation-function-name-as-the-operationid }
|
||||
|
||||
Si quieres usar los nombres de las funciones de tus APIs como `operationId`s, puedes iterar sobre todas ellas y sobrescribir el `operation_id` de cada *path operation* usando su `APIRoute.name`.
|
||||
|
||||
Deberías hacerlo después de agregar todas tus *path operations*.
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial002.py hl[2, 12:21, 24] *}
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial002_py39.py hl[2, 12:21, 24] *}
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
@@ -30,29 +30,29 @@ Si llamas manualmente a `app.openapi()`, deberías actualizar los `operationId`s
|
||||
|
||||
/// warning | Advertencia
|
||||
|
||||
Si haces esto, tienes que asegurarte de que cada una de tus *funciones de path operation* tenga un nombre único.
|
||||
Si haces esto, tienes que asegurarte de que cada una de tus *path operation functions* tenga un nombre único.
|
||||
|
||||
Incluso si están en diferentes módulos (archivos de Python).
|
||||
|
||||
///
|
||||
|
||||
## Excluir de OpenAPI
|
||||
## Excluir de OpenAPI { #exclude-from-openapi }
|
||||
|
||||
Para excluir una *path operation* del esquema OpenAPI generado (y por lo tanto, de los sistemas de documentación automática), utiliza el parámetro `include_in_schema` y configúralo en `False`:
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial003.py hl[6] *}
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial003_py39.py hl[6] *}
|
||||
|
||||
## Descripción avanzada desde el docstring
|
||||
## Descripción avanzada desde el docstring { #advanced-description-from-docstring }
|
||||
|
||||
Puedes limitar las líneas usadas del docstring de una *función de path operation* para OpenAPI.
|
||||
Puedes limitar las líneas usadas del docstring de una *path operation function* para OpenAPI.
|
||||
|
||||
Añadir un `\f` (un carácter de separación de página escapado) hace que **FastAPI** trunque la salida usada para OpenAPI en este punto.
|
||||
Añadir un `\f` (un carácter "form feed" escapado) hace que **FastAPI** trunque la salida usada para OpenAPI en este punto.
|
||||
|
||||
No aparecerá en la documentación, pero otras herramientas (como Sphinx) podrán usar el resto.
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial004.py hl[19:29] *}
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial004_py310.py hl[17:27] *}
|
||||
|
||||
## Responses Adicionales
|
||||
## Responses Adicionales { #additional-responses }
|
||||
|
||||
Probablemente has visto cómo declarar el `response_model` y el `status_code` para una *path operation*.
|
||||
|
||||
@@ -62,11 +62,11 @@ También puedes declarar responses adicionales con sus modelos, códigos de esta
|
||||
|
||||
Hay un capítulo entero en la documentación sobre ello, puedes leerlo en [Responses Adicionales en OpenAPI](additional-responses.md){.internal-link target=_blank}.
|
||||
|
||||
## OpenAPI Extra
|
||||
## OpenAPI Extra { #openapi-extra }
|
||||
|
||||
Cuando declaras una *path operation* en tu aplicación, **FastAPI** genera automáticamente los metadatos relevantes sobre esa *path operation* para incluirlos en el esquema de OpenAPI.
|
||||
|
||||
/// note | Nota
|
||||
/// note | Detalles técnicos
|
||||
|
||||
En la especificación de OpenAPI se llama el <a href="https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#operation-object" class="external-link" target="_blank">Objeto de Operación</a>.
|
||||
|
||||
@@ -88,11 +88,11 @@ Si solo necesitas declarar responses adicionales, una forma más conveniente de
|
||||
|
||||
Puedes extender el esquema de OpenAPI para una *path operation* usando el parámetro `openapi_extra`.
|
||||
|
||||
### Extensiones de OpenAPI
|
||||
### Extensiones de OpenAPI { #openapi-extensions }
|
||||
|
||||
Este `openapi_extra` puede ser útil, por ejemplo, para declarar [Extensiones de OpenAPI](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#specificationExtensions):
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial005.py hl[6] *}
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial005_py39.py hl[6] *}
|
||||
|
||||
Si abres la documentación automática de la API, tu extensión aparecerá en la parte inferior de la *path operation* específica.
|
||||
|
||||
@@ -129,7 +129,7 @@ Y si ves el OpenAPI resultante (en `/openapi.json` en tu API), verás tu extensi
|
||||
}
|
||||
```
|
||||
|
||||
### Esquema de *path operation* personalizada de OpenAPI
|
||||
### Esquema de *path operation* personalizada de OpenAPI { #custom-openapi-path-operation-schema }
|
||||
|
||||
El diccionario en `openapi_extra` se combinará profundamente con el esquema de OpenAPI generado automáticamente para la *path operation*.
|
||||
|
||||
@@ -139,61 +139,29 @@ Por ejemplo, podrías decidir leer y validar el request con tu propio código, s
|
||||
|
||||
Podrías hacer eso con `openapi_extra`:
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial006.py hl[19:36, 39:40] *}
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial006_py39.py hl[19:36, 39:40] *}
|
||||
|
||||
En este ejemplo, no declaramos ningún modelo Pydantic. De hecho, el cuerpo del request ni siquiera se <abbr title="converted from some plain format, like bytes, into Python objects">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 se <abbr title="converted from some plain format, like bytes, into Python objects - convertido de algún formato plano, como bytes, a objetos de Python">parse</abbr> como JSON, se lee directamente como `bytes`, y la función `magic_data_reader()` sería la encargada de parsearlo de alguna manera.
|
||||
|
||||
Sin embargo, podemos declarar el esquema esperado para el cuerpo del request.
|
||||
Sin embargo, podemos declarar el esquema esperado para el request body.
|
||||
|
||||
### Tipo de contenido personalizado de OpenAPI
|
||||
### Tipo de contenido personalizado de OpenAPI { #custom-openapi-content-type }
|
||||
|
||||
Usando este mismo truco, podrías usar un modelo Pydantic para definir el esquema JSON que luego se incluye en la sección personalizada del esquema OpenAPI para la *path operation*.
|
||||
Usando este mismo truco, podrías usar un modelo Pydantic para definir el JSON Schema que luego se incluye en la sección personalizada del esquema OpenAPI para la *path operation*.
|
||||
|
||||
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 esquema JSON 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:
|
||||
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:
|
||||
|
||||
//// tab | Pydantic v2
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial007_py39.py hl[15:20, 22] *}
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial007.py hl[17:22, 24] *}
|
||||
|
||||
////
|
||||
|
||||
//// tab | Pydantic v1
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial007_pv1.py hl[17:22, 24] *}
|
||||
|
||||
////
|
||||
|
||||
/// info | Información
|
||||
|
||||
En la versión 1 de Pydantic el método para obtener el esquema JSON para un modelo se llamaba `Item.schema()`, en la versión 2 de Pydantic, el método se llama `Item.model_json_schema()`.
|
||||
|
||||
///
|
||||
|
||||
Sin embargo, aunque no estamos usando la funcionalidad integrada por defecto, aún estamos usando un modelo Pydantic para generar manualmente el esquema JSON para los datos que queremos recibir en YAML.
|
||||
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.
|
||||
|
||||
Luego usamos el request directamente, y extraemos el cuerpo como `bytes`. Esto significa que FastAPI ni siquiera intentará parsear la carga útil del request como JSON.
|
||||
|
||||
Y luego en nuestro código, parseamos ese contenido YAML directamente, y nuevamente estamos usando el mismo modelo Pydantic para validar el contenido YAML:
|
||||
|
||||
//// tab | Pydantic v2
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial007.py hl[26:33] *}
|
||||
|
||||
////
|
||||
|
||||
//// tab | Pydantic v1
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial007_pv1.py hl[26:33] *}
|
||||
|
||||
////
|
||||
|
||||
/// info | Información
|
||||
|
||||
En la versión 1 de Pydantic el método para parsear y validar un objeto era `Item.parse_obj()`, en la versión 2 de Pydantic, el método se llama `Item.model_validate()`.
|
||||
|
||||
///
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial007_py39.py hl[24:31] *}
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# Response - Cambiar Código de Estado
|
||||
# Response - Cambiar Código de Estado { #response-change-status-code }
|
||||
|
||||
Probablemente leíste antes que puedes establecer un [Código de Estado de Response](../tutorial/response-status-code.md){.internal-link target=_blank} por defecto.
|
||||
|
||||
Pero en algunos casos necesitas devolver un código de estado diferente al predeterminado.
|
||||
|
||||
## Caso de uso
|
||||
## Caso de uso { #use-case }
|
||||
|
||||
Por ejemplo, imagina que quieres devolver un código de estado HTTP de "OK" `200` por defecto.
|
||||
|
||||
@@ -14,13 +14,13 @@ Pero todavía quieres poder filtrar y convertir los datos que devuelves con un `
|
||||
|
||||
Para esos casos, puedes usar un parámetro `Response`.
|
||||
|
||||
## Usa un parámetro `Response`
|
||||
## Usa un parámetro `Response` { #use-a-response-parameter }
|
||||
|
||||
Puedes declarar un parámetro de tipo `Response` en tu *función de path operation* (como puedes hacer para cookies y headers).
|
||||
Puedes declarar un parámetro de tipo `Response` en tu *path operation function* (como puedes hacer para cookies y headers).
|
||||
|
||||
Y luego puedes establecer el `status_code` en ese objeto de response *temporal*.
|
||||
|
||||
{* ../../docs_src/response_change_status_code/tutorial001.py hl[1,9,12] *}
|
||||
{* ../../docs_src/response_change_status_code/tutorial001_py39.py hl[1,9,12] *}
|
||||
|
||||
Y luego puedes devolver cualquier objeto que necesites, como lo harías normalmente (un `dict`, un modelo de base de datos, etc.).
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# Cookies de Response
|
||||
# Cookies de Response { #response-cookies }
|
||||
|
||||
## Usar un parámetro `Response`
|
||||
## Usar un parámetro `Response` { #use-a-response-parameter }
|
||||
|
||||
Puedes declarar un parámetro de tipo `Response` en tu *path operation function*.
|
||||
|
||||
Y luego puedes establecer cookies en ese objeto de response *temporal*.
|
||||
|
||||
{* ../../docs_src/response_cookies/tutorial002.py hl[1, 8:9] *}
|
||||
{* ../../docs_src/response_cookies/tutorial002_py39.py hl[1, 8:9] *}
|
||||
|
||||
Y entonces puedes devolver cualquier objeto que necesites, como normalmente lo harías (un `dict`, un modelo de base de datos, etc).
|
||||
|
||||
@@ -16,7 +16,7 @@ Y si declaraste un `response_model`, todavía se utilizará para filtrar y conve
|
||||
|
||||
También puedes declarar el parámetro `Response` en las dependencias, y establecer cookies (y headers) en ellas.
|
||||
|
||||
## Devolver una `Response` directamente
|
||||
## Devolver una `Response` directamente { #return-a-response-directly }
|
||||
|
||||
También puedes crear cookies al devolver una `Response` directamente en tu código.
|
||||
|
||||
@@ -24,7 +24,7 @@ Para hacer eso, puedes crear un response como se describe en [Devolver un Respon
|
||||
|
||||
Luego establece Cookies en ella, y luego devuélvela:
|
||||
|
||||
{* ../../docs_src/response_cookies/tutorial001.py hl[10:12] *}
|
||||
{* ../../docs_src/response_cookies/tutorial001_py39.py hl[10:12] *}
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
@@ -36,7 +36,7 @@ Y también que no estés enviando ningún dato que debería haber sido filtrado
|
||||
|
||||
///
|
||||
|
||||
### Más información
|
||||
### Más información { #more-info }
|
||||
|
||||
/// note | Detalles Técnicos
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Devolver una Response Directamente
|
||||
# Devolver una Response Directamente { #return-a-response-directly }
|
||||
|
||||
Cuando creas una *path operation* en **FastAPI**, normalmente puedes devolver cualquier dato desde ella: un `dict`, una `list`, un modelo de Pydantic, un modelo de base de datos, etc.
|
||||
|
||||
@@ -10,7 +10,7 @@ Pero puedes devolver un `JSONResponse` directamente desde tus *path operations*.
|
||||
|
||||
Esto podría ser útil, por ejemplo, para devolver headers o cookies personalizados.
|
||||
|
||||
## Devolver una `Response`
|
||||
## Devolver una `Response` { #return-a-response }
|
||||
|
||||
De hecho, puedes devolver cualquier `Response` o cualquier subclase de ella.
|
||||
|
||||
@@ -26,7 +26,7 @@ No hará ninguna conversión de datos con los modelos de Pydantic, no convertir
|
||||
|
||||
Esto te da mucha flexibilidad. Puedes devolver cualquier tipo de datos, sobrescribir cualquier declaración o validación de datos, etc.
|
||||
|
||||
## Usar el `jsonable_encoder` en una `Response`
|
||||
## Usar el `jsonable_encoder` en una `Response` { #using-the-jsonable-encoder-in-a-response }
|
||||
|
||||
Como **FastAPI** no realiza cambios en una `Response` que devuelves, tienes que asegurarte de que sus contenidos estén listos para ello.
|
||||
|
||||
@@ -34,9 +34,9 @@ Por ejemplo, no puedes poner un modelo de Pydantic en un `JSONResponse` sin prim
|
||||
|
||||
Para esos casos, puedes usar el `jsonable_encoder` para convertir tus datos antes de pasarlos a un response:
|
||||
|
||||
{* ../../docs_src/response_directly/tutorial001.py hl[6:7,21:22] *}
|
||||
{* ../../docs_src/response_directly/tutorial001_py310.py hl[5:6,20:21] *}
|
||||
|
||||
/// note | Nota
|
||||
/// note | Detalles técnicos
|
||||
|
||||
También podrías usar `from starlette.responses import JSONResponse`.
|
||||
|
||||
@@ -44,7 +44,7 @@ También podrías usar `from starlette.responses import JSONResponse`.
|
||||
|
||||
///
|
||||
|
||||
## Devolver una `Response` personalizada
|
||||
## Devolver una `Response` personalizada { #returning-a-custom-response }
|
||||
|
||||
El ejemplo anterior muestra todas las partes que necesitas, pero aún no es muy útil, ya que podrías haber devuelto el `item` directamente, y **FastAPI** lo colocaría en un `JSONResponse` por ti, convirtiéndolo a un `dict`, etc. Todo eso por defecto.
|
||||
|
||||
@@ -54,9 +54,9 @@ Digamos que quieres devolver un response en <a href="https://en.wikipedia.org/wi
|
||||
|
||||
Podrías poner tu contenido XML en un string, poner eso en un `Response`, y devolverlo:
|
||||
|
||||
{* ../../docs_src/response_directly/tutorial002.py hl[1,18] *}
|
||||
{* ../../docs_src/response_directly/tutorial002_py39.py hl[1,18] *}
|
||||
|
||||
## Notas
|
||||
## Notas { #notes }
|
||||
|
||||
Cuando devuelves una `Response` directamente, sus datos no son validados, convertidos (serializados), ni documentados automáticamente.
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# Response Headers
|
||||
# Headers de Response { #response-headers }
|
||||
|
||||
## Usa un parámetro `Response`
|
||||
## Usa un parámetro `Response` { #use-a-response-parameter }
|
||||
|
||||
Puedes declarar un parámetro de tipo `Response` en tu *función de path operation* (como puedes hacer para cookies).
|
||||
Puedes declarar un parámetro de tipo `Response` en tu *path operation function* (como puedes hacer para cookies).
|
||||
|
||||
Y luego puedes establecer headers en ese objeto de response *temporal*.
|
||||
|
||||
{* ../../docs_src/response_headers/tutorial002.py hl[1, 7:8] *}
|
||||
{* ../../docs_src/response_headers/tutorial002_py39.py hl[1, 7:8] *}
|
||||
|
||||
Y luego puedes devolver cualquier objeto que necesites, como harías normalmente (un `dict`, un modelo de base de datos, etc).
|
||||
|
||||
@@ -16,13 +16,13 @@ Y si declaraste un `response_model`, aún se usará para filtrar y convertir el
|
||||
|
||||
También puedes declarar el parámetro `Response` en dependencias y establecer headers (y cookies) en ellas.
|
||||
|
||||
## Retorna una `Response` directamente
|
||||
## Retorna una `Response` directamente { #return-a-response-directly }
|
||||
|
||||
También puedes agregar headers cuando devuelves un `Response` directamente.
|
||||
|
||||
Crea un response como se describe en [Retorna un Response Directamente](response-directly.md){.internal-link target=_blank} y pasa los headers como un parámetro adicional:
|
||||
|
||||
{* ../../docs_src/response_headers/tutorial001.py hl[10:12] *}
|
||||
{* ../../docs_src/response_headers/tutorial001_py39.py hl[10:12] *}
|
||||
|
||||
/// note | Detalles Técnicos
|
||||
|
||||
@@ -34,8 +34,8 @@ Y como el `Response` se puede usar frecuentemente para establecer headers y cook
|
||||
|
||||
///
|
||||
|
||||
## Headers Personalizados
|
||||
## Headers Personalizados { #custom-headers }
|
||||
|
||||
Ten en cuenta que los headers propietarios personalizados se pueden agregar <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers" class="external-link" target="_blank">usando el prefijo 'X-'</a>.
|
||||
Ten en cuenta que los headers propietarios personalizados se pueden agregar <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers" class="external-link" target="_blank">usando el prefijo `X-`</a>.
|
||||
|
||||
Pero si tienes headers personalizados que quieres que un cliente en un navegador pueda ver, necesitas agregarlos a tus configuraciones de CORS (leer más en [CORS (Cross-Origin Resource Sharing)](../tutorial/cors.md){.internal-link target=_blank}), usando el parámetro `expose_headers` documentado en <a href="https://www.starlette.dev/middleware/#corsmiddleware" class="external-link" target="_blank">la documentación CORS de Starlette</a>.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# HTTP Basic Auth
|
||||
# HTTP Basic Auth { #http-basic-auth }
|
||||
|
||||
Para los casos más simples, puedes usar HTTP Basic Auth.
|
||||
|
||||
@@ -12,7 +12,7 @@ Eso le dice al navegador que muestre el prompt integrado para un nombre de usuar
|
||||
|
||||
Luego, cuando escribes ese nombre de usuario y contraseña, el navegador los envía automáticamente en el header.
|
||||
|
||||
## Simple HTTP Basic Auth
|
||||
## Simple HTTP Basic Auth { #simple-http-basic-auth }
|
||||
|
||||
* Importa `HTTPBasic` y `HTTPBasicCredentials`.
|
||||
* Crea un "esquema de `security`" usando `HTTPBasic`.
|
||||
@@ -26,7 +26,7 @@ Cuando intentas abrir la URL por primera vez (o haces clic en el botón "Execute
|
||||
|
||||
<img src="/img/tutorial/security/image12.png">
|
||||
|
||||
## Revisa el nombre de usuario
|
||||
## Revisa el nombre de usuario { #check-the-username }
|
||||
|
||||
Aquí hay un ejemplo más completo.
|
||||
|
||||
@@ -46,13 +46,13 @@ Esto sería similar a:
|
||||
|
||||
```Python
|
||||
if not (credentials.username == "stanleyjobson") or not (credentials.password == "swordfish"):
|
||||
# Return some error
|
||||
# Devuelve algún error
|
||||
...
|
||||
```
|
||||
|
||||
Pero al usar `secrets.compare_digest()` será seguro contra un tipo de ataques llamados "timing attacks".
|
||||
|
||||
### Timing Attacks
|
||||
### Timing attacks { #timing-attacks }
|
||||
|
||||
¿Pero qué es un "timing attack"?
|
||||
|
||||
@@ -80,19 +80,19 @@ if "stanleyjobsox" == "stanleyjobson" and "love123" == "swordfish":
|
||||
|
||||
Python tendrá que comparar todo `stanleyjobso` en ambos `stanleyjobsox` y `stanleyjobson` antes de darse cuenta de que ambas strings no son las mismas. Así que tomará algunos microsegundos extra para responder "Nombre de usuario o contraseña incorrectos".
|
||||
|
||||
#### El tiempo de respuesta ayuda a los atacantes
|
||||
#### El tiempo de respuesta ayuda a los atacantes { #the-time-to-answer-helps-the-attackers }
|
||||
|
||||
En ese punto, al notar que el servidor tardó algunos microsegundos más en enviar el response "Nombre de usuario o contraseña incorrectos", los atacantes sabrán que acertaron en _algo_, algunas de las letras iniciales eran correctas.
|
||||
|
||||
Y luego pueden intentar de nuevo sabiendo que probablemente es algo más similar a `stanleyjobsox` que a `johndoe`.
|
||||
|
||||
#### Un ataque "profesional"
|
||||
#### Un ataque "profesional" { #a-professional-attack }
|
||||
|
||||
Por supuesto, los atacantes no intentarían todo esto a mano, escribirían un programa para hacerlo, posiblemente con miles o millones de pruebas por segundo. Y obtendrían solo una letra correcta adicional a la vez.
|
||||
|
||||
Pero haciendo eso, en algunos minutos u horas, los atacantes habrían adivinado el nombre de usuario y la contraseña correctos, con la "ayuda" de nuestra aplicación, solo usando el tiempo tomado para responder.
|
||||
|
||||
#### Arréglalo con `secrets.compare_digest()`
|
||||
#### Arréglalo con `secrets.compare_digest()` { #fix-it-with-secrets-compare-digest }
|
||||
|
||||
Pero en nuestro código estamos usando realmente `secrets.compare_digest()`.
|
||||
|
||||
@@ -100,7 +100,7 @@ En resumen, tomará el mismo tiempo comparar `stanleyjobsox` con `stanleyjobson`
|
||||
|
||||
De esa manera, usando `secrets.compare_digest()` en el código de tu aplicación, será seguro contra todo este rango de ataques de seguridad.
|
||||
|
||||
### Devuelve el error
|
||||
### Devuelve el error { #return-the-error }
|
||||
|
||||
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:
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Seguridad Avanzada
|
||||
# Seguridad Avanzada { #advanced-security }
|
||||
|
||||
## Funcionalidades Adicionales
|
||||
## Funcionalidades Adicionales { #additional-features }
|
||||
|
||||
Hay algunas funcionalidades extra para manejar la seguridad aparte de las cubiertas en el [Tutorial - Guía del Usuario: Seguridad](../../tutorial/security/index.md){.internal-link target=_blank}.
|
||||
|
||||
@@ -12,8 +12,8 @@ Y es posible que para tu caso de uso, la solución esté en una de ellas.
|
||||
|
||||
///
|
||||
|
||||
## Lee primero el Tutorial
|
||||
## Lee primero el Tutorial { #read-the-tutorial-first }
|
||||
|
||||
Las siguientes secciones asumen que ya leíste el [Tutorial - Guía del Usuario: Seguridad](../../tutorial/security/index.md){.internal-link target=_blank}.
|
||||
Las siguientes secciones asumen que ya leíste el [Tutorial - Guía del Usuario: Seguridad](../../tutorial/security/index.md){.internal-link target=_blank} principal.
|
||||
|
||||
Todas están basadas en los mismos conceptos, pero permiten algunas funcionalidades adicionales.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Scopes de OAuth2
|
||||
# Scopes de OAuth2 { #oauth2-scopes }
|
||||
|
||||
Puedes usar scopes de OAuth2 directamente con **FastAPI**, están integrados para funcionar de manera fluida.
|
||||
|
||||
@@ -26,7 +26,7 @@ Pero si sabes que lo necesitas, o tienes curiosidad, sigue leyendo.
|
||||
|
||||
///
|
||||
|
||||
## Scopes de OAuth2 y OpenAPI
|
||||
## Scopes de OAuth2 y OpenAPI { #oauth2-scopes-and-openapi }
|
||||
|
||||
La especificación de OAuth2 define "scopes" como una lista de strings separados por espacios.
|
||||
|
||||
@@ -58,15 +58,15 @@ Para OAuth2 son solo strings.
|
||||
|
||||
///
|
||||
|
||||
## Vista global
|
||||
## Vista global { #global-view }
|
||||
|
||||
Primero, echemos un vistazo rápido a las partes que cambian desde los ejemplos en el **Tutorial - User Guide** principal para [OAuth2 con Password (y hashing), Bearer con tokens JWT](../../tutorial/security/oauth2-jwt.md){.internal-link target=_blank}. Ahora usando scopes de OAuth2:
|
||||
|
||||
{* ../../docs_src/security/tutorial005_an_py310.py hl[5,9,13,47,65,106,108:116,122:125,129:135,140,156] *}
|
||||
{* ../../docs_src/security/tutorial005_an_py310.py hl[5,9,13,47,65,106,108:116,122:126,130:136,141,157] *}
|
||||
|
||||
Ahora revisemos esos cambios paso a paso.
|
||||
|
||||
## Esquema de seguridad OAuth2
|
||||
## Esquema de seguridad OAuth2 { #oauth2-security-scheme }
|
||||
|
||||
El primer cambio es que ahora estamos declarando el esquema de seguridad OAuth2 con dos scopes disponibles, `me` y `items`.
|
||||
|
||||
@@ -82,7 +82,7 @@ Este es el mismo mecanismo utilizado cuando das permisos al iniciar sesión con
|
||||
|
||||
<img src="/img/tutorial/security/image11.png">
|
||||
|
||||
## Token JWT con scopes
|
||||
## Token JWT con scopes { #jwt-token-with-scopes }
|
||||
|
||||
Ahora, modifica la *path operation* del token para devolver los scopes solicitados.
|
||||
|
||||
@@ -98,9 +98,9 @@ Pero en tu aplicación, por seguridad, deberías asegurarte de añadir solo los
|
||||
|
||||
///
|
||||
|
||||
{* ../../docs_src/security/tutorial005_an_py310.py hl[156] *}
|
||||
{* ../../docs_src/security/tutorial005_an_py310.py hl[157] *}
|
||||
|
||||
## Declarar scopes en *path operations* y dependencias
|
||||
## Declarar scopes en *path operations* y dependencias { #declare-scopes-in-path-operations-and-dependencies }
|
||||
|
||||
Ahora declaramos que la *path operation* para `/users/me/items/` requiere el scope `items`.
|
||||
|
||||
@@ -124,7 +124,7 @@ Lo estamos haciendo aquí para demostrar cómo **FastAPI** maneja scopes declara
|
||||
|
||||
///
|
||||
|
||||
{* ../../docs_src/security/tutorial005_an_py310.py hl[5,140,171] *}
|
||||
{* ../../docs_src/security/tutorial005_an_py310.py hl[5,141,172] *}
|
||||
|
||||
/// info | Información Técnica
|
||||
|
||||
@@ -136,7 +136,7 @@ Pero cuando importas `Query`, `Path`, `Depends`, `Security` y otros de `fastapi`
|
||||
|
||||
///
|
||||
|
||||
## Usar `SecurityScopes`
|
||||
## Usar `SecurityScopes` { #use-securityscopes }
|
||||
|
||||
Ahora actualiza la dependencia `get_current_user`.
|
||||
|
||||
@@ -152,7 +152,7 @@ Esta clase `SecurityScopes` es similar a `Request` (`Request` se usó para obten
|
||||
|
||||
{* ../../docs_src/security/tutorial005_an_py310.py hl[9,106] *}
|
||||
|
||||
## Usar los `scopes`
|
||||
## Usar los `scopes` { #use-the-scopes }
|
||||
|
||||
El parámetro `security_scopes` será del tipo `SecurityScopes`.
|
||||
|
||||
@@ -166,7 +166,7 @@ En esta excepción, incluimos los scopes requeridos (si los hay) como un string
|
||||
|
||||
{* ../../docs_src/security/tutorial005_an_py310.py hl[106,108:116] *}
|
||||
|
||||
## Verificar el `username` y la forma de los datos
|
||||
## Verificar el `username` y la forma de los datos { #verify-the-username-and-data-shape }
|
||||
|
||||
Verificamos que obtenemos un `username`, y extraemos los scopes.
|
||||
|
||||
@@ -180,17 +180,17 @@ En lugar de, por ejemplo, un `dict`, o algo más, ya que podría romper la aplic
|
||||
|
||||
También verificamos que tenemos un usuario con ese username, y si no, lanzamos esa misma excepción que creamos antes.
|
||||
|
||||
{* ../../docs_src/security/tutorial005_an_py310.py hl[47,117:128] *}
|
||||
{* ../../docs_src/security/tutorial005_an_py310.py hl[47,117:129] *}
|
||||
|
||||
## Verificar los `scopes`
|
||||
## Verificar los `scopes` { #verify-the-scopes }
|
||||
|
||||
Ahora verificamos que todos los scopes requeridos, por esta dependencia y todos los dependientes (incluyendo *path operations*), estén incluidos en los scopes proporcionados en el token recibido, de lo contrario, lanzamos una `HTTPException`.
|
||||
|
||||
Para esto, usamos `security_scopes.scopes`, que contiene una `list` con todos estos scopes como `str`.
|
||||
|
||||
{* ../../docs_src/security/tutorial005_an_py310.py hl[129:135] *}
|
||||
{* ../../docs_src/security/tutorial005_an_py310.py hl[130:136] *}
|
||||
|
||||
## Árbol de dependencias y scopes
|
||||
## Árbol de dependencias y scopes { #dependency-tree-and-scopes }
|
||||
|
||||
Revisemos de nuevo este árbol de dependencias y los scopes.
|
||||
|
||||
@@ -223,7 +223,7 @@ Todo depende de los `scopes` declarados en cada *path operation* y cada dependen
|
||||
|
||||
///
|
||||
|
||||
## Más detalles sobre `SecurityScopes`
|
||||
## Más detalles sobre `SecurityScopes` { #more-details-about-securityscopes }
|
||||
|
||||
Puedes usar `SecurityScopes` en cualquier punto, y en múltiples lugares, no tiene que ser en la dependencia "raíz".
|
||||
|
||||
@@ -233,7 +233,7 @@ Debido a que `SecurityScopes` tendrá todos los scopes declarados por dependient
|
||||
|
||||
Serán verificados independientemente para cada *path operation*.
|
||||
|
||||
## Revisa
|
||||
## Revisa { #check-it }
|
||||
|
||||
Si abres la documentación de la API, puedes autenticarte y especificar qué scopes deseas autorizar.
|
||||
|
||||
@@ -245,7 +245,7 @@ Y si seleccionas el scope `me` pero no el scope `items`, podrás acceder a `/use
|
||||
|
||||
Eso es lo que pasaría a una aplicación de terceros que intentara acceder a una de estas *path operations* con un token proporcionado por un usuario, dependiendo de cuántos permisos el usuario otorgó a la aplicación.
|
||||
|
||||
## Acerca de las integraciones de terceros
|
||||
## Acerca de las integraciones de terceros { #about-third-party-integrations }
|
||||
|
||||
En este ejemplo estamos usando el flujo de OAuth2 "password".
|
||||
|
||||
@@ -269,6 +269,6 @@ Pero al final, están implementando el mismo estándar OAuth2.
|
||||
|
||||
**FastAPI** incluye utilidades para todos estos flujos de autenticación OAuth2 en `fastapi.security.oauth2`.
|
||||
|
||||
## `Security` en `dependencies` del decorador
|
||||
## `Security` en `dependencies` del decorador { #security-in-decorator-dependencies }
|
||||
|
||||
De la misma manera que puedes definir una `list` de `Depends` en el parámetro `dependencies` del decorador (como se explica en [Dependencias en decoradores de path operation](../../tutorial/dependencies/dependencies-in-path-operation-decorators.md){.internal-link target=_blank}), también podrías usar `Security` con `scopes` allí.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Configuraciones y Variables de Entorno
|
||||
# Configuraciones y Variables de Entorno { #settings-and-environment-variables }
|
||||
|
||||
En muchos casos, tu aplicación podría necesitar algunas configuraciones o ajustes externos, por ejemplo, claves secretas, credenciales de base de datos, credenciales para servicios de correo electrónico, etc.
|
||||
|
||||
@@ -12,17 +12,17 @@ Para entender las variables de entorno, puedes leer [Variables de Entorno](../en
|
||||
|
||||
///
|
||||
|
||||
## Tipos y validación
|
||||
## Tipos y validación { #types-and-validation }
|
||||
|
||||
Estas variables de entorno solo pueden manejar strings de texto, ya que son externas a Python y tienen que ser compatibles con otros programas y el resto del sistema (e incluso con diferentes sistemas operativos, como Linux, Windows, macOS).
|
||||
|
||||
Eso significa que cualquier valor leído en Python desde una variable de entorno será un `str`, y cualquier conversión a un tipo diferente o cualquier validación tiene que hacerse en código.
|
||||
|
||||
## Pydantic `Settings`
|
||||
## Pydantic `Settings` { #pydantic-settings }
|
||||
|
||||
Afortunadamente, Pydantic proporciona una gran utilidad para manejar estas configuraciones provenientes de variables de entorno con <a href="https://docs.pydantic.dev/latest/concepts/pydantic_settings/" class="external-link" target="_blank">Pydantic: Settings management</a>.
|
||||
|
||||
### Instalar `pydantic-settings`
|
||||
### Instalar `pydantic-settings` { #install-pydantic-settings }
|
||||
|
||||
Primero, asegúrate de crear tu [entorno virtual](../virtual-environments.md){.internal-link target=_blank}, actívalo y luego instala el paquete `pydantic-settings`:
|
||||
|
||||
@@ -46,13 +46,7 @@ $ pip install "fastapi[all]"
|
||||
|
||||
</div>
|
||||
|
||||
/// info | Información
|
||||
|
||||
En Pydantic v1 venía incluido con el paquete principal. Ahora se distribuye como este paquete independiente para que puedas elegir si instalarlo o no si no necesitas esa funcionalidad.
|
||||
|
||||
///
|
||||
|
||||
### Crear el objeto `Settings`
|
||||
### Crear el objeto `Settings` { #create-the-settings-object }
|
||||
|
||||
Importa `BaseSettings` de Pydantic y crea una sub-clase, muy similar a un modelo de Pydantic.
|
||||
|
||||
@@ -60,23 +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()`.
|
||||
|
||||
//// tab | Pydantic v2
|
||||
|
||||
{* ../../docs_src/settings/tutorial001.py hl[2,5:8,11] *}
|
||||
|
||||
////
|
||||
|
||||
//// tab | Pydantic v1
|
||||
|
||||
/// info | Información
|
||||
|
||||
En Pydantic v1 importarías `BaseSettings` directamente desde `pydantic` en lugar de desde `pydantic_settings`.
|
||||
|
||||
///
|
||||
|
||||
{* ../../docs_src/settings/tutorial001_pv1.py hl[2,5:8,11] *}
|
||||
|
||||
////
|
||||
{* ../../docs_src/settings/tutorial001_py39.py hl[2,5:8,11] *}
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
@@ -84,17 +62,17 @@ Si quieres algo rápido para copiar y pegar, no uses este ejemplo, usa el últim
|
||||
|
||||
///
|
||||
|
||||
Luego, cuando creas una instance de esa clase `Settings` (en este caso, en el objeto `settings`), Pydantic leerá las variables de entorno de una manera indiferente a mayúsculas y minúsculas, por lo que una variable en mayúsculas `APP_NAME` aún será leída para el atributo `app_name`.
|
||||
Luego, cuando creas un instance de esa clase `Settings` (en este caso, en el objeto `settings`), Pydantic leerá las variables de entorno de una manera indiferente a mayúsculas y minúsculas, por lo que una variable en mayúsculas `APP_NAME` aún será leída para el atributo `app_name`.
|
||||
|
||||
Luego convertirá y validará los datos. Así que, cuando uses ese objeto `settings`, tendrás datos de los tipos que declaraste (por ejemplo, `items_per_user` será un `int`).
|
||||
|
||||
### Usar el `settings`
|
||||
### Usar el `settings` { #use-the-settings }
|
||||
|
||||
Luego puedes usar el nuevo objeto `settings` en tu aplicación:
|
||||
|
||||
{* ../../docs_src/settings/tutorial001.py hl[18:20] *}
|
||||
{* ../../docs_src/settings/tutorial001_py39.py hl[18:20] *}
|
||||
|
||||
### Ejecutar el servidor
|
||||
### Ejecutar el servidor { #run-the-server }
|
||||
|
||||
Luego, ejecutarías el servidor pasando las configuraciones como variables de entorno, por ejemplo, podrías establecer un `ADMIN_EMAIL` y `APP_NAME` con:
|
||||
|
||||
@@ -110,7 +88,7 @@ $ ADMIN_EMAIL="deadpool@example.com" APP_NAME="ChimichangApp" fastapi run main.p
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
Para establecer múltiples variables de entorno para un solo comando, simplemente sepáralas con un espacio y ponlas todas antes del comando.
|
||||
Para establecer múltiples env vars para un solo comando, simplemente sepáralas con un espacio y ponlas todas antes del comando.
|
||||
|
||||
///
|
||||
|
||||
@@ -120,17 +98,17 @@ El `app_name` sería `"ChimichangApp"`.
|
||||
|
||||
Y el `items_per_user` mantendría su valor por defecto de `50`.
|
||||
|
||||
## Configuraciones en otro módulo
|
||||
## Configuraciones en otro módulo { #settings-in-another-module }
|
||||
|
||||
Podrías poner esas configuraciones en otro archivo de módulo como viste en [Aplicaciones Más Grandes - Múltiples Archivos](../tutorial/bigger-applications.md){.internal-link target=_blank}.
|
||||
|
||||
Por ejemplo, podrías tener un archivo `config.py` con:
|
||||
|
||||
{* ../../docs_src/settings/app01/config.py *}
|
||||
{* ../../docs_src/settings/app01_py39/config.py *}
|
||||
|
||||
Y luego usarlo en un archivo `main.py`:
|
||||
|
||||
{* ../../docs_src/settings/app01/main.py hl[3,11:13] *}
|
||||
{* ../../docs_src/settings/app01_py39/main.py hl[3,11:13] *}
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
@@ -138,21 +116,21 @@ También necesitarías un archivo `__init__.py` como viste en [Aplicaciones Más
|
||||
|
||||
///
|
||||
|
||||
## Configuraciones en una dependencia
|
||||
## Configuraciones en una dependencia { #settings-in-a-dependency }
|
||||
|
||||
En algunas ocasiones podría ser útil proporcionar las configuraciones desde una dependencia, en lugar de tener un objeto global con `settings` que se use en todas partes.
|
||||
|
||||
Esto podría ser especialmente útil durante las pruebas, ya que es muy fácil sobrescribir una dependencia con tus propias configuraciones personalizadas.
|
||||
|
||||
### El archivo de configuración
|
||||
### El archivo de configuración { #the-config-file }
|
||||
|
||||
Proveniente del ejemplo anterior, tu archivo `config.py` podría verse como:
|
||||
|
||||
{* ../../docs_src/settings/app02/config.py hl[10] *}
|
||||
{* ../../docs_src/settings/app02_an_py39/config.py hl[10] *}
|
||||
|
||||
Nota que ahora no creamos una instance por defecto `settings = Settings()`.
|
||||
Nota que ahora no creamos un instance por defecto `settings = Settings()`.
|
||||
|
||||
### El archivo principal de la app
|
||||
### El archivo principal de la app { #the-main-app-file }
|
||||
|
||||
Ahora creamos una dependencia que devuelve un nuevo `config.Settings()`.
|
||||
|
||||
@@ -170,17 +148,17 @@ Y luego podemos requerirlo desde la *path operation function* como una dependenc
|
||||
|
||||
{* ../../docs_src/settings/app02_an_py39/main.py hl[17,19:21] *}
|
||||
|
||||
### Configuraciones y pruebas
|
||||
### Configuraciones y pruebas { #settings-and-testing }
|
||||
|
||||
Luego sería muy fácil proporcionar un objeto de configuraciones diferente durante las pruebas al sobrescribir una dependencia para `get_settings`:
|
||||
Luego sería muy fácil proporcionar un objeto de configuraciones diferente durante las pruebas al crear una sobrescritura de dependencia para `get_settings`:
|
||||
|
||||
{* ../../docs_src/settings/app02/test_main.py hl[9:10,13,21] *}
|
||||
{* ../../docs_src/settings/app02_an_py39/test_main.py hl[9:10,13,21] *}
|
||||
|
||||
En la dependencia sobreescrita establecemos un nuevo valor para el `admin_email` al crear el nuevo objeto `Settings`, y luego devolvemos ese nuevo objeto.
|
||||
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.
|
||||
|
||||
Luego podemos probar que se está usando.
|
||||
|
||||
## Leer un archivo `.env`
|
||||
## Leer un archivo `.env` { #reading-a-env-file }
|
||||
|
||||
Si tienes muchas configuraciones que posiblemente cambien mucho, tal vez en diferentes entornos, podría ser útil ponerlos en un archivo y luego leerlos desde allí como si fueran variables de entorno.
|
||||
|
||||
@@ -202,7 +180,7 @@ Para que esto funcione, necesitas `pip install python-dotenv`.
|
||||
|
||||
///
|
||||
|
||||
### El archivo `.env`
|
||||
### El archivo `.env` { #the-env-file }
|
||||
|
||||
Podrías tener un archivo `.env` con:
|
||||
|
||||
@@ -211,13 +189,11 @@ ADMIN_EMAIL="deadpool@example.com"
|
||||
APP_NAME="ChimichangApp"
|
||||
```
|
||||
|
||||
### Leer configuraciones desde `.env`
|
||||
### Leer configuraciones desde `.env` { #read-settings-from-env }
|
||||
|
||||
Y luego actualizar tu `config.py` con:
|
||||
|
||||
//// tab | Pydantic v2
|
||||
|
||||
{* ../../docs_src/settings/app03_an/config.py hl[9] *}
|
||||
{* ../../docs_src/settings/app03_an_py39/config.py hl[9] *}
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
@@ -225,29 +201,9 @@ El atributo `model_config` se usa solo para configuración de Pydantic. Puedes l
|
||||
|
||||
///
|
||||
|
||||
////
|
||||
|
||||
//// tab | Pydantic v1
|
||||
|
||||
{* ../../docs_src/settings/app03_an/config_pv1.py hl[9:10] *}
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
La clase `Config` se usa solo para configuración de Pydantic. Puedes leer más en <a href="https://docs.pydantic.dev/1.10/usage/model_config/" class="external-link" target="_blank">Pydantic Model Config</a>.
|
||||
|
||||
///
|
||||
|
||||
////
|
||||
|
||||
/// info | Información
|
||||
|
||||
En la versión 1 de Pydantic la configuración se hacía en una clase interna `Config`, en la versión 2 de Pydantic se hace en un atributo `model_config`. Este atributo toma un `dict`, y para obtener autocompletado y errores en línea, puedes importar y usar `SettingsConfigDict` para definir ese `dict`.
|
||||
|
||||
///
|
||||
|
||||
Aquí definimos la configuración `env_file` dentro de tu clase Pydantic `Settings`, y establecemos el valor en el nombre del archivo con el archivo dotenv que queremos usar.
|
||||
|
||||
### Creando el `Settings` solo una vez con `lru_cache`
|
||||
### Creando el `Settings` solo una vez con `lru_cache` { #creating-the-settings-only-once-with-lru-cache }
|
||||
|
||||
Leer un archivo desde el disco es normalmente una operación costosa (lenta), por lo que probablemente quieras hacerlo solo una vez y luego reutilizar el mismo objeto de configuraciones, en lugar de leerlo para cada request.
|
||||
|
||||
@@ -274,7 +230,7 @@ Pero como estamos usando el decorador `@lru_cache` encima, el objeto `Settings`
|
||||
|
||||
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.
|
||||
|
||||
#### Detalles Técnicos de `lru_cache`
|
||||
#### Detalles Técnicos de `lru_cache` { #lru-cache-technical-details }
|
||||
|
||||
`@lru_cache` modifica la función que decora para devolver el mismo valor que se devolvió la primera vez, en lugar de calcularlo nuevamente, ejecutando el código de la función cada vez.
|
||||
|
||||
@@ -331,13 +287,13 @@ participant execute as Ejecutar función
|
||||
end
|
||||
```
|
||||
|
||||
En el caso de nuestra dependencia `get_settings()`, la función ni siquiera toma argumentos, por lo que siempre devolverá el mismo valor.
|
||||
En el caso de nuestra dependencia `get_settings()`, la función ni siquiera toma argumentos, por lo que siempre devuelve el mismo valor.
|
||||
|
||||
De esa manera, se comporta casi como si fuera solo una variable global. Pero como usa una función de dependencia, entonces podemos sobrescribirla fácilmente para las pruebas.
|
||||
|
||||
`@lru_cache` es parte de `functools`, que es parte del library estándar de Python, puedes leer más sobre él en las <a href="https://docs.python.org/3/library/functools.html#functools.lru_cache" class="external-link" target="_blank">docs de Python para `@lru_cache`</a>.
|
||||
`@lru_cache` es parte de `functools`, que es parte del paquete estándar de Python, puedes leer más sobre él en las <a href="https://docs.python.org/3/library/functools.html#functools.lru_cache" class="external-link" target="_blank">docs de Python para `@lru_cache`</a>.
|
||||
|
||||
## Resumen
|
||||
## Resumen { #recap }
|
||||
|
||||
Puedes usar Pydantic Settings para manejar las configuraciones o ajustes de tu aplicación, con todo el poder de los modelos de Pydantic.
|
||||
|
||||
|
||||
@@ -1,34 +1,34 @@
|
||||
# Sub Aplicaciones - Mounts
|
||||
# Sub Aplicaciones - Mounts { #sub-applications-mounts }
|
||||
|
||||
Si necesitas tener dos aplicaciones de **FastAPI** independientes, cada una con su propio OpenAPI independiente y su propia interfaz de docs, puedes tener una aplicación principal y "montar" una (o más) sub-aplicación(es).
|
||||
|
||||
## Montar una aplicación **FastAPI**
|
||||
## Montar una aplicación **FastAPI** { #mounting-a-fastapi-application }
|
||||
|
||||
"Montar" significa añadir una aplicación completamente "independiente" en un path específico, que luego se encarga de manejar todo bajo ese path, con las _path operations_ declaradas en esa sub-aplicación.
|
||||
|
||||
### Aplicación de nivel superior
|
||||
### Aplicación de nivel superior { #top-level-application }
|
||||
|
||||
Primero, crea la aplicación principal de nivel superior de **FastAPI**, y sus *path operations*:
|
||||
|
||||
{* ../../docs_src/sub_applications/tutorial001.py hl[3, 6:8] *}
|
||||
{* ../../docs_src/sub_applications/tutorial001_py39.py hl[3, 6:8] *}
|
||||
|
||||
### Sub-aplicación
|
||||
### Sub-aplicación { #sub-application }
|
||||
|
||||
Luego, crea tu sub-aplicación, y sus *path operations*.
|
||||
|
||||
Esta sub-aplicación es solo otra aplicación estándar de FastAPI, pero es la que se "montará":
|
||||
|
||||
{* ../../docs_src/sub_applications/tutorial001.py hl[11, 14:16] *}
|
||||
{* ../../docs_src/sub_applications/tutorial001_py39.py hl[11, 14:16] *}
|
||||
|
||||
### Montar la sub-aplicación
|
||||
### Montar la sub-aplicación { #mount-the-sub-application }
|
||||
|
||||
En tu aplicación de nivel superior, `app`, monta la sub-aplicación, `subapi`.
|
||||
|
||||
En este caso, se montará en el path `/subapi`:
|
||||
|
||||
{* ../../docs_src/sub_applications/tutorial001.py hl[11, 19] *}
|
||||
{* ../../docs_src/sub_applications/tutorial001_py39.py hl[11, 19] *}
|
||||
|
||||
### Revisa la documentación automática de la API
|
||||
### Revisa la documentación automática de la API { #check-the-automatic-api-docs }
|
||||
|
||||
Ahora, ejecuta el comando `fastapi` con tu archivo:
|
||||
|
||||
@@ -56,7 +56,7 @@ Verás la documentación automática de la API para la sub-aplicación, incluyen
|
||||
|
||||
Si intentas interactuar con cualquiera de las dos interfaces de usuario, funcionarán correctamente, porque el navegador podrá comunicarse con cada aplicación o sub-aplicación específica.
|
||||
|
||||
### Detalles Técnicos: `root_path`
|
||||
### Detalles Técnicos: `root_path` { #technical-details-root-path }
|
||||
|
||||
Cuando montas una sub-aplicación como se describe arriba, FastAPI se encargará de comunicar el path de montaje para la sub-aplicación usando un mecanismo de la especificación ASGI llamado `root_path`.
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Plantillas
|
||||
# Plantillas { #templates }
|
||||
|
||||
Puedes usar cualquier motor de plantillas que desees con **FastAPI**.
|
||||
|
||||
@@ -6,7 +6,7 @@ Una elección común es Jinja2, el mismo que usa Flask y otras herramientas.
|
||||
|
||||
Hay utilidades para configurarlo fácilmente que puedes usar directamente en tu aplicación de **FastAPI** (proporcionadas por Starlette).
|
||||
|
||||
## Instalar dependencias
|
||||
## Instala dependencias { #install-dependencies }
|
||||
|
||||
Asegúrate de crear un [entorno virtual](../virtual-environments.md){.internal-link target=_blank}, activarlo e instalar `jinja2`:
|
||||
|
||||
@@ -20,14 +20,14 @@ $ pip install jinja2
|
||||
|
||||
</div>
|
||||
|
||||
## Usando `Jinja2Templates`
|
||||
## Usando `Jinja2Templates` { #using-jinja2templates }
|
||||
|
||||
* Importa `Jinja2Templates`.
|
||||
* Crea un objeto `templates` que puedas reutilizar más tarde.
|
||||
* Declara un parámetro `Request` en la *path operation* que devolverá una plantilla.
|
||||
* Usa los `templates` que creaste para renderizar y devolver un `TemplateResponse`, pasa el nombre de la plantilla, el objeto de request, y un diccionario "context" con pares clave-valor que se usarán dentro de la plantilla Jinja2.
|
||||
|
||||
{* ../../docs_src/templates/tutorial001.py hl[4,11,15:18] *}
|
||||
{* ../../docs_src/templates/tutorial001_py39.py hl[4,11,15:18] *}
|
||||
|
||||
/// note | Nota
|
||||
|
||||
@@ -51,7 +51,7 @@ También podrías usar `from starlette.templating import Jinja2Templates`.
|
||||
|
||||
///
|
||||
|
||||
## Escribiendo plantillas
|
||||
## Escribiendo plantillas { #writing-templates }
|
||||
|
||||
Luego puedes escribir una plantilla en `templates/item.html` con, por ejemplo:
|
||||
|
||||
@@ -59,7 +59,7 @@ Luego puedes escribir una plantilla en `templates/item.html` con, por ejemplo:
|
||||
{!../../docs_src/templates/templates/item.html!}
|
||||
```
|
||||
|
||||
### Valores de Contexto de la Plantilla
|
||||
### Valores de Contexto de la Plantilla { #template-context-values }
|
||||
|
||||
En el HTML que contiene:
|
||||
|
||||
@@ -83,7 +83,7 @@ Por ejemplo, con un ID de `42`, esto se renderizaría como:
|
||||
Item ID: 42
|
||||
```
|
||||
|
||||
### Argumentos de la Plantilla `url_for`
|
||||
### Argumentos de la Plantilla `url_for` { #template-url-for-arguments }
|
||||
|
||||
También puedes usar `url_for()` dentro de la plantilla, toma como argumentos los mismos que usaría tu *path operation function*.
|
||||
|
||||
@@ -105,7 +105,7 @@ Por ejemplo, con un ID de `42`, esto se renderizaría como:
|
||||
<a href="/items/42">
|
||||
```
|
||||
|
||||
## Plantillas y archivos estáticos
|
||||
## Plantillas y archivos estáticos { #templates-and-static-files }
|
||||
|
||||
También puedes usar `url_for()` dentro de la plantilla, y usarlo, por ejemplo, con los `StaticFiles` que montaste con el `name="static"`.
|
||||
|
||||
@@ -121,6 +121,6 @@ En este ejemplo, enlazaría a un archivo CSS en `static/styles.css` con:
|
||||
|
||||
Y porque estás usando `StaticFiles`, ese archivo CSS sería servido automáticamente por tu aplicación de **FastAPI** en la URL `/static/styles.css`.
|
||||
|
||||
## Más detalles
|
||||
## Más detalles { #more-details }
|
||||
|
||||
Para más detalles, incluyendo cómo testear plantillas, revisa <a href="https://www.starlette.dev/templates/" class="external-link" target="_blank">la documentación de Starlette sobre plantillas</a>.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Probando Dependencias con Overrides
|
||||
# Probando Dependencias con Overrides { #testing-dependencies-with-overrides }
|
||||
|
||||
## Sobrescribir dependencias durante las pruebas
|
||||
## Sobrescribir dependencias durante las pruebas { #overriding-dependencies-during-testing }
|
||||
|
||||
Hay algunos escenarios donde podrías querer sobrescribir una dependencia durante las pruebas.
|
||||
|
||||
@@ -8,7 +8,7 @@ No quieres que la dependencia original se ejecute (ni ninguna de las sub-depende
|
||||
|
||||
En cambio, quieres proporcionar una dependencia diferente que se usará solo durante las pruebas (posiblemente solo algunas pruebas específicas), y que proporcionará un valor que pueda ser usado donde se usó el valor de la dependencia original.
|
||||
|
||||
### Casos de uso: servicio externo
|
||||
### Casos de uso: servicio externo { #use-cases-external-service }
|
||||
|
||||
Un ejemplo podría ser que tienes un proveedor de autenticación externo al que necesitas llamar.
|
||||
|
||||
@@ -20,7 +20,7 @@ Probablemente quieras probar el proveedor externo una vez, pero no necesariament
|
||||
|
||||
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.
|
||||
|
||||
### Usa el atributo `app.dependency_overrides`
|
||||
### Usa el atributo `app.dependency_overrides` { #use-the-app-dependency-overrides-attribute }
|
||||
|
||||
Para estos casos, tu aplicación **FastAPI** tiene un atributo `app.dependency_overrides`, es un simple `dict`.
|
||||
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# Testing Events: startup - shutdown
|
||||
# Eventos de testing: lifespan y startup - shutdown { #testing-events-lifespan-and-startup-shutdown }
|
||||
|
||||
Cuando necesitas que tus manejadores de eventos (`startup` y `shutdown`) se ejecuten en tus tests, puedes usar el `TestClient` con un statement `with`:
|
||||
Cuando necesitas que `lifespan` se ejecute en tus tests, puedes usar el `TestClient` con un statement `with`:
|
||||
|
||||
{* ../../docs_src/app_testing/tutorial003.py hl[9:12,20:24] *}
|
||||
{* ../../docs_src/app_testing/tutorial004_py39.py hl[9:15,18,27:28,30:32,41:43] *}
|
||||
|
||||
|
||||
Puedes leer más detalles sobre ["Ejecutar lifespan en tests en el sitio oficial de documentación de Starlette."](https://www.starlette.dev/lifespan/#running-lifespan-in-tests)
|
||||
|
||||
Para los eventos obsoletos `startup` y `shutdown`, puedes usar el `TestClient` así:
|
||||
|
||||
{* ../../docs_src/app_testing/tutorial003_py39.py hl[9:12,20:24] *}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
# Probando WebSockets
|
||||
# Probando WebSockets { #testing-websockets }
|
||||
|
||||
Puedes usar el mismo `TestClient` para probar WebSockets.
|
||||
|
||||
Para esto, usas el `TestClient` en un statement `with`, conectándote al WebSocket:
|
||||
|
||||
{* ../../docs_src/app_testing/tutorial002.py hl[27:31] *}
|
||||
{* ../../docs_src/app_testing/tutorial002_py39.py hl[27:31] *}
|
||||
|
||||
/// note | Nota
|
||||
|
||||
Para más detalles, revisa la documentación de Starlette sobre <a href="https://www.starlette.dev/testclient/#testing-websocket-sessions" class="external-link" target="_blank">probando sesiones WebSocket</a>.
|
||||
Para más detalles, revisa la documentación de Starlette sobre <a href="https://www.starlette.dev/testclient/#testing-websocket-sessions" class="external-link" target="_blank">probar WebSockets</a>.
|
||||
|
||||
///
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Usar el Request Directamente
|
||||
# Usar el Request Directamente { #using-the-request-directly }
|
||||
|
||||
Hasta ahora, has estado declarando las partes del request que necesitas con sus tipos.
|
||||
|
||||
@@ -13,7 +13,7 @@ Y al hacerlo, **FastAPI** está validando esos datos, convirtiéndolos y generan
|
||||
|
||||
Pero hay situaciones donde podrías necesitar acceder al objeto `Request` directamente.
|
||||
|
||||
## Detalles sobre el objeto `Request`
|
||||
## Detalles sobre el objeto `Request` { #details-about-the-request-object }
|
||||
|
||||
Como **FastAPI** es en realidad **Starlette** por debajo, con una capa de varias herramientas encima, puedes usar el objeto <a href="https://www.starlette.dev/requests/" class="external-link" target="_blank">`Request`</a> de Starlette directamente cuando lo necesites.
|
||||
|
||||
@@ -23,13 +23,13 @@ Aunque cualquier otro parámetro declarado normalmente (por ejemplo, el cuerpo c
|
||||
|
||||
Pero hay casos específicos donde es útil obtener el objeto `Request`.
|
||||
|
||||
## Usa el objeto `Request` directamente
|
||||
## Usa el objeto `Request` directamente { #use-the-request-object-directly }
|
||||
|
||||
Imaginemos que quieres obtener la dirección IP/host del cliente dentro de tu *path operation function*.
|
||||
|
||||
Para eso necesitas acceder al request directamente.
|
||||
|
||||
{* ../../docs_src/using_request_directly/tutorial001.py hl[1,7:8] *}
|
||||
{* ../../docs_src/using_request_directly/tutorial001_py39.py hl[1,7:8] *}
|
||||
|
||||
Al declarar un parámetro de *path operation function* con el tipo siendo `Request`, **FastAPI** sabrá pasar el `Request` en ese parámetro.
|
||||
|
||||
@@ -43,7 +43,7 @@ De la misma manera, puedes declarar cualquier otro parámetro como normalmente,
|
||||
|
||||
///
|
||||
|
||||
## Documentación de `Request`
|
||||
## Documentación de `Request` { #request-documentation }
|
||||
|
||||
Puedes leer más detalles sobre el <a href="https://www.starlette.dev/requests/" class="external-link" target="_blank">objeto `Request` en el sitio de documentación oficial de Starlette</a>.
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# WebSockets
|
||||
# WebSockets { #websockets }
|
||||
|
||||
Puedes usar <a href="https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API" class="external-link" target="_blank">WebSockets</a> con **FastAPI**.
|
||||
|
||||
## Instalar `WebSockets`
|
||||
## Instalar `websockets` { #install-websockets }
|
||||
|
||||
Asegúrate de crear un [entorno virtual](../virtual-environments.md){.internal-link target=_blank}, activarlo e instalar `websockets`:
|
||||
Asegúrate de crear un [entorno virtual](../virtual-environments.md){.internal-link target=_blank}, activarlo e instalar `websockets` (un paquete de Python que facilita usar el protocolo "WebSocket"):
|
||||
|
||||
<div class="termy">
|
||||
|
||||
@@ -16,9 +16,9 @@ $ pip install websockets
|
||||
|
||||
</div>
|
||||
|
||||
## Cliente WebSockets
|
||||
## Cliente WebSockets { #websockets-client }
|
||||
|
||||
### En producción
|
||||
### En producción { #in-production }
|
||||
|
||||
En tu sistema de producción, probablemente tengas un frontend creado con un framework moderno como React, Vue.js o Angular.
|
||||
|
||||
@@ -38,13 +38,13 @@ En producción tendrías una de las opciones anteriores.
|
||||
|
||||
Pero es la forma más sencilla de enfocarse en el lado del servidor de WebSockets y tener un ejemplo funcional:
|
||||
|
||||
{* ../../docs_src/websockets/tutorial001.py hl[2,6:38,41:43] *}
|
||||
{* ../../docs_src/websockets/tutorial001_py39.py hl[2,6:38,41:43] *}
|
||||
|
||||
## Crear un `websocket`
|
||||
## Crear un `websocket` { #create-a-websocket }
|
||||
|
||||
En tu aplicación de **FastAPI**, crea un `websocket`:
|
||||
|
||||
{* ../../docs_src/websockets/tutorial001.py hl[1,46:47] *}
|
||||
{* ../../docs_src/websockets/tutorial001_py39.py hl[1,46:47] *}
|
||||
|
||||
/// note | Detalles Técnicos
|
||||
|
||||
@@ -54,15 +54,15 @@ También podrías usar `from starlette.websockets import WebSocket`.
|
||||
|
||||
///
|
||||
|
||||
## Esperar mensajes y enviar mensajes
|
||||
## Esperar mensajes y enviar mensajes { #await-for-messages-and-send-messages }
|
||||
|
||||
En tu ruta de WebSocket puedes `await` para recibir mensajes y enviar mensajes.
|
||||
|
||||
{* ../../docs_src/websockets/tutorial001.py hl[48:52] *}
|
||||
{* ../../docs_src/websockets/tutorial001_py39.py hl[48:52] *}
|
||||
|
||||
Puedes recibir y enviar datos binarios, de texto y JSON.
|
||||
|
||||
## Pruébalo
|
||||
## Pruébalo { #try-it }
|
||||
|
||||
Si tu archivo se llama `main.py`, ejecuta tu aplicación con:
|
||||
|
||||
@@ -96,7 +96,7 @@ Puedes enviar (y recibir) muchos mensajes:
|
||||
|
||||
Y todos usarán la misma conexión WebSocket.
|
||||
|
||||
## Usando `Depends` y otros
|
||||
## Usando `Depends` y otros { #using-depends-and-others }
|
||||
|
||||
En endpoints de WebSocket puedes importar desde `fastapi` y usar:
|
||||
|
||||
@@ -119,7 +119,7 @@ Puedes usar un código de cierre de los <a href="https://tools.ietf.org/html/rfc
|
||||
|
||||
///
|
||||
|
||||
### Prueba los WebSockets con dependencias
|
||||
### Prueba los WebSockets con dependencias { #try-the-websockets-with-dependencies }
|
||||
|
||||
Si tu archivo se llama `main.py`, ejecuta tu aplicación con:
|
||||
|
||||
@@ -150,7 +150,7 @@ Con eso puedes conectar el WebSocket y luego enviar y recibir mensajes:
|
||||
|
||||
<img src="/img/tutorial/websockets/image05.png">
|
||||
|
||||
## Manejar desconexiones y múltiples clientes
|
||||
## Manejar desconexiones y múltiples clientes { #handling-disconnections-and-multiple-clients }
|
||||
|
||||
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.
|
||||
|
||||
@@ -178,7 +178,7 @@ Si necesitas algo fácil de integrar con FastAPI pero que sea más robusto, sopo
|
||||
|
||||
///
|
||||
|
||||
## Más información
|
||||
## Más información { #more-info }
|
||||
|
||||
Para aprender más sobre las opciones, revisa la documentación de Starlette para:
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# Incluyendo WSGI - Flask, Django, otros
|
||||
# Incluyendo WSGI - Flask, Django, otros { #including-wsgi-flask-django-others }
|
||||
|
||||
Puedes montar aplicaciones WSGI como viste con [Sub Aplicaciones - Mounts](sub-applications.md){.internal-link target=_blank}, [Detrás de un Proxy](behind-a-proxy.md){.internal-link target=_blank}.
|
||||
|
||||
Para eso, puedes usar `WSGIMiddleware` y usarlo para envolver tu aplicación WSGI, por ejemplo, Flask, Django, etc.
|
||||
|
||||
## Usando `WSGIMiddleware`
|
||||
## Usando `WSGIMiddleware` { #using-wsgimiddleware }
|
||||
|
||||
Necesitas importar `WSGIMiddleware`.
|
||||
|
||||
@@ -12,9 +12,9 @@ Luego envuelve la aplicación WSGI (p. ej., Flask) con el middleware.
|
||||
|
||||
Y luego móntala bajo un path.
|
||||
|
||||
{* ../../docs_src/wsgi/tutorial001.py hl[2:3,3] *}
|
||||
{* ../../docs_src/wsgi/tutorial001_py39.py hl[2:3,3] *}
|
||||
|
||||
## Revisa
|
||||
## Revisa { #check-it }
|
||||
|
||||
Ahora, cada request bajo el path `/v1/` será manejado por la aplicación Flask.
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# Alternativas, Inspiración y Comparaciones
|
||||
# Alternativas, Inspiración y Comparaciones { #alternatives-inspiration-and-comparisons }
|
||||
|
||||
Lo que inspiró a **FastAPI**, cómo se compara con las alternativas y lo que aprendió de ellas.
|
||||
|
||||
## Introducción
|
||||
## Introducción { #intro }
|
||||
|
||||
**FastAPI** no existiría si no fuera por el trabajo previo de otros.
|
||||
|
||||
@@ -12,17 +12,17 @@ He estado evitando la creación de un nuevo framework durante varios años. Prim
|
||||
|
||||
Pero en algún punto, no hubo otra opción que crear algo que proporcionara todas estas funcionalidades, tomando las mejores ideas de herramientas previas y combinándolas de la mejor manera posible, usando funcionalidades del lenguaje que ni siquiera estaban disponibles antes (anotaciones de tipos de Python 3.6+).
|
||||
|
||||
## Herramientas previas
|
||||
## Herramientas previas { #previous-tools }
|
||||
|
||||
### <a href="https://www.djangoproject.com/" class="external-link" target="_blank">Django</a>
|
||||
### <a href="https://www.djangoproject.com/" class="external-link" target="_blank">Django</a> { #django }
|
||||
|
||||
Es el framework más popular de Python y es ampliamente confiable. Se utiliza para construir sistemas como Instagram.
|
||||
|
||||
Está relativamente acoplado con bases de datos relacionales (como MySQL o PostgreSQL), por lo que tener una base de datos NoSQL (como Couchbase, MongoDB, Cassandra, etc) como motor de almacenamiento principal no es muy fácil.
|
||||
|
||||
Fue creado para generar el HTML en el backend, no para crear APIs utilizadas por un frontend moderno (como React, Vue.js y Angular) o por otros sistemas (como dispositivos del <abbr title="Internet of Things">IoT</abbr>) comunicándose con él.
|
||||
Fue creado para generar el HTML en el backend, no para crear APIs utilizadas por un frontend moderno (como React, Vue.js y Angular) o por otros sistemas (como dispositivos del <abbr title="Internet of Things – Internet de las cosas">IoT</abbr>) comunicándose con él.
|
||||
|
||||
### <a href="https://www.django-rest-framework.org/" class="external-link" target="_blank">Django REST Framework</a>
|
||||
### <a href="https://www.django-rest-framework.org/" class="external-link" target="_blank">Django REST Framework</a> { #django-rest-framework }
|
||||
|
||||
El framework Django REST fue creado para ser un kit de herramientas flexible para construir APIs Web utilizando Django, mejorando sus capacidades API.
|
||||
|
||||
@@ -42,7 +42,7 @@ Tener una interfaz de usuario web de documentación automática de APIs.
|
||||
|
||||
///
|
||||
|
||||
### <a href="https://flask.palletsprojects.com" class="external-link" target="_blank">Flask</a>
|
||||
### <a href="https://flask.palletsprojects.com" class="external-link" target="_blank">Flask</a> { #flask }
|
||||
|
||||
Flask es un "microframework", no incluye integraciones de bases de datos ni muchas de las cosas que vienen por defecto en Django.
|
||||
|
||||
@@ -64,7 +64,7 @@ Tener un sistema de routing simple y fácil de usar.
|
||||
|
||||
///
|
||||
|
||||
### <a href="https://requests.readthedocs.io" class="external-link" target="_blank">Requests</a>
|
||||
### <a href="https://requests.readthedocs.io" class="external-link" target="_blank">Requests</a> { #requests }
|
||||
|
||||
**FastAPI** no es en realidad una alternativa a **Requests**. Su ámbito es muy diferente.
|
||||
|
||||
@@ -106,7 +106,7 @@ Mira las similitudes entre `requests.get(...)` y `@app.get(...)`.
|
||||
|
||||
///
|
||||
|
||||
### <a href="https://swagger.io/" class="external-link" target="_blank">Swagger</a> / <a href="https://github.com/OAI/OpenAPI-Specification/" class="external-link" target="_blank">OpenAPI</a>
|
||||
### <a href="https://swagger.io/" class="external-link" target="_blank">Swagger</a> / <a href="https://github.com/OAI/OpenAPI-Specification/" class="external-link" target="_blank">OpenAPI</a> { #swagger-openapi }
|
||||
|
||||
La principal funcionalidad que quería de Django REST Framework era la documentación automática de la API.
|
||||
|
||||
@@ -131,11 +131,11 @@ Estas dos fueron elegidas por ser bastante populares y estables, pero haciendo u
|
||||
|
||||
///
|
||||
|
||||
### Frameworks REST para Flask
|
||||
### Frameworks REST para Flask { #flask-rest-frameworks }
|
||||
|
||||
Existen varios frameworks REST para Flask, pero después de invertir tiempo y trabajo investigándolos, encontré que muchos son descontinuados o abandonados, con varios problemas existentes que los hacían inadecuados.
|
||||
|
||||
### <a href="https://marshmallow.readthedocs.io/en/stable/" class="external-link" target="_blank">Marshmallow</a>
|
||||
### <a href="https://marshmallow.readthedocs.io/en/stable/" class="external-link" target="_blank">Marshmallow</a> { #marshmallow }
|
||||
|
||||
Una de las principales funcionalidades necesitadas por los sistemas API es la "<abbr title="también llamada marshalling, conversión">serialización</abbr>" de datos, que consiste en tomar datos del código (Python) y convertirlos en algo que pueda ser enviado a través de la red. Por ejemplo, convertir un objeto que contiene datos de una base de datos en un objeto JSON. Convertir objetos `datetime` en strings, etc.
|
||||
|
||||
@@ -153,7 +153,7 @@ Usar código para definir "esquemas" que proporcionen tipos de datos y validaci
|
||||
|
||||
///
|
||||
|
||||
### <a href="https://webargs.readthedocs.io/en/latest/" class="external-link" target="_blank">Webargs</a>
|
||||
### <a href="https://webargs.readthedocs.io/en/latest/" class="external-link" target="_blank">Webargs</a> { #webargs }
|
||||
|
||||
Otra gran funcionalidad requerida por las APIs es el <abbr title="lectura y conversión a datos de Python">parse</abbr> de datos de las requests entrantes.
|
||||
|
||||
@@ -175,7 +175,7 @@ Tener validación automática de datos entrantes en una request.
|
||||
|
||||
///
|
||||
|
||||
### <a href="https://apispec.readthedocs.io/en/stable/" class="external-link" target="_blank">APISpec</a>
|
||||
### <a href="https://apispec.readthedocs.io/en/stable/" class="external-link" target="_blank">APISpec</a> { #apispec }
|
||||
|
||||
Marshmallow y Webargs proporcionan validación, parse y serialización como plug-ins.
|
||||
|
||||
@@ -205,7 +205,7 @@ Soportar el estándar abierto para APIs, OpenAPI.
|
||||
|
||||
///
|
||||
|
||||
### <a href="https://flask-apispec.readthedocs.io/en/latest/" class="external-link" target="_blank">Flask-apispec</a>
|
||||
### <a href="https://flask-apispec.readthedocs.io/en/latest/" class="external-link" target="_blank">Flask-apispec</a> { #flask-apispec }
|
||||
|
||||
Es un plug-in de Flask, que conecta juntos Webargs, Marshmallow y APISpec.
|
||||
|
||||
@@ -237,7 +237,7 @@ Generar el esquema OpenAPI automáticamente, desde el mismo código que define l
|
||||
|
||||
///
|
||||
|
||||
### <a href="https://nestjs.com/" class="external-link" target="_blank">NestJS</a> (y <a href="https://angular.io/" class="external-link" target="_blank">Angular</a>)
|
||||
### <a href="https://nestjs.com/" class="external-link" target="_blank">NestJS</a> (y <a href="https://angular.io/" class="external-link" target="_blank">Angular</a>) { #nestjs-and-angular }
|
||||
|
||||
Esto ni siquiera es Python, NestJS es un framework de JavaScript (TypeScript) NodeJS inspirado por Angular.
|
||||
|
||||
@@ -259,7 +259,7 @@ Tener un poderoso sistema de inyección de dependencias. Encontrar una forma de
|
||||
|
||||
///
|
||||
|
||||
### <a href="https://sanic.readthedocs.io/en/latest/" class="external-link" target="_blank">Sanic</a>
|
||||
### <a href="https://sanic.readthedocs.io/en/latest/" class="external-link" target="_blank">Sanic</a> { #sanic }
|
||||
|
||||
Fue uno de los primeros frameworks de Python extremadamente rápidos basados en `asyncio`. Fue hecho para ser muy similar a Flask.
|
||||
|
||||
@@ -279,7 +279,7 @@ Por eso **FastAPI** se basa en Starlette, ya que es el framework más rápido di
|
||||
|
||||
///
|
||||
|
||||
### <a href="https://falconframework.org/" class="external-link" target="_blank">Falcon</a>
|
||||
### <a href="https://falconframework.org/" class="external-link" target="_blank">Falcon</a> { #falcon }
|
||||
|
||||
Falcon es otro framework de Python de alto rendimiento, está diseñado para ser minimalista y funcionar como la base de otros frameworks como Hug.
|
||||
|
||||
@@ -297,7 +297,7 @@ Aunque en FastAPI es opcional, y se utiliza principalmente para configurar heade
|
||||
|
||||
///
|
||||
|
||||
### <a href="https://moltenframework.com/" class="external-link" target="_blank">Molten</a>
|
||||
### <a href="https://moltenframework.com/" class="external-link" target="_blank">Molten</a> { #molten }
|
||||
|
||||
Descubrí Molten en las primeras etapas de construcción de **FastAPI**. Y tiene ideas bastante similares:
|
||||
|
||||
@@ -321,7 +321,7 @@ Esto en realidad inspiró la actualización de partes de Pydantic, para soportar
|
||||
|
||||
///
|
||||
|
||||
### <a href="https://github.com/hugapi/hug" class="external-link" target="_blank">Hug</a>
|
||||
### <a href="https://github.com/hugapi/hug" class="external-link" target="_blank">Hug</a> { #hug }
|
||||
|
||||
Hug fue uno de los primeros frameworks en implementar la declaración de tipos de parámetros API usando las anotaciones de tipos de Python. Esta fue una gran idea que inspiró a otras herramientas a hacer lo mismo.
|
||||
|
||||
@@ -351,7 +351,7 @@ Hug inspiró a **FastAPI** a declarar un parámetro `response` en funciones para
|
||||
|
||||
///
|
||||
|
||||
### <a href="https://github.com/encode/apistar" class="external-link" target="_blank">APIStar</a> (<= 0.5)
|
||||
### <a href="https://github.com/encode/apistar" class="external-link" target="_blank">APIStar</a> (<= 0.5) { #apistar-0-5 }
|
||||
|
||||
Justo antes de decidir construir **FastAPI** encontré **APIStar** server. Tenía casi todo lo que estaba buscando y tenía un gran diseño.
|
||||
|
||||
@@ -399,9 +399,9 @@ Considero a **FastAPI** un "sucesor espiritual" de APIStar, mientras mejora y au
|
||||
|
||||
///
|
||||
|
||||
## Usado por **FastAPI**
|
||||
## Usado por **FastAPI** { #used-by-fastapi }
|
||||
|
||||
### <a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic</a>
|
||||
### <a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic</a> { #pydantic }
|
||||
|
||||
Pydantic es un paquete para definir validación de datos, serialización y documentación (usando JSON Schema) basándose en las anotaciones de tipos de Python.
|
||||
|
||||
@@ -417,9 +417,9 @@ Manejar toda la validación de datos, serialización de datos y documentación a
|
||||
|
||||
///
|
||||
|
||||
### <a href="https://www.starlette.dev/" class="external-link" target="_blank">Starlette</a>
|
||||
### <a href="https://www.starlette.dev/" class="external-link" target="_blank">Starlette</a> { #starlette }
|
||||
|
||||
Starlette es un framework/toolkit <abbr title="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 <abbr title="The new standard for building asynchronous Python web applications – El nuevo estándar para construir aplicaciones web asíncronas en Python">ASGI</abbr> liviano, ideal para construir servicios asyncio de alto rendimiento.
|
||||
|
||||
Es muy simple e intuitivo. Está diseñado para ser fácilmente extensible y tener componentes modulares.
|
||||
|
||||
@@ -462,7 +462,7 @@ Por lo tanto, cualquier cosa que puedas hacer con Starlette, puedes hacerlo dire
|
||||
|
||||
///
|
||||
|
||||
### <a href="https://www.uvicorn.dev/" class="external-link" target="_blank">Uvicorn</a>
|
||||
### <a href="https://www.uvicorn.dev/" class="external-link" target="_blank">Uvicorn</a> { #uvicorn }
|
||||
|
||||
Uvicorn es un servidor ASGI extremadamente rápido, construido sobre uvloop y httptools.
|
||||
|
||||
@@ -480,6 +480,6 @@ Revisa más detalles en la sección [Despliegue](deployment/index.md){.internal-
|
||||
|
||||
///
|
||||
|
||||
## Benchmarks y velocidad
|
||||
## Benchmarks y velocidad { #benchmarks-and-speed }
|
||||
|
||||
Para entender, comparar, y ver la diferencia entre Uvicorn, Starlette y FastAPI, revisa la sección sobre [Benchmarks](benchmarks.md){.internal-link target=_blank}.
|
||||
|
||||
+27
-27
@@ -1,8 +1,8 @@
|
||||
# Concurrencia y async / await
|
||||
# Concurrencia y async / await { #concurrency-and-async-await }
|
||||
|
||||
Detalles sobre la sintaxis `async def` para *path operation functions* y algunos antecedentes sobre el código asíncrono, la concurrencia y el paralelismo.
|
||||
|
||||
## ¿Con prisa?
|
||||
## ¿Con prisa? { #in-a-hurry }
|
||||
|
||||
<abbr title="too long; didn't read"><strong>TL;DR:</strong></abbr>
|
||||
|
||||
@@ -40,7 +40,7 @@ def results():
|
||||
|
||||
---
|
||||
|
||||
Si tu aplicación (de alguna manera) no tiene que comunicarse con nada más y esperar a que responda, usa `async def`.
|
||||
Si tu aplicación (de alguna manera) no tiene que comunicarse con nada más y esperar a que responda, usa `async def`, incluso si no necesitas usar `await` dentro.
|
||||
|
||||
---
|
||||
|
||||
@@ -54,7 +54,7 @@ De todos modos, en cualquiera de los casos anteriores, FastAPI seguirá funciona
|
||||
|
||||
Pero al seguir los pasos anteriores, podrá hacer algunas optimizaciones de rendimiento.
|
||||
|
||||
## Detalles Técnicos
|
||||
## Detalles Técnicos { #technical-details }
|
||||
|
||||
Las versiones modernas de Python tienen soporte para **"código asíncrono"** utilizando algo llamado **"coroutines"**, con la sintaxis **`async` y `await`**.
|
||||
|
||||
@@ -64,7 +64,7 @@ Veamos esa frase por partes en las secciones a continuación:
|
||||
* **`async` y `await`**
|
||||
* **Coroutines**
|
||||
|
||||
## Código Asíncrono
|
||||
## Código Asíncrono { #asynchronous-code }
|
||||
|
||||
El código asíncrono simplemente significa que el lenguaje 💬 tiene una forma de decirle a la computadora / programa 🤖 que en algún momento del código, tendrá que esperar que *otra cosa* termine en otro lugar. Digamos que esa *otra cosa* se llama "archivo-lento" 📝.
|
||||
|
||||
@@ -74,7 +74,7 @@ Luego la computadora / programa 🤖 volverá cada vez que tenga una oportunidad
|
||||
|
||||
Después, 🤖 toma la primera tarea que termine (digamos, nuestro "archivo-lento" 📝) y continúa con lo que tenía que hacer con ella.
|
||||
|
||||
Ese "esperar otra cosa" normalmente se refiere a las operaciones de <abbr title="Input and Output">I/O</abbr> que son relativamente "lentas" (comparadas con la velocidad del procesador y la memoria RAM), como esperar:
|
||||
Ese "esperar otra cosa" normalmente se refiere a las operaciones de <abbr title="Input and Output – Entrada y salida">I/O</abbr> que son relativamente "lentas" (comparadas con la velocidad del procesador y la memoria RAM), como esperar:
|
||||
|
||||
* que los datos del cliente se envíen a través de la red
|
||||
* que los datos enviados por tu programa sean recibidos por el cliente a través de la red
|
||||
@@ -85,7 +85,7 @@ Ese "esperar otra cosa" normalmente se refiere a las operaciones de <abbr title=
|
||||
* que una query de base de datos devuelva los resultados
|
||||
* etc.
|
||||
|
||||
Como el tiempo de ejecución se consume principalmente esperando operaciones de <abbr title="Input and Output">I/O</abbr>, las llaman operaciones "I/O bound".
|
||||
Como el tiempo de ejecución se consume principalmente esperando operaciones de <abbr title="Input and Output – Entrada y salida">I/O</abbr>, las llaman operaciones "I/O bound".
|
||||
|
||||
Se llama "asíncrono" porque la computadora / programa no tiene que estar "sincronizado" con la tarea lenta, esperando el momento exacto en que la tarea termine, sin hacer nada, para poder tomar el resultado de la tarea y continuar el trabajo.
|
||||
|
||||
@@ -93,7 +93,7 @@ En lugar de eso, al ser un sistema "asíncrono", una vez terminado, la tarea pue
|
||||
|
||||
Para el "sincrónico" (contrario al "asíncrono") comúnmente también usan el término "secuencial", porque la computadora / programa sigue todos los pasos en secuencia antes de cambiar a una tarea diferente, incluso si esos pasos implican esperar.
|
||||
|
||||
### Concurrencia y Hamburguesas
|
||||
### Concurrencia y Hamburguesas { #concurrency-and-burgers }
|
||||
|
||||
Esta idea de código **asíncrono** descrita anteriormente a veces también se llama **"concurrencia"**. Es diferente del **"paralelismo"**.
|
||||
|
||||
@@ -103,7 +103,7 @@ Pero los detalles entre *concurrencia* y *paralelismo* son bastante diferentes.
|
||||
|
||||
Para ver la diferencia, imagina la siguiente historia sobre hamburguesas:
|
||||
|
||||
### Hamburguesas Concurrentes
|
||||
### Hamburguesas Concurrentes { #concurrent-burgers }
|
||||
|
||||
Vas con tu crush a conseguir comida rápida, te pones en fila mientras el cajero toma los pedidos de las personas frente a ti. 😍
|
||||
|
||||
@@ -163,7 +163,7 @@ Así que esperas a que tu crush termine la historia (termine el trabajo ⏯ / ta
|
||||
|
||||
Luego vas al mostrador 🔀, a la tarea inicial que ahora está terminada ⏯, recoges las hamburguesas, das las gracias y las llevas a la mesa. Eso termina ese paso / tarea de interacción con el mostrador ⏹. Eso a su vez, crea una nueva tarea, de "comer hamburguesas" 🔀 ⏯, pero la anterior de "obtener hamburguesas" ha terminado ⏹.
|
||||
|
||||
### Hamburguesas Paralelas
|
||||
### Hamburguesas Paralelas { #parallel-burgers }
|
||||
|
||||
Ahora imaginemos que estas no son "Hamburguesas Concurrentes", sino "Hamburguesas Paralelas".
|
||||
|
||||
@@ -233,7 +233,7 @@ Y tienes que esperar 🕙 en la fila por mucho tiempo o pierdes tu turno.
|
||||
|
||||
Probablemente no querrías llevar a tu crush 😍 contigo a hacer trámites en el banco 🏦.
|
||||
|
||||
### Conclusión de las Hamburguesas
|
||||
### Conclusión de las Hamburguesas { #burger-conclusion }
|
||||
|
||||
En este escenario de "hamburguesas de comida rápida con tu crush", como hay mucha espera 🕙, tiene mucho más sentido tener un sistema concurrente ⏸🔀⏯.
|
||||
|
||||
@@ -253,7 +253,7 @@ Y ese es el mismo nivel de rendimiento que obtienes con **FastAPI**.
|
||||
|
||||
Y como puedes tener paralelismo y asincronía al mismo tiempo, obtienes un mayor rendimiento que la mayoría de los frameworks de NodeJS probados y a la par con Go, que es un lenguaje compilado más cercano a C <a href="https://www.techempower.com/benchmarks/#section=data-r17&hw=ph&test=query&l=zijmkf-1" class="external-link" target="_blank">(todo gracias a Starlette)</a>.
|
||||
|
||||
### ¿Es la concurrencia mejor que el paralelismo?
|
||||
### ¿Es la concurrencia mejor que el paralelismo? { #is-concurrency-better-than-parallelism }
|
||||
|
||||
¡No! Esa no es la moraleja de la historia.
|
||||
|
||||
@@ -277,7 +277,7 @@ Pero en este caso, si pudieras traer a los 8 ex-cajeros/cocineros/ahora-limpiado
|
||||
|
||||
En este escenario, cada uno de los limpiadores (incluyéndote) sería un procesador, haciendo su parte del trabajo.
|
||||
|
||||
Y como la mayor parte del tiempo de ejecución se dedica al trabajo real (en lugar de esperar), y el trabajo en una computadora lo realiza una <abbr title="Central Processing Unit">CPU</abbr>, llaman a estos problemas "CPU bound".
|
||||
Y como la mayor parte del tiempo de ejecución se dedica al trabajo real (en lugar de esperar), y el trabajo en una computadora lo realiza una <abbr title="Central Processing Unit – Unidad Central de Procesamiento">CPU</abbr>, llaman a estos problemas "CPU bound".
|
||||
|
||||
---
|
||||
|
||||
@@ -290,7 +290,7 @@ Por ejemplo:
|
||||
* **Machine Learning**: normalmente requiere muchas multiplicaciones de "matrices" y "vectores". Piensa en una enorme hoja de cálculo con números y multiplicando todos juntos al mismo tiempo.
|
||||
* **Deep Learning**: este es un subcampo de Machine Learning, por lo tanto, se aplica lo mismo. Es solo que no hay una sola hoja de cálculo de números para multiplicar, sino un enorme conjunto de ellas, y en muchos casos, usas un procesador especial para construir y / o usar esos modelos.
|
||||
|
||||
### Concurrencia + Paralelismo: Web + Machine Learning
|
||||
### Concurrencia + Paralelismo: Web + Machine Learning { #concurrency-parallelism-web-machine-learning }
|
||||
|
||||
Con **FastAPI** puedes aprovechar la concurrencia que es muy común para el desarrollo web (la misma atracción principal de NodeJS).
|
||||
|
||||
@@ -300,7 +300,7 @@ Eso, más el simple hecho de que Python es el lenguaje principal para **Data Sci
|
||||
|
||||
Para ver cómo lograr este paralelismo en producción, consulta la sección sobre [Deployment](deployment/index.md){.internal-link target=_blank}.
|
||||
|
||||
## `async` y `await`
|
||||
## `async` y `await` { #async-and-await }
|
||||
|
||||
Las versiones modernas de Python tienen una forma muy intuitiva de definir código asíncrono. Esto hace que se vea igual que el código "secuencial" normal y hace el "wait" por ti en los momentos adecuados.
|
||||
|
||||
@@ -349,7 +349,7 @@ async def read_burgers():
|
||||
return burgers
|
||||
```
|
||||
|
||||
### Más detalles técnicos
|
||||
### Más detalles técnicos { #more-technical-details }
|
||||
|
||||
Podrías haber notado que `await` solo se puede usar dentro de funciones definidas con `async def`.
|
||||
|
||||
@@ -361,9 +361,9 @@ Si estás trabajando con **FastAPI** no tienes que preocuparte por eso, porque e
|
||||
|
||||
Pero si deseas usar `async` / `await` sin FastAPI, también puedes hacerlo.
|
||||
|
||||
### Escribe tu propio código async
|
||||
### Escribe tu propio código async { #write-your-own-async-code }
|
||||
|
||||
Starlette (y **FastAPI**) están basados en <a href="https://anyio.readthedocs.io/en/stable/" class="external-link" target="_blank">AnyIO</a>, lo que lo hace compatible tanto con la librería estándar de Python <a href="https://docs.python.org/3/library/asyncio-task.html" class="external-link" target="_blank">asyncio</a> como con <a href="https://trio.readthedocs.io/en/stable/" class="external-link" target="_blank">Trio</a>.
|
||||
Starlette (y **FastAPI**) están basados en <a href="https://anyio.readthedocs.io/en/stable/" class="external-link" target="_blank">AnyIO</a>, lo que lo hace compatible tanto con el <a href="https://docs.python.org/3/library/asyncio-task.html" class="external-link" target="_blank">asyncio</a> del paquete estándar de Python como con <a href="https://trio.readthedocs.io/en/stable/" class="external-link" target="_blank">Trio</a>.
|
||||
|
||||
En particular, puedes usar directamente <a href="https://anyio.readthedocs.io/en/stable/" class="external-link" target="_blank">AnyIO</a> para tus casos de uso avanzados de concurrencia que requieran patrones más avanzados en tu propio código.
|
||||
|
||||
@@ -371,7 +371,7 @@ E incluso si no estuvieras usando FastAPI, también podrías escribir tus propia
|
||||
|
||||
Creé otro paquete sobre AnyIO, como una capa delgada, para mejorar un poco las anotaciones de tipos y obtener mejor **autocompletado**, **errores en línea**, etc. También tiene una introducción amigable y tutorial para ayudarte a **entender** y escribir **tu propio código async**: <a href="https://asyncer.tiangolo.com/" class="external-link" target="_blank">Asyncer</a>. Sería particularmente útil si necesitas **combinar código async con regular** (bloqueante/sincrónico).
|
||||
|
||||
### Otras formas de código asíncrono
|
||||
### Otras formas de código asíncrono { #other-forms-of-asynchronous-code }
|
||||
|
||||
Este estilo de usar `async` y `await` es relativamente nuevo en el lenguaje.
|
||||
|
||||
@@ -385,13 +385,13 @@ En versiones previas de Python, podrías haber usado hilos o <a href="https://ww
|
||||
|
||||
En versiones previas de NodeJS / JavaScript en el Navegador, habrías usado "callbacks". Lo que lleva al "callback hell".
|
||||
|
||||
## Coroutines
|
||||
## Coroutines { #coroutines }
|
||||
|
||||
**Coroutines** es simplemente el término muy elegante para la cosa que devuelve una función `async def`. Python sabe que es algo parecido a una función, que puede comenzar y que terminará en algún momento, pero que podría pausar ⏸ internamente también, siempre que haya un `await` dentro de él.
|
||||
|
||||
Pero toda esta funcionalidad de usar código asíncrono con `async` y `await` a menudo se resume como utilizar "coroutines". Es comparable a la funcionalidad clave principal de Go, las "Goroutines".
|
||||
|
||||
## Conclusión
|
||||
## Conclusión { #conclusion }
|
||||
|
||||
Veamos la misma frase de arriba:
|
||||
|
||||
@@ -401,7 +401,7 @@ Eso debería tener más sentido ahora. ✨
|
||||
|
||||
Todo eso es lo que impulsa FastAPI (a través de Starlette) y lo que hace que tenga un rendimiento tan impresionante.
|
||||
|
||||
## Detalles Muy Técnicos
|
||||
## Detalles Muy Técnicos { #very-technical-details }
|
||||
|
||||
/// warning | Advertencia
|
||||
|
||||
@@ -413,23 +413,23 @@ Si tienes bastante conocimiento técnico (coroutines, hilos, bloqueo, etc.) y ti
|
||||
|
||||
///
|
||||
|
||||
### Funciones de *path operation*
|
||||
### Funciones de *path operation* { #path-operation-functions }
|
||||
|
||||
Cuando declaras una *path operation function* con `def` normal en lugar de `async def`, se ejecuta en un threadpool externo que luego es esperado, en lugar de ser llamado directamente (ya que bloquearía el servidor).
|
||||
|
||||
Si vienes de otro framework async que no funciona de la manera descrita anteriormente y estás acostumbrado a definir funciones de *path operation* solo de cómputo trivial con `def` normal para una pequeña ganancia de rendimiento (alrededor de 100 nanosegundos), ten en cuenta que en **FastAPI** el efecto sería bastante opuesto. En estos casos, es mejor usar `async def` a menos que tus *path operation functions* usen código que realice <abbr title="Input/Output: lectura o escritura en disco, comunicaciones de red.">I/O</abbr> de bloqueo.
|
||||
Si vienes de otro framework async que no funciona de la manera descrita anteriormente y estás acostumbrado a definir funciones de *path operation* solo de cómputo trivial con `def` normal para una pequeña ganancia de rendimiento (alrededor de 100 nanosegundos), ten en cuenta que en **FastAPI** el efecto sería bastante opuesto. En estos casos, es mejor usar `async def` a menos que tus *path operation functions* usen código que realice <abbr title="Input/Output – Entrada/Salida: lectura o escritura en disco, comunicaciones de red.">I/O</abbr> de bloqueo.
|
||||
|
||||
Aun así, en ambas situaciones, es probable que **FastAPI** [siga siendo más rápida](index.md#performance){.internal-link target=_blank} que (o al menos comparable a) tu framework anterior.
|
||||
|
||||
### Dependencias
|
||||
### Dependencias { #dependencies }
|
||||
|
||||
Lo mismo aplica para las [dependencias](tutorial/dependencies/index.md){.internal-link target=_blank}. Si una dependencia es una función estándar `def` en lugar de `async def`, se ejecuta en el threadpool externo.
|
||||
|
||||
### Sub-dependencias
|
||||
### Sub-dependencias { #sub-dependencies }
|
||||
|
||||
Puedes tener múltiples dependencias y [sub-dependencias](tutorial/dependencies/sub-dependencies.md){.internal-link target=_blank} requiriéndose mutuamente (como parámetros de las definiciones de funciones), algunas de ellas podrían ser creadas con `async def` y algunas con `def` normal. Aun funcionará, y las que fueron creadas con `def` normal serían llamadas en un hilo externo (del threadpool) en lugar de ser "awaited".
|
||||
|
||||
### Otras funciones de utilidad
|
||||
### Otras funciones de utilidad { #other-utility-functions }
|
||||
|
||||
Cualquier otra función de utilidad que llames directamente puede ser creada con `def` normal o `async def` y FastAPI no afectará la forma en que la llames.
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# Benchmarks
|
||||
# Benchmarks { #benchmarks }
|
||||
|
||||
Los benchmarks independientes de TechEmpower muestran aplicaciones de **FastAPI** ejecutándose bajo Uvicorn como <a href="https://www.techempower.com/benchmarks/#section=test&runid=7464e520-0dc2-473d-bd34-dbdfd7e85911&hw=ph&test=query&l=zijzen-7" class="external-link" target="_blank">uno de los frameworks de Python más rápidos disponibles</a>, solo por debajo de Starlette y Uvicorn en sí mismos (utilizados internamente por FastAPI).
|
||||
|
||||
Pero al revisar benchmarks y comparaciones, debes tener en cuenta lo siguiente.
|
||||
|
||||
## Benchmarks y velocidad
|
||||
## Benchmarks y velocidad { #benchmarks-and-speed }
|
||||
|
||||
Cuando ves los benchmarks, es común ver varias herramientas de diferentes tipos comparadas como equivalentes.
|
||||
|
||||
|
||||
@@ -1,15 +1,24 @@
|
||||
# Despliega FastAPI en Proveedores de Nube
|
||||
# Despliega FastAPI en Proveedores de Nube { #deploy-fastapi-on-cloud-providers }
|
||||
|
||||
Puedes usar prácticamente **cualquier proveedor de nube** para desplegar tu aplicación FastAPI.
|
||||
|
||||
En la mayoría de los casos, los principales proveedores de nube tienen guías para desplegar FastAPI con ellos.
|
||||
|
||||
## Proveedores de Nube - Sponsors
|
||||
## FastAPI Cloud { #fastapi-cloud }
|
||||
|
||||
Algunos proveedores de nube ✨ [**son sponsors de FastAPI**](../help-fastapi.md#sponsor-the-author){.internal-link target=_blank} ✨, esto asegura el desarrollo **continuado** y **saludable** de FastAPI y su **ecosistema**.
|
||||
**<a href="https://fastapicloud.com" class="external-link" target="_blank">FastAPI Cloud</a>** está construido por el mismo autor y equipo detrás de **FastAPI**.
|
||||
|
||||
Y muestra su verdadero compromiso con FastAPI y su **comunidad** (tú), ya que no solo quieren proporcionarte un **buen servicio**, sino también asegurarse de que tengas un **framework bueno y saludable**, FastAPI. 🙇
|
||||
Simplifica el proceso de **construir**, **desplegar** y **acceder** a una API con un esfuerzo mínimo.
|
||||
|
||||
Podrías querer probar sus servicios y seguir sus guías:
|
||||
Trae la misma experiencia de desarrollador de construir aplicaciones con FastAPI a desplegarlas en la nube. 🎉
|
||||
|
||||
FastAPI Cloud es el sponsor principal y proveedor de financiamiento de los proyectos open source *FastAPI and friends*. ✨
|
||||
|
||||
## Proveedores de Nube - Sponsors { #cloud-providers-sponsors }
|
||||
|
||||
Otros proveedores de nube ✨ [**son sponsors de FastAPI**](../help-fastapi.md#sponsor-the-author){.internal-link target=_blank} ✨ también. 🙇
|
||||
|
||||
También podrías considerarlos para seguir sus guías y probar sus servicios:
|
||||
|
||||
* <a href="https://docs.render.com/deploy-fastapi?utm_source=deploydoc&utm_medium=referral&utm_campaign=fastapi" class="external-link" target="_blank">Render</a>
|
||||
* <a href="https://docs.railway.com/guides/fastapi?utm_medium=integration&utm_source=docs&utm_campaign=fastapi" class="external-link" target="_blank">Railway</a>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Conceptos de Implementación
|
||||
# Conceptos de Implementación { #deployments-concepts }
|
||||
|
||||
Cuando implementas una aplicación **FastAPI**, o en realidad, cualquier tipo de API web, hay varios conceptos que probablemente te importen, y al entenderlos, puedes encontrar la **forma más adecuada** de **implementar tu aplicación**.
|
||||
|
||||
@@ -23,7 +23,7 @@ En los próximos capítulos, te daré más **recetas concretas** para implementa
|
||||
|
||||
Pero por ahora, revisemos estas importantes **ideas conceptuales**. Estos conceptos también se aplican a cualquier otro tipo de API web. 💡
|
||||
|
||||
## Seguridad - HTTPS
|
||||
## Seguridad - HTTPS { #security-https }
|
||||
|
||||
En el [capítulo anterior sobre HTTPS](https.md){.internal-link target=_blank} aprendimos sobre cómo HTTPS proporciona cifrado para tu API.
|
||||
|
||||
@@ -31,7 +31,7 @@ También vimos que HTTPS es normalmente proporcionado por un componente **extern
|
||||
|
||||
Y debe haber algo encargado de **renovar los certificados HTTPS**, podría ser el mismo componente o algo diferente.
|
||||
|
||||
### Herramientas de Ejemplo para HTTPS
|
||||
### Herramientas de Ejemplo para HTTPS { #example-tools-for-https }
|
||||
|
||||
Algunas de las herramientas que podrías usar como Proxy de Terminación TLS son:
|
||||
|
||||
@@ -55,11 +55,11 @@ Te mostraré algunos ejemplos concretos en los próximos capítulos.
|
||||
|
||||
Luego, los siguientes conceptos a considerar son todos acerca del programa que ejecuta tu API real (por ejemplo, Uvicorn).
|
||||
|
||||
## Programa y Proceso
|
||||
## Programa y Proceso { #program-and-process }
|
||||
|
||||
Hablaremos mucho sobre el "**proceso**" en ejecución, así que es útil tener claridad sobre lo que significa y cuál es la diferencia con la palabra "**programa**".
|
||||
|
||||
### Qué es un Programa
|
||||
### Qué es un Programa { #what-is-a-program }
|
||||
|
||||
La palabra **programa** se usa comúnmente para describir muchas cosas:
|
||||
|
||||
@@ -67,7 +67,7 @@ La palabra **programa** se usa comúnmente para describir muchas cosas:
|
||||
* El **archivo** que puede ser **ejecutado** por el sistema operativo, por ejemplo: `python`, `python.exe` o `uvicorn`.
|
||||
* Un programa específico mientras está siendo **ejecutado** en el sistema operativo, usando la CPU y almacenando cosas en la memoria. Esto también se llama **proceso**.
|
||||
|
||||
### Qué es un Proceso
|
||||
### Qué es un Proceso { #what-is-a-process }
|
||||
|
||||
La palabra **proceso** se usa normalmente de una manera más específica, refiriéndose solo a lo que está ejecutándose en el sistema operativo (como en el último punto anterior):
|
||||
|
||||
@@ -88,11 +88,11 @@ Y, por ejemplo, probablemente verás que hay múltiples procesos ejecutando el m
|
||||
|
||||
Ahora que conocemos la diferencia entre los términos **proceso** y **programa**, sigamos hablando sobre implementaciones.
|
||||
|
||||
## Ejecución al Iniciar
|
||||
## Ejecución al Iniciar { #running-on-startup }
|
||||
|
||||
En la mayoría de los casos, cuando creas una API web, quieres que esté **siempre en ejecución**, ininterrumpida, para que tus clientes puedan acceder a ella en cualquier momento. Esto, por supuesto, a menos que tengas una razón específica para que se ejecute solo en ciertas situaciones, pero la mayoría de las veces quieres que esté constantemente en ejecución y **disponible**.
|
||||
|
||||
### En un Servidor Remoto
|
||||
### En un Servidor Remoto { #in-a-remote-server }
|
||||
|
||||
Cuando configuras un servidor remoto (un servidor en la nube, una máquina virtual, etc.) lo más sencillo que puedes hacer es usar `fastapi run` (que utiliza Uvicorn) o algo similar, manualmente, de la misma manera que lo haces al desarrollar localmente.
|
||||
|
||||
@@ -102,15 +102,15 @@ Pero si pierdes la conexión con el servidor, el **proceso en ejecución** proba
|
||||
|
||||
Y si el servidor se reinicia (por ejemplo, después de actualizaciones o migraciones del proveedor de la nube) probablemente **no lo notarás**. Y debido a eso, ni siquiera sabrás que tienes que reiniciar el proceso manualmente. Así, tu API simplemente quedará muerta. 😱
|
||||
|
||||
### Ejecutar Automáticamente al Iniciar
|
||||
### Ejecutar Automáticamente al Iniciar { #run-automatically-on-startup }
|
||||
|
||||
En general, probablemente querrás que el programa del servidor (por ejemplo, Uvicorn) se inicie automáticamente al arrancar el servidor, y sin necesidad de ninguna **intervención humana**, para tener siempre un proceso en ejecución con tu API (por ejemplo, Uvicorn ejecutando tu aplicación FastAPI).
|
||||
|
||||
### Programa Separado
|
||||
### Programa Separado { #separate-program }
|
||||
|
||||
Para lograr esto, normalmente tendrás un **programa separado** que se asegurará de que tu aplicación se ejecute al iniciarse. Y en muchos casos, también se asegurará de que otros componentes o aplicaciones se ejecuten, por ejemplo, una base de datos.
|
||||
|
||||
### Herramientas de Ejemplo para Ejecutar al Iniciar
|
||||
### Herramientas de Ejemplo para Ejecutar al Iniciar { #example-tools-to-run-at-startup }
|
||||
|
||||
Algunos ejemplos de las herramientas que pueden hacer este trabajo son:
|
||||
|
||||
@@ -125,29 +125,29 @@ Algunos ejemplos de las herramientas que pueden hacer este trabajo son:
|
||||
|
||||
Te daré más ejemplos concretos en los próximos capítulos.
|
||||
|
||||
## Reinicios
|
||||
## Reinicios { #restarts }
|
||||
|
||||
De manera similar a asegurarte de que tu aplicación se ejecute al iniciar, probablemente también quieras asegurarte de que se **reinicie** después de fallos.
|
||||
|
||||
### Cometemos Errores
|
||||
### Cometemos Errores { #we-make-mistakes }
|
||||
|
||||
Nosotros, como humanos, cometemos **errores**, todo el tiempo. El software casi *siempre* tiene **bugs** ocultos en diferentes lugares. 🐛
|
||||
|
||||
Y nosotros, como desarrolladores, seguimos mejorando el código a medida que encontramos esos bugs y a medida que implementamos nuevas funcionalidades (posiblemente agregando nuevos bugs también 😅).
|
||||
|
||||
### Errores Pequeños Manejados Automáticamente
|
||||
### Errores Pequeños Manejados Automáticamente { #small-errors-automatically-handled }
|
||||
|
||||
Al construir APIs web con FastAPI, si hay un error en nuestro código, FastAPI normalmente lo contiene a la solicitud única que desencadenó el error. 🛡
|
||||
Al construir APIs web con FastAPI, si hay un error en nuestro código, FastAPI normalmente lo contiene al request único que desencadenó el error. 🛡
|
||||
|
||||
El cliente obtendrá un **500 Internal Server Error** para esa solicitud, pero la aplicación continuará funcionando para las siguientes solicitudes en lugar de simplemente colapsar por completo.
|
||||
El cliente obtendrá un **500 Internal Server Error** para ese request, pero la aplicación continuará funcionando para los siguientes requests en lugar de simplemente colapsar por completo.
|
||||
|
||||
### Errores Mayores - Colapsos
|
||||
### Errores Mayores - Colapsos { #bigger-errors-crashes }
|
||||
|
||||
Sin embargo, puede haber casos en los que escribamos algún código que **colapse toda la aplicación** haciendo que Uvicorn y Python colapsen. 💥
|
||||
|
||||
Y aún así, probablemente no querrías que la aplicación quede muerta porque hubo un error en un lugar, probablemente querrás que **siga ejecutándose** al menos para las *path operations* que no estén rotas.
|
||||
|
||||
### Reiniciar Después del Colapso
|
||||
### Reiniciar Después del Colapso { #restart-after-crash }
|
||||
|
||||
Pero en esos casos con errores realmente malos que colapsan el **proceso en ejecución**, querrías un componente externo encargado de **reiniciar** el proceso, al menos un par de veces...
|
||||
|
||||
@@ -161,7 +161,7 @@ Así que enfoquémonos en los casos principales, donde podría colapsar por comp
|
||||
|
||||
Probablemente querrías que la cosa encargada de reiniciar tu aplicación sea un **componente externo**, porque para ese punto, la misma aplicación con Uvicorn y Python ya colapsó, así que no hay nada en el mismo código de la misma aplicación que pueda hacer algo al respecto.
|
||||
|
||||
### Herramientas de Ejemplo para Reiniciar Automáticamente
|
||||
### Herramientas de Ejemplo para Reiniciar Automáticamente { #example-tools-to-restart-automatically }
|
||||
|
||||
En la mayoría de los casos, la misma herramienta que se utiliza para **ejecutar el programa al iniciar** también se utiliza para manejar reinicios automáticos.
|
||||
|
||||
@@ -176,19 +176,19 @@ Por ejemplo, esto podría ser manejado por:
|
||||
* Manejado internamente por un proveedor de nube como parte de sus servicios
|
||||
* Otros...
|
||||
|
||||
## Replicación - Procesos y Memoria
|
||||
## Replicación - Procesos y Memoria { #replication-processes-and-memory }
|
||||
|
||||
Con una aplicación FastAPI, usando un programa servidor como el comando `fastapi` que ejecuta Uvicorn, ejecutarlo una vez en **un proceso** puede servir a múltiples clientes concurrentemente.
|
||||
|
||||
Pero en muchos casos, querrás ejecutar varios worker processes al mismo tiempo.
|
||||
|
||||
### Múltiples Procesos - Workers
|
||||
### Múltiples Procesos - Workers { #multiple-processes-workers }
|
||||
|
||||
Si tienes más clientes de los que un solo proceso puede manejar (por ejemplo, si la máquina virtual no es muy grande) y tienes **múltiples núcleos** en la CPU del servidor, entonces podrías tener **múltiples procesos** ejecutando la misma aplicación al mismo tiempo, y distribuir todas las requests entre ellos.
|
||||
|
||||
Cuando ejecutas **múltiples procesos** del mismo programa de API, comúnmente se les llama **workers**.
|
||||
|
||||
### Worker Processes y Puertos
|
||||
### Worker Processes y Puertos { #worker-processes-and-ports }
|
||||
|
||||
Recuerda de la documentación [Sobre HTTPS](https.md){.internal-link target=_blank} que solo un proceso puede estar escuchando en una combinación de puerto y dirección IP en un servidor.
|
||||
|
||||
@@ -196,19 +196,19 @@ Esto sigue siendo cierto.
|
||||
|
||||
Así que, para poder tener **múltiples procesos** al mismo tiempo, tiene que haber un **solo proceso escuchando en un puerto** que luego transmita la comunicación a cada worker process de alguna forma.
|
||||
|
||||
### Memoria por Proceso
|
||||
### Memoria por Proceso { #memory-per-process }
|
||||
|
||||
Ahora, cuando el programa carga cosas en memoria, por ejemplo, un modelo de machine learning en una variable, o el contenido de un archivo grande en una variable, todo eso **consume un poco de la memoria (RAM)** del servidor.
|
||||
Ahora, cuando el programa carga cosas en memoria, por ejemplo, un modelo de Machine Learning en una variable, o el contenido de un archivo grande en una variable, todo eso **consume un poco de la memoria (RAM)** del servidor.
|
||||
|
||||
Y múltiples procesos normalmente **no comparten ninguna memoria**. Esto significa que cada proceso en ejecución tiene sus propias cosas, variables y memoria. Y si estás consumiendo una gran cantidad de memoria en tu código, **cada proceso** consumirá una cantidad equivalente de memoria.
|
||||
|
||||
### Memoria del Servidor
|
||||
### Memoria del Servidor { #server-memory }
|
||||
|
||||
Por ejemplo, si tu código carga un modelo de Machine Learning con **1 GB de tamaño**, cuando ejecutas un proceso con tu API, consumirá al menos 1 GB de RAM. Y si inicias **4 procesos** (4 workers), cada uno consumirá 1 GB de RAM. Así que, en total, tu API consumirá **4 GB de RAM**.
|
||||
|
||||
Y si tu servidor remoto o máquina virtual solo tiene 3 GB de RAM, intentar cargar más de 4 GB de RAM causará problemas. 🚨
|
||||
|
||||
### Múltiples Procesos - Un Ejemplo
|
||||
### Múltiples Procesos - Un Ejemplo { #multiple-processes-an-example }
|
||||
|
||||
En este ejemplo, hay un **Proceso Administrador** que inicia y controla dos **Worker Processes**.
|
||||
|
||||
@@ -224,7 +224,7 @@ Un detalle interesante es que el porcentaje de **CPU utilizado** por cada proces
|
||||
|
||||
Si tienes una API que hace una cantidad comparable de cálculos cada vez y tienes muchos clientes, entonces la **utilización de CPU** probablemente *también sea estable* (en lugar de constantemente subir y bajar rápidamente).
|
||||
|
||||
### Ejemplos de Herramientas y Estrategias de Replicación
|
||||
### Ejemplos de Herramientas y Estrategias de Replicación { #examples-of-replication-tools-and-strategies }
|
||||
|
||||
Puede haber varios enfoques para lograr esto, y te contaré más sobre estrategias específicas en los próximos capítulos, por ejemplo, al hablar sobre Docker y contenedores.
|
||||
|
||||
@@ -247,7 +247,7 @@ Te contaré más sobre imágenes de contenedores, Docker, Kubernetes, etc. en un
|
||||
|
||||
///
|
||||
|
||||
## Pasos Previos Antes de Iniciar
|
||||
## Pasos Previos Antes de Iniciar { #previous-steps-before-starting }
|
||||
|
||||
Hay muchos casos en los que quieres realizar algunos pasos **antes de iniciar** tu aplicación.
|
||||
|
||||
@@ -269,7 +269,7 @@ En ese caso, no tendrías que preocuparte por nada de esto. 🤷
|
||||
|
||||
///
|
||||
|
||||
### Ejemplos de Estrategias para Pasos Previos
|
||||
### Ejemplos de Estrategias para Pasos Previos { #examples-of-previous-steps-strategies }
|
||||
|
||||
Esto **dependerá mucho** de la forma en que **implementarás tu sistema**, y probablemente estará conectado con la forma en que inicias programas, manejas reinicios, etc.
|
||||
|
||||
@@ -285,7 +285,7 @@ Te daré más ejemplos concretos para hacer esto con contenedores en un capítul
|
||||
|
||||
///
|
||||
|
||||
## Utilización de Recursos
|
||||
## Utilización de Recursos { #resource-utilization }
|
||||
|
||||
Tu(s) servidor(es) es(son) un **recurso** que puedes consumir o **utilizar**, con tus programas, el tiempo de cómputo en las CPUs y la memoria RAM disponible.
|
||||
|
||||
@@ -305,7 +305,7 @@ Podrías establecer un **número arbitrario** para alcanzar, por ejemplo, algo *
|
||||
|
||||
Puedes usar herramientas simples como `htop` para ver la CPU y RAM utilizadas en tu servidor o la cantidad utilizada por cada proceso. O puedes usar herramientas de monitoreo más complejas, que pueden estar distribuidas a través de servidores, etc.
|
||||
|
||||
## Resumen
|
||||
## Resumen { #recap }
|
||||
|
||||
Has estado leyendo aquí algunos de los conceptos principales que probablemente necesitarás tener en mente al decidir cómo implementar tu aplicación:
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# FastAPI en Contenedores - Docker
|
||||
# FastAPI en Contenedores - Docker { #fastapi-in-containers-docker }
|
||||
|
||||
Al desplegar aplicaciones de FastAPI, un enfoque común es construir una **imagen de contenedor de Linux**. Normalmente se realiza usando <a href="https://www.docker.com/" class="external-link" target="_blank">**Docker**</a>. Luego puedes desplegar esa imagen de contenedor de varias formas.
|
||||
|
||||
@@ -6,7 +6,7 @@ Usar contenedores de Linux tiene varias ventajas, incluyendo **seguridad**, **re
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
¿Tienes prisa y ya conoces esto? Salta al [`Dockerfile` más abajo 👇](#construir-una-imagen-de-docker-para-fastapi).
|
||||
¿Tienes prisa y ya conoces esto? Salta al [`Dockerfile` más abajo 👇](#build-a-docker-image-for-fastapi).
|
||||
|
||||
///
|
||||
|
||||
@@ -32,7 +32,7 @@ CMD ["fastapi", "run", "app/main.py", "--port", "80"]
|
||||
|
||||
</details>
|
||||
|
||||
## Qué es un Contenedor
|
||||
## Qué es un Contenedor { #what-is-a-container }
|
||||
|
||||
Los contenedores (principalmente contenedores de Linux) son una forma muy **ligera** de empaquetar aplicaciones incluyendo todas sus dependencias y archivos necesarios, manteniéndolos aislados de otros contenedores (otras aplicaciones o componentes) en el mismo sistema.
|
||||
|
||||
@@ -42,11 +42,11 @@ De esta forma, los contenedores consumen **pocos recursos**, una cantidad compar
|
||||
|
||||
Los contenedores también tienen sus propios procesos de ejecución **aislados** (normalmente solo un proceso), sistema de archivos y red, simplificando el despliegue, la seguridad, el desarrollo, etc.
|
||||
|
||||
## Qué es una Imagen de Contenedor
|
||||
## Qué es una Imagen de Contenedor { #what-is-a-container-image }
|
||||
|
||||
Un **contenedor** se ejecuta desde una **imagen de contenedor**.
|
||||
|
||||
Una imagen de contenedor es una versión **estática** de todos los archivos, variables de entorno y el comando/programa por defecto que debería estar presente en un contenedor. **Estático** aquí significa que la imagen de contenedor **no se está ejecutando**, no está siendo ejecutada, son solo los archivos empaquetados y los metadatos.
|
||||
Una imagen de contenedor es una versión **estática** de todos los archivos, variables de entorno y el comando/programa por defecto que debería estar presente en un contenedor. **Estático** aquí significa que la **imagen** de contenedor no se está ejecutando, no está siendo ejecutada, son solo los archivos empaquetados y los metadatos.
|
||||
|
||||
En contraste con una "**imagen de contenedor**" que son los contenidos estáticos almacenados, un "**contenedor**" normalmente se refiere a la instance en ejecución, lo que está siendo **ejecutado**.
|
||||
|
||||
@@ -56,7 +56,7 @@ Una imagen de contenedor es comparable al archivo de **programa** y sus contenid
|
||||
|
||||
Y el **contenedor** en sí (en contraste con la **imagen de contenedor**) es la instance real en ejecución de la imagen, comparable a un **proceso**. De hecho, un contenedor solo se está ejecutando cuando tiene un **proceso en ejecución** (y normalmente es solo un proceso). El contenedor se detiene cuando no hay un proceso en ejecución en él.
|
||||
|
||||
## Imágenes de Contenedor
|
||||
## Imágenes de Contenedor { #container-images }
|
||||
|
||||
Docker ha sido una de las herramientas principales para crear y gestionar **imágenes de contenedor** y **contenedores**.
|
||||
|
||||
@@ -77,9 +77,9 @@ De esta manera, en muchos casos puedes aprender sobre contenedores y Docker y re
|
||||
|
||||
Así, ejecutarías **múltiples contenedores** con diferentes cosas, como una base de datos, una aplicación de Python, un servidor web con una aplicación frontend en React, y conectarlos entre sí a través de su red interna.
|
||||
|
||||
Todos los sistemas de gestión de contenedores (como Docker o Kubernetes) tienen estas características de redes integradas en ellos.
|
||||
Todos los sistemas de gestión de contenedores (como Docker o Kubernetes) tienen estas funcionalidades de redes integradas.
|
||||
|
||||
## Contenedores y Procesos
|
||||
## Contenedores y Procesos { #containers-and-processes }
|
||||
|
||||
Una **imagen de contenedor** normalmente incluye en sus metadatos el programa o comando por defecto que debería ser ejecutado cuando el **contenedor** se inicie y los parámetros que deben pasar a ese programa. Muy similar a lo que sería si estuviera en la línea de comandos.
|
||||
|
||||
@@ -91,7 +91,7 @@ Un contenedor normalmente tiene un **proceso único**, pero también es posible
|
||||
|
||||
Pero no es posible tener un contenedor en ejecución sin **al menos un proceso en ejecución**. Si el proceso principal se detiene, el contenedor se detiene.
|
||||
|
||||
## Construir una Imagen de Docker para FastAPI
|
||||
## Construir una Imagen de Docker para FastAPI { #build-a-docker-image-for-fastapi }
|
||||
|
||||
¡Bien, construyamos algo ahora! 🚀
|
||||
|
||||
@@ -103,7 +103,7 @@ Esto es lo que querrías hacer en **la mayoría de los casos**, por ejemplo:
|
||||
* Al ejecutar en un **Raspberry Pi**
|
||||
* Usando un servicio en la nube que ejecutaría una imagen de contenedor por ti, etc.
|
||||
|
||||
### Requisitos del Paquete
|
||||
### Requisitos del Paquete { #package-requirements }
|
||||
|
||||
Normalmente tendrías los **requisitos del paquete** para tu aplicación en algún archivo.
|
||||
|
||||
@@ -138,7 +138,7 @@ Existen otros formatos y herramientas para definir e instalar dependencias de pa
|
||||
|
||||
///
|
||||
|
||||
### Crear el Código de **FastAPI**
|
||||
### Crear el Código de **FastAPI** { #create-the-fastapi-code }
|
||||
|
||||
* Crea un directorio `app` y entra en él.
|
||||
* Crea un archivo vacío `__init__.py`.
|
||||
@@ -162,7 +162,7 @@ def read_item(item_id: int, q: Union[str, None] = None):
|
||||
return {"item_id": item_id, "q": q}
|
||||
```
|
||||
|
||||
### Dockerfile
|
||||
### Dockerfile { #dockerfile }
|
||||
|
||||
Ahora, en el mismo directorio del proyecto, crea un archivo `Dockerfile` con:
|
||||
|
||||
@@ -238,7 +238,7 @@ Asegúrate de **siempre** usar la **forma exec** de la instrucción `CMD`, como
|
||||
|
||||
///
|
||||
|
||||
#### Usar `CMD` - Forma Exec
|
||||
#### Usar `CMD` - Forma Exec { #use-cmd-exec-form }
|
||||
|
||||
La instrucción Docker <a href="https://docs.docker.com/reference/dockerfile/#cmd" class="external-link" target="_blank">`CMD`</a> se puede escribir usando dos formas:
|
||||
|
||||
@@ -262,20 +262,20 @@ Puedes leer más sobre esto en las <a href="https://docs.docker.com/reference/do
|
||||
|
||||
Esto puede ser bastante notorio al usar `docker compose`. Consulta esta sección de preguntas frecuentes de Docker Compose para más detalles técnicos: <a href="https://docs.docker.com/compose/faq/#why-do-my-services-take-10-seconds-to-recreate-or-stop" class="external-link" target="_blank">¿Por qué mis servicios tardan 10 segundos en recrearse o detenerse?</a>.
|
||||
|
||||
#### Estructura de Directorios
|
||||
#### Estructura de Directorios { #directory-structure }
|
||||
|
||||
Ahora deberías tener una estructura de directorios como:
|
||||
|
||||
```
|
||||
.
|
||||
├── app
|
||||
│ ├── __init__.py
|
||||
│ ├── __init__.py
|
||||
│ └── main.py
|
||||
├── Dockerfile
|
||||
└── requirements.txt
|
||||
```
|
||||
|
||||
#### Detrás de un Proxy de Terminación TLS
|
||||
#### Detrás de un Proxy de Terminación TLS { #behind-a-tls-termination-proxy }
|
||||
|
||||
Si estás ejecutando tu contenedor detrás de un Proxy de Terminación TLS (load balancer) como Nginx o Traefik, añade la opción `--proxy-headers`, esto le dirá a Uvicorn (a través de la CLI de FastAPI) que confíe en los headers enviados por ese proxy indicando que la aplicación se está ejecutando detrás de HTTPS, etc.
|
||||
|
||||
@@ -283,7 +283,7 @@ Si estás ejecutando tu contenedor detrás de un Proxy de Terminación TLS (load
|
||||
CMD ["fastapi", "run", "app/main.py", "--proxy-headers", "--port", "80"]
|
||||
```
|
||||
|
||||
#### Cache de Docker
|
||||
#### Caché de Docker { #docker-cache }
|
||||
|
||||
Hay un truco importante en este `Dockerfile`, primero copiamos **el archivo con las dependencias solo**, no el resto del código. Déjame decirte por qué es así.
|
||||
|
||||
@@ -315,7 +315,7 @@ Luego, cerca del final del `Dockerfile`, copiamos todo el código. Como esto es
|
||||
COPY ./app /code/app
|
||||
```
|
||||
|
||||
### Construir la Imagen de Docker
|
||||
### Construir la Imagen de Docker { #build-the-docker-image }
|
||||
|
||||
Ahora que todos los archivos están en su lugar, vamos a construir la imagen del contenedor.
|
||||
|
||||
@@ -340,7 +340,7 @@ En este caso, es el mismo directorio actual (`.`).
|
||||
|
||||
///
|
||||
|
||||
### Iniciar el Contenedor Docker
|
||||
### Iniciar el Contenedor Docker { #start-the-docker-container }
|
||||
|
||||
* Ejecuta un contenedor basado en tu imagen:
|
||||
|
||||
@@ -352,7 +352,7 @@ $ docker run -d --name mycontainer -p 80:80 myimage
|
||||
|
||||
</div>
|
||||
|
||||
## Revísalo
|
||||
## Revísalo { #check-it }
|
||||
|
||||
Deberías poder revisarlo en la URL de tu contenedor de Docker, por ejemplo: <a href="http://192.168.99.100/items/5?q=somequery" class="external-link" target="_blank">http://192.168.99.100/items/5?q=somequery</a> o <a href="http://127.0.0.1/items/5?q=somequery" class="external-link" target="_blank">http://127.0.0.1/items/5?q=somequery</a> (o equivalente, usando tu host de Docker).
|
||||
|
||||
@@ -362,7 +362,7 @@ Verás algo como:
|
||||
{"item_id": 5, "q": "somequery"}
|
||||
```
|
||||
|
||||
## Documentación Interactiva de la API
|
||||
## Documentación Interactiva de la API { #interactive-api-docs }
|
||||
|
||||
Ahora puedes ir a <a href="http://192.168.99.100/docs" class="external-link" target="_blank">http://192.168.99.100/docs</a> o <a href="http://127.0.0.1/docs" class="external-link" target="_blank">http://127.0.0.1/docs</a> (o equivalente, usando tu host de Docker).
|
||||
|
||||
@@ -370,7 +370,7 @@ Verás la documentación interactiva automática de la API (proporcionada por <a
|
||||
|
||||

|
||||
|
||||
## Documentación Alternativa de la API
|
||||
## Documentación Alternativa de la API { #alternative-api-docs }
|
||||
|
||||
Y también puedes ir a <a href="http://192.168.99.100/redoc" class="external-link" target="_blank">http://192.168.99.100/redoc</a> o <a href="http://127.0.0.1/redoc" class="external-link" target="_blank">http://127.0.0.1/redoc</a> (o equivalente, usando tu host de Docker).
|
||||
|
||||
@@ -378,7 +378,7 @@ Verás la documentación alternativa automática (proporcionada por <a href="htt
|
||||
|
||||

|
||||
|
||||
## Construir una Imagen de Docker con un FastAPI de Un Solo Archivo
|
||||
## Construir una Imagen de Docker con un FastAPI de Un Solo Archivo { #build-a-docker-image-with-a-single-file-fastapi }
|
||||
|
||||
Si tu FastAPI es un solo archivo, por ejemplo, `main.py` sin un directorio `./app`, tu estructura de archivos podría verse así:
|
||||
|
||||
@@ -413,7 +413,7 @@ CMD ["fastapi", "run", "main.py", "--port", "80"]
|
||||
|
||||
Cuando pasas el archivo a `fastapi run`, detectará automáticamente que es un archivo único y no parte de un paquete y sabrá cómo importarlo y servir tu aplicación FastAPI. 😎
|
||||
|
||||
## Conceptos de Despliegue
|
||||
## Conceptos de Despliegue { #deployment-concepts }
|
||||
|
||||
Hablemos nuevamente de algunos de los mismos [Conceptos de Despliegue](concepts.md){.internal-link target=_blank} en términos de contenedores.
|
||||
|
||||
@@ -430,7 +430,7 @@ Revisemos estos **conceptos de despliegue** en términos de contenedores:
|
||||
* Memoria
|
||||
* Pasos previos antes de comenzar
|
||||
|
||||
## HTTPS
|
||||
## HTTPS { #https }
|
||||
|
||||
Si nos enfocamos solo en la **imagen de contenedor** para una aplicación FastAPI (y luego el **contenedor** en ejecución), HTTPS normalmente sería manejado **externamente** por otra herramienta.
|
||||
|
||||
@@ -444,7 +444,7 @@ Traefik tiene integraciones con Docker, Kubernetes, y otros, por lo que es muy f
|
||||
|
||||
Alternativamente, HTTPS podría ser manejado por un proveedor de la nube como uno de sus servicios (mientras que la aplicación aún se ejecuta en un contenedor).
|
||||
|
||||
## Ejecutar en el Inicio y Reinicios
|
||||
## Ejecutar en el Inicio y Reinicios { #running-on-startup-and-restarts }
|
||||
|
||||
Normalmente hay otra herramienta encargada de **iniciar y ejecutar** tu contenedor.
|
||||
|
||||
@@ -454,7 +454,7 @@ En la mayoría (o todas) de las casos, hay una opción sencilla para habilitar l
|
||||
|
||||
Sin usar contenedores, hacer que las aplicaciones se ejecuten al inicio y con reinicios puede ser engorroso y difícil. Pero al **trabajar con contenedores** en la mayoría de los casos, esa funcionalidad se incluye por defecto. ✨
|
||||
|
||||
## Replicación - Número de Procesos
|
||||
## Replicación - Número de Procesos { #replication-number-of-processes }
|
||||
|
||||
Si tienes un <abbr title="Un grupo de máquinas que están configuradas para estar conectadas y trabajar juntas de alguna manera.">cluster</abbr> de máquinas con **Kubernetes**, Docker Swarm Mode, Nomad, u otro sistema complejo similar para gestionar contenedores distribuidos en varias máquinas, entonces probablemente querrás manejar la **replicación** a nivel de **cluster** en lugar de usar un **gestor de procesos** (como Uvicorn con workers) en cada contenedor.
|
||||
|
||||
@@ -462,7 +462,7 @@ Uno de esos sistemas de gestión de contenedores distribuidos como Kubernetes no
|
||||
|
||||
En esos casos, probablemente desearías construir una **imagen de Docker desde cero** como se [explica arriba](#dockerfile), instalando tus dependencias, y ejecutando **un solo proceso de Uvicorn** en lugar de usar múltiples workers de Uvicorn.
|
||||
|
||||
### Load Balancer
|
||||
### Load Balancer { #load-balancer }
|
||||
|
||||
Al usar contenedores, normalmente tendrías algún componente **escuchando en el puerto principal**. Podría posiblemente ser otro contenedor que es también un **Proxy de Terminación TLS** para manejar **HTTPS** o alguna herramienta similar.
|
||||
|
||||
@@ -476,17 +476,17 @@ El mismo componente **Proxy de Terminación TLS** usado para HTTPS probablemente
|
||||
|
||||
Y al trabajar con contenedores, el mismo sistema que usas para iniciarlos y gestionarlos ya tendría herramientas internas para transmitir la **comunicación en red** (e.g., requests HTTP) desde ese **load balancer** (que también podría ser un **Proxy de Terminación TLS**) a los contenedores con tu aplicación.
|
||||
|
||||
### Un Load Balancer - Múltiples Contenedores Worker
|
||||
### Un Load Balancer - Múltiples Contenedores Worker { #one-load-balancer-multiple-worker-containers }
|
||||
|
||||
Al trabajar con **Kubernetes** u otros sistemas de gestión de contenedores distribuidos similares, usar sus mecanismos de red internos permitiría que el único **load balancer** que está escuchando en el **puerto** principal transmita la comunicación (requests) a posiblemente **múltiples contenedores** ejecutando tu aplicación.
|
||||
|
||||
Cada uno de estos contenedores ejecutando tu aplicación normalmente tendría **solo un proceso** (e.g., un proceso Uvicorn ejecutando tu aplicación FastAPI). Todos serían **contenedores idénticos**, ejecutando lo mismo, pero cada uno con su propio proceso, memoria, etc. De esa forma, aprovecharías la **paralelización** en **diferentes núcleos** de la CPU, o incluso en **diferentes máquinas**.
|
||||
|
||||
Y el sistema de contenedores distribuido con el **load balancer** **distribuiría las requests** a cada uno de los contenedores **replicados** que ejecutan tu aplicación **en turnos**. Así, cada request podría ser manejado por uno de los múltiples **contenedores replicados** ejecutando tu aplicación.
|
||||
Y el sistema de contenedores distribuido con el **load balancer** **distribuiría las requests** a cada uno de los contenedores con tu aplicación **en turnos**. Así, cada request podría ser manejada por uno de los múltiples **contenedores replicados** ejecutando tu aplicación.
|
||||
|
||||
Y normalmente este **load balancer** podría manejar requests que vayan a *otras* aplicaciones en tu cluster (p. ej., a un dominio diferente, o bajo un prefijo de ruta de URL diferente), y transmitiría esa comunicación a los contenedores correctos para *esa otra* aplicación ejecutándose en tu cluster.
|
||||
Y normalmente este **load balancer** podría manejar requests que vayan a *otras* aplicaciones en tu cluster (p. ej., a un dominio diferente, o bajo un prefijo de path de URL diferente), y transmitiría esa comunicación a los contenedores correctos para *esa otra* aplicación ejecutándose en tu cluster.
|
||||
|
||||
### Un Proceso por Contenedor
|
||||
### Un Proceso por Contenedor { #one-process-per-container }
|
||||
|
||||
En este tipo de escenario, probablemente querrías tener **un solo proceso (Uvicorn) por contenedor**, ya que ya estarías manejando la replicación a nivel de cluster.
|
||||
|
||||
@@ -494,7 +494,7 @@ Así que, en este caso, **no** querrías tener múltiples workers en el contened
|
||||
|
||||
Tener otro gestor de procesos dentro del contenedor (como sería con múltiples workers) solo añadiría **complejidad innecesaria** que probablemente ya estés manejando con tu sistema de cluster.
|
||||
|
||||
### Contenedores con Múltiples Procesos y Casos Especiales
|
||||
### Contenedores con Múltiples Procesos y Casos Especiales { #containers-with-multiple-processes-and-special-cases }
|
||||
|
||||
Por supuesto, hay **casos especiales** donde podrías querer tener **un contenedor** con varios **worker processes de Uvicorn** dentro.
|
||||
|
||||
@@ -519,11 +519,11 @@ CMD ["fastapi", "run", "app/main.py", "--port", "80", "--workers", "4"]
|
||||
|
||||
Aquí hay algunos ejemplos de cuándo eso podría tener sentido:
|
||||
|
||||
#### Una Aplicación Simple
|
||||
#### Una Aplicación Simple { #a-simple-app }
|
||||
|
||||
Podrías querer un gestor de procesos en el contenedor si tu aplicación es **lo suficientemente simple** que pueda ejecutarse en un **servidor único**, no un cluster.
|
||||
|
||||
#### Docker Compose
|
||||
#### Docker Compose { #docker-compose }
|
||||
|
||||
Podrías estar desplegando en un **servidor único** (no un cluster) con **Docker Compose**, por lo que no tendrías una forma fácil de gestionar la replicación de contenedores (con Docker Compose) mientras se preserva la red compartida y el **load balancing**.
|
||||
|
||||
@@ -540,7 +540,7 @@ El punto principal es que, **ninguna** de estas son **reglas escritas en piedra*
|
||||
* Memoria
|
||||
* Pasos previos antes de comenzar
|
||||
|
||||
## Memoria
|
||||
## Memoria { #memory }
|
||||
|
||||
Si ejecutas **un solo proceso por contenedor**, tendrás una cantidad de memoria más o menos bien definida, estable y limitada consumida por cada uno de esos contenedores (más de uno si están replicados).
|
||||
|
||||
@@ -550,11 +550,11 @@ Si tu aplicación es **simple**, probablemente esto **no será un problema**, y
|
||||
|
||||
Si ejecutas **múltiples procesos por contenedor**, tendrás que asegurarte de que el número de procesos iniciados no **consuma más memoria** de la que está disponible.
|
||||
|
||||
## Pasos Previos Antes de Comenzar y Contenedores
|
||||
## Pasos Previos Antes de Comenzar y Contenedores { #previous-steps-before-starting-and-containers }
|
||||
|
||||
Si estás usando contenedores (por ejemplo, Docker, Kubernetes), entonces hay dos enfoques principales que puedes usar.
|
||||
|
||||
### Múltiples Contenedores
|
||||
### Múltiples Contenedores { #multiple-containers }
|
||||
|
||||
Si tienes **múltiples contenedores**, probablemente cada uno ejecutando un **proceso único** (por ejemplo, en un cluster de **Kubernetes**), entonces probablemente querrías tener un **contenedor separado** realizando el trabajo de los **pasos previos** en un solo contenedor, ejecutando un solo proceso, **antes** de ejecutar los contenedores worker replicados.
|
||||
|
||||
@@ -566,13 +566,13 @@ Si estás usando Kubernetes, probablemente sería un <a href="https://kubernetes
|
||||
|
||||
Si en tu caso de uso no hay problema en ejecutar esos pasos previos **múltiples veces en paralelo** (por ejemplo, si no estás ejecutando migraciones de base de datos, sino simplemente verificando si la base de datos está lista), entonces también podrías simplemente ponerlos en cada contenedor justo antes de iniciar el proceso principal.
|
||||
|
||||
### Un Contenedor Único
|
||||
### Un Contenedor Único { #single-container }
|
||||
|
||||
Si tienes una configuración simple, con un **contenedor único** que luego inicia múltiples **worker processes** (o también solo un proceso), entonces podrías ejecutar esos pasos previos en el mismo contenedor, justo antes de iniciar el proceso con la aplicación.
|
||||
|
||||
### Imagen Base de Docker
|
||||
### Imagen Base de Docker { #base-docker-image }
|
||||
|
||||
Solía haber una imagen official de Docker de FastAPI: <a href="https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker" class="external-link" target="_blank">tiangolo/uvicorn-gunicorn-fastapi</a>. Pero ahora está obsoleta. ⛔️
|
||||
Solía haber una imagen official de Docker de FastAPI: <a href="https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker" class="external-link" target="_blank">tiangolo/uvicorn-gunicorn-fastapi-docker</a>. Pero ahora está obsoleta. ⛔️
|
||||
|
||||
Probablemente **no** deberías usar esta imagen base de Docker (o cualquier otra similar).
|
||||
|
||||
@@ -588,7 +588,7 @@ Pero ahora que Uvicorn (y el comando `fastapi`) soportan el uso de `--workers`,
|
||||
|
||||
///
|
||||
|
||||
## Desplegar la Imagen del Contenedor
|
||||
## Desplegar la Imagen del Contenedor { #deploy-the-container-image }
|
||||
|
||||
Después de tener una Imagen de Contenedor (Docker) hay varias maneras de desplegarla.
|
||||
|
||||
@@ -600,11 +600,11 @@ Por ejemplo:
|
||||
* Con otra herramienta como Nomad
|
||||
* Con un servicio en la nube que tome tu imagen de contenedor y la despliegue
|
||||
|
||||
## Imagen de Docker con `uv`
|
||||
## Imagen de Docker con `uv` { #docker-image-with-uv }
|
||||
|
||||
Si estás usando <a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">uv</a> para instalar y gestionar tu proyecto, puedes seguir su <a href="https://docs.astral.sh/uv/guides/integration/docker/" class="external-link" target="_blank">guía de Docker de uv</a>.
|
||||
|
||||
## Resumen
|
||||
## Resumen { #recap }
|
||||
|
||||
Usando sistemas de contenedores (por ejemplo, con **Docker** y **Kubernetes**) se vuelve bastante sencillo manejar todos los **conceptos de despliegue**:
|
||||
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
# FastAPI Cloud { #fastapi-cloud }
|
||||
|
||||
Puedes desplegar tu app de FastAPI en <a href="https://fastapicloud.com" class="external-link" target="_blank">FastAPI Cloud</a> con un solo comando; ve y únete a la lista de espera si aún no lo has hecho. 🚀
|
||||
|
||||
## Iniciar sesión { #login }
|
||||
|
||||
Asegúrate de que ya tienes una cuenta de **FastAPI Cloud** (te invitamos desde la lista de espera 😉).
|
||||
|
||||
Luego inicia sesión:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ fastapi login
|
||||
|
||||
You are logged in to FastAPI Cloud 🚀
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
## Desplegar { #deploy }
|
||||
|
||||
Ahora despliega tu app, con un solo comando:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ fastapi deploy
|
||||
|
||||
Deploying to FastAPI Cloud...
|
||||
|
||||
✅ Deployment successful!
|
||||
|
||||
🐔 Ready the chicken! Your app is ready at https://myapp.fastapicloud.dev
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
¡Eso es todo! Ahora puedes acceder a tu app en esa URL. ✨
|
||||
|
||||
## Acerca de FastAPI Cloud { #about-fastapi-cloud }
|
||||
|
||||
**<a href="https://fastapicloud.com" class="external-link" target="_blank">FastAPI Cloud</a>** está creado por el mismo autor y equipo detrás de **FastAPI**.
|
||||
|
||||
Agiliza el proceso de **crear**, **desplegar** y **acceder** a una API con el mínimo esfuerzo.
|
||||
|
||||
Aporta la misma **experiencia de desarrollador** de crear apps con FastAPI al **desplegarlas** en la nube. 🎉
|
||||
|
||||
También se encargará de la mayoría de las cosas que necesitas al desplegar una app, como:
|
||||
|
||||
* HTTPS
|
||||
* Replicación, con autoescalado basado en requests
|
||||
* etc.
|
||||
|
||||
FastAPI Cloud es el sponsor principal y proveedor de financiación de los proyectos open source de *FastAPI and friends*. ✨
|
||||
|
||||
## Desplegar en otros proveedores de la nube { #deploy-to-other-cloud-providers }
|
||||
|
||||
FastAPI es open source y está basado en estándares. Puedes desplegar apps de FastAPI en cualquier proveedor de la nube que elijas.
|
||||
|
||||
Sigue las guías de tu proveedor de la nube para desplegar apps de FastAPI con ellos. 🤓
|
||||
|
||||
## Despliega tu propio servidor { #deploy-your-own-server }
|
||||
|
||||
También te enseñaré más adelante en esta guía de **Despliegue** todos los detalles, para que puedas entender qué está pasando, qué tiene que ocurrir o cómo desplegar apps de FastAPI por tu cuenta, también con tus propios servidores. 🤓
|
||||
@@ -1,4 +1,4 @@
|
||||
# Sobre HTTPS
|
||||
# Sobre HTTPS { #about-https }
|
||||
|
||||
Es fácil asumir que HTTPS es algo que simplemente está "activado" o no.
|
||||
|
||||
@@ -28,7 +28,7 @@ Ahora, desde una **perspectiva de desarrollador**, aquí hay varias cosas a tene
|
||||
* **Por defecto**, eso significaría que solo puedes tener **un certificado HTTPS por dirección IP**.
|
||||
* No importa cuán grande sea tu servidor o qué tan pequeña pueda ser cada aplicación que tengas en él.
|
||||
* Sin embargo, hay una **solución** para esto.
|
||||
* Hay una **extensión** para el protocolo **TLS** (el que maneja la encriptación a nivel de TCP, antes de HTTP) llamada **<a href="https://en.wikipedia.org/wiki/Server_Name_Indication" class="external-link" target="_blank"><abbr title="Server Name Indication">SNI</abbr></a>**.
|
||||
* Hay una **extensión** para el protocolo **TLS** (el que maneja la encriptación a nivel de TCP, antes de HTTP) llamada **<a href="https://en.wikipedia.org/wiki/Server_Name_Indication" class="external-link" target="_blank"><abbr title="Server Name Indication – Indicación del nombre del servidor">SNI</abbr></a>**.
|
||||
* Esta extensión SNI permite que un solo servidor (con una **sola dirección IP**) tenga **varios certificados HTTPS** y sirva **múltiples dominios/aplicaciones HTTPS**.
|
||||
* Para que esto funcione, un componente (programa) **único** que se ejecute en el servidor, escuchando en la **dirección IP pública**, debe tener **todos los certificados HTTPS** en el servidor.
|
||||
* **Después** de obtener una conexión segura, el protocolo de comunicación sigue siendo **HTTP**.
|
||||
@@ -43,7 +43,7 @@ Algunas de las opciones que podrías usar como un TLS Termination Proxy son:
|
||||
* Nginx
|
||||
* HAProxy
|
||||
|
||||
## Let's Encrypt
|
||||
## Let's Encrypt { #lets-encrypt }
|
||||
|
||||
Antes de Let's Encrypt, estos **certificados HTTPS** eran vendidos por terceros.
|
||||
|
||||
@@ -51,17 +51,17 @@ El proceso para adquirir uno de estos certificados solía ser complicado, requer
|
||||
|
||||
Pero luego se creó **<a href="https://letsencrypt.org/" class="external-link" target="_blank">Let's Encrypt</a>**.
|
||||
|
||||
Es un proyecto de la Linux Foundation. Proporciona **certificados HTTPS de forma gratuita**, de manera automatizada. Estos certificados usan toda la seguridad criptográfica estándar, y tienen una corta duración (aproximadamente 3 meses), por lo que la **seguridad es en realidad mejor** debido a su corta vida útil.
|
||||
Es un proyecto de la Linux Foundation. Proporciona **certificados HTTPS de forma gratuita**, de manera automatizada. Estos certificados usan toda la seguridad criptográfica estándar, y tienen una corta duración (aproximadamente 3 meses), por lo que la **seguridad es en realidad mejor** debido a su lifespan reducida.
|
||||
|
||||
Los dominios son verificados de manera segura y los certificados se generan automáticamente. Esto también permite automatizar la renovación de estos certificados.
|
||||
|
||||
La idea es automatizar la adquisición y renovación de estos certificados para que puedas tener **HTTPS seguro, gratuito, para siempre**.
|
||||
|
||||
## HTTPS para Desarrolladores
|
||||
## HTTPS para Desarrolladores { #https-for-developers }
|
||||
|
||||
Aquí tienes un ejemplo de cómo podría ser una API HTTPS, paso a paso, prestando atención principalmente a las ideas importantes para los desarrolladores.
|
||||
|
||||
### Nombre de Dominio
|
||||
### Nombre de Dominio { #domain-name }
|
||||
|
||||
Probablemente todo comenzaría adquiriendo un **nombre de dominio**. Luego, lo configurarías en un servidor DNS (posiblemente tu mismo proveedor de la nube).
|
||||
|
||||
@@ -77,7 +77,7 @@ Esta parte del Nombre de Dominio es mucho antes de HTTPS, pero como todo depende
|
||||
|
||||
///
|
||||
|
||||
### DNS
|
||||
### DNS { #dns }
|
||||
|
||||
Ahora centrémonos en todas las partes realmente de HTTPS.
|
||||
|
||||
@@ -87,7 +87,7 @@ Los servidores DNS le dirían al navegador que use una **dirección IP** especí
|
||||
|
||||
<img src="/img/deployment/https/https01.drawio.svg">
|
||||
|
||||
### Inicio del Handshake TLS
|
||||
### Inicio del Handshake TLS { #tls-handshake-start }
|
||||
|
||||
El navegador luego se comunicaría con esa dirección IP en el **puerto 443** (el puerto HTTPS).
|
||||
|
||||
@@ -97,7 +97,7 @@ La primera parte de la comunicación es solo para establecer la conexión entre
|
||||
|
||||
Esta interacción entre el cliente y el servidor para establecer la conexión TLS se llama **handshake TLS**.
|
||||
|
||||
### TLS con Extensión SNI
|
||||
### TLS con Extensión SNI { #tls-with-sni-extension }
|
||||
|
||||
**Solo un proceso** en el servidor puede estar escuchando en un **puerto** específico en una **dirección IP** específica. Podría haber otros procesos escuchando en otros puertos en la misma dirección IP, pero solo uno para cada combinación de dirección IP y puerto.
|
||||
|
||||
@@ -127,7 +127,7 @@ Ten en cuenta que la encriptación de la comunicación ocurre a nivel de **TCP**
|
||||
|
||||
///
|
||||
|
||||
### Request HTTPS
|
||||
### Request HTTPS { #https-request }
|
||||
|
||||
Ahora que el cliente y el servidor (específicamente el navegador y el TLS Termination Proxy) tienen una **conexión TCP encriptada**, pueden iniciar la **comunicación HTTP**.
|
||||
|
||||
@@ -135,19 +135,19 @@ Así que, el cliente envía un **request HTTPS**. Esto es simplemente un request
|
||||
|
||||
<img src="/img/deployment/https/https04.drawio.svg">
|
||||
|
||||
### Desencriptar el Request
|
||||
### Desencriptar el Request { #decrypt-the-request }
|
||||
|
||||
El TLS Termination Proxy usaría la encriptación acordada para **desencriptar el request**, y transmitiría el **request HTTP simple (desencriptado)** al proceso que ejecuta la aplicación (por ejemplo, un proceso con Uvicorn ejecutando la aplicación FastAPI).
|
||||
|
||||
<img src="/img/deployment/https/https05.drawio.svg">
|
||||
|
||||
### Response HTTP
|
||||
### Response HTTP { #http-response }
|
||||
|
||||
La aplicación procesaría el request y enviaría un **response HTTP simple (sin encriptar)** al TLS Termination Proxy.
|
||||
|
||||
<img src="/img/deployment/https/https06.drawio.svg">
|
||||
|
||||
### Response HTTPS
|
||||
### Response HTTPS { #https-response }
|
||||
|
||||
El TLS Termination Proxy entonces **encriptaría el response** usando la criptografía acordada antes (que comenzó con el certificado para `someapp.example.com`), y lo enviaría de vuelta al navegador.
|
||||
|
||||
@@ -157,7 +157,7 @@ Luego, el navegador verificaría que el response sea válido y encriptado con la
|
||||
|
||||
El cliente (navegador) sabrá que el response proviene del servidor correcto porque está utilizando la criptografía que acordaron usando el **certificado HTTPS** anteriormente.
|
||||
|
||||
### Múltiples Aplicaciones
|
||||
### Múltiples Aplicaciones { #multiple-applications }
|
||||
|
||||
En el mismo servidor (o servidores), podrían haber **múltiples aplicaciones**, por ejemplo, otros programas API o una base de datos.
|
||||
|
||||
@@ -167,7 +167,7 @@ Solo un proceso puede estar gestionando la IP y puerto específica (el TLS Termi
|
||||
|
||||
De esa manera, el TLS Termination Proxy podría gestionar HTTPS y certificados para **múltiples dominios**, para múltiples aplicaciones, y luego transmitir los requests a la aplicación correcta en cada caso.
|
||||
|
||||
### Renovación de Certificados
|
||||
### Renovación de Certificados { #certificate-renewal }
|
||||
|
||||
En algún momento en el futuro, cada certificado **expiraría** (alrededor de 3 meses después de haberlo adquirido).
|
||||
|
||||
@@ -190,7 +190,39 @@ Para hacer eso, y para acomodar diferentes necesidades de aplicaciones, hay vari
|
||||
|
||||
Todo este proceso de renovación, mientras aún se sirve la aplicación, es una de las principales razones por las que querrías tener un **sistema separado para gestionar el HTTPS** con un TLS Termination Proxy en lugar de simplemente usar los certificados TLS con el servidor de aplicaciones directamente (por ejemplo, Uvicorn).
|
||||
|
||||
## Resumen
|
||||
## Headers reenviados por el proxy { #proxy-forwarded-headers }
|
||||
|
||||
Al usar un proxy para gestionar HTTPS, tu **servidor de aplicaciones** (por ejemplo Uvicorn vía FastAPI CLI) no sabe nada sobre el proceso HTTPS, se comunica con HTTP simple con el **TLS Termination Proxy**.
|
||||
|
||||
Este **proxy** normalmente configuraría algunos headers HTTP sobre la marcha antes de transmitir el request al **servidor de aplicaciones**, para hacerle saber al servidor de aplicaciones que el request está siendo **reenviado** por el proxy.
|
||||
|
||||
/// note | Detalles técnicos
|
||||
|
||||
Los headers del proxy son:
|
||||
|
||||
* <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-For" class="external-link" target="_blank">X-Forwarded-For</a>
|
||||
* <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Proto" class="external-link" target="_blank">X-Forwarded-Proto</a>
|
||||
* <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Host" class="external-link" target="_blank">X-Forwarded-Host</a>
|
||||
|
||||
///
|
||||
|
||||
Aun así, como el **servidor de aplicaciones** no sabe que está detrás de un **proxy** de confianza, por defecto, no confiaría en esos headers.
|
||||
|
||||
Pero puedes configurar el **servidor de aplicaciones** para confiar en los headers reenviados enviados por el **proxy**. Si estás usando FastAPI CLI, puedes usar la *Opción de la CLI* `--forwarded-allow-ips` para indicarle desde qué IPs debería confiar en esos headers reenviados.
|
||||
|
||||
Por ejemplo, si el **servidor de aplicaciones** solo está recibiendo comunicación del **proxy** de confianza, puedes establecerlo en `--forwarded-allow-ips="*"` para hacer que confíe en todas las IPs entrantes, ya que solo recibirá requests desde la IP que sea utilizada por el **proxy**.
|
||||
|
||||
De esta manera la aplicación podrá saber cuál es su propia URL pública, si está usando HTTPS, el dominio, etc.
|
||||
|
||||
Esto sería útil, por ejemplo, para manejar correctamente redirecciones.
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
Puedes aprender más sobre esto en la documentación de [Detrás de un proxy - Habilitar headers reenviados por el proxy](../advanced/behind-a-proxy.md#enable-proxy-forwarded-headers){.internal-link target=_blank}
|
||||
|
||||
///
|
||||
|
||||
## Resumen { #recap }
|
||||
|
||||
Tener **HTTPS** es muy importante y bastante **crítico** en la mayoría de los casos. La mayor parte del esfuerzo que como desarrollador tienes que poner en torno a HTTPS es solo sobre **entender estos conceptos** y cómo funcionan.
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# Despliegue
|
||||
# Despliegue { #deployment }
|
||||
|
||||
Desplegar una aplicación **FastAPI** es relativamente fácil.
|
||||
|
||||
## Qué Significa Despliegue
|
||||
## Qué Significa Despliegue { #what-does-deployment-mean }
|
||||
|
||||
**Desplegar** una aplicación significa realizar los pasos necesarios para hacerla **disponible para los usuarios**.
|
||||
|
||||
@@ -10,12 +10,14 @@ Para una **API web**, normalmente implica ponerla en una **máquina remota**, co
|
||||
|
||||
Esto contrasta con las etapas de **desarrollo**, donde estás constantemente cambiando el código, rompiéndolo y arreglándolo, deteniendo y reiniciando el servidor de desarrollo, etc.
|
||||
|
||||
## Estrategias de Despliegue
|
||||
## Estrategias de Despliegue { #deployment-strategies }
|
||||
|
||||
Hay varias maneras de hacerlo dependiendo de tu caso de uso específico y las herramientas que utilices.
|
||||
|
||||
Podrías **desplegar un servidor** tú mismo utilizando una combinación de herramientas, podrías usar un **servicio en la nube** que hace parte del trabajo por ti, u otras opciones posibles.
|
||||
|
||||
Por ejemplo, nosotros, el equipo detrás de FastAPI, construimos <a href="https://fastapicloud.com" class="external-link" target="_blank">**FastAPI Cloud**</a>, para hacer que desplegar aplicaciones de FastAPI en la nube sea lo más ágil posible, con la misma experiencia de desarrollador de trabajar con FastAPI.
|
||||
|
||||
Te mostraré algunos de los conceptos principales que probablemente deberías tener en cuenta al desplegar una aplicación **FastAPI** (aunque la mayoría se aplica a cualquier otro tipo de aplicación web).
|
||||
|
||||
Verás más detalles a tener en cuenta y algunas de las técnicas para hacerlo en las siguientes secciones. ✨
|
||||
|
||||
@@ -1,51 +1,39 @@
|
||||
# Ejecutar un Servidor Manualmente
|
||||
# Ejecutar un Servidor Manualmente { #run-a-server-manually }
|
||||
|
||||
## Usa el Comando `fastapi run`
|
||||
## Usa el Comando `fastapi run` { #use-the-fastapi-run-command }
|
||||
|
||||
En resumen, usa `fastapi run` para servir tu aplicación FastAPI:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ <font color="#4E9A06">fastapi</font> run <u style="text-decoration-style:single">main.py</u>
|
||||
<font color="#3465A4">INFO </font> Usando path <font color="#3465A4">main.py</font>
|
||||
<font color="#3465A4">INFO </font> Path absoluto resuelto <font color="#75507B">/home/user/code/awesomeapp/</font><font color="#AD7FA8">main.py</font>
|
||||
<font color="#3465A4">INFO </font> Buscando una estructura de archivos de paquete desde directorios con archivos <font color="#3465A4">__init__.py</font>
|
||||
<font color="#3465A4">INFO </font> Importando desde <font color="#75507B">/home/user/code/</font><font color="#AD7FA8">awesomeapp</font>
|
||||
$ <font color="#4E9A06">fastapi</font> run <u style="text-decoration-style:solid">main.py</u>
|
||||
|
||||
╭─ <font color="#8AE234"><b>Archivo de módulo de Python</b></font> ─╮
|
||||
│ │
|
||||
│ 🐍 main.py │
|
||||
│ │
|
||||
╰──────────────────────╯
|
||||
<span style="background-color:#009485"><font color="#D3D7CF"> FastAPI </font></span> Starting production server 🚀
|
||||
|
||||
<font color="#3465A4">INFO </font> Importando módulo <font color="#4E9A06">main</font>
|
||||
<font color="#3465A4">INFO </font> Encontrada aplicación FastAPI importable
|
||||
Searching for package file structure from directories
|
||||
with <font color="#3465A4">__init__.py</font> files
|
||||
Importing from <font color="#75507B">/home/user/code/</font><font color="#AD7FA8">awesomeapp</font>
|
||||
|
||||
╭─ <font color="#8AE234"><b>Aplicación FastAPI importable</b></font> ─╮
|
||||
│ │
|
||||
│ <span style="background-color:#272822"><font color="#FF4689">from</font></span><span style="background-color:#272822"><font color="#F8F8F2"> main </font></span><span style="background-color:#272822"><font color="#FF4689">import</font></span><span style="background-color:#272822"><font color="#F8F8F2"> app</font></span><span style="background-color:#272822"> </span> │
|
||||
│ │
|
||||
╰──────────────────────────╯
|
||||
<span style="background-color:#007166"><font color="#D3D7CF"> module </font></span> 🐍 main.py
|
||||
|
||||
<font color="#3465A4">INFO </font> Usando la cadena de import <font color="#8AE234"><b>main:app</b></font>
|
||||
<span style="background-color:#007166"><font color="#D3D7CF"> code </font></span> Importing the FastAPI app object from the module with
|
||||
the following code:
|
||||
|
||||
<font color="#4E9A06">╭─────────── CLI de FastAPI - Modo Producción ───────────╮</font>
|
||||
<font color="#4E9A06">│ │</font>
|
||||
<font color="#4E9A06">│ Sirviendo en: http://0.0.0.0:8000 │</font>
|
||||
<font color="#4E9A06">│ │</font>
|
||||
<font color="#4E9A06">│ Docs de API: http://0.0.0.0:8000/docs │</font>
|
||||
<font color="#4E9A06">│ │</font>
|
||||
<font color="#4E9A06">│ Corriendo en modo producción, para desarrollo usa: │</font>
|
||||
<font color="#4E9A06">│ │</font>
|
||||
<font color="#4E9A06">│ </font><font color="#8AE234"><b>fastapi dev</b></font><font color="#4E9A06"> │</font>
|
||||
<font color="#4E9A06">│ │</font>
|
||||
<font color="#4E9A06">╰─────────────────────────────────────────────────────╯</font>
|
||||
<u style="text-decoration-style:solid">from </u><u style="text-decoration-style:solid"><b>main</b></u><u style="text-decoration-style:solid"> import </u><u style="text-decoration-style:solid"><b>app</b></u>
|
||||
|
||||
<font color="#4E9A06">INFO</font>: Iniciado el proceso del servidor [<font color="#06989A">2306215</font>]
|
||||
<font color="#4E9A06">INFO</font>: Esperando el inicio de la aplicación.
|
||||
<font color="#4E9A06">INFO</font>: Inicio de la aplicación completado.
|
||||
<font color="#4E9A06">INFO</font>: Uvicorn corriendo en <b>http://0.0.0.0:8000</b> (Presiona CTRL+C para salir)
|
||||
<span style="background-color:#007166"><font color="#D3D7CF"> app </font></span> Using import string: <font color="#3465A4">main:app</font>
|
||||
|
||||
<span style="background-color:#007166"><font color="#D3D7CF"> server </font></span> Server started at <font color="#729FCF"><u style="text-decoration-style:solid">http://0.0.0.0:8000</u></font>
|
||||
<span style="background-color:#007166"><font color="#D3D7CF"> server </font></span> Documentation at <font color="#729FCF"><u style="text-decoration-style:solid">http://0.0.0.0:8000/docs</u></font>
|
||||
|
||||
Logs:
|
||||
|
||||
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Started server process <b>[</b><font color="#34E2E2"><b>2306215</b></font><b>]</b>
|
||||
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Waiting for application startup.
|
||||
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Application startup complete.
|
||||
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Uvicorn running on <font color="#729FCF"><u style="text-decoration-style:solid">http://0.0.0.0:8000</u></font> <b>(</b>Press CTRL+C
|
||||
to quit<b>)</b>
|
||||
```
|
||||
|
||||
</div>
|
||||
@@ -54,11 +42,11 @@ Eso funcionaría para la mayoría de los casos. 😎
|
||||
|
||||
Podrías usar ese comando, por ejemplo, para iniciar tu app **FastAPI** en un contenedor, en un servidor, etc.
|
||||
|
||||
## Servidores ASGI
|
||||
## Servidores ASGI { #asgi-servers }
|
||||
|
||||
Vamos a profundizar un poquito en los detalles.
|
||||
|
||||
FastAPI usa un estándar para construir frameworks de web y servidores de Python llamado <abbr title="Asynchronous Server Gateway Interface">ASGI</abbr>. FastAPI es un framework web ASGI.
|
||||
FastAPI usa un estándar para construir frameworks de web y servidores de Python llamado <abbr title="Asynchronous Server Gateway Interface – Interfaz de puerta de enlace de servidor asíncrona">ASGI</abbr>. FastAPI es un framework web ASGI.
|
||||
|
||||
Lo principal que necesitas para ejecutar una aplicación **FastAPI** (o cualquier otra aplicación ASGI) en una máquina de servidor remota es un programa de servidor ASGI como **Uvicorn**, que es el que viene por defecto en el comando `fastapi`.
|
||||
|
||||
@@ -70,7 +58,7 @@ Hay varias alternativas, incluyendo:
|
||||
* <a href="https://github.com/emmett-framework/granian" class="external-link" target="_blank">Granian</a>: Un servidor HTTP Rust para aplicaciones en Python.
|
||||
* <a href="https://unit.nginx.org/howto/fastapi/" class="external-link" target="_blank">NGINX Unit</a>: NGINX Unit es un runtime para aplicaciones web ligero y versátil.
|
||||
|
||||
## Máquina Servidor y Programa Servidor
|
||||
## Máquina Servidor y Programa Servidor { #server-machine-and-server-program }
|
||||
|
||||
Hay un pequeño detalle sobre los nombres que hay que tener en cuenta. 💡
|
||||
|
||||
@@ -80,7 +68,7 @@ Solo ten en cuenta que cuando leas "servidor" en general, podría referirse a un
|
||||
|
||||
Al referirse a la máquina remota, es común llamarla **servidor**, pero también **máquina**, **VM** (máquina virtual), **nodo**. Todos esos se refieren a algún tipo de máquina remota, generalmente con Linux, donde ejecutas programas.
|
||||
|
||||
## Instala el Programa del Servidor
|
||||
## Instala el Programa del Servidor { #install-the-server-program }
|
||||
|
||||
Cuando instalas FastAPI, viene con un servidor de producción, Uvicorn, y puedes iniciarlo con el comando `fastapi run`.
|
||||
|
||||
@@ -112,7 +100,7 @@ Cuando instalas FastAPI con algo como `pip install "fastapi[standard]"` ya obtie
|
||||
|
||||
///
|
||||
|
||||
## Ejecuta el Programa del Servidor
|
||||
## Ejecuta el Programa del Servidor { #run-the-server-program }
|
||||
|
||||
Si instalaste un servidor ASGI manualmente, normalmente necesitarías pasar una cadena de import en un formato especial para que importe tu aplicación FastAPI:
|
||||
|
||||
@@ -121,7 +109,7 @@ Si instalaste un servidor ASGI manualmente, normalmente necesitarías pasar una
|
||||
```console
|
||||
$ uvicorn main:app --host 0.0.0.0 --port 80
|
||||
|
||||
<span style="color: green;">INFO</span>: Uvicorn corriendo en http://0.0.0.0:80 (Presiona CTRL+C para salir)
|
||||
<span style="color: green;">INFO</span>: Uvicorn running on http://0.0.0.0:80 (Press CTRL+C to quit)
|
||||
```
|
||||
|
||||
</div>
|
||||
@@ -153,7 +141,7 @@ Ayuda mucho durante el **desarrollo**, pero **no** deberías usarla en **producc
|
||||
|
||||
///
|
||||
|
||||
## Conceptos de Despliegue
|
||||
## Conceptos de Despliegue { #deployment-concepts }
|
||||
|
||||
Estos ejemplos ejecutan el programa del servidor (por ejemplo, Uvicorn), iniciando **un solo proceso**, escuchando en todas las IPs (`0.0.0.0`) en un puerto predefinido (por ejemplo, `80`).
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Servidores Workers - Uvicorn con Workers
|
||||
# Servidores Workers - Uvicorn con Workers { #server-workers-uvicorn-with-workers }
|
||||
|
||||
Vamos a revisar esos conceptos de despliegue de antes:
|
||||
|
||||
@@ -25,7 +25,7 @@ En particular, cuando corras en **Kubernetes** probablemente **no** querrás usa
|
||||
|
||||
///
|
||||
|
||||
## Múltiples Workers
|
||||
## Múltiples Workers { #multiple-workers }
|
||||
|
||||
Puedes iniciar múltiples workers con la opción de línea de comando `--workers`:
|
||||
|
||||
@@ -36,56 +36,43 @@ Si usas el comando `fastapi`:
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ <pre> <font color="#4E9A06">fastapi</font> run --workers 4 <u style="text-decoration-style:single">main.py</u>
|
||||
<font color="#3465A4">INFO </font> Using path <font color="#3465A4">main.py</font>
|
||||
<font color="#3465A4">INFO </font> Resolved absolute path <font color="#75507B">/home/user/code/awesomeapp/</font><font color="#AD7FA8">main.py</font>
|
||||
<font color="#3465A4">INFO </font> Searching for package file structure from directories with <font color="#3465A4">__init__.py</font> files
|
||||
<font color="#3465A4">INFO </font> Importing from <font color="#75507B">/home/user/code/</font><font color="#AD7FA8">awesomeapp</font>
|
||||
$ <font color="#4E9A06">fastapi</font> run --workers 4 <u style="text-decoration-style:solid">main.py</u>
|
||||
|
||||
╭─ <font color="#8AE234"><b>Python module file</b></font> ─╮
|
||||
│ │
|
||||
│ 🐍 main.py │
|
||||
│ │
|
||||
╰──────────────────────╯
|
||||
<span style="background-color:#009485"><font color="#D3D7CF"> FastAPI </font></span> Starting production server 🚀
|
||||
|
||||
<font color="#3465A4">INFO </font> Importing module <font color="#4E9A06">main</font>
|
||||
<font color="#3465A4">INFO </font> Found importable FastAPI app
|
||||
Searching for package file structure from directories with
|
||||
<font color="#3465A4">__init__.py</font> files
|
||||
Importing from <font color="#75507B">/home/user/code/</font><font color="#AD7FA8">awesomeapp</font>
|
||||
|
||||
╭─ <font color="#8AE234"><b>Importable FastAPI app</b></font> ─╮
|
||||
│ │
|
||||
│ <span style="background-color:#272822"><font color="#FF4689">from</font></span><span style="background-color:#272822"><font color="#F8F8F2"> main </font></span><span style="background-color:#272822"><font color="#FF4689">import</font></span><span style="background-color:#272822"><font color="#F8F8F2"> app</font></span><span style="background-color:#272822"> </span> │
|
||||
│ │
|
||||
╰──────────────────────────╯
|
||||
<span style="background-color:#007166"><font color="#D3D7CF"> module </font></span> 🐍 main.py
|
||||
|
||||
<font color="#3465A4">INFO </font> Using import string <font color="#8AE234"><b>main:app</b></font>
|
||||
<span style="background-color:#007166"><font color="#D3D7CF"> code </font></span> Importing the FastAPI app object from the module with the
|
||||
following code:
|
||||
|
||||
<font color="#4E9A06">╭─────────── FastAPI CLI - Production mode ───────────╮</font>
|
||||
<font color="#4E9A06">│ │</font>
|
||||
<font color="#4E9A06">│ Serving at: http://0.0.0.0:8000 │</font>
|
||||
<font color="#4E9A06">│ │</font>
|
||||
<font color="#4E9A06">│ API docs: http://0.0.0.0:8000/docs │</font>
|
||||
<font color="#4E9A06">│ │</font>
|
||||
<font color="#4E9A06">│ Running in production mode, for development use: │</font>
|
||||
<font color="#4E9A06">│ │</font>
|
||||
<font color="#4E9A06">│ </font><font color="#8AE234"><b>fastapi dev</b></font><font color="#4E9A06"> │</font>
|
||||
<font color="#4E9A06">│ │</font>
|
||||
<font color="#4E9A06">╰─────────────────────────────────────────────────────╯</font>
|
||||
<u style="text-decoration-style:solid">from </u><u style="text-decoration-style:solid"><b>main</b></u><u style="text-decoration-style:solid"> import </u><u style="text-decoration-style:solid"><b>app</b></u>
|
||||
|
||||
<font color="#4E9A06">INFO</font>: Uvicorn running on <b>http://0.0.0.0:8000</b> (Press CTRL+C to quit)
|
||||
<font color="#4E9A06">INFO</font>: Started parent process [<font color="#34E2E2"><b>27365</b></font>]
|
||||
<font color="#4E9A06">INFO</font>: Started server process [<font color="#06989A">27368</font>]
|
||||
<font color="#4E9A06">INFO</font>: Waiting for application startup.
|
||||
<font color="#4E9A06">INFO</font>: Application startup complete.
|
||||
<font color="#4E9A06">INFO</font>: Started server process [<font color="#06989A">27369</font>]
|
||||
<font color="#4E9A06">INFO</font>: Waiting for application startup.
|
||||
<font color="#4E9A06">INFO</font>: Application startup complete.
|
||||
<font color="#4E9A06">INFO</font>: Started server process [<font color="#06989A">27370</font>]
|
||||
<font color="#4E9A06">INFO</font>: Waiting for application startup.
|
||||
<font color="#4E9A06">INFO</font>: Application startup complete.
|
||||
<font color="#4E9A06">INFO</font>: Started server process [<font color="#06989A">27367</font>]
|
||||
<font color="#4E9A06">INFO</font>: Waiting for application startup.
|
||||
<font color="#4E9A06">INFO</font>: Application startup complete.
|
||||
</pre>
|
||||
<span style="background-color:#007166"><font color="#D3D7CF"> app </font></span> Using import string: <font color="#3465A4">main:app</font>
|
||||
|
||||
<span style="background-color:#007166"><font color="#D3D7CF"> server </font></span> Server started at <font color="#729FCF"><u style="text-decoration-style:solid">http://0.0.0.0:8000</u></font>
|
||||
<span style="background-color:#007166"><font color="#D3D7CF"> server </font></span> Documentation at <font color="#729FCF"><u style="text-decoration-style:solid">http://0.0.0.0:8000/docs</u></font>
|
||||
|
||||
Logs:
|
||||
|
||||
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Uvicorn running on <font color="#729FCF"><u style="text-decoration-style:solid">http://0.0.0.0:8000</u></font> <b>(</b>Press CTRL+C to
|
||||
quit<b>)</b>
|
||||
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Started parent process <b>[</b><font color="#34E2E2"><b>27365</b></font><b>]</b>
|
||||
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Started server process <b>[</b><font color="#34E2E2"><b>27368</b></font><b>]</b>
|
||||
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Started server process <b>[</b><font color="#34E2E2"><b>27369</b></font><b>]</b>
|
||||
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Started server process <b>[</b><font color="#34E2E2"><b>27370</b></font><b>]</b>
|
||||
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Started server process <b>[</b><font color="#34E2E2"><b>27367</b></font><b>]</b>
|
||||
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Waiting for application startup.
|
||||
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Waiting for application startup.
|
||||
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Waiting for application startup.
|
||||
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Waiting for application startup.
|
||||
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Application startup complete.
|
||||
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Application startup complete.
|
||||
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Application startup complete.
|
||||
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Application startup complete.
|
||||
```
|
||||
|
||||
</div>
|
||||
@@ -124,7 +111,7 @@ La única opción nueva aquí es `--workers` indicando a Uvicorn que inicie 4 wo
|
||||
|
||||
También puedes ver que muestra el **PID** de cada proceso, `27365` para el proceso padre (este es el **gestor de procesos**) y uno para cada worker process: `27368`, `27369`, `27370`, y `27367`.
|
||||
|
||||
## Conceptos de Despliegue
|
||||
## Conceptos de Despliegue { #deployment-concepts }
|
||||
|
||||
Aquí viste cómo usar múltiples **workers** para **paralelizar** la ejecución de la aplicación, aprovechar los **múltiples núcleos** del CPU, y poder servir **más requests**.
|
||||
|
||||
@@ -137,13 +124,13 @@ De la lista de conceptos de despliegue de antes, usar workers ayudaría principa
|
||||
* **Memoria**
|
||||
* **Pasos previos antes de empezar**
|
||||
|
||||
## Contenedores y Docker
|
||||
## Contenedores y Docker { #containers-and-docker }
|
||||
|
||||
En el próximo capítulo sobre [FastAPI en Contenedores - Docker](docker.md){.internal-link target=_blank} te explicaré algunas estrategias que podrías usar para manejar los otros **conceptos de despliegue**.
|
||||
|
||||
Te mostraré cómo **construir tu propia imagen desde cero** para ejecutar un solo proceso de Uvicorn. Es un proceso sencillo y probablemente es lo que querrías hacer al usar un sistema de gestión de contenedores distribuido como **Kubernetes**.
|
||||
|
||||
## Resumen
|
||||
## Resumen { #recap }
|
||||
|
||||
Puedes usar múltiples worker processes con la opción CLI `--workers` con los comandos `fastapi` o `uvicorn` para aprovechar los **CPUs de múltiples núcleos**, para ejecutar **múltiples procesos en paralelo**.
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Sobre las versiones de FastAPI
|
||||
# Sobre las versiones de FastAPI { #about-fastapi-versions }
|
||||
|
||||
**FastAPI** ya se está utilizando en producción en muchas aplicaciones y sistemas. Y la cobertura de tests se mantiene al 100%. Pero su desarrollo sigue avanzando rápidamente.
|
||||
|
||||
@@ -8,7 +8,7 @@ Por eso las versiones actuales siguen siendo `0.x.x`, esto refleja que cada vers
|
||||
|
||||
Puedes crear aplicaciones de producción con **FastAPI** ahora mismo (y probablemente ya lo has estado haciendo desde hace algún tiempo), solo debes asegurarte de que utilizas una versión que funciona correctamente con el resto de tu código.
|
||||
|
||||
## Fijar tu versión de `fastapi`
|
||||
## Fija tu versión de `fastapi` { #pin-your-fastapi-version }
|
||||
|
||||
Lo primero que debes hacer es "fijar" la versión de **FastAPI** que estás usando a la versión específica más reciente que sabes que funciona correctamente para tu aplicación.
|
||||
|
||||
@@ -32,11 +32,11 @@ eso significaría que usarías las versiones `0.112.0` o superiores, pero menore
|
||||
|
||||
Si utilizas cualquier otra herramienta para gestionar tus instalaciones, como `uv`, Poetry, Pipenv, u otras, todas tienen una forma que puedes usar para definir versiones específicas para tus paquetes.
|
||||
|
||||
## Versiones disponibles
|
||||
## Versiones disponibles { #available-versions }
|
||||
|
||||
Puedes ver las versiones disponibles (por ejemplo, para revisar cuál es la más reciente) en las [Release Notes](../release-notes.md){.internal-link target=_blank}.
|
||||
|
||||
## Sobre las versiones
|
||||
## Sobre las versiones { #about-versions }
|
||||
|
||||
Siguiendo las convenciones del Semantic Versioning, cualquier versión por debajo de `1.0.0` podría potencialmente añadir cambios incompatibles.
|
||||
|
||||
@@ -62,7 +62,7 @@ El "MINOR" es el número en el medio, por ejemplo, en `0.2.3`, la versión MINOR
|
||||
|
||||
///
|
||||
|
||||
## Actualizando las versiones de FastAPI
|
||||
## Actualizando las versiones de FastAPI { #upgrading-the-fastapi-versions }
|
||||
|
||||
Deberías añadir tests para tu aplicación.
|
||||
|
||||
@@ -72,7 +72,7 @@ Después de tener tests, puedes actualizar la versión de **FastAPI** a una más
|
||||
|
||||
Si todo está funcionando, o después de hacer los cambios necesarios, y todos tus tests pasan, entonces puedes fijar tu `fastapi` a esa nueva versión más reciente.
|
||||
|
||||
## Sobre Starlette
|
||||
## Sobre Starlette { #about-starlette }
|
||||
|
||||
No deberías fijar la versión de `starlette`.
|
||||
|
||||
@@ -80,7 +80,7 @@ Diferentes versiones de **FastAPI** utilizarán una versión más reciente espec
|
||||
|
||||
Así que, puedes simplemente dejar que **FastAPI** use la versión correcta de Starlette.
|
||||
|
||||
## Sobre Pydantic
|
||||
## Sobre Pydantic { #about-pydantic }
|
||||
|
||||
Pydantic incluye los tests para **FastAPI** con sus propios tests, así que nuevas versiones de Pydantic (por encima de `1.0.0`) siempre son compatibles con FastAPI.
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Variables de Entorno
|
||||
# Variables de Entorno { #environment-variables }
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
@@ -10,7 +10,7 @@ Una variable de entorno (también conocida como "**env var**") es una variable q
|
||||
|
||||
Las variables de entorno pueden ser útiles para manejar **configuraciones** de aplicaciones, como parte de la **instalación** de Python, etc.
|
||||
|
||||
## Crear y Usar Variables de Entorno
|
||||
## Crear y Usar Variables de Entorno { #create-and-use-env-vars }
|
||||
|
||||
Puedes **crear** y usar variables de entorno en la **shell (terminal)**, sin necesidad de Python:
|
||||
|
||||
@@ -50,7 +50,7 @@ Hello Wade Wilson
|
||||
|
||||
////
|
||||
|
||||
## Leer Variables de Entorno en Python
|
||||
## Leer Variables de Entorno en Python { #read-env-vars-in-python }
|
||||
|
||||
También podrías crear variables de entorno **fuera** de Python, en la terminal (o con cualquier otro método), y luego **leerlas en Python**.
|
||||
|
||||
@@ -157,7 +157,7 @@ Puedes leer más al respecto en <a href="https://12factor.net/config" class="ext
|
||||
|
||||
///
|
||||
|
||||
## Tipos y Validación
|
||||
## Tipos y Validación { #types-and-validation }
|
||||
|
||||
Estas variables de entorno solo pueden manejar **strings de texto**, ya que son externas a Python y deben ser compatibles con otros programas y el resto del sistema (e incluso con diferentes sistemas operativos, como Linux, Windows, macOS).
|
||||
|
||||
@@ -165,7 +165,7 @@ Esto significa que **cualquier valor** leído en Python desde una variable de en
|
||||
|
||||
Aprenderás más sobre cómo usar variables de entorno para manejar **configuraciones de aplicación** en la [Guía del Usuario Avanzado - Ajustes y Variables de Entorno](./advanced/settings.md){.internal-link target=_blank}.
|
||||
|
||||
## Variable de Entorno `PATH`
|
||||
## Variable de Entorno `PATH` { #path-environment-variable }
|
||||
|
||||
Hay una variable de entorno **especial** llamada **`PATH`** que es utilizada por los sistemas operativos (Linux, macOS, Windows) para encontrar programas a ejecutar.
|
||||
|
||||
@@ -209,7 +209,7 @@ Por ejemplo, cuando escribes `python` en la terminal, el sistema operativo busca
|
||||
|
||||
Si lo encuentra, entonces lo **utilizará**. De lo contrario, continúa buscando en los **otros directorios**.
|
||||
|
||||
### Instalando Python y Actualizando el `PATH`
|
||||
### Instalando Python y Actualizando el `PATH` { #installing-python-and-updating-the-path }
|
||||
|
||||
Cuando instalas Python, se te podría preguntar si deseas actualizar la variable de entorno `PATH`.
|
||||
|
||||
@@ -287,7 +287,7 @@ $ C:\opt\custompython\bin\python
|
||||
|
||||
Esta información será útil al aprender sobre [Entornos Virtuales](virtual-environments.md){.internal-link target=_blank}.
|
||||
|
||||
## Conclusión
|
||||
## Conclusión { #conclusion }
|
||||
|
||||
Con esto deberías tener una comprensión básica de qué son las **variables de entorno** y cómo usarlas en Python.
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# FastAPI CLI
|
||||
# FastAPI CLI { #fastapi-cli }
|
||||
|
||||
**FastAPI CLI** es un programa de línea de comandos que puedes usar para servir tu aplicación FastAPI, gestionar tu proyecto FastAPI, y más.
|
||||
|
||||
@@ -54,13 +54,13 @@ Para producción usarías `fastapi run` en su lugar. 🚀
|
||||
|
||||
Internamente, **FastAPI CLI** usa <a href="https://www.uvicorn.dev" class="external-link" target="_blank">Uvicorn</a>, un servidor ASGI de alto rendimiento y listo para producción. 😎
|
||||
|
||||
## `fastapi dev`
|
||||
## `fastapi dev` { #fastapi-dev }
|
||||
|
||||
Ejecutar `fastapi dev` inicia el modo de desarrollo.
|
||||
|
||||
Por defecto, **auto-reload** está habilitado, recargando automáticamente el servidor cuando realizas cambios en tu código. Esto consume muchos recursos y podría ser menos estable que cuando está deshabilitado. Deberías usarlo solo para desarrollo. También escucha en la dirección IP `127.0.0.1`, que es la IP para que tu máquina se comunique solo consigo misma (`localhost`).
|
||||
|
||||
## `fastapi run`
|
||||
## `fastapi run` { #fastapi-run }
|
||||
|
||||
Ejecutar `fastapi run` inicia FastAPI en modo de producción por defecto.
|
||||
|
||||
|
||||
+20
-20
@@ -1,17 +1,17 @@
|
||||
# Funcionalidades
|
||||
# Funcionalidades { #features }
|
||||
|
||||
## Funcionalidades de FastAPI
|
||||
## Funcionalidades de FastAPI { #fastapi-features }
|
||||
|
||||
**FastAPI** te ofrece lo siguiente:
|
||||
|
||||
### Basado en estándares abiertos
|
||||
### Basado en estándares abiertos { #based-on-open-standards }
|
||||
|
||||
* <a href="https://github.com/OAI/OpenAPI-Specification" class="external-link" target="_blank"><strong>OpenAPI</strong></a> para la creación de APIs, incluyendo declaraciones de <abbr title="también conocido como: endpoints, rutas">path</abbr> <abbr title="también conocido como métodos HTTP, como POST, GET, PUT, DELETE">operations</abbr>, parámetros, request bodies, seguridad, etc.
|
||||
* Documentación automática de modelos de datos con <a href="https://json-schema.org/" class="external-link" target="_blank"><strong>JSON Schema</strong></a> (ya que OpenAPI en sí mismo está basado en JSON Schema).
|
||||
* Diseñado alrededor de estos estándares, tras un estudio meticuloso. En lugar de ser una capa adicional.
|
||||
* Esto también permite el uso de **generación de código cliente automática** en muchos idiomas.
|
||||
|
||||
### Documentación automática
|
||||
### Documentación automática { #automatic-docs }
|
||||
|
||||
Interfaces web de documentación y exploración de APIs interactivas. Como el framework está basado en OpenAPI, hay múltiples opciones, 2 incluidas por defecto.
|
||||
|
||||
@@ -23,7 +23,7 @@ Interfaces web de documentación y exploración de APIs interactivas. Como el fr
|
||||
|
||||

|
||||
|
||||
### Solo Python moderno
|
||||
### Solo Python moderno { #just-modern-python }
|
||||
|
||||
Todo está basado en declaraciones estándar de **tipos en Python** (gracias a Pydantic). Sin nueva sintaxis que aprender. Solo Python moderno estándar.
|
||||
|
||||
@@ -71,7 +71,7 @@ Pasa las claves y valores del dict `second_user_data` directamente como argument
|
||||
|
||||
///
|
||||
|
||||
### Soporte del editor
|
||||
### Soporte del editor { #editor-support }
|
||||
|
||||
Todo el framework fue diseñado para ser fácil e intuitivo de usar, todas las decisiones fueron probadas en múltiples editores incluso antes de comenzar el desarrollo, para asegurar la mejor experiencia de desarrollo.
|
||||
|
||||
@@ -95,13 +95,13 @@ Obtendrás autocompletado en código que podrías considerar imposible antes. Po
|
||||
|
||||
No más escribir nombres de claves incorrectos, yendo de un lado a otro entre la documentación, o desplazándote hacia arriba y abajo para encontrar si finalmente usaste `username` o `user_name`.
|
||||
|
||||
### Breve
|
||||
### Breve { #short }
|
||||
|
||||
Tiene **valores predeterminados** sensatos para todo, con configuraciones opcionales en todas partes. Todos los parámetros se pueden ajustar finamente para hacer lo que necesitas y para definir el API que necesitas.
|
||||
Tiene **valores por defecto** sensatos para todo, con configuraciones opcionales en todas partes. Todos los parámetros se pueden ajustar finamente para hacer lo que necesitas y para definir el API que necesitas.
|
||||
|
||||
Pero por defecto, todo **"simplemente funciona"**.
|
||||
|
||||
### Validación
|
||||
### Validación { #validation }
|
||||
|
||||
* Validación para la mayoría (¿o todas?) de los **tipos de datos** de Python, incluyendo:
|
||||
* Objetos JSON (`dict`).
|
||||
@@ -117,7 +117,7 @@ Pero por defecto, todo **"simplemente funciona"**.
|
||||
|
||||
Toda la validación es manejada por **Pydantic**, una herramienta bien establecida y robusta.
|
||||
|
||||
### Seguridad y autenticación
|
||||
### Seguridad y autenticación { #security-and-authentication }
|
||||
|
||||
Seguridad y autenticación integradas. Sin ningún compromiso con bases de datos o modelos de datos.
|
||||
|
||||
@@ -134,30 +134,30 @@ Además de todas las características de seguridad de Starlette (incluyendo **co
|
||||
|
||||
Todo construido como herramientas y componentes reutilizables que son fáciles de integrar con tus sistemas, almacenes de datos, bases de datos relacionales y NoSQL, etc.
|
||||
|
||||
### Inyección de dependencias
|
||||
### Inyección de dependencias { #dependency-injection }
|
||||
|
||||
FastAPI incluye un sistema de <abbr title='también conocido como "componentes", "recursos", "servicios", "proveedores"'><strong>Inyección de Dependencias</strong></abbr> extremadamente fácil de usar, pero extremadamente potente.
|
||||
|
||||
* Incluso las dependencias pueden tener dependencias, creando una jerarquía o **"gráfico de dependencias"**.
|
||||
* Incluso las dependencias pueden tener dependencias, creando una jerarquía o **"grafo de dependencias"**.
|
||||
* Todo **manejado automáticamente** por el framework.
|
||||
* Todas las dependencias pueden requerir datos de los requests y **aumentar las restricciones de la path operation** y la documentación automática.
|
||||
* **Validación automática** incluso para los parámetros de *path operation* definidos en las dependencias.
|
||||
* Soporte para sistemas de autenticación de usuario complejos, **conexiones a bases de datos**, etc.
|
||||
* **Sin compromisos** con bases de datos, frontends, etc. Pero fácil integración con todos ellos.
|
||||
|
||||
### "Plug-ins" ilimitados
|
||||
### "Plug-ins" ilimitados { #unlimited-plug-ins }
|
||||
|
||||
O de otra manera, no hay necesidad de ellos, importa y usa el código que necesitas.
|
||||
|
||||
Cualquier integración está diseñada para ser tan simple de usar (con dependencias) que puedes crear un "plug-in" para tu aplicación en 2 líneas de código usando la misma estructura y sintaxis utilizada para tus *path operations*.
|
||||
|
||||
### Probado
|
||||
### Probado { #tested }
|
||||
|
||||
* 100% de <abbr title="La cantidad de código que se prueba automáticamente">cobertura de tests</abbr>.
|
||||
* Código completamente <abbr title="Anotaciones de tipos en Python, con esto tu editor y herramientas externas pueden ofrecerte mejor soporte">anotado con tipos</abbr>.
|
||||
* 100% <abbr title="Anotaciones de tipos en Python, con esto tu editor y herramientas externas pueden ofrecerte mejor soporte">anotada con tipos</abbr> code base.
|
||||
* Usado en aplicaciones en producción.
|
||||
|
||||
## Funcionalidades de Starlette
|
||||
## Funcionalidades de Starlette { #starlette-features }
|
||||
|
||||
**FastAPI** es totalmente compatible con (y está basado en) <a href="https://www.starlette.dev/" class="external-link" target="_blank"><strong>Starlette</strong></a>. Así que, cualquier código adicional de Starlette que tengas, también funcionará.
|
||||
|
||||
@@ -173,13 +173,13 @@ 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%.
|
||||
* Código completamente anotado con tipos.
|
||||
* code base completamente anotada con tipos.
|
||||
|
||||
## Funcionalidades de Pydantic
|
||||
## Funcionalidades de Pydantic { #pydantic-features }
|
||||
|
||||
**FastAPI** es totalmente compatible con (y está basado en) <a href="https://docs.pydantic.dev/" class="external-link" target="_blank"><strong>Pydantic</strong></a>. Por lo tanto, cualquier código adicional de Pydantic que tengas, también funcionará.
|
||||
|
||||
Incluyendo paquetes externos también basados en Pydantic, como <abbr title="Object-Relational Mapper">ORM</abbr>s, <abbr title="Object-Document Mapper">ODM</abbr>s para bases de datos.
|
||||
Incluyendo paquetes externos también basados en Pydantic, como <abbr title="Object-Relational Mapper – Mapeador Objeto-Relacional">ORM</abbr>s, <abbr title="Object-Document Mapper – Mapeador Objeto-Documento">ODM</abbr>s para bases de datos.
|
||||
|
||||
Esto también significa que, en muchos casos, puedes pasar el mismo objeto que obtienes de un request **directamente a la base de datos**, ya que todo se valida automáticamente.
|
||||
|
||||
@@ -190,7 +190,7 @@ Con **FastAPI** obtienes todas las funcionalidades de **Pydantic** (ya que FastA
|
||||
* **Sin complicaciones**:
|
||||
* Sin micro-lenguaje de definición de esquemas nuevo que aprender.
|
||||
* Si conoces los tipos en Python sabes cómo usar Pydantic.
|
||||
* Se lleva bien con tu **<abbr title="Entorno de Desarrollo Integrado, similar a un editor de código">IDE</abbr>/<abbr title="Un programa que verifica errores de código">linter</abbr>/cerebro**:
|
||||
* Se lleva bien con tu **<abbr title="Integrated Development Environment – Entorno de Desarrollo Integrado: similar a un editor de código">IDE</abbr>/<abbr title="Un programa que verifica errores de código">linter</abbr>/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.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Ayuda a FastAPI - Consigue Ayuda
|
||||
# Ayuda a FastAPI - Consigue Ayuda { #help-fastapi-get-help }
|
||||
|
||||
¿Te gusta **FastAPI**?
|
||||
|
||||
@@ -10,7 +10,7 @@ Hay formas muy sencillas de ayudar (varias implican solo uno o dos clics).
|
||||
|
||||
Y también hay varias formas de conseguir ayuda.
|
||||
|
||||
## Suscríbete al boletín
|
||||
## Suscríbete al boletín { #subscribe-to-the-newsletter }
|
||||
|
||||
Puedes suscribirte al (esporádico) boletín [**FastAPI and friends**](newsletter.md){.internal-link target=_blank} para mantenerte al día sobre:
|
||||
|
||||
@@ -20,17 +20,17 @@ Puedes suscribirte al (esporádico) boletín [**FastAPI and friends**](newslette
|
||||
* Cambios importantes 🚨
|
||||
* Consejos y trucos ✅
|
||||
|
||||
## Sigue a FastAPI en X (Twitter)
|
||||
## Sigue a FastAPI en X (Twitter) { #follow-fastapi-on-x-twitter }
|
||||
|
||||
<a href="https://x.com/fastapi" class="external-link" target="_blank">Sigue a @fastapi en **X (Twitter)**</a> para obtener las últimas noticias sobre **FastAPI**. 🐦
|
||||
|
||||
## Dale una estrella a **FastAPI** en GitHub
|
||||
## Dale una estrella a **FastAPI** en GitHub { #star-fastapi-in-github }
|
||||
|
||||
Puedes "darle una estrella" a FastAPI en GitHub (haciendo clic en el botón de estrella en la parte superior derecha): <a href="https://github.com/fastapi/fastapi" class="external-link" target="_blank">https://github.com/fastapi/fastapi</a>. ⭐️
|
||||
|
||||
Al agregar una estrella, otros usuarios podrán encontrarlo más fácilmente y ver que ya ha sido útil para otros.
|
||||
|
||||
## Observa el repositorio de GitHub para lanzamientos
|
||||
## Observa el repositorio de GitHub para lanzamientos { #watch-the-github-repository-for-releases }
|
||||
|
||||
Puedes "observar" FastAPI en GitHub (haciendo clic en el botón "watch" en la parte superior derecha): <a href="https://github.com/fastapi/fastapi" class="external-link" target="_blank">https://github.com/fastapi/fastapi</a>. 👀
|
||||
|
||||
@@ -38,7 +38,7 @@ Allí puedes seleccionar "Releases only".
|
||||
|
||||
Al hacerlo, recibirás notificaciones (en tu email) cada vez que haya un nuevo lanzamiento (una nueva versión) de **FastAPI** con correcciones de bugs y nuevas funcionalidades.
|
||||
|
||||
## Conéctate con el autor
|
||||
## Conéctate con el autor { #connect-with-the-author }
|
||||
|
||||
Puedes conectar <a href="https://tiangolo.com" class="external-link" target="_blank">conmigo (Sebastián Ramírez / `tiangolo`)</a>, el autor.
|
||||
|
||||
@@ -57,19 +57,19 @@ Puedes:
|
||||
* Leer otras ideas, artículos, y leer sobre las herramientas que he creado.
|
||||
* Seguirme para leer lo que publico nuevo.
|
||||
|
||||
## Twittea sobre **FastAPI**
|
||||
## Twittea sobre **FastAPI** { #tweet-about-fastapi }
|
||||
|
||||
<a href="https://x.com/compose/tweet?text=I'm loving @fastapi because... https://github.com/fastapi/fastapi" class="external-link" target="_blank">Twittea sobre **FastAPI**</a> y dime a mí y a otros por qué te gusta. 🎉
|
||||
|
||||
Me encanta escuchar cómo se está utilizando **FastAPI**, qué te ha gustado, en qué proyecto/empresa lo estás usando, etc.
|
||||
|
||||
## Vota por FastAPI
|
||||
## Vota por FastAPI { #vote-for-fastapi }
|
||||
|
||||
* <a href="https://www.slant.co/options/34241/~fastapi-review" class="external-link" target="_blank">Vota por **FastAPI** en Slant</a>.
|
||||
* <a href="https://alternativeto.net/software/fastapi/about/" class="external-link" target="_blank">Vota por **FastAPI** en AlternativeTo</a>.
|
||||
* <a href="https://stackshare.io/pypi-fastapi" class="external-link" target="_blank">Di que usas **FastAPI** en StackShare</a>.
|
||||
|
||||
## Ayuda a otros con preguntas en GitHub
|
||||
## Ayuda a otros con preguntas en GitHub { #help-others-with-questions-in-github }
|
||||
|
||||
Puedes intentar ayudar a otros con sus preguntas en:
|
||||
|
||||
@@ -88,7 +88,7 @@ La idea es que la comunidad de **FastAPI** sea amable y acogedora. Al mismo tiem
|
||||
|
||||
Aquí te explico cómo ayudar a otros con preguntas (en discusiones o issues):
|
||||
|
||||
### Entiende la pregunta
|
||||
### Entiende la pregunta { #understand-the-question }
|
||||
|
||||
* Revisa si puedes entender cuál es el **propósito** y el caso de uso de la persona que pregunta.
|
||||
|
||||
@@ -98,7 +98,7 @@ Aquí te explico cómo ayudar a otros con preguntas (en discusiones o issues):
|
||||
|
||||
* Si no puedes entender la pregunta, pide más **detalles**.
|
||||
|
||||
### Reproduce el problema
|
||||
### Reproduce el problema { #reproduce-the-problem }
|
||||
|
||||
En la mayoría de los casos y preguntas hay algo relacionado con el **código original** de la persona.
|
||||
|
||||
@@ -108,13 +108,13 @@ En muchos casos solo copiarán un fragmento del código, pero eso no es suficien
|
||||
|
||||
* Si te sientes muy generoso, puedes intentar **crear un ejemplo** así tú mismo, solo basado en la descripción del problema. Solo ten en cuenta que esto podría llevar mucho tiempo y podría ser mejor pedirles que aclaren el problema primero.
|
||||
|
||||
### Sugerir soluciones
|
||||
### Sugerir soluciones { #suggest-solutions }
|
||||
|
||||
* Después de poder entender la pregunta, puedes darles un 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.
|
||||
|
||||
### Pide cerrar
|
||||
### Pide cerrar { #ask-to-close }
|
||||
|
||||
Si responden, hay una alta probabilidad de que hayas resuelto su problema, felicidades, ¡**eres un héroe**! 🦸
|
||||
|
||||
@@ -123,7 +123,7 @@ Si responden, hay una alta probabilidad de que hayas resuelto su problema, felic
|
||||
* En GitHub Discussions: marquen el comentario como la **respuesta**.
|
||||
* En GitHub Issues: **cierren** el issue.
|
||||
|
||||
## Observa el repositorio de GitHub
|
||||
## Observa el repositorio de GitHub { #watch-the-github-repository }
|
||||
|
||||
Puedes "observar" FastAPI en GitHub (haciendo clic en el botón "watch" en la parte superior derecha): <a href="https://github.com/fastapi/fastapi" class="external-link" target="_blank">https://github.com/fastapi/fastapi</a>. 👀
|
||||
|
||||
@@ -131,7 +131,7 @@ Si seleccionas "Watching" en lugar de "Releases only", recibirás notificaciones
|
||||
|
||||
Luego puedes intentar ayudarlos a resolver esas preguntas.
|
||||
|
||||
## Haz preguntas
|
||||
## Haz preguntas { #ask-questions }
|
||||
|
||||
Puedes <a href="https://github.com/fastapi/fastapi/discussions/new?category=questions" class="external-link" target="_blank">crear una nueva pregunta</a> en el repositorio de GitHub, por ejemplo, para:
|
||||
|
||||
@@ -140,7 +140,7 @@ Puedes <a href="https://github.com/fastapi/fastapi/discussions/new?category=ques
|
||||
|
||||
**Nota**: si lo haces, entonces te voy a pedir que también ayudes a otros. 😉
|
||||
|
||||
## Revisa Pull Requests
|
||||
## Revisa Pull Requests { #review-pull-requests }
|
||||
|
||||
Puedes ayudarme a revisar pull requests de otros.
|
||||
|
||||
@@ -150,13 +150,13 @@ De nuevo, por favor, haz tu mejor esfuerzo por ser amable. 🤗
|
||||
|
||||
Aquí está lo que debes tener en cuenta y cómo revisar un pull request:
|
||||
|
||||
### Entiende el problema
|
||||
### Entiende el problema { #understand-the-problem }
|
||||
|
||||
* Primero, asegúrate de **entender el problema** que el pull request está intentando resolver. Podría tener una discusión más larga en una GitHub Discussion o issue.
|
||||
|
||||
* También hay una buena posibilidad de que el pull request no sea realmente necesario porque el problema se puede resolver de una manera **diferente**. Entonces puedes sugerir o preguntar sobre eso.
|
||||
|
||||
### No te preocupes por el estilo
|
||||
### No te preocupes por el estilo { #dont-worry-about-style }
|
||||
|
||||
* No te preocupes demasiado por cosas como los estilos de los mensajes de commit, yo haré squash y merge personalizando el commit manualmente.
|
||||
|
||||
@@ -164,7 +164,7 @@ Aquí está lo que debes tener en cuenta y cómo revisar un pull request:
|
||||
|
||||
Y si hay alguna otra necesidad de estilo o consistencia, pediré directamente eso, o agregaré commits encima con los cambios necesarios.
|
||||
|
||||
### Revisa el código
|
||||
### Revisa el código { #check-the-code }
|
||||
|
||||
* Revisa y lee el código, ve si tiene sentido, **ejecútalo localmente** y ve si realmente resuelve el problema.
|
||||
|
||||
@@ -182,7 +182,7 @@ Así que, es realmente importante que realmente leas y ejecutes el código, y me
|
||||
|
||||
* Si el PR se puede simplificar de alguna manera, puedes pedir eso, pero no hay necesidad de ser demasiado exigente, podría haber muchos puntos de vista subjetivos (y yo tendré el mío también 🙈), así que es mejor si puedes centrarte en las cosas fundamentales.
|
||||
|
||||
### Tests
|
||||
### Tests { #tests }
|
||||
|
||||
* Ayúdame a verificar que el PR tenga **tests**.
|
||||
|
||||
@@ -194,7 +194,7 @@ Así que, es realmente importante que realmente leas y ejecutes el código, y me
|
||||
|
||||
* Luego también comenta lo que intentaste, de esa manera sabré que lo revisaste. 🤓
|
||||
|
||||
## Crea un Pull Request
|
||||
## Crea un Pull Request { #create-a-pull-request }
|
||||
|
||||
Puedes [contribuir](contributing.md){.internal-link target=_blank} al código fuente con Pull Requests, por ejemplo:
|
||||
|
||||
@@ -210,7 +210,7 @@ Puedes [contribuir](contributing.md){.internal-link target=_blank} al código fu
|
||||
* Asegúrate de agregar tests.
|
||||
* Asegúrate de agregar documentación si es relevante.
|
||||
|
||||
## Ayuda a Mantener FastAPI
|
||||
## Ayuda a Mantener FastAPI { #help-maintain-fastapi }
|
||||
|
||||
¡Ayúdame a mantener **FastAPI**! 🤓
|
||||
|
||||
@@ -225,7 +225,7 @@ Esas dos tareas son las que **consumen más tiempo**. Ese es el trabajo principa
|
||||
|
||||
Si puedes ayudarme con eso, **me estás ayudando a mantener FastAPI** y asegurando que siga **avanzando más rápido y mejor**. 🚀
|
||||
|
||||
## Únete al chat
|
||||
## Únete al chat { #join-the-chat }
|
||||
|
||||
Únete al servidor de chat 👥 <a href="https://discord.gg/VQjSZaeJmf" class="external-link" target="_blank">Discord</a> 👥 y charla con otros en la comunidad de FastAPI.
|
||||
|
||||
@@ -237,7 +237,7 @@ Usa el chat solo para otras conversaciones generales.
|
||||
|
||||
///
|
||||
|
||||
### No uses el chat para preguntas
|
||||
### No uses el chat para preguntas { #dont-use-the-chat-for-questions }
|
||||
|
||||
Ten en cuenta que dado que los chats permiten una "conversación más libre", es fácil hacer preguntas que son demasiado generales y más difíciles de responder, por lo que es posible que no recibas respuestas.
|
||||
|
||||
@@ -247,22 +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
|
||||
## Patrocina al autor { #sponsor-the-author }
|
||||
|
||||
También puedes apoyar financieramente al autor (a mí) a través de <a href="https://github.com/sponsors/tiangolo" class="external-link" target="_blank">GitHub sponsors</a>.
|
||||
|
||||
Allí podrías comprarme un café ☕️ para decir gracias. 😄
|
||||
|
||||
Y también puedes convertirte en un sponsor de Plata o de Oro para FastAPI. 🏅🎉
|
||||
|
||||
## Patrocina las herramientas que impulsan FastAPI
|
||||
|
||||
Como habrás visto en la documentación, FastAPI se apoya en los hombros de gigantes, Starlette y Pydantic.
|
||||
|
||||
También puedes patrocinar:
|
||||
|
||||
* <a href="https://github.com/sponsors/samuelcolvin" class="external-link" target="_blank">Samuel Colvin (Pydantic)</a>
|
||||
* <a href="https://github.com/sponsors/encode" class="external-link" target="_blank">Encode (Starlette, Uvicorn)</a>
|
||||
Si tu **producto/empresa** depende de o está relacionado con **FastAPI** y quieres llegar a sus usuarios, puedes patrocinar al autor (a mí) a través de <a href="https://github.com/sponsors/tiangolo" class="external-link" target="_blank">GitHub sponsors</a>. Según el nivel, podrías obtener algunos beneficios extra, como una insignia en la documentación. 🎁
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Historia, Diseño y Futuro
|
||||
# Historia, Diseño y Futuro { #history-design-and-future }
|
||||
|
||||
Hace algún tiempo, <a href="https://github.com/fastapi/fastapi/issues/3#issuecomment-454956920" class="external-link" target="_blank">un usuario de **FastAPI** preguntó</a>:
|
||||
|
||||
@@ -6,7 +6,7 @@ Hace algún tiempo, <a href="https://github.com/fastapi/fastapi/issues/3#issueco
|
||||
|
||||
Aquí hay un poquito de esa historia.
|
||||
|
||||
## Alternativas
|
||||
## Alternativas { #alternatives }
|
||||
|
||||
He estado creando APIs con requisitos complejos durante varios años (Machine Learning, sistemas distribuidos, trabajos asíncronos, bases de datos NoSQL, etc.), liderando varios equipos de desarrolladores.
|
||||
|
||||
@@ -22,13 +22,13 @@ Como se dice en la sección [Alternativas](alternatives.md){.internal-link targe
|
||||
|
||||
Ha habido muchas herramientas creadas antes que han ayudado a inspirar su creación.
|
||||
|
||||
He estado evitando la creación de un nuevo framework durante varios años. Primero traté de resolver todas las funcionalidades cubiertas por **FastAPI** usando varios frameworks, complementos y herramientas diferentes.
|
||||
He estado evitando la creación de un nuevo framework durante varios años. Primero traté de resolver todas las funcionalidades cubiertas por **FastAPI** usando varios frameworks, plug-ins y herramientas diferentes.
|
||||
|
||||
Pero en algún momento, no había otra opción que crear algo que proporcionara todas estas funcionalidades, tomando las mejores ideas de herramientas anteriores y combinándolas de la mejor manera posible, usando funcionalidades del lenguaje que ni siquiera estaban disponibles antes (anotaciones de tipos de Python 3.6+).
|
||||
|
||||
</blockquote>
|
||||
|
||||
## Investigación
|
||||
## Investigación { #investigation }
|
||||
|
||||
Al usar todas las alternativas anteriores, tuve la oportunidad de aprender de todas ellas, tomar ideas y combinarlas de la mejor manera que pude encontrar para mí y los equipos de desarrolladores con los que he trabajado.
|
||||
|
||||
@@ -38,7 +38,7 @@ También, el mejor enfoque era usar estándares ya existentes.
|
||||
|
||||
Entonces, antes de siquiera empezar a programar **FastAPI**, pasé varios meses estudiando las especificaciones de OpenAPI, JSON Schema, OAuth2, etc. Entendiendo su relación, superposición y diferencias.
|
||||
|
||||
## Diseño
|
||||
## Diseño { #design }
|
||||
|
||||
Luego pasé algún tiempo diseñando la "API" de desarrollador que quería tener como usuario (como desarrollador usando FastAPI).
|
||||
|
||||
@@ -52,7 +52,7 @@ De esa manera, pude encontrar las mejores maneras de reducir la duplicación de
|
||||
|
||||
Todo de una manera que proporcionara la mejor experiencia de desarrollo para todos los desarrolladores.
|
||||
|
||||
## Requisitos
|
||||
## Requisitos { #requirements }
|
||||
|
||||
Después de probar varias alternativas, decidí que iba a usar <a href="https://docs.pydantic.dev/" class="external-link" target="_blank">**Pydantic**</a> por sus ventajas.
|
||||
|
||||
@@ -60,11 +60,11 @@ Luego contribuí a este, para hacerlo totalmente compatible con JSON Schema, par
|
||||
|
||||
Durante el desarrollo, también contribuí a <a href="https://www.starlette.dev/" class="external-link" target="_blank">**Starlette**</a>, el otro requisito clave.
|
||||
|
||||
## Desarrollo
|
||||
## Desarrollo { #development }
|
||||
|
||||
Para cuando comencé a crear el propio **FastAPI**, la mayoría de las piezas ya estaban en su lugar, el diseño estaba definido, los requisitos y herramientas estaban listos, y el conocimiento sobre los estándares y especificaciones estaba claro y fresco.
|
||||
|
||||
## Futuro
|
||||
## Futuro { #future }
|
||||
|
||||
A este punto, ya está claro que **FastAPI** con sus ideas está siendo útil para muchas personas.
|
||||
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
# Usar los códigos de estado antiguos 403 para errores de autenticación { #use-old-403-authentication-error-status-codes }
|
||||
|
||||
Antes de FastAPI versión `0.122.0`, cuando las utilidades de seguridad integradas devolvían un error al cliente después de una autenticación fallida, usaban el código de estado HTTP `403 Forbidden`.
|
||||
|
||||
A partir de FastAPI versión `0.122.0`, usan el código de estado HTTP `401 Unauthorized`, más apropiado, y devuelven un `WWW-Authenticate` header adecuado en la response, siguiendo las especificaciones HTTP, <a href="https://datatracker.ietf.org/doc/html/rfc7235#section-3.1" class="external-link" target="_blank">RFC 7235</a>, <a href="https://datatracker.ietf.org/doc/html/rfc9110#name-401-unauthorized" class="external-link" target="_blank">RFC 9110</a>.
|
||||
|
||||
Pero si por alguna razón tus clientes dependen del comportamiento anterior, puedes volver a él sobrescribiendo el método `make_not_authenticated_error` en tus clases de seguridad.
|
||||
|
||||
Por ejemplo, puedes crear una subclase de `HTTPBearer` que devuelva un error `403 Forbidden` en lugar del `401 Unauthorized` por defecto:
|
||||
|
||||
{* ../../docs_src/authentication_error_status_code/tutorial001_an_py39.py hl[9:13] *}
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
Ten en cuenta que la función devuelve la instance de la excepción, no la lanza. El lanzamiento se hace en el resto del código interno.
|
||||
|
||||
///
|
||||
@@ -1,8 +1,8 @@
|
||||
# OpenAPI Condicional
|
||||
# OpenAPI condicional { #conditional-openapi }
|
||||
|
||||
Si lo necesitaras, podrías usar configuraciones y variables de entorno para configurar OpenAPI condicionalmente según el entorno, e incluso desactivarlo por completo.
|
||||
|
||||
## Sobre seguridad, APIs y documentación
|
||||
## Sobre seguridad, APIs y documentación { #about-security-apis-and-docs }
|
||||
|
||||
Ocultar las interfaces de usuario de la documentación en producción *no debería* ser la forma de proteger tu API.
|
||||
|
||||
@@ -17,21 +17,21 @@ Si quieres asegurar tu API, hay varias cosas mejores que puedes hacer, por ejemp
|
||||
* Asegúrate de tener modelos Pydantic bien definidos para tus request bodies y responses.
|
||||
* Configura los permisos y roles necesarios usando dependencias.
|
||||
* Nunca guardes contraseñas en texto plano, solo hashes de contraseñas.
|
||||
* Implementa y utiliza herramientas criptográficas bien conocidas, como Passlib y JWT tokens, etc.
|
||||
* Añade controles de permisos más detallados con OAuth2 scopes donde sea necesario.
|
||||
* Implementa y utiliza herramientas criptográficas bien conocidas, como pwdlib y JWT tokens, etc.
|
||||
* Añade controles de permisos más detallados con Scopes de OAuth2 donde sea necesario.
|
||||
* ...etc.
|
||||
|
||||
No obstante, podrías tener un caso de uso muy específico donde realmente necesites desactivar la documentación de la API para algún entorno (por ejemplo, para producción) o dependiendo de configuraciones de variables de entorno.
|
||||
|
||||
## OpenAPI condicional desde configuraciones y variables de entorno
|
||||
## OpenAPI condicional desde configuraciones y variables de entorno { #conditional-openapi-from-settings-and-env-vars }
|
||||
|
||||
Puedes usar fácilmente las mismas configuraciones de Pydantic para configurar tu OpenAPI generado y las interfaces de usuario de la documentación.
|
||||
|
||||
Por ejemplo:
|
||||
|
||||
{* ../../docs_src/conditional_openapi/tutorial001.py hl[6,11] *}
|
||||
{* ../../docs_src/conditional_openapi/tutorial001_py39.py hl[6,11] *}
|
||||
|
||||
Aquí declaramos la configuración `openapi_url` con el mismo valor predeterminado de `"/openapi.json"`.
|
||||
Aquí declaramos la configuración `openapi_url` con el mismo valor por defecto de `"/openapi.json"`.
|
||||
|
||||
Y luego la usamos al crear la app de `FastAPI`.
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Configurar Swagger UI
|
||||
# Configurar Swagger UI { #configure-swagger-ui }
|
||||
|
||||
Puedes configurar algunos <a href="https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration/" class="external-link" target="_blank">parámetros adicionales de Swagger UI</a>.
|
||||
|
||||
@@ -8,7 +8,7 @@ Para configurarlos, pasa el argumento `swagger_ui_parameters` al crear el objeto
|
||||
|
||||
FastAPI convierte las configuraciones a **JSON** para hacerlas compatibles con JavaScript, ya que eso es lo que Swagger UI necesita.
|
||||
|
||||
## Desactivar el resaltado de sintaxis
|
||||
## Desactivar el resaltado de sintaxis { #disable-syntax-highlighting }
|
||||
|
||||
Por ejemplo, podrías desactivar el resaltado de sintaxis en Swagger UI.
|
||||
|
||||
@@ -18,41 +18,41 @@ Sin cambiar la configuración, el resaltado de sintaxis está activado por defec
|
||||
|
||||
Pero puedes desactivarlo estableciendo `syntaxHighlight` en `False`:
|
||||
|
||||
{* ../../docs_src/configure_swagger_ui/tutorial001.py hl[3] *}
|
||||
{* ../../docs_src/configure_swagger_ui/tutorial001_py39.py hl[3] *}
|
||||
|
||||
...y entonces Swagger UI ya no mostrará el resaltado de sintaxis:
|
||||
|
||||
<img src="/img/tutorial/extending-openapi/image03.png">
|
||||
|
||||
## Cambiar el tema
|
||||
## Cambiar el tema { #change-the-theme }
|
||||
|
||||
De la misma manera, podrías configurar el tema del resaltado de sintaxis con la clave `"syntaxHighlight.theme"` (ten en cuenta que tiene un punto en el medio):
|
||||
|
||||
{* ../../docs_src/configure_swagger_ui/tutorial002.py hl[3] *}
|
||||
{* ../../docs_src/configure_swagger_ui/tutorial002_py39.py hl[3] *}
|
||||
|
||||
Esa configuración cambiaría el tema de color del resaltado de sintaxis:
|
||||
|
||||
<img src="/img/tutorial/extending-openapi/image04.png">
|
||||
|
||||
## Cambiar los parámetros predeterminados de Swagger UI
|
||||
## Cambiar los parámetros predeterminados de Swagger UI { #change-default-swagger-ui-parameters }
|
||||
|
||||
FastAPI incluye algunos parámetros de configuración predeterminados apropiados para la mayoría de los casos de uso.
|
||||
|
||||
Incluye estas configuraciones predeterminadas:
|
||||
|
||||
{* ../../fastapi/openapi/docs.py ln[8:23] hl[17:23] *}
|
||||
{* ../../fastapi/openapi/docs.py ln[9:24] hl[18:24] *}
|
||||
|
||||
Puedes sobrescribir cualquiera de ellos estableciendo un valor diferente en el argumento `swagger_ui_parameters`.
|
||||
|
||||
Por ejemplo, para desactivar `deepLinking` podrías pasar estas configuraciones a `swagger_ui_parameters`:
|
||||
|
||||
{* ../../docs_src/configure_swagger_ui/tutorial003.py hl[3] *}
|
||||
{* ../../docs_src/configure_swagger_ui/tutorial003_py39.py hl[3] *}
|
||||
|
||||
## Otros parámetros de Swagger UI
|
||||
## Otros parámetros de Swagger UI { #other-swagger-ui-parameters }
|
||||
|
||||
Para ver todas las demás configuraciones posibles que puedes usar, lee la <a href="https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration/" class="external-link" target="_blank">documentación oficial de los parámetros de Swagger UI</a>.
|
||||
|
||||
## Configuraciones solo de JavaScript
|
||||
## Configuraciones solo de JavaScript { #javascript-only-settings }
|
||||
|
||||
Swagger UI también permite otras configuraciones que son objetos **solo de JavaScript** (por ejemplo, funciones de JavaScript).
|
||||
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
# Recursos Estáticos Personalizados para la Docs UI (Self-Hosting)
|
||||
# Recursos Estáticos Personalizados para la Docs UI (self hosting) { #custom-docs-ui-static-assets-self-hosting }
|
||||
|
||||
La documentación de la API utiliza **Swagger UI** y **ReDoc**, y cada uno de estos necesita algunos archivos JavaScript y CSS.
|
||||
|
||||
Por defecto, esos archivos se sirven desde un <abbr title="Content Delivery Network: Un servicio, normalmente compuesto de varios servidores, que proporciona archivos estáticos, como JavaScript y CSS. Se utiliza comúnmente para servir esos archivos desde el servidor más cercano al cliente, mejorando el rendimiento.">CDN</abbr>.
|
||||
Por defecto, esos archivos se sirven desde un <abbr title="Content Delivery Network – Red de entrega de contenidos: Un servicio, normalmente compuesto de varios servidores, que proporciona archivos estáticos, como JavaScript y CSS. Se usa comúnmente para servir esos archivos desde el servidor más cercano al cliente, mejorando el rendimiento.">CDN</abbr>.
|
||||
|
||||
Pero es posible personalizarlo, puedes establecer un CDN específico, o servir los archivos tú mismo.
|
||||
|
||||
## CDN Personalizado para JavaScript y CSS
|
||||
## CDN Personalizado para JavaScript y CSS { #custom-cdn-for-javascript-and-css }
|
||||
|
||||
Digamos que quieres usar un <abbr title="Content Delivery Network">CDN</abbr> diferente, por ejemplo, quieres usar `https://unpkg.com/`.
|
||||
Digamos que quieres usar un <abbr title="Content Delivery Network – Red de entrega de contenidos">CDN</abbr> diferente, por ejemplo, quieres usar `https://unpkg.com/`.
|
||||
|
||||
Esto podría ser útil si, por ejemplo, vives en un país que restringe algunas URLs.
|
||||
|
||||
### Desactiva la documentación automática
|
||||
### Desactiva la documentación automática { #disable-the-automatic-docs }
|
||||
|
||||
El primer paso es desactivar la documentación automática, ya que por defecto, esos usan el CDN predeterminado.
|
||||
El primer paso es desactivar la documentación automática, ya que por defecto, esos usan el CDN por defecto.
|
||||
|
||||
Para desactivarlos, establece sus URLs en `None` cuando crees tu aplicación de `FastAPI`:
|
||||
|
||||
{* ../../docs_src/custom_docs_ui/tutorial001.py hl[8] *}
|
||||
{* ../../docs_src/custom_docs_ui/tutorial001_py39.py hl[8] *}
|
||||
|
||||
### Incluye la documentación personalizada
|
||||
### Incluye la documentación personalizada { #include-the-custom-docs }
|
||||
|
||||
Ahora puedes crear las *path operations* para la documentación personalizada.
|
||||
|
||||
@@ -28,13 +28,13 @@ Puedes reutilizar las funciones internas de FastAPI para crear las páginas HTML
|
||||
|
||||
* `openapi_url`: la URL donde la página HTML para la documentación puede obtener el OpenAPI esquema de tu API. Puedes usar aquí el atributo `app.openapi_url`.
|
||||
* `title`: el título de tu API.
|
||||
* `oauth2_redirect_url`: puedes usar `app.swagger_ui_oauth2_redirect_url` aquí para usar el valor predeterminado.
|
||||
* `oauth2_redirect_url`: puedes usar `app.swagger_ui_oauth2_redirect_url` aquí para usar el valor por defecto.
|
||||
* `swagger_js_url`: la URL donde el HTML para tu documentación de Swagger UI puede obtener el archivo **JavaScript**. Esta es la URL personalizada del CDN.
|
||||
* `swagger_css_url`: la URL donde el HTML para tu documentación de Swagger UI puede obtener el archivo **CSS**. Esta es la URL personalizada del CDN.
|
||||
|
||||
Y de manera similar para ReDoc...
|
||||
|
||||
{* ../../docs_src/custom_docs_ui/tutorial001.py hl[2:6,11:19,22:24,27:33] *}
|
||||
{* ../../docs_src/custom_docs_ui/tutorial001_py39.py hl[2:6,11:19,22:24,27:33] *}
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
@@ -46,23 +46,23 @@ Swagger UI lo manejará detrás de escena para ti, pero necesita este auxiliar d
|
||||
|
||||
///
|
||||
|
||||
### Crea una *path operation* para probarlo
|
||||
### Crea una *path operation* para probarlo { #create-a-path-operation-to-test-it }
|
||||
|
||||
Ahora, para poder probar que todo funciona, crea una *path operation*:
|
||||
|
||||
{* ../../docs_src/custom_docs_ui/tutorial001.py hl[36:38] *}
|
||||
{* ../../docs_src/custom_docs_ui/tutorial001_py39.py hl[36:38] *}
|
||||
|
||||
### Pruébalo
|
||||
### Pruébalo { #test-it }
|
||||
|
||||
Ahora, deberías poder ir a tu documentación en <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>, y recargar la página, cargará esos recursos desde el nuevo CDN.
|
||||
|
||||
## Self-hosting de JavaScript y CSS para la documentación
|
||||
## self hosting de JavaScript y CSS para la documentación { #self-hosting-javascript-and-css-for-docs }
|
||||
|
||||
El self-hosting de JavaScript y CSS podría ser útil si, por ejemplo, necesitas que tu aplicación siga funcionando incluso offline, sin acceso a Internet, o en una red local.
|
||||
El self hosting de JavaScript y CSS podría ser útil si, por ejemplo, necesitas que tu aplicación siga funcionando incluso offline, sin acceso a Internet, o en una red local.
|
||||
|
||||
Aquí verás cómo servir esos archivos tú mismo, en la misma aplicación de FastAPI, y configurar la documentación para usarla.
|
||||
|
||||
### Estructura de archivos del proyecto
|
||||
### Estructura de archivos del proyecto { #project-file-structure }
|
||||
|
||||
Supongamos que la estructura de archivos de tu proyecto se ve así:
|
||||
|
||||
@@ -85,7 +85,7 @@ Tu nueva estructura de archivos podría verse así:
|
||||
└── static/
|
||||
```
|
||||
|
||||
### Descarga los archivos
|
||||
### Descarga los archivos { #download-the-files }
|
||||
|
||||
Descarga los archivos estáticos necesarios para la documentación y ponlos en ese directorio `static/`.
|
||||
|
||||
@@ -113,14 +113,14 @@ Después de eso, tu estructura de archivos podría verse así:
|
||||
└── swagger-ui.css
|
||||
```
|
||||
|
||||
### Sirve los archivos estáticos
|
||||
### Sirve los archivos estáticos { #serve-the-static-files }
|
||||
|
||||
* Importa `StaticFiles`.
|
||||
* "Monta" una instance de `StaticFiles()` en un path específico.
|
||||
|
||||
{* ../../docs_src/custom_docs_ui/tutorial002.py hl[7,11] *}
|
||||
{* ../../docs_src/custom_docs_ui/tutorial002_py39.py hl[7,11] *}
|
||||
|
||||
### Prueba los archivos estáticos
|
||||
### Prueba los archivos estáticos { #test-the-static-files }
|
||||
|
||||
Inicia tu aplicación y ve a <a href="http://127.0.0.1:8000/static/redoc.standalone.js" class="external-link" target="_blank">http://127.0.0.1:8000/static/redoc.standalone.js</a>.
|
||||
|
||||
@@ -138,15 +138,15 @@ Eso confirma que puedes servir archivos estáticos desde tu aplicación, y que c
|
||||
|
||||
Ahora podemos configurar la aplicación para usar esos archivos estáticos para la documentación.
|
||||
|
||||
### Desactiva la documentación automática para archivos estáticos
|
||||
### Desactiva la documentación automática para archivos estáticos { #disable-the-automatic-docs-for-static-files }
|
||||
|
||||
Igual que cuando usas un CDN personalizado, el primer paso es desactivar la documentación automática, ya que esos usan el CDN por defecto.
|
||||
|
||||
Para desactivarlos, establece sus URLs en `None` cuando crees tu aplicación de `FastAPI`:
|
||||
|
||||
{* ../../docs_src/custom_docs_ui/tutorial002.py hl[9] *}
|
||||
{* ../../docs_src/custom_docs_ui/tutorial002_py39.py hl[9] *}
|
||||
|
||||
### Incluye la documentación personalizada para archivos estáticos
|
||||
### Incluye la documentación personalizada para archivos estáticos { #include-the-custom-docs-for-static-files }
|
||||
|
||||
Y de la misma manera que con un CDN personalizado, ahora puedes crear las *path operations* para la documentación personalizada.
|
||||
|
||||
@@ -154,13 +154,13 @@ Nuevamente, puedes reutilizar las funciones internas de FastAPI para crear las p
|
||||
|
||||
* `openapi_url`: la URL donde la página HTML para la documentación puede obtener el OpenAPI esquema de tu API. Puedes usar aquí el atributo `app.openapi_url`.
|
||||
* `title`: el título de tu API.
|
||||
* `oauth2_redirect_url`: puedes usar `app.swagger_ui_oauth2_redirect_url` aquí para usar el valor predeterminado.
|
||||
* `oauth2_redirect_url`: puedes usar `app.swagger_ui_oauth2_redirect_url` aquí para usar el valor por defecto.
|
||||
* `swagger_js_url`: la URL donde el HTML para tu documentación de Swagger UI puede obtener el archivo **JavaScript**. **Este es el que tu propia aplicación está sirviendo ahora**.
|
||||
* `swagger_css_url`: la URL donde el HTML para tu documentación de Swagger UI puede obtener el archivo **CSS**. **Este es el que tu propia aplicación está sirviendo ahora**.
|
||||
|
||||
Y de manera similar para ReDoc...
|
||||
|
||||
{* ../../docs_src/custom_docs_ui/tutorial002.py hl[2:6,14:22,25:27,30:36] *}
|
||||
{* ../../docs_src/custom_docs_ui/tutorial002_py39.py hl[2:6,14:22,25:27,30:36] *}
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
@@ -172,13 +172,13 @@ Swagger UI lo manejará detrás de escena para ti, pero necesita este auxiliar d
|
||||
|
||||
///
|
||||
|
||||
### Crea una *path operation* para probar archivos estáticos
|
||||
### Crea una *path operation* para probar archivos estáticos { #create-a-path-operation-to-test-static-files }
|
||||
|
||||
Ahora, para poder probar que todo funciona, crea una *path operation*:
|
||||
|
||||
{* ../../docs_src/custom_docs_ui/tutorial002.py hl[39:41] *}
|
||||
{* ../../docs_src/custom_docs_ui/tutorial002_py39.py hl[39:41] *}
|
||||
|
||||
### Prueba la UI de Archivos Estáticos
|
||||
### Prueba la UI de Archivos Estáticos { #test-static-files-ui }
|
||||
|
||||
Ahora, deberías poder desconectar tu WiFi, ir a tu documentación en <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>, y recargar la página.
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Clase personalizada de Request y APIRoute
|
||||
# Clase personalizada de Request y APIRoute { #custom-request-and-apiroute-class }
|
||||
|
||||
En algunos casos, puede que quieras sobrescribir la lógica utilizada por las clases `Request` y `APIRoute`.
|
||||
|
||||
@@ -14,7 +14,7 @@ Si apenas estás comenzando con **FastAPI**, quizás quieras saltar esta secció
|
||||
|
||||
///
|
||||
|
||||
## Casos de uso
|
||||
## Casos de uso { #use-cases }
|
||||
|
||||
Algunos casos de uso incluyen:
|
||||
|
||||
@@ -22,13 +22,13 @@ Algunos casos de uso incluyen:
|
||||
* Descomprimir cuerpos de requests comprimidos con gzip.
|
||||
* Registrar automáticamente todos los request bodies.
|
||||
|
||||
## Manejo de codificaciones personalizadas de request body
|
||||
## Manejo de codificaciones personalizadas de request body { #handling-custom-request-body-encodings }
|
||||
|
||||
Veamos cómo hacer uso de una subclase personalizada de `Request` para descomprimir requests gzip.
|
||||
|
||||
Y una subclase de `APIRoute` para usar esa clase de request personalizada.
|
||||
|
||||
### Crear una clase personalizada `GzipRequest`
|
||||
### Crear una clase personalizada `GzipRequest` { #create-a-custom-gziprequest-class }
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
@@ -42,9 +42,9 @@ Si no hay `gzip` en el header, no intentará descomprimir el cuerpo.
|
||||
|
||||
De esa manera, la misma clase de ruta puede manejar requests comprimidos con gzip o no comprimidos.
|
||||
|
||||
{* ../../docs_src/custom_request_and_route/tutorial001.py hl[8:15] *}
|
||||
{* ../../docs_src/custom_request_and_route/tutorial001_an_py310.py hl[9:16] *}
|
||||
|
||||
### Crear una clase personalizada `GzipRoute`
|
||||
### Crear una clase personalizada `GzipRoute` { #create-a-custom-gziproute-class }
|
||||
|
||||
A continuación, creamos una subclase personalizada de `fastapi.routing.APIRoute` que hará uso de `GzipRequest`.
|
||||
|
||||
@@ -54,7 +54,7 @@ Este método devuelve una función. Y esa función es la que recibirá un reques
|
||||
|
||||
Aquí lo usamos para crear un `GzipRequest` a partir del request original.
|
||||
|
||||
{* ../../docs_src/custom_request_and_route/tutorial001.py hl[18:26] *}
|
||||
{* ../../docs_src/custom_request_and_route/tutorial001_an_py310.py hl[19:27] *}
|
||||
|
||||
/// note | Detalles técnicos
|
||||
|
||||
@@ -78,7 +78,7 @@ Después de eso, toda la lógica de procesamiento es la misma.
|
||||
|
||||
Pero debido a nuestros cambios en `GzipRequest.body`, el request body se descomprimirá automáticamente cuando sea cargado por **FastAPI** si es necesario.
|
||||
|
||||
## Accediendo al request body en un manejador de excepciones
|
||||
## Accediendo al request body en un manejador de excepciones { #accessing-the-request-body-in-an-exception-handler }
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
@@ -92,18 +92,18 @@ También podemos usar este mismo enfoque para acceder al request body en un mane
|
||||
|
||||
Todo lo que necesitamos hacer es manejar el request dentro de un bloque `try`/`except`:
|
||||
|
||||
{* ../../docs_src/custom_request_and_route/tutorial002.py hl[13,15] *}
|
||||
{* ../../docs_src/custom_request_and_route/tutorial002_an_py310.py hl[14,16] *}
|
||||
|
||||
Si ocurre una excepción, la `Request instance` aún estará en el alcance, así que podemos leer y hacer uso del request body cuando manejamos el error:
|
||||
|
||||
{* ../../docs_src/custom_request_and_route/tutorial002.py hl[16:18] *}
|
||||
{* ../../docs_src/custom_request_and_route/tutorial002_an_py310.py hl[17:19] *}
|
||||
|
||||
## Clase personalizada `APIRoute` en un router
|
||||
## Clase personalizada `APIRoute` en un router { #custom-apiroute-class-in-a-router }
|
||||
|
||||
También puedes establecer el parámetro `route_class` de un `APIRouter`:
|
||||
|
||||
{* ../../docs_src/custom_request_and_route/tutorial003.py hl[26] *}
|
||||
{* ../../docs_src/custom_request_and_route/tutorial003_py310.py hl[26] *}
|
||||
|
||||
En este ejemplo, las *path operations* bajo el `router` usarán la clase personalizada `TimedRoute`, y tendrán un header `X-Response-Time` extra en el response con el tiempo que tomó generar el response:
|
||||
|
||||
{* ../../docs_src/custom_request_and_route/tutorial003.py hl[13:20] *}
|
||||
{* ../../docs_src/custom_request_and_route/tutorial003_py310.py hl[13:20] *}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# Extender OpenAPI
|
||||
# Extender OpenAPI { #extending-openapi }
|
||||
|
||||
Hay algunos casos en los que podrías necesitar modificar el esquema de OpenAPI generado.
|
||||
|
||||
En esta sección verás cómo hacerlo.
|
||||
|
||||
## El proceso normal
|
||||
## El proceso normal { #the-normal-process }
|
||||
|
||||
El proceso normal (por defecto) es el siguiente.
|
||||
|
||||
@@ -33,31 +33,31 @@ El parámetro `summary` está disponible en OpenAPI 3.1.0 y versiones superiores
|
||||
|
||||
///
|
||||
|
||||
## Sobrescribir los valores por defecto
|
||||
## Sobrescribir los valores por defecto { #overriding-the-defaults }
|
||||
|
||||
Usando la información anterior, puedes usar la misma función de utilidad para generar el esquema de OpenAPI y sobrescribir cada parte que necesites.
|
||||
|
||||
Por ejemplo, vamos a añadir <a href="https://github.com/Rebilly/ReDoc/blob/master/docs/redoc-vendor-extensions.md#x-logo" class="external-link" target="_blank">la extensión OpenAPI de ReDoc para incluir un logo personalizado</a>.
|
||||
|
||||
### **FastAPI** normal
|
||||
### **FastAPI** normal { #normal-fastapi }
|
||||
|
||||
Primero, escribe toda tu aplicación **FastAPI** como normalmente:
|
||||
|
||||
{* ../../docs_src/extending_openapi/tutorial001.py hl[1,4,7:9] *}
|
||||
{* ../../docs_src/extending_openapi/tutorial001_py39.py hl[1,4,7:9] *}
|
||||
|
||||
### Generar el esquema de OpenAPI
|
||||
### Generar el esquema de OpenAPI { #generate-the-openapi-schema }
|
||||
|
||||
Luego, usa la misma función de utilidad para generar el esquema de OpenAPI, dentro de una función `custom_openapi()`:
|
||||
|
||||
{* ../../docs_src/extending_openapi/tutorial001.py hl[2,15:21] *}
|
||||
{* ../../docs_src/extending_openapi/tutorial001_py39.py hl[2,15:21] *}
|
||||
|
||||
### Modificar el esquema de OpenAPI
|
||||
### Modificar el esquema de OpenAPI { #modify-the-openapi-schema }
|
||||
|
||||
Ahora puedes añadir la extensión de ReDoc, agregando un `x-logo` personalizado al "objeto" `info` en el esquema de OpenAPI:
|
||||
|
||||
{* ../../docs_src/extending_openapi/tutorial001.py hl[22:24] *}
|
||||
{* ../../docs_src/extending_openapi/tutorial001_py39.py hl[22:24] *}
|
||||
|
||||
### Cachear el esquema de OpenAPI
|
||||
### Cachear el esquema de OpenAPI { #cache-the-openapi-schema }
|
||||
|
||||
Puedes usar la propiedad `.openapi_schema` como un "cache", para almacenar tu esquema generado.
|
||||
|
||||
@@ -65,15 +65,15 @@ De esa forma, tu aplicación no tendrá que generar el esquema cada vez que un u
|
||||
|
||||
Se generará solo una vez, y luego se usará el mismo esquema cacheado para las siguientes requests.
|
||||
|
||||
{* ../../docs_src/extending_openapi/tutorial001.py hl[13:14,25:26] *}
|
||||
{* ../../docs_src/extending_openapi/tutorial001_py39.py hl[13:14,25:26] *}
|
||||
|
||||
### Sobrescribir el método
|
||||
### Sobrescribir el método { #override-the-method }
|
||||
|
||||
Ahora puedes reemplazar el método `.openapi()` por tu nueva función.
|
||||
|
||||
{* ../../docs_src/extending_openapi/tutorial001.py hl[29] *}
|
||||
{* ../../docs_src/extending_openapi/tutorial001_py39.py hl[29] *}
|
||||
|
||||
### Revisa
|
||||
### Revisa { #check-it }
|
||||
|
||||
Una vez que vayas a <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a> verás que estás usando tu logo personalizado (en este ejemplo, el logo de **FastAPI**):
|
||||
|
||||
|
||||
@@ -1,39 +1,39 @@
|
||||
# General - Cómo Hacer - Recetas
|
||||
# General - Cómo Hacer - Recetas { #general-how-to-recipes }
|
||||
|
||||
Aquí tienes varias indicaciones hacia otros lugares en la documentación, para preguntas generales o frecuentes.
|
||||
|
||||
## Filtrar Datos - Seguridad
|
||||
## Filtrar Datos - Seguridad { #filter-data-security }
|
||||
|
||||
Para asegurarte de que no devuelves más datos de los que deberías, lee la documentación para [Tutorial - Modelo de Response - Tipo de Retorno](../tutorial/response-model.md){.internal-link target=_blank}.
|
||||
|
||||
## Etiquetas de Documentación - OpenAPI
|
||||
## Etiquetas de Documentación - OpenAPI { #documentation-tags-openapi }
|
||||
|
||||
Para agregar etiquetas a tus *path operations*, y agruparlas en la interfaz de usuario de la documentación, lee la documentación para [Tutorial - Configuraciones de Path Operation - Etiquetas](../tutorial/path-operation-configuration.md#tags){.internal-link target=_blank}.
|
||||
|
||||
## Resumen y Descripción de Documentación - OpenAPI
|
||||
## Resumen y Descripción de Documentación - OpenAPI { #documentation-summary-and-description-openapi }
|
||||
|
||||
Para agregar un resumen y descripción a tus *path operations*, y mostrarlos en la interfaz de usuario de la documentación, lee la documentación para [Tutorial - Configuraciones de Path Operation - Resumen y Descripción](../tutorial/path-operation-configuration.md#summary-and-description){.internal-link target=_blank}.
|
||||
|
||||
## Documentación de Descripción de Response - OpenAPI
|
||||
## Documentación de Descripción de Response - OpenAPI { #documentation-response-description-openapi }
|
||||
|
||||
Para definir la descripción del response, mostrada en la interfaz de usuario de la documentación, lee la documentación para [Tutorial - Configuraciones de Path Operation - Descripción del Response](../tutorial/path-operation-configuration.md#response-description){.internal-link target=_blank}.
|
||||
|
||||
## Documentar la Deprecación de una *Path Operation* - OpenAPI
|
||||
## Documentar la Deprecación de una *Path Operation* - OpenAPI { #documentation-deprecate-a-path-operation-openapi }
|
||||
|
||||
Para deprecar una *path operation*, y mostrarla en la interfaz de usuario de la documentación, lee la documentación para [Tutorial - Configuraciones de Path Operation - Deprecación](../tutorial/path-operation-configuration.md#deprecate-a-path-operation){.internal-link target=_blank}.
|
||||
|
||||
## Convertir cualquier Dato a Compatible con JSON
|
||||
## Convertir cualquier Dato a Compatible con JSON { #convert-any-data-to-json-compatible }
|
||||
|
||||
Para convertir cualquier dato a compatible con JSON, lee la documentación para [Tutorial - Codificador Compatible con JSON](../tutorial/encoder.md){.internal-link target=_blank}.
|
||||
|
||||
## Metadatos OpenAPI - Documentación
|
||||
## Metadatos OpenAPI - Documentación { #openapi-metadata-docs }
|
||||
|
||||
Para agregar metadatos a tu esquema de OpenAPI, incluyendo una licencia, versión, contacto, etc, lee la documentación para [Tutorial - Metadatos y URLs de Documentación](../tutorial/metadata.md){.internal-link target=_blank}.
|
||||
|
||||
## URL Personalizada de OpenAPI
|
||||
## URL Personalizada de OpenAPI { #openapi-custom-url }
|
||||
|
||||
Para personalizar la URL de OpenAPI (o eliminarla), lee la documentación para [Tutorial - Metadatos y URLs de Documentación](../tutorial/metadata.md#openapi-url){.internal-link target=_blank}.
|
||||
|
||||
## URLs de Documentación de OpenAPI
|
||||
## URLs de Documentación de OpenAPI { #openapi-docs-urls }
|
||||
|
||||
Para actualizar las URLs usadas para las interfaces de usuario de documentación generadas automáticamente, lee la documentación para [Tutorial - Metadatos y URLs de Documentación](../tutorial/metadata.md#docs-urls){.internal-link target=_blank}.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# GraphQL
|
||||
# GraphQL { #graphql }
|
||||
|
||||
Como **FastAPI** se basa en el estándar **ASGI**, es muy fácil integrar cualquier paquete de **GraphQL** que también sea compatible con ASGI.
|
||||
|
||||
@@ -14,7 +14,7 @@ Asegúrate de evaluar si los **beneficios** para tu caso de uso compensan los **
|
||||
|
||||
///
|
||||
|
||||
## Paquetes de GraphQL
|
||||
## Paquetes de GraphQL { #graphql-libraries }
|
||||
|
||||
Aquí algunos de los paquetes de **GraphQL** que tienen soporte **ASGI**. Podrías usarlos con **FastAPI**:
|
||||
|
||||
@@ -27,7 +27,7 @@ Aquí algunos de los paquetes de **GraphQL** que tienen soporte **ASGI**. Podrí
|
||||
* <a href="https://graphene-python.org/" class="external-link" target="_blank">Graphene</a>
|
||||
* Con <a href="https://github.com/ciscorn/starlette-graphene3" class="external-link" target="_blank">starlette-graphene3</a>
|
||||
|
||||
## GraphQL con Strawberry
|
||||
## GraphQL con Strawberry { #graphql-with-strawberry }
|
||||
|
||||
Si necesitas o quieres trabajar con **GraphQL**, <a href="https://strawberry.rocks/" class="external-link" target="_blank">**Strawberry**</a> es el paquete **recomendado** ya que tiene un diseño muy similar al diseño de **FastAPI**, todo basado en **anotaciones de tipos**.
|
||||
|
||||
@@ -35,13 +35,13 @@ Dependiendo de tu caso de uso, podrías preferir usar un paquete diferente, pero
|
||||
|
||||
Aquí tienes una pequeña vista previa de cómo podrías integrar Strawberry con FastAPI:
|
||||
|
||||
{* ../../docs_src/graphql/tutorial001.py hl[3,22,25:26] *}
|
||||
{* ../../docs_src/graphql_/tutorial001_py39.py hl[3,22,25] *}
|
||||
|
||||
Puedes aprender más sobre Strawberry en la <a href="https://strawberry.rocks/" class="external-link" target="_blank">documentación de Strawberry</a>.
|
||||
|
||||
Y también la documentación sobre <a href="https://strawberry.rocks/docs/integrations/fastapi" class="external-link" target="_blank">Strawberry con FastAPI</a>.
|
||||
|
||||
## `GraphQLApp` viejo de Starlette
|
||||
## `GraphQLApp` viejo de Starlette { #older-graphqlapp-from-starlette }
|
||||
|
||||
Las versiones anteriores de Starlette incluían una clase `GraphQLApp` para integrar con <a href="https://graphene-python.org/" class="external-link" target="_blank">Graphene</a>.
|
||||
|
||||
@@ -53,7 +53,7 @@ Si necesitas GraphQL, aún te recomendaría revisar <a href="https://strawberry.
|
||||
|
||||
///
|
||||
|
||||
## Aprende Más
|
||||
## Aprende Más { #learn-more }
|
||||
|
||||
Puedes aprender más sobre **GraphQL** en la <a href="https://graphql.org/" class="external-link" target="_blank">documentación oficial de GraphQL</a>.
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# How To - Recetas
|
||||
# Cómo hacer - Recetas { #how-to-recipes }
|
||||
|
||||
Aquí verás diferentes recetas o guías de "cómo hacer" para **varios temas**.
|
||||
|
||||
@@ -8,6 +8,6 @@ Si algo parece interesante y útil para tu proyecto, adelante y revísalo, pero
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
Si quieres **aprender FastAPI** de una manera estructurada (recomendado), ve y lee el [Tutorial - User Guide](../tutorial/index.md){.internal-link target=_blank} capítulo por capítulo.
|
||||
Si quieres **aprender FastAPI** de una manera estructurada (recomendado), ve y lee el [Tutorial - Guía de Usuario](../tutorial/index.md){.internal-link target=_blank} capítulo por capítulo en su lugar.
|
||||
|
||||
///
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
# Migra de Pydantic v1 a Pydantic v2 { #migrate-from-pydantic-v1-to-pydantic-v2 }
|
||||
|
||||
Si tienes una app de FastAPI antigua, podrías estar usando Pydantic versión 1.
|
||||
|
||||
FastAPI versión 0.100.0 tenía compatibilidad con Pydantic v1 o v2. Usaba la que tuvieras instalada.
|
||||
|
||||
FastAPI versión 0.119.0 introdujo compatibilidad parcial con Pydantic v1 desde dentro de Pydantic v2 (como `pydantic.v1`), para facilitar la migración a v2.
|
||||
|
||||
FastAPI 0.126.0 eliminó la compatibilidad con Pydantic v1, aunque siguió soportando `pydantic.v1` por un poquito más de tiempo.
|
||||
|
||||
/// warning | Advertencia
|
||||
|
||||
El equipo de Pydantic dejó de dar soporte a Pydantic v1 para las versiones más recientes de Python, comenzando con **Python 3.14**.
|
||||
|
||||
Esto incluye `pydantic.v1`, que ya no está soportado en Python 3.14 y superiores.
|
||||
|
||||
Si quieres usar las funcionalidades más recientes de Python, tendrás que asegurarte de usar Pydantic v2.
|
||||
|
||||
///
|
||||
|
||||
Si tienes una app de FastAPI antigua con Pydantic v1, aquí te muestro cómo migrarla a Pydantic v2, y las **funcionalidades en FastAPI 0.119.0** para ayudarte con una migración gradual.
|
||||
|
||||
## Guía oficial { #official-guide }
|
||||
|
||||
Pydantic tiene una <a href="https://docs.pydantic.dev/latest/migration/" class="external-link" target="_blank">Guía de migración</a> oficial de v1 a v2.
|
||||
|
||||
También incluye qué cambió, cómo las validaciones ahora son más correctas y estrictas, posibles consideraciones, etc.
|
||||
|
||||
Puedes leerla para entender mejor qué cambió.
|
||||
|
||||
## Tests { #tests }
|
||||
|
||||
Asegúrate de tener [tests](../tutorial/testing.md){.internal-link target=_blank} para tu app y de ejecutarlos en integración continua (CI).
|
||||
|
||||
Así podrás hacer la actualización y asegurarte de que todo sigue funcionando como esperas.
|
||||
|
||||
## `bump-pydantic` { #bump-pydantic }
|
||||
|
||||
En muchos casos, cuando usas modelos de Pydantic normales sin personalizaciones, podrás automatizar gran parte del proceso de migración de Pydantic v1 a Pydantic v2.
|
||||
|
||||
Puedes usar <a href="https://github.com/pydantic/bump-pydantic" class="external-link" target="_blank">`bump-pydantic`</a> del mismo equipo de Pydantic.
|
||||
|
||||
Esta herramienta te ayudará a cambiar automáticamente la mayor parte del código que necesita cambiarse.
|
||||
|
||||
Después de esto, puedes ejecutar los tests y revisa si todo funciona. Si es así, ya terminaste. 😎
|
||||
|
||||
## Pydantic v1 en v2 { #pydantic-v1-in-v2 }
|
||||
|
||||
Pydantic v2 incluye todo lo de Pydantic v1 como un submódulo `pydantic.v1`. Pero esto ya no está soportado en versiones por encima de Python 3.13.
|
||||
|
||||
Esto significa que puedes instalar la versión más reciente de Pydantic v2 e importar y usar los componentes viejos de Pydantic v1 desde este submódulo, como si tuvieras instalado el Pydantic v1 antiguo.
|
||||
|
||||
{* ../../docs_src/pydantic_v1_in_v2/tutorial001_an_py310.py hl[1,4] *}
|
||||
|
||||
### Compatibilidad de FastAPI con Pydantic v1 en v2 { #fastapi-support-for-pydantic-v1-in-v2 }
|
||||
|
||||
Desde FastAPI 0.119.0, también hay compatibilidad parcial para Pydantic v1 desde dentro de Pydantic v2, para facilitar la migración a v2.
|
||||
|
||||
Así que podrías actualizar Pydantic a la última versión 2 y cambiar los imports para usar el submódulo `pydantic.v1`, y en muchos casos simplemente funcionaría.
|
||||
|
||||
{* ../../docs_src/pydantic_v1_in_v2/tutorial002_an_py310.py hl[2,5,15] *}
|
||||
|
||||
/// warning | Advertencia
|
||||
|
||||
Ten en cuenta que, como el equipo de Pydantic ya no da soporte a Pydantic v1 en versiones recientes de Python, empezando por Python 3.14, usar `pydantic.v1` tampoco está soportado en Python 3.14 y superiores.
|
||||
|
||||
///
|
||||
|
||||
### Pydantic v1 y v2 en la misma app { #pydantic-v1-and-v2-on-the-same-app }
|
||||
|
||||
**No está soportado** por Pydantic tener un modelo de Pydantic v2 con sus propios campos definidos como modelos de Pydantic v1 o viceversa.
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph "❌ Not Supported"
|
||||
direction TB
|
||||
subgraph V2["Pydantic v2 Model"]
|
||||
V1Field["Pydantic v1 Model"]
|
||||
end
|
||||
subgraph V1["Pydantic v1 Model"]
|
||||
V2Field["Pydantic v2 Model"]
|
||||
end
|
||||
end
|
||||
|
||||
style V2 fill:#f9fff3
|
||||
style V1 fill:#fff6f0
|
||||
style V1Field fill:#fff6f0
|
||||
style V2Field fill:#f9fff3
|
||||
```
|
||||
|
||||
...pero puedes tener modelos separados usando Pydantic v1 y v2 en la misma app.
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph "✅ Supported"
|
||||
direction TB
|
||||
subgraph V2["Pydantic v2 Model"]
|
||||
V2Field["Pydantic v2 Model"]
|
||||
end
|
||||
subgraph V1["Pydantic v1 Model"]
|
||||
V1Field["Pydantic v1 Model"]
|
||||
end
|
||||
end
|
||||
|
||||
style V2 fill:#f9fff3
|
||||
style V1 fill:#fff6f0
|
||||
style V1Field fill:#fff6f0
|
||||
style V2Field fill:#f9fff3
|
||||
```
|
||||
|
||||
En algunos casos, incluso es posible tener modelos de Pydantic v1 y v2 en la misma **path operation** de tu app de FastAPI:
|
||||
|
||||
{* ../../docs_src/pydantic_v1_in_v2/tutorial003_an_py310.py hl[2:3,6,12,21:22] *}
|
||||
|
||||
En el ejemplo anterior, el modelo de entrada es un modelo de Pydantic v1 y el modelo de salida (definido en `response_model=ItemV2`) es un modelo de Pydantic v2.
|
||||
|
||||
### Parámetros de Pydantic v1 { #pydantic-v1-parameters }
|
||||
|
||||
Si necesitas usar algunas de las herramientas específicas de FastAPI para parámetros como `Body`, `Query`, `Form`, etc. con modelos de Pydantic v1, puedes importarlas de `fastapi.temp_pydantic_v1_params` mientras terminas la migración a Pydantic v2:
|
||||
|
||||
{* ../../docs_src/pydantic_v1_in_v2/tutorial004_an_py310.py hl[4,18] *}
|
||||
|
||||
### Migra por pasos { #migrate-in-steps }
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
Primero prueba con `bump-pydantic`, si tus tests pasan y eso funciona, entonces terminaste con un solo comando. ✨
|
||||
|
||||
///
|
||||
|
||||
Si `bump-pydantic` no funciona para tu caso de uso, puedes usar la compatibilidad de modelos Pydantic v1 y v2 en la misma app para hacer la migración a Pydantic v2 de forma gradual.
|
||||
|
||||
Podrías primero actualizar Pydantic para usar la última versión 2, y cambiar los imports para usar `pydantic.v1` para todos tus modelos.
|
||||
|
||||
Luego puedes empezar a migrar tus modelos de Pydantic v1 a v2 por grupos, en pasos graduales. 🚶
|
||||
@@ -1,18 +1,18 @@
|
||||
# Separación de Esquemas OpenAPI para Entrada y Salida o No
|
||||
# Separación de Esquemas OpenAPI para Entrada y Salida o No { #separate-openapi-schemas-for-input-and-output-or-not }
|
||||
|
||||
Al usar **Pydantic v2**, el OpenAPI generado es un poco más exacto y **correcto** que antes. 😎
|
||||
Desde que se lanzó **Pydantic v2**, el OpenAPI generado es un poco más exacto y **correcto** que antes. 😎
|
||||
|
||||
De hecho, en algunos casos, incluso tendrá **dos JSON Schemas** en OpenAPI para el mismo modelo Pydantic, para entrada y salida, dependiendo de si tienen **valores por defecto**.
|
||||
|
||||
Veamos cómo funciona eso y cómo cambiarlo si necesitas hacerlo.
|
||||
|
||||
## Modelos Pydantic para Entrada y Salida
|
||||
## Modelos Pydantic para Entrada y Salida { #pydantic-models-for-input-and-output }
|
||||
|
||||
Digamos que tienes un modelo Pydantic con valores por defecto, como este:
|
||||
|
||||
{* ../../docs_src/separate_openapi_schemas/tutorial001_py310.py ln[1:7] hl[7] *}
|
||||
|
||||
### Modelo para Entrada
|
||||
### Modelo para Entrada { #model-for-input }
|
||||
|
||||
Si usas este modelo como entrada, como aquí:
|
||||
|
||||
@@ -20,7 +20,7 @@ Si usas este modelo como entrada, como aquí:
|
||||
|
||||
...entonces el campo `description` **no será requerido**. Porque tiene un valor por defecto de `None`.
|
||||
|
||||
### Modelo de Entrada en la Documentación
|
||||
### Modelo de Entrada en la Documentación { #input-model-in-docs }
|
||||
|
||||
Puedes confirmar eso en la documentación, el campo `description` no tiene un **asterisco rojo**, no está marcado como requerido:
|
||||
|
||||
@@ -28,7 +28,7 @@ Puedes confirmar eso en la documentación, el campo `description` no tiene un **
|
||||
<img src="/img/tutorial/separate-openapi-schemas/image01.png">
|
||||
</div>
|
||||
|
||||
### Modelo para Salida
|
||||
### Modelo para Salida { #model-for-output }
|
||||
|
||||
Pero si usas el mismo modelo como salida, como aquí:
|
||||
|
||||
@@ -36,7 +36,7 @@ Pero si usas el mismo modelo como salida, como aquí:
|
||||
|
||||
...entonces, porque `description` tiene un valor por defecto, si **no devuelves nada** para ese campo, aún tendrá ese **valor por defecto**.
|
||||
|
||||
### Modelo para Datos de Response de Salida
|
||||
### Modelo para Datos de Response de Salida { #model-for-output-response-data }
|
||||
|
||||
Si interactúas con la documentación y revisas el response, aunque el código no agregó nada en uno de los campos `description`, el response JSON contiene el valor por defecto (`null`):
|
||||
|
||||
@@ -46,7 +46,7 @@ Si interactúas con la documentación y revisas el response, aunque el código n
|
||||
|
||||
Esto significa que **siempre tendrá un valor**, solo que a veces el valor podría ser `None` (o `null` en JSON).
|
||||
|
||||
Eso significa que, los clientes que usan tu API no tienen que comprobar si el valor existe o no, pueden **asumir que el campo siempre estará allí**, pero solo que en algunos casos tendrá el valor por defecto de `None`.
|
||||
Eso significa que, los clientes que usan tu API no tienen que revisar si el valor existe o no, pueden **asumir que el campo siempre estará allí**, pero solo que en algunos casos tendrá el valor por defecto de `None`.
|
||||
|
||||
La forma de describir esto en OpenAPI es marcar ese campo como **requerido**, porque siempre estará allí.
|
||||
|
||||
@@ -55,7 +55,7 @@ Debido a eso, el JSON Schema para un modelo puede ser diferente dependiendo de s
|
||||
* para **entrada** el `description` **no será requerido**
|
||||
* para **salida** será **requerido** (y posiblemente `None`, o en términos de JSON, `null`)
|
||||
|
||||
### Modelo para Salida en la Documentación
|
||||
### Modelo para Salida en la Documentación { #model-for-output-in-docs }
|
||||
|
||||
También puedes revisar el modelo de salida en la documentación, **ambos** `name` y `description` están marcados como **requeridos** con un **asterisco rojo**:
|
||||
|
||||
@@ -63,7 +63,7 @@ También puedes revisar el modelo de salida en la documentación, **ambos** `nam
|
||||
<img src="/img/tutorial/separate-openapi-schemas/image03.png">
|
||||
</div>
|
||||
|
||||
### Modelo para Entrada y Salida en la Documentación
|
||||
### Modelo para Entrada y Salida en la Documentación { #model-for-input-and-output-in-docs }
|
||||
|
||||
Y si revisas todos los esquemas disponibles (JSON Schemas) en OpenAPI, verás que hay dos, uno `Item-Input` y uno `Item-Output`.
|
||||
|
||||
@@ -77,7 +77,7 @@ Pero para `Item-Output`, `description` **es requerido**, tiene un asterisco rojo
|
||||
|
||||
Con esta funcionalidad de **Pydantic v2**, la documentación de tu API es más **precisa**, y si tienes clientes y SDKs autogenerados, también serán más precisos, con una mejor **experiencia para desarrolladores** y consistencia. 🎉
|
||||
|
||||
## No Separar Esquemas
|
||||
## No Separar Esquemas { #do-not-separate-schemas }
|
||||
|
||||
Ahora, hay algunos casos donde podrías querer tener el **mismo esquema para entrada y salida**.
|
||||
|
||||
@@ -85,7 +85,7 @@ Probablemente el caso principal para esto es si ya tienes algún código cliente
|
||||
|
||||
En ese caso, puedes desactivar esta funcionalidad en **FastAPI**, con el parámetro `separate_input_output_schemas=False`.
|
||||
|
||||
/// info | Información
|
||||
/// info
|
||||
|
||||
El soporte para `separate_input_output_schemas` fue agregado en FastAPI `0.102.0`. 🤓
|
||||
|
||||
@@ -93,12 +93,10 @@ El soporte para `separate_input_output_schemas` fue agregado en FastAPI `0.102.0
|
||||
|
||||
{* ../../docs_src/separate_openapi_schemas/tutorial002_py310.py hl[10] *}
|
||||
|
||||
### Mismo Esquema para Modelos de Entrada y Salida en la Documentación
|
||||
### Mismo Esquema para Modelos de Entrada y Salida en la Documentación { #same-schema-for-input-and-output-models-in-docs }
|
||||
|
||||
Y ahora habrá un único esquema para entrada y salida para el modelo, solo `Item`, y tendrá `description` como **no requerido**:
|
||||
|
||||
<div class="screenshot">
|
||||
<img src="/img/tutorial/separate_openapi_schemas/image05.png">
|
||||
<img src="/img/tutorial/separate-openapi-schemas/image05.png">
|
||||
</div>
|
||||
|
||||
Este es el mismo comportamiento que en Pydantic v1. 🤓
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Probando una Base de Datos
|
||||
# Probando una Base de Datos { #testing-a-database }
|
||||
|
||||
Puedes estudiar sobre bases de datos, SQL y SQLModel en la <a href="https://sqlmodel.tiangolo.com/" class="external-link" target="_blank">documentación de SQLModel</a>. 🤓
|
||||
|
||||
|
||||
+114
-45
@@ -1,11 +1,11 @@
|
||||
# FastAPI
|
||||
# FastAPI { #fastapi }
|
||||
|
||||
<style>
|
||||
.md-content .md-typeset h1 { display: none; }
|
||||
</style>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://fastapi.tiangolo.com"><img src="https://fastapi.tiangolo.com/img/logo-margin/logo-teal.png" alt="FastAPI"></a>
|
||||
<a href="https://fastapi.tiangolo.com/es"><img src="https://fastapi.tiangolo.com/img/logo-margin/logo-teal.png" alt="FastAPI"></a>
|
||||
</p>
|
||||
<p align="center">
|
||||
<em>FastAPI framework, alto rendimiento, fácil de aprender, rápido de programar, listo para producción</em>
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
---
|
||||
|
||||
**Documentación**: <a href="https://fastapi.tiangolo.com" target="_blank">https://fastapi.tiangolo.com</a>
|
||||
**Documentación**: <a href="https://fastapi.tiangolo.com/es" target="_blank">https://fastapi.tiangolo.com</a>
|
||||
|
||||
**Código Fuente**: <a href="https://github.com/fastapi/fastapi" target="_blank">https://github.com/fastapi/fastapi</a>
|
||||
|
||||
@@ -35,12 +35,12 @@
|
||||
|
||||
FastAPI es un framework web moderno, rápido (de alto rendimiento), para construir APIs con Python basado en las anotaciones de tipos estándar de Python.
|
||||
|
||||
Las características clave son:
|
||||
Las funcionalidades clave son:
|
||||
|
||||
* **Rápido**: Muy alto rendimiento, a la par con **NodeJS** y **Go** (gracias a Starlette y Pydantic). [Uno de los frameworks Python más rápidos disponibles](#performance).
|
||||
* **Rápido de programar**: Aumenta la velocidad para desarrollar funcionalidades en aproximadamente un 200% a 300%. *
|
||||
* **Menos bugs**: Reduce en aproximadamente un 40% los errores inducidos por humanos (desarrolladores). *
|
||||
* **Intuitivo**: Gran soporte para editores. <abbr title="también conocido como autocompletado, IntelliSense">Autocompletado</abbr> en todas partes. Menos tiempo depurando.
|
||||
* **Intuitivo**: Gran soporte para editores. <abbr title="también conocido como auto-complete, autocompletado, IntelliSense">Autocompletado</abbr> 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.
|
||||
@@ -48,24 +48,30 @@ Las características clave son:
|
||||
|
||||
<small>* estimación basada en pruebas con un equipo de desarrollo interno, construyendo aplicaciones de producción.</small>
|
||||
|
||||
## Sponsors
|
||||
## Sponsors { #sponsors }
|
||||
|
||||
<!-- sponsors -->
|
||||
|
||||
{% if sponsors %}
|
||||
### Sponsor Keystone { #keystone-sponsor }
|
||||
|
||||
{% for sponsor in sponsors.keystone -%}
|
||||
<a href="{{ sponsor.url }}" target="_blank" title="{{ sponsor.title }}"><img src="{{ sponsor.img }}" style="border-radius:15px"></a>
|
||||
{% endfor -%}
|
||||
|
||||
### Sponsors Oro y Plata { #gold-and-silver-sponsors }
|
||||
|
||||
{% for sponsor in sponsors.gold -%}
|
||||
<a href="{{ sponsor.url }}" target="_blank" title="{{ sponsor.title }}"><img src="{{ sponsor.img }}" style="border-radius:15px"></a>
|
||||
{% endfor -%}
|
||||
{%- for sponsor in sponsors.silver -%}
|
||||
<a href="{{ sponsor.url }}" target="_blank" title="{{ sponsor.title }}"><img src="{{ sponsor.img }}" style="border-radius:15px"></a>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
<!-- /sponsors -->
|
||||
|
||||
<a href="https://fastapi.tiangolo.com/fastapi-people/#sponsors" class="external-link" target="_blank">Otros sponsors</a>
|
||||
<a href="https://fastapi.tiangolo.com/es/fastapi-people/#sponsors" class="external-link" target="_blank">Otros sponsors</a>
|
||||
|
||||
## Opiniones
|
||||
## Opiniones { #opinions }
|
||||
|
||||
"_[...] Estoy usando **FastAPI** un montón estos días. [...] De hecho, estoy planeando usarlo para todos los servicios de **ML de mi equipo en Microsoft**. Algunos de ellos se están integrando en el núcleo del producto **Windows** y algunos productos de **Office**._"
|
||||
|
||||
@@ -87,13 +93,13 @@ Las características clave son:
|
||||
|
||||
"_Estoy súper emocionado con **FastAPI**. ¡Es tan divertido!_"
|
||||
|
||||
<div style="text-align: right; margin-right: 10%;">Brian Okken - <strong><a href="https://pythonbytes.fm/episodes/show/123/time-to-right-the-py-wrongs?time_in_sec=855" target="_blank">host del podcast Python Bytes</a></strong> <a href="https://x.com/brianokken/status/1112220079972728832" target="_blank"><small>(ref)</small></a></div>
|
||||
<div style="text-align: right; margin-right: 10%;">Brian Okken - <strong><a href="https://pythonbytes.fm/episodes/show/123/time-to-right-the-py-wrongs?time_in_sec=855" target="_blank">Python Bytes</a> host del podcast</strong> <a href="https://x.com/brianokken/status/1112220079972728832" target="_blank"><small>(ref)</small></a></div>
|
||||
|
||||
---
|
||||
|
||||
"_Honestamente, lo que has construido parece súper sólido y pulido. En muchos aspectos, es lo que quería que **Hug** fuera; es realmente inspirador ver a alguien construir eso._"
|
||||
|
||||
<div style="text-align: right; margin-right: 10%;">Timothy Crosley - <strong><a href="https://github.com/hugapi/hug" target="_blank">creador de Hug</a></strong> <a href="https://news.ycombinator.com/item?id=19455465" target="_blank"><small>(ref)</small></a></div>
|
||||
<div style="text-align: right; margin-right: 10%;">Timothy Crosley - <strong><a href="https://github.com/hugapi/hug" target="_blank">Hug</a> creador</strong> <a href="https://news.ycombinator.com/item?id=19455465" target="_blank"><small>(ref)</small></a></div>
|
||||
|
||||
---
|
||||
|
||||
@@ -111,24 +117,30 @@ Las características clave son:
|
||||
|
||||
---
|
||||
|
||||
## **Typer**, el FastAPI de las CLIs
|
||||
## Mini documental de FastAPI { #fastapi-mini-documentary }
|
||||
|
||||
Hay un <a href="https://www.youtube.com/watch?v=mpR8ngthqiE" class="external-link" target="_blank">mini documental de FastAPI</a> lanzado a finales de 2025, puedes verlo online:
|
||||
|
||||
<a href="https://www.youtube.com/watch?v=mpR8ngthqiE" target="_blank"><img src="https://fastapi.tiangolo.com/img/fastapi-documentary.jpg" alt="FastAPI Mini Documentary"></a>
|
||||
|
||||
## **Typer**, el FastAPI de las CLIs { #typer-the-fastapi-of-clis }
|
||||
|
||||
<a href="https://typer.tiangolo.com" target="_blank"><img src="https://typer.tiangolo.com/img/logo-margin/logo-margin-vector.svg" style="width: 20%;"></a>
|
||||
|
||||
Si estás construyendo una aplicación de <abbr title="Interfaz de Línea de Comandos">CLI</abbr> para ser usada en el terminal en lugar de una API web, revisa <a href="https://typer.tiangolo.com/" class="external-link" target="_blank">**Typer**</a>.
|
||||
Si estás construyendo una aplicación de <abbr title="Command Line Interface – Interfaz de Línea de Comandos">CLI</abbr> para ser usada en la terminal en lugar de una API web, revisa <a href="https://typer.tiangolo.com/" class="external-link" target="_blank">**Typer**</a>.
|
||||
|
||||
**Typer** es el hermano pequeño de FastAPI. Y está destinado a ser el **FastAPI de las CLIs**. ⌨️ 🚀
|
||||
|
||||
## Requisitos
|
||||
## Requisitos { #requirements }
|
||||
|
||||
FastAPI se apoya en hombros de gigantes:
|
||||
|
||||
* <a href="https://www.starlette.dev/" class="external-link" target="_blank">Starlette</a> para las partes web.
|
||||
* <a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic</a> para las partes de datos.
|
||||
|
||||
## Instalación
|
||||
## Instalación { #installation }
|
||||
|
||||
Crea y activa un <a href="https://fastapi.tiangolo.com/virtual-environments/" class="external-link" target="_blank">entorno virtual</a> y luego instala FastAPI:
|
||||
Crea y activa un <a href="https://fastapi.tiangolo.com/es/virtual-environments/" class="external-link" target="_blank">entorno virtual</a> y luego instala FastAPI:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
@@ -142,11 +154,11 @@ $ pip install "fastapi[standard]"
|
||||
|
||||
**Nota**: Asegúrate de poner `"fastapi[standard]"` entre comillas para asegurar que funcione en todas las terminales.
|
||||
|
||||
## Ejemplo
|
||||
## Ejemplo { #example }
|
||||
|
||||
### Créalo
|
||||
### Créalo { #create-it }
|
||||
|
||||
* Crea un archivo `main.py` con:
|
||||
Crea un archivo `main.py` con:
|
||||
|
||||
```Python
|
||||
from typing import Union
|
||||
@@ -191,11 +203,11 @@ async def read_item(item_id: int, q: Union[str, None] = None):
|
||||
|
||||
**Nota**:
|
||||
|
||||
Si no lo sabes, revisa la sección _"¿Con prisa?"_ sobre <a href="https://fastapi.tiangolo.com/async/#in-a-hurry" target="_blank">`async` y `await` en la documentación</a>.
|
||||
Si no lo sabes, revisa la sección _"¿Con prisa?"_ sobre <a href="https://fastapi.tiangolo.com/es/async/#in-a-hurry" target="_blank">`async` y `await` en la documentación</a>.
|
||||
|
||||
</details>
|
||||
|
||||
### Córrelo
|
||||
### Córrelo { #run-it }
|
||||
|
||||
Corre el servidor con:
|
||||
|
||||
@@ -233,11 +245,11 @@ El comando `fastapi dev` lee tu archivo `main.py`, detecta la app **FastAPI** en
|
||||
|
||||
Por defecto, `fastapi dev` comenzará con auto-recarga habilitada para el desarrollo local.
|
||||
|
||||
Puedes leer más sobre esto en la <a href="https://fastapi.tiangolo.com/fastapi-cli/" target="_blank">documentación del CLI de FastAPI</a>.
|
||||
Puedes leer más sobre esto en la <a href="https://fastapi.tiangolo.com/es/fastapi-cli/" target="_blank">documentación del CLI de FastAPI</a>.
|
||||
|
||||
</details>
|
||||
|
||||
### Revísalo
|
||||
### Revísalo { #check-it }
|
||||
|
||||
Abre tu navegador en <a href="http://127.0.0.1:8000/items/5?q=somequery" class="external-link" target="_blank">http://127.0.0.1:8000/items/5?q=somequery</a>.
|
||||
|
||||
@@ -254,7 +266,7 @@ Ya creaste una API que:
|
||||
* El _path_ `/items/{item_id}` tiene un _parámetro de path_ `item_id` que debe ser un `int`.
|
||||
* El _path_ `/items/{item_id}` tiene un _parámetro de query_ `q` opcional que es un `str`.
|
||||
|
||||
### Documentación interactiva de la API
|
||||
### Documentación interactiva de la API { #interactive-api-docs }
|
||||
|
||||
Ahora ve a <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
|
||||
|
||||
@@ -262,7 +274,7 @@ Verás la documentación interactiva automática de la API (proporcionada por <a
|
||||
|
||||

|
||||
|
||||
### Documentación de API Alternativa
|
||||
### Documentación alternativa de la API { #alternative-api-docs }
|
||||
|
||||
Y ahora, ve a <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>.
|
||||
|
||||
@@ -270,7 +282,7 @@ Verás la documentación alternativa automática (proporcionada por <a href="htt
|
||||
|
||||

|
||||
|
||||
## Actualización del Ejemplo
|
||||
## Actualización del ejemplo { #example-upgrade }
|
||||
|
||||
Ahora modifica el archivo `main.py` para recibir un body desde un request `PUT`.
|
||||
|
||||
@@ -308,7 +320,7 @@ def update_item(item_id: int, item: Item):
|
||||
|
||||
El servidor `fastapi dev` debería recargarse automáticamente.
|
||||
|
||||
### Actualización de la Documentación Interactiva de la API
|
||||
### Actualización de la documentación interactiva de la API { #interactive-api-docs-upgrade }
|
||||
|
||||
Ahora ve a <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
|
||||
|
||||
@@ -324,7 +336,7 @@ Ahora ve a <a href="http://127.0.0.1:8000/docs" class="external-link" target="_b
|
||||
|
||||

|
||||
|
||||
### Actualización de la Documentación Alternativa de la API
|
||||
### Actualización de la documentación alternativa de la API { #alternative-api-docs-upgrade }
|
||||
|
||||
Y ahora, ve a <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>.
|
||||
|
||||
@@ -332,7 +344,7 @@ Y ahora, ve a <a href="http://127.0.0.1:8000/redoc" class="external-link" target
|
||||
|
||||

|
||||
|
||||
### Resumen
|
||||
### Resumen { #recap }
|
||||
|
||||
En resumen, declaras **una vez** los tipos de parámetros, body, etc. como parámetros de función.
|
||||
|
||||
@@ -387,13 +399,13 @@ Volviendo al ejemplo de código anterior, **FastAPI**:
|
||||
* Validará que haya un `item_id` en el path para requests `GET` y `PUT`.
|
||||
* Validará que el `item_id` sea del tipo `int` para requests `GET` y `PUT`.
|
||||
* Si no lo es, el cliente verá un error útil y claro.
|
||||
* Comprobará si hay un parámetro de query opcional llamado `q` (como en `http://127.0.0.1:8000/items/foo?q=somequery`) para requests `GET`.
|
||||
* Revisa si hay un parámetro de query opcional llamado `q` (como en `http://127.0.0.1:8000/items/foo?q=somequery`) para requests `GET`.
|
||||
* Como el parámetro `q` está declarado con `= None`, es opcional.
|
||||
* Sin el `None` sería requerido (como lo es el body en el caso con `PUT`).
|
||||
* Para requests `PUT` a `/items/{item_id}`, leerá el body como JSON:
|
||||
* Comprobará que tiene un atributo requerido `name` que debe ser un `str`.
|
||||
* Comprobará que tiene un atributo requerido `price` que debe ser un `float`.
|
||||
* Comprobará que tiene un atributo opcional `is_offer`, que debe ser un `bool`, si está presente.
|
||||
* Revisa que tiene un atributo requerido `name` que debe ser un `str`.
|
||||
* Revisa que tiene un atributo requerido `price` que debe ser un `float`.
|
||||
* Revisa que tiene un atributo opcional `is_offer`, que debe ser un `bool`, si está presente.
|
||||
* Todo esto también funcionaría para objetos JSON profundamente anidados.
|
||||
* Convertirá de y a JSON automáticamente.
|
||||
* Documentará todo con OpenAPI, que puede ser usado por:
|
||||
@@ -427,7 +439,7 @@ Intenta cambiar la línea con:
|
||||
|
||||

|
||||
|
||||
Para un ejemplo más completo incluyendo más funcionalidades, ve al <a href="https://fastapi.tiangolo.com/tutorial/">Tutorial - Guía del Usuario</a>.
|
||||
Para un ejemplo más completo incluyendo más funcionalidades, ve al <a href="https://fastapi.tiangolo.com/es/tutorial/">Tutorial - Guía del Usuario</a>.
|
||||
|
||||
**Alerta de spoilers**: el tutorial - guía del usuario incluye:
|
||||
|
||||
@@ -444,17 +456,69 @@ Para un ejemplo más completo incluyendo más funcionalidades, ve al <a href="ht
|
||||
* **Sesiones de Cookies**
|
||||
* ...y más.
|
||||
|
||||
## Rendimiento
|
||||
### Despliega tu app (opcional) { #deploy-your-app-optional }
|
||||
|
||||
Opcionalmente puedes desplegar tu app de FastAPI en <a href="https://fastapicloud.com" class="external-link" target="_blank">FastAPI Cloud</a>, ve y únete a la lista de espera si no lo has hecho. 🚀
|
||||
|
||||
Si ya tienes una cuenta de **FastAPI Cloud** (te invitamos desde la lista de espera 😉), puedes desplegar tu aplicación con un solo comando.
|
||||
|
||||
Antes de desplegar, asegúrate de haber iniciado sesión:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ fastapi login
|
||||
|
||||
You are logged in to FastAPI Cloud 🚀
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
Luego despliega tu app:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ fastapi deploy
|
||||
|
||||
Deploying to FastAPI Cloud...
|
||||
|
||||
✅ Deployment successful!
|
||||
|
||||
🐔 Ready the chicken! Your app is ready at https://myapp.fastapicloud.dev
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
¡Eso es todo! Ahora puedes acceder a tu app en esa URL. ✨
|
||||
|
||||
#### Acerca de FastAPI Cloud { #about-fastapi-cloud }
|
||||
|
||||
**<a href="https://fastapicloud.com" class="external-link" target="_blank">FastAPI Cloud</a>** está construido por el mismo autor y equipo detrás de **FastAPI**.
|
||||
|
||||
Optimiza el proceso de **construir**, **desplegar** y **acceder** a una API con un esfuerzo mínimo.
|
||||
|
||||
Trae la misma **experiencia de desarrollador** de construir apps con FastAPI a **desplegarlas** en la nube. 🎉
|
||||
|
||||
FastAPI Cloud es el sponsor principal y proveedor de financiamiento para los proyectos open source *FastAPI and friends*. ✨
|
||||
|
||||
#### Despliega en otros proveedores de cloud { #deploy-to-other-cloud-providers }
|
||||
|
||||
FastAPI es open source y está basado en estándares. Puedes desplegar apps de FastAPI en cualquier proveedor de cloud que elijas.
|
||||
|
||||
Sigue las guías de tu proveedor de cloud para desplegar apps de FastAPI con ellos. 🤓
|
||||
|
||||
## Rendimiento { #performance }
|
||||
|
||||
Benchmarks independientes de TechEmpower muestran aplicaciones **FastAPI** ejecutándose bajo Uvicorn como <a href="https://www.techempower.com/benchmarks/#section=test&runid=7464e520-0dc2-473d-bd34-dbdfd7e85911&hw=ph&test=query&l=zijzen-7" class="external-link" target="_blank">uno de los frameworks Python más rápidos disponibles</a>, solo por debajo de Starlette y Uvicorn (usados internamente por FastAPI). (*)
|
||||
|
||||
Para entender más sobre esto, ve la sección <a href="https://fastapi.tiangolo.com/benchmarks/" class="internal-link" target="_blank">Benchmarks</a>.
|
||||
Para entender más sobre esto, ve la sección <a href="https://fastapi.tiangolo.com/es/benchmarks/" class="internal-link" target="_blank">Benchmarks</a>.
|
||||
|
||||
## Dependencias
|
||||
## Dependencias { #dependencies }
|
||||
|
||||
FastAPI depende de Pydantic y Starlette.
|
||||
|
||||
### Dependencias `standard`
|
||||
### Dependencias `standard` { #standard-dependencies }
|
||||
|
||||
Cuando instalas FastAPI con `pip install "fastapi[standard]"` viene con el grupo `standard` de dependencias opcionales:
|
||||
|
||||
@@ -465,19 +529,24 @@ Usadas por Pydantic:
|
||||
Usadas por Starlette:
|
||||
|
||||
* <a href="https://www.python-httpx.org" target="_blank"><code>httpx</code></a> - Requerido si deseas usar el `TestClient`.
|
||||
* <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - Requerido si deseas usar la configuración de plantilla predeterminada.
|
||||
* <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - Requerido si deseas usar la configuración de plantilla por defecto.
|
||||
* <a href="https://github.com/Kludex/python-multipart" target="_blank"><code>python-multipart</code></a> - Requerido si deseas soportar <abbr title="convertir el string que viene de un request HTTP en datos de Python">"parsing"</abbr> de forms, con `request.form()`.
|
||||
|
||||
Usadas por FastAPI / Starlette:
|
||||
Usadas por FastAPI:
|
||||
|
||||
* <a href="https://www.uvicorn.dev" target="_blank"><code>uvicorn</code></a> - para el servidor que carga y sirve tu aplicación. Esto incluye `uvicorn[standard]`, que incluye algunas dependencias (por ejemplo, `uvloop`) necesarias para servir con alto rendimiento.
|
||||
* `fastapi-cli` - para proporcionar el comando `fastapi`.
|
||||
* `fastapi-cli[standard]` - para proporcionar el comando `fastapi`.
|
||||
* Esto incluye `fastapi-cloud-cli`, que te permite desplegar tu aplicación de FastAPI en <a href="https://fastapicloud.com" class="external-link" target="_blank">FastAPI Cloud</a>.
|
||||
|
||||
### Sin Dependencias `standard`
|
||||
### Sin Dependencias `standard` { #without-standard-dependencies }
|
||||
|
||||
Si no deseas incluir las dependencias opcionales `standard`, puedes instalar con `pip install fastapi` en lugar de `pip install "fastapi[standard]"`.
|
||||
|
||||
### Dependencias Opcionales Adicionales
|
||||
### Sin `fastapi-cloud-cli` { #without-fastapi-cloud-cli }
|
||||
|
||||
Si quieres instalar FastAPI con las dependencias standard pero sin `fastapi-cloud-cli`, puedes instalar con `pip install "fastapi[standard-no-fastapi-cloud-cli]"`.
|
||||
|
||||
### Dependencias Opcionales Adicionales { #additional-optional-dependencies }
|
||||
|
||||
Existen algunas dependencias adicionales que podrías querer instalar.
|
||||
|
||||
@@ -491,6 +560,6 @@ Dependencias opcionales adicionales de FastAPI:
|
||||
* <a href="https://github.com/ijl/orjson" target="_blank"><code>orjson</code></a> - Requerido si deseas usar `ORJSONResponse`.
|
||||
* <a href="https://github.com/esnme/ultrajson" target="_blank"><code>ujson</code></a> - Requerido si deseas usar `UJSONResponse`.
|
||||
|
||||
## Licencia
|
||||
## Licencia { #license }
|
||||
|
||||
Este proyecto tiene licencia bajo los términos de la licencia MIT.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Aprende
|
||||
# Aprende { #learn }
|
||||
|
||||
Aquí están las secciones introductorias y los tutoriales para aprender **FastAPI**.
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Plantilla Full Stack FastAPI
|
||||
# Plantilla Full Stack FastAPI { #full-stack-fastapi-template }
|
||||
|
||||
Las plantillas, aunque normalmente vienen con una configuración específica, están diseñadas para ser flexibles y personalizables. Esto te permite modificarlas y adaptarlas a los requisitos de tu proyecto, haciéndolas un excelente punto de partida. 🏁
|
||||
|
||||
@@ -6,23 +6,23 @@ Puedes usar esta plantilla para comenzar, ya que incluye gran parte de la config
|
||||
|
||||
Repositorio de GitHub: <a href="https://github.com/tiangolo/full-stack-fastapi-template" class="external-link" target="_blank">Plantilla Full Stack FastAPI</a>
|
||||
|
||||
## Plantilla Full Stack FastAPI - Tecnología y Funcionalidades
|
||||
## Plantilla Full Stack FastAPI - Tecnología y Funcionalidades { #full-stack-fastapi-template-technology-stack-and-features }
|
||||
|
||||
- ⚡ [**FastAPI**](https://fastapi.tiangolo.com) para la API del backend en Python.
|
||||
- ⚡ [**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).
|
||||
- 🔍 [Pydantic](https://docs.pydantic.dev), utilizado por FastAPI, para la validación de datos y gestión de configuraciones.
|
||||
- 💾 [PostgreSQL](https://www.postgresql.org) como base de datos SQL.
|
||||
- 🚀 [React](https://react.dev) para el frontend.
|
||||
- 💃 Usando TypeScript, hooks, [Vite](https://vitejs.dev), y otras partes de una stack moderna de frontend.
|
||||
- 🎨 [Chakra UI](https://chakra-ui.com) para los componentes del frontend.
|
||||
- 💃 Usando TypeScript, hooks, Vite, y otras partes de una stack moderna de frontend.
|
||||
- 🎨 [Tailwind CSS](https://tailwindcss.com) y [shadcn/ui](https://ui.shadcn.com) para los componentes del frontend.
|
||||
- 🤖 Un cliente de frontend generado automáticamente.
|
||||
- 🧪 [Playwright](https://playwright.dev) para pruebas End-to-End.
|
||||
- 🧪 [Playwright](https://playwright.dev) para escribir pruebas End-to-End.
|
||||
- 🦇 Soporte para modo oscuro.
|
||||
- 🐋 [Docker Compose](https://www.docker.com) para desarrollo y producción.
|
||||
- 🔒 Hashing seguro de contraseñas por defecto.
|
||||
- 🔑 Autenticación con tokens JWT.
|
||||
- 📫 Recuperación de contraseñas basada en email.
|
||||
- ✅ Pruebas con [Pytest](https://pytest.org).
|
||||
- 📞 [Traefik](https://traefik.io) como proxy inverso / balanceador de carga.
|
||||
- 📞 [Traefik](https://traefik.io) como proxy inverso / load balancer.
|
||||
- 🚢 Instrucciones de despliegue usando Docker Compose, incluyendo cómo configurar un proxy Traefik frontend para manejar certificados HTTPS automáticos.
|
||||
- 🏭 CI (integración continua) y CD (despliegue continuo) basados en GitHub Actions.
|
||||
|
||||
+47
-159
@@ -1,4 +1,4 @@
|
||||
# Introducción a Tipos en Python
|
||||
# Introducción a Tipos en Python { #python-types-intro }
|
||||
|
||||
Python tiene soporte para "anotaciones de tipos" opcionales (también llamadas "type hints").
|
||||
|
||||
@@ -18,11 +18,11 @@ Si eres un experto en Python, y ya sabes todo sobre las anotaciones de tipos, sa
|
||||
|
||||
///
|
||||
|
||||
## Motivación
|
||||
## Motivación { #motivation }
|
||||
|
||||
Comencemos con un ejemplo simple:
|
||||
|
||||
{* ../../docs_src/python_types/tutorial001.py *}
|
||||
{* ../../docs_src/python_types/tutorial001_py39.py *}
|
||||
|
||||
Llamar a este programa genera:
|
||||
|
||||
@@ -36,9 +36,9 @@ La función hace lo siguiente:
|
||||
* Convierte la primera letra de cada uno a mayúsculas con `title()`.
|
||||
* <abbr title="Los une, como uno. Con el contenido de uno después del otro.">Concatena</abbr> ambos con un espacio en el medio.
|
||||
|
||||
{* ../../docs_src/python_types/tutorial001.py hl[2] *}
|
||||
{* ../../docs_src/python_types/tutorial001_py39.py hl[2] *}
|
||||
|
||||
### Edítalo
|
||||
### Edítalo { #edit-it }
|
||||
|
||||
Es un programa muy simple.
|
||||
|
||||
@@ -58,7 +58,7 @@ Pero, tristemente, no obtienes nada útil:
|
||||
|
||||
<img src="/img/python-types/image01.png">
|
||||
|
||||
### Añadir tipos
|
||||
### Añadir tipos { #add-types }
|
||||
|
||||
Modifiquemos una sola línea de la versión anterior.
|
||||
|
||||
@@ -78,7 +78,7 @@ Eso es todo.
|
||||
|
||||
Esas son las "anotaciones de tipos":
|
||||
|
||||
{* ../../docs_src/python_types/tutorial002.py hl[1] *}
|
||||
{* ../../docs_src/python_types/tutorial002_py39.py hl[1] *}
|
||||
|
||||
Eso no es lo mismo que declarar valores predeterminados como sería con:
|
||||
|
||||
@@ -102,11 +102,11 @@ Con eso, puedes desplazarte, viendo las opciones, hasta que encuentres la que "t
|
||||
|
||||
<img src="/img/python-types/image03.png">
|
||||
|
||||
## Más motivación
|
||||
## Más motivación { #more-motivation }
|
||||
|
||||
Revisa esta función, ya tiene anotaciones de tipos:
|
||||
|
||||
{* ../../docs_src/python_types/tutorial003.py hl[1] *}
|
||||
{* ../../docs_src/python_types/tutorial003_py39.py hl[1] *}
|
||||
|
||||
Porque el editor conoce los tipos de las variables, no solo obtienes autocompletado, también obtienes chequeo de errores:
|
||||
|
||||
@@ -114,15 +114,15 @@ Porque el editor conoce los tipos de las variables, no solo obtienes autocomplet
|
||||
|
||||
Ahora sabes que debes corregirlo, convertir `age` a un string con `str(age)`:
|
||||
|
||||
{* ../../docs_src/python_types/tutorial004.py hl[2] *}
|
||||
{* ../../docs_src/python_types/tutorial004_py39.py hl[2] *}
|
||||
|
||||
## Declaración de tipos
|
||||
## Declaración de tipos { #declaring-types }
|
||||
|
||||
Acabas de ver el lugar principal para declarar anotaciones de tipos. Como parámetros de función.
|
||||
|
||||
Este también es el lugar principal donde los utilizarías con **FastAPI**.
|
||||
|
||||
### Tipos simples
|
||||
### Tipos simples { #simple-types }
|
||||
|
||||
Puedes declarar todos los tipos estándar de Python, no solo `str`.
|
||||
|
||||
@@ -133,9 +133,9 @@ Puedes usar, por ejemplo:
|
||||
* `bool`
|
||||
* `bytes`
|
||||
|
||||
{* ../../docs_src/python_types/tutorial005.py hl[1] *}
|
||||
{* ../../docs_src/python_types/tutorial005_py39.py hl[1] *}
|
||||
|
||||
### Tipos genéricos con parámetros de tipo
|
||||
### Tipos genéricos con parámetros de tipo { #generic-types-with-type-parameters }
|
||||
|
||||
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.
|
||||
|
||||
@@ -143,7 +143,7 @@ Estos tipos que tienen tipos internos se denominan tipos "**genéricos**". Y es
|
||||
|
||||
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
|
||||
#### 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.
|
||||
|
||||
@@ -157,60 +157,28 @@ Por ejemplo, "**Python 3.6+**" significa que es compatible con Python 3.6 o supe
|
||||
|
||||
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+**".
|
||||
|
||||
#### Lista
|
||||
#### Lista { #list }
|
||||
|
||||
Por ejemplo, vamos a definir una variable para ser una `list` de `str`.
|
||||
|
||||
//// tab | Python 3.9+
|
||||
|
||||
Declara la variable, con la misma sintaxis de dos puntos (`:`).
|
||||
|
||||
Como tipo, pon `list`.
|
||||
|
||||
Como la lista es un tipo que contiene algunos tipos internos, los pones entre corchetes:
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!> ../../docs_src/python_types/tutorial006_py39.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+
|
||||
|
||||
De `typing`, importa `List` (con una `L` mayúscula):
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!> ../../docs_src/python_types/tutorial006.py!}
|
||||
```
|
||||
|
||||
Declara la variable, con la misma sintaxis de dos puntos (`:`).
|
||||
|
||||
Como tipo, pon el `List` que importaste de `typing`.
|
||||
|
||||
Como la lista es un tipo que contiene algunos tipos internos, los pones entre corchetes:
|
||||
|
||||
```Python hl_lines="4"
|
||||
{!> ../../docs_src/python_types/tutorial006.py!}
|
||||
```
|
||||
|
||||
////
|
||||
{* ../../docs_src/python_types/tutorial006_py39.py hl[1] *}
|
||||
|
||||
/// info | Información
|
||||
|
||||
Esos tipos internos en los corchetes se denominan "parámetros de tipo".
|
||||
|
||||
En este caso, `str` es el parámetro de tipo pasado a `List` (o `list` en Python 3.9 y superior).
|
||||
En este caso, `str` es el parámetro de tipo pasado a `list`.
|
||||
|
||||
///
|
||||
|
||||
Eso significa: "la variable `items` es una `list`, y cada uno de los ítems en esta lista es un `str`".
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
Si usas Python 3.9 o superior, no tienes que importar `List` de `typing`, puedes usar el mismo tipo `list` regular en su lugar.
|
||||
|
||||
///
|
||||
|
||||
Al hacer eso, tu editor puede proporcionar soporte incluso mientras procesa elementos de la lista:
|
||||
|
||||
<img src="/img/python-types/image05.png">
|
||||
@@ -221,32 +189,18 @@ Nota que la variable `item` es uno de los elementos en la lista `items`.
|
||||
|
||||
Y aún así, el editor sabe que es un `str` y proporciona soporte para eso.
|
||||
|
||||
#### Tuple y Set
|
||||
#### Tuple y Set { #tuple-and-set }
|
||||
|
||||
Harías lo mismo para declarar `tuple`s y `set`s:
|
||||
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!> ../../docs_src/python_types/tutorial007_py39.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!> ../../docs_src/python_types/tutorial007.py!}
|
||||
```
|
||||
|
||||
////
|
||||
{* ../../docs_src/python_types/tutorial007_py39.py hl[1] *}
|
||||
|
||||
Esto significa:
|
||||
|
||||
* La variable `items_t` es un `tuple` con 3 ítems, un `int`, otro `int`, y un `str`.
|
||||
* La variable `items_s` es un `set`, y cada uno de sus ítems es del tipo `bytes`.
|
||||
|
||||
#### Dict
|
||||
#### Dict { #dict }
|
||||
|
||||
Para definir un `dict`, pasas 2 parámetros de tipo, separados por comas.
|
||||
|
||||
@@ -254,21 +208,7 @@ El primer parámetro de tipo es para las claves del `dict`.
|
||||
|
||||
El segundo parámetro de tipo es para los valores del `dict`:
|
||||
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!> ../../docs_src/python_types/tutorial008_py39.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!> ../../docs_src/python_types/tutorial008.py!}
|
||||
```
|
||||
|
||||
////
|
||||
{* ../../docs_src/python_types/tutorial008_py39.py hl[1] *}
|
||||
|
||||
Esto significa:
|
||||
|
||||
@@ -276,7 +216,7 @@ Esto significa:
|
||||
* Las claves de este `dict` son del tipo `str` (digamos, el nombre de cada ítem).
|
||||
* Los valores de este `dict` son del tipo `float` (digamos, el precio de cada ítem).
|
||||
|
||||
#### Union
|
||||
#### Union { #union }
|
||||
|
||||
Puedes declarar que una variable puede ser cualquier de **varios tipos**, por ejemplo, un `int` o un `str`.
|
||||
|
||||
@@ -292,24 +232,24 @@ En Python 3.10 también hay una **nueva sintaxis** donde puedes poner los posibl
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!> ../../docs_src/python_types/tutorial008b.py!}
|
||||
{!> ../../docs_src/python_types/tutorial008b_py39.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
En ambos casos, esto significa que `item` podría ser un `int` o un `str`.
|
||||
|
||||
#### Posiblemente `None`
|
||||
#### Posiblemente `None` { #possibly-none }
|
||||
|
||||
Puedes declarar que un valor podría tener un tipo, como `str`, pero que también podría ser `None`.
|
||||
|
||||
En Python 3.6 y posteriores (incluyendo Python 3.10) puedes declararlo importando y usando `Optional` del módulo `typing`.
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!../../docs_src/python_types/tutorial009.py!}
|
||||
{!../../docs_src/python_types/tutorial009_py39.py!}
|
||||
```
|
||||
|
||||
Usar `Optional[str]` en lugar de solo `str` te permitirá al editor ayudarte a detectar errores donde podrías estar asumiendo que un valor siempre es un `str`, cuando en realidad también podría ser `None`.
|
||||
@@ -326,23 +266,23 @@ Esto también significa que en Python 3.10, puedes usar `Something | None`:
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!> ../../docs_src/python_types/tutorial009.py!}
|
||||
{!> ../../docs_src/python_types/tutorial009_py39.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+ alternative
|
||||
//// tab | Python 3.9+ alternativa
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!> ../../docs_src/python_types/tutorial009b.py!}
|
||||
{!> ../../docs_src/python_types/tutorial009b_py39.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
#### Uso de `Union` u `Optional`
|
||||
#### 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**:
|
||||
|
||||
@@ -357,7 +297,7 @@ Se trata solo de las palabras y nombres. Pero esas palabras pueden afectar cómo
|
||||
|
||||
Como ejemplo, tomemos esta función:
|
||||
|
||||
{* ../../docs_src/python_types/tutorial009c.py hl[1,4] *}
|
||||
{* ../../docs_src/python_types/tutorial009c_py39.py hl[1,4] *}
|
||||
|
||||
El parámetro `name` está definido como `Optional[str]`, pero **no es opcional**, no puedes llamar a la función sin el parámetro:
|
||||
|
||||
@@ -377,7 +317,7 @@ La buena noticia es que, una vez que estés en Python 3.10, no tendrás que preo
|
||||
|
||||
Y entonces no tendrás que preocuparte por nombres como `Optional` y `Union`. 😎
|
||||
|
||||
#### Tipos genéricos
|
||||
#### 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:
|
||||
|
||||
@@ -390,10 +330,10 @@ Puedes usar los mismos tipos integrados como genéricos (con corchetes y tipos d
|
||||
* `set`
|
||||
* `dict`
|
||||
|
||||
Y lo mismo que con Python 3.8, desde el módulo `typing`:
|
||||
Y, como con versiones anteriores de Python, desde el módulo `typing`:
|
||||
|
||||
* `Union`
|
||||
* `Optional` (lo mismo que con Python 3.8)
|
||||
* `Optional`
|
||||
* ...y otros.
|
||||
|
||||
En Python 3.10, como alternativa a usar los genéricos `Union` y `Optional`, puedes usar la <abbr title='también llamado "operador OR a nivel de bits", pero ese significado no es relevante aquí'>barra vertical (`|`)</abbr> para declarar uniones de tipos, eso es mucho mejor y más simple.
|
||||
@@ -409,7 +349,7 @@ Puedes usar los mismos tipos integrados como genéricos (con corchetes y tipos d
|
||||
* `set`
|
||||
* `dict`
|
||||
|
||||
Y lo mismo que con Python 3.8, desde el módulo `typing`:
|
||||
Y generics desde el módulo `typing`:
|
||||
|
||||
* `Union`
|
||||
* `Optional`
|
||||
@@ -417,29 +357,17 @@ Y lo mismo que con Python 3.8, desde el módulo `typing`:
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+
|
||||
|
||||
* `List`
|
||||
* `Tuple`
|
||||
* `Set`
|
||||
* `Dict`
|
||||
* `Union`
|
||||
* `Optional`
|
||||
* ...y otros.
|
||||
|
||||
////
|
||||
|
||||
### Clases como tipos
|
||||
### Clases como tipos { #classes-as-types }
|
||||
|
||||
También puedes declarar una clase como el tipo de una variable.
|
||||
|
||||
Digamos que tienes una clase `Person`, con un nombre:
|
||||
|
||||
{* ../../docs_src/python_types/tutorial010.py hl[1:3] *}
|
||||
{* ../../docs_src/python_types/tutorial010_py39.py hl[1:3] *}
|
||||
|
||||
Luego puedes declarar una variable para que sea de tipo `Person`:
|
||||
|
||||
{* ../../docs_src/python_types/tutorial010.py hl[6] *}
|
||||
{* ../../docs_src/python_types/tutorial010_py39.py hl[6] *}
|
||||
|
||||
Y luego, nuevamente, obtienes todo el soporte del editor:
|
||||
|
||||
@@ -449,7 +377,7 @@ Nota que esto significa "`one_person` es una **instance** de la clase `Person`".
|
||||
|
||||
No significa "`one_person` es la **clase** llamada `Person`".
|
||||
|
||||
## Modelos Pydantic
|
||||
## Modelos Pydantic { #pydantic-models }
|
||||
|
||||
<a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic</a> es un paquete de Python para realizar la validación de datos.
|
||||
|
||||
@@ -463,29 +391,7 @@ Y obtienes todo el soporte del editor con ese objeto resultante.
|
||||
|
||||
Un ejemplo de la documentación oficial de Pydantic:
|
||||
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python
|
||||
{!> ../../docs_src/python_types/tutorial011_py310.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python
|
||||
{!> ../../docs_src/python_types/tutorial011_py39.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+
|
||||
|
||||
```Python
|
||||
{!> ../../docs_src/python_types/tutorial011.py!}
|
||||
```
|
||||
|
||||
////
|
||||
{* ../../docs_src/python_types/tutorial011_py310.py *}
|
||||
|
||||
/// info | Información
|
||||
|
||||
@@ -503,31 +409,13 @@ Pydantic tiene un comportamiento especial cuando utilizas `Optional` o `Union[So
|
||||
|
||||
///
|
||||
|
||||
## Anotaciones de tipos con metadata
|
||||
## Anotaciones de tipos con metadata { #type-hints-with-metadata-annotations }
|
||||
|
||||
Python también tiene una funcionalidad que permite poner **<abbr title="Datos sobre los datos, en este caso, información sobre el tipo, por ejemplo, una descripción.">metadata</abbr> adicional** en estas anotaciones de tipos usando `Annotated`.
|
||||
Python también tiene una funcionalidad que permite poner **<abbr title="Datos sobre los datos, en este caso, información sobre el tipo, por ejemplo, una descripción.">metadatos</abbr> adicional** en estas anotaciones de tipos usando `Annotated`.
|
||||
|
||||
//// tab | Python 3.9+
|
||||
Desde Python 3.9, `Annotated` es parte de la standard library, así que puedes importarlo desde `typing`.
|
||||
|
||||
En Python 3.9, `Annotated` es parte de la librería estándar, así que puedes importarlo desde `typing`.
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!> ../../docs_src/python_types/tutorial013_py39.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+
|
||||
|
||||
En versiones por debajo de Python 3.9, importas `Annotated` de `typing_extensions`.
|
||||
|
||||
Ya estará instalado con **FastAPI**.
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!> ../../docs_src/python_types/tutorial013.py!}
|
||||
```
|
||||
|
||||
////
|
||||
{* ../../docs_src/python_types/tutorial013_py39.py hl[1,4] *}
|
||||
|
||||
Python en sí no hace nada con este `Annotated`. Y para los editores y otras herramientas, el tipo sigue siendo `str`.
|
||||
|
||||
@@ -547,7 +435,7 @@ Y también que tu código será muy compatible con muchas otras herramientas y p
|
||||
|
||||
///
|
||||
|
||||
## Anotaciones de tipos en **FastAPI**
|
||||
## Anotaciones de tipos en **FastAPI** { #type-hints-in-fastapi }
|
||||
|
||||
**FastAPI** aprovecha estas anotaciones de tipos para hacer varias cosas.
|
||||
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
# Recursos
|
||||
# Recursos { #resources }
|
||||
|
||||
Recursos adicionales, enlaces externos, artículos y más. ✈️
|
||||
Recursos adicionales, enlaces externos y más. ✈️
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Tareas en Segundo Plano
|
||||
# Tareas en Segundo Plano { #background-tasks }
|
||||
|
||||
Puedes definir tareas en segundo plano para que se ejecuten *después* de devolver un response.
|
||||
|
||||
@@ -11,15 +11,15 @@ Esto incluye, por ejemplo:
|
||||
* Procesamiento de datos:
|
||||
* Por ejemplo, supongamos que recibes un archivo que debe pasar por un proceso lento, puedes devolver un response de "Accepted" (HTTP 202) y procesar el archivo en segundo plano.
|
||||
|
||||
## Usando `BackgroundTasks`
|
||||
## Usando `BackgroundTasks` { #using-backgroundtasks }
|
||||
|
||||
Primero, importa `BackgroundTasks` y define un parámetro en tu *path operation function* con una declaración de tipo de `BackgroundTasks`:
|
||||
|
||||
{* ../../docs_src/background_tasks/tutorial001.py hl[1,13] *}
|
||||
{* ../../docs_src/background_tasks/tutorial001_py39.py hl[1,13] *}
|
||||
|
||||
**FastAPI** creará el objeto de tipo `BackgroundTasks` por ti y lo pasará como ese parámetro.
|
||||
|
||||
## Crear una función de tarea
|
||||
## Crear una función de tarea { #create-a-task-function }
|
||||
|
||||
Crea una función para que se ejecute como la tarea en segundo plano.
|
||||
|
||||
@@ -31,13 +31,13 @@ En este caso, la función de tarea escribirá en un archivo (simulando el envío
|
||||
|
||||
Y como la operación de escritura no usa `async` y `await`, definimos la función con un `def` normal:
|
||||
|
||||
{* ../../docs_src/background_tasks/tutorial001.py hl[6:9] *}
|
||||
{* ../../docs_src/background_tasks/tutorial001_py39.py hl[6:9] *}
|
||||
|
||||
## Agregar la tarea en segundo plano
|
||||
## Agregar la tarea en segundo plano { #add-the-background-task }
|
||||
|
||||
Dentro de tu *path operation function*, pasa tu función de tarea al objeto de *background tasks* con el método `.add_task()`:
|
||||
|
||||
{* ../../docs_src/background_tasks/tutorial001.py hl[14] *}
|
||||
{* ../../docs_src/background_tasks/tutorial001_py39.py hl[14] *}
|
||||
|
||||
`.add_task()` recibe como argumentos:
|
||||
|
||||
@@ -45,7 +45,7 @@ Dentro de tu *path operation function*, pasa tu función de tarea al objeto de *
|
||||
* Cualquier secuencia de argumentos que deba pasarse a la función de tarea en orden (`email`).
|
||||
* Cualquier argumento de palabras clave que deba pasarse a la función de tarea (`message="some notification"`).
|
||||
|
||||
## Inyección de Dependencias
|
||||
## Inyección de Dependencias { #dependency-injection }
|
||||
|
||||
Usar `BackgroundTasks` también funciona con el sistema de inyección de dependencias, puedes declarar un parámetro de tipo `BackgroundTasks` en varios niveles: en una *path operation function*, en una dependencia (dependable), en una sub-dependencia, etc.
|
||||
|
||||
@@ -59,7 +59,7 @@ Si hay un query en el request, se escribirá en el log en una tarea en segundo p
|
||||
|
||||
Y luego otra tarea en segundo plano generada en la *path operation function* escribirá un mensaje usando el parámetro de path `email`.
|
||||
|
||||
## Detalles Técnicos
|
||||
## Detalles Técnicos { #technical-details }
|
||||
|
||||
La clase `BackgroundTasks` proviene directamente de <a href="https://www.starlette.dev/background/" class="external-link" target="_blank">`starlette.background`</a>.
|
||||
|
||||
@@ -71,7 +71,7 @@ Todavía es posible usar `BackgroundTask` solo en FastAPI, pero debes crear el o
|
||||
|
||||
Puedes ver más detalles en <a href="https://www.starlette.dev/background/" class="external-link" target="_blank">la documentación oficial de Starlette sobre Background Tasks</a>.
|
||||
|
||||
## Advertencia
|
||||
## Advertencia { #caveat }
|
||||
|
||||
Si necesitas realizar una computación intensa en segundo plano y no necesariamente necesitas que se ejecute por el mismo proceso (por ejemplo, no necesitas compartir memoria, variables, etc.), podrías beneficiarte del uso de otras herramientas más grandes como <a href="https://docs.celeryq.dev" class="external-link" target="_blank">Celery</a>.
|
||||
|
||||
@@ -79,6 +79,6 @@ Tienden a requerir configuraciones más complejas, un gestor de cola de mensajes
|
||||
|
||||
Pero si necesitas acceder a variables y objetos de la misma app de **FastAPI**, o necesitas realizar pequeñas tareas en segundo plano (como enviar una notificación por email), simplemente puedes usar `BackgroundTasks`.
|
||||
|
||||
## Resumen
|
||||
## Resumen { #recap }
|
||||
|
||||
Importa y usa `BackgroundTasks` con parámetros en *path operation functions* y dependencias para agregar tareas en segundo plano.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Aplicaciones más grandes - Múltiples archivos
|
||||
# Aplicaciones más grandes - Múltiples archivos { #bigger-applications-multiple-files }
|
||||
|
||||
Si estás construyendo una aplicación o una API web, rara vez podrás poner todo en un solo archivo.
|
||||
|
||||
@@ -10,7 +10,7 @@ Si vienes de Flask, esto sería el equivalente a los Blueprints de Flask.
|
||||
|
||||
///
|
||||
|
||||
## Un ejemplo de estructura de archivos
|
||||
## Un ejemplo de estructura de archivos { #an-example-file-structure }
|
||||
|
||||
Digamos que tienes una estructura de archivos como esta:
|
||||
|
||||
@@ -56,7 +56,7 @@ from app.routers import items
|
||||
|
||||
La misma estructura de archivos con comentarios:
|
||||
|
||||
```
|
||||
```bash
|
||||
.
|
||||
├── app # "app" es un paquete de Python
|
||||
│ ├── __init__.py # este archivo hace que "app" sea un "paquete de Python"
|
||||
@@ -71,7 +71,7 @@ La misma estructura de archivos con comentarios:
|
||||
│ └── admin.py # submódulo "admin", por ejemplo import app.internal.admin
|
||||
```
|
||||
|
||||
## `APIRouter`
|
||||
## `APIRouter` { #apirouter }
|
||||
|
||||
Digamos que el archivo dedicado solo a manejar usuarios es el submódulo en `/app/routers/users.py`.
|
||||
|
||||
@@ -81,23 +81,19 @@ Pero todavía es parte de la misma aplicación/web API de **FastAPI** (es parte
|
||||
|
||||
Puedes crear las *path operations* para ese módulo usando `APIRouter`.
|
||||
|
||||
### Importar `APIRouter`
|
||||
### Importar `APIRouter` { #import-apirouter }
|
||||
|
||||
Lo importas y creas una "instance" de la misma manera que lo harías con la clase `FastAPI`:
|
||||
|
||||
```Python hl_lines="1 3" title="app/routers/users.py"
|
||||
{!../../docs_src/bigger_applications/app/routers/users.py!}
|
||||
```
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/routers/users.py hl[1,3] title["app/routers/users.py"] *}
|
||||
|
||||
### *Path operations* con `APIRouter`
|
||||
### *Path operations* con `APIRouter` { #path-operations-with-apirouter }
|
||||
|
||||
Y luego lo usas para declarar tus *path operations*.
|
||||
|
||||
Úsalo de la misma manera que usarías la clase `FastAPI`:
|
||||
|
||||
```Python hl_lines="6 11 16" title="app/routers/users.py"
|
||||
{!../../docs_src/bigger_applications/app/routers/users.py!}
|
||||
```
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/routers/users.py hl[6,11,16] title["app/routers/users.py"] *}
|
||||
|
||||
Puedes pensar en `APIRouter` como una clase "mini `FastAPI`".
|
||||
|
||||
@@ -113,53 +109,25 @@ En este ejemplo, la variable se llama `router`, pero puedes nombrarla como quier
|
||||
|
||||
Vamos a incluir este `APIRouter` en la aplicación principal de `FastAPI`, pero primero, revisemos las dependencias y otro `APIRouter`.
|
||||
|
||||
## Dependencias
|
||||
## Dependencias { #dependencies }
|
||||
|
||||
Vemos que vamos a necesitar algunas dependencias usadas en varios lugares de la aplicación.
|
||||
|
||||
Así que las ponemos en su propio módulo `dependencies` (`app/dependencies.py`).
|
||||
|
||||
Ahora utilizaremos una dependencia simple para leer un encabezado `X-Token` personalizado:
|
||||
Ahora utilizaremos una dependencia simple para leer un header `X-Token` personalizado:
|
||||
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python hl_lines="3 6-8" title="app/dependencies.py"
|
||||
{!> ../../docs_src/bigger_applications/app_an_py39/dependencies.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+
|
||||
|
||||
```Python hl_lines="1 5-7" title="app/dependencies.py"
|
||||
{!> ../../docs_src/bigger_applications/app_an/dependencies.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+ non-Annotated
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/dependencies.py hl[3,6:8] title["app/dependencies.py"] *}
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
Preferiblemente usa la versión `Annotated` si es posible.
|
||||
|
||||
///
|
||||
|
||||
```Python hl_lines="1 4-6" title="app/dependencies.py"
|
||||
{!> ../../docs_src/bigger_applications/app/dependencies.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
Estamos usando un encabezado inventado para simplificar este ejemplo.
|
||||
Estamos usando un header inventado para simplificar este ejemplo.
|
||||
|
||||
Pero en casos reales obtendrás mejores resultados usando las [utilidades de Seguridad](security/index.md){.internal-link target=_blank} integradas.
|
||||
|
||||
///
|
||||
|
||||
## Otro módulo con `APIRouter`
|
||||
## Otro módulo con `APIRouter` { #another-module-with-apirouter }
|
||||
|
||||
Digamos que también tienes los endpoints dedicados a manejar "items" de tu aplicación en el módulo `app/routers/items.py`.
|
||||
|
||||
@@ -181,9 +149,7 @@ Sabemos que todas las *path operations* en este módulo tienen el mismo:
|
||||
|
||||
Entonces, en lugar de agregar todo eso a cada *path operation*, podemos agregarlo al `APIRouter`.
|
||||
|
||||
```Python hl_lines="5-10 16 21" title="app/routers/items.py"
|
||||
{!../../docs_src/bigger_applications/app/routers/items.py!}
|
||||
```
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/routers/items.py hl[5:10,16,21] title["app/routers/items.py"] *}
|
||||
|
||||
Como el path de cada *path operation* tiene que empezar con `/`, como en:
|
||||
|
||||
@@ -219,7 +185,7 @@ El resultado final es que los paths de item son ahora:
|
||||
* Todos incluirán las `responses` predefinidas.
|
||||
* Todas estas *path operations* tendrán la lista de `dependencies` evaluadas/ejecutadas antes de ellas.
|
||||
* Si también declaras dependencias en una *path operation* específica, **también se ejecutarán**.
|
||||
* Las dependencias del router se ejecutan primero, luego las [dependencias en el decorador](dependencies/dependencies-in-path-operation-decorators.md){.internal-link target=_blank}, y luego las dependencias de parámetros normales.
|
||||
* Las dependencias del router se ejecutan primero, luego las [`dependencies` en el decorador](dependencies/dependencies-in-path-operation-decorators.md){.internal-link target=_blank}, y luego las dependencias de parámetros normales.
|
||||
* También puedes agregar [dependencias de `Security` con `scopes`](../advanced/security/oauth2-scopes.md){.internal-link target=_blank}.
|
||||
|
||||
/// tip | Consejo
|
||||
@@ -234,7 +200,7 @@ Los parámetros `prefix`, `tags`, `responses`, y `dependencies` son (como en muc
|
||||
|
||||
///
|
||||
|
||||
### Importar las dependencias
|
||||
### Importar las dependencias { #import-the-dependencies }
|
||||
|
||||
Este código vive en el módulo `app.routers.items`, el archivo `app/routers/items.py`.
|
||||
|
||||
@@ -242,15 +208,13 @@ Y necesitamos obtener la función de dependencia del módulo `app.dependencies`,
|
||||
|
||||
Así que usamos un import relativo con `..` para las dependencias:
|
||||
|
||||
```Python hl_lines="3" title="app/routers/items.py"
|
||||
{!../../docs_src/bigger_applications/app/routers/items.py!}
|
||||
```
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/routers/items.py hl[3] title["app/routers/items.py"] *}
|
||||
|
||||
#### Cómo funcionan los imports relativos
|
||||
#### Cómo funcionan los imports relativos { #how-relative-imports-work }
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
Si sabes perfectamente cómo funcionan los imports, continúa a la siguiente sección.
|
||||
Si sabes perfectamente cómo funcionan los imports, continúa a la siguiente sección abajo.
|
||||
|
||||
///
|
||||
|
||||
@@ -307,27 +271,25 @@ eso significaría:
|
||||
|
||||
Eso se referiría a algún paquete arriba de `app/`, con su propio archivo `__init__.py`, etc. Pero no tenemos eso. Así que, eso lanzaría un error en nuestro ejemplo. 🚨
|
||||
|
||||
Pero ahora sabes cómo funciona, para que puedas usar imports relativos en tus propias aplicaciones sin importar cuán complejas sean. 🤓
|
||||
Pero ahora sabes cómo funciona, para que puedas usar imports relativos en tus propias apps sin importar cuán complejas sean. 🤓
|
||||
|
||||
### Agregar algunos `tags`, `responses`, y `dependencies` personalizados
|
||||
### Agregar algunos `tags`, `responses`, y `dependencies` personalizados { #add-some-custom-tags-responses-and-dependencies }
|
||||
|
||||
No estamos agregando el prefijo `/items` ni los `tags=["items"]` a cada *path operation* porque los hemos añadido al `APIRouter`.
|
||||
|
||||
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*:
|
||||
|
||||
```Python hl_lines="30-31" title="app/routers/items.py"
|
||||
{!../../docs_src/bigger_applications/app/routers/items.py!}
|
||||
```
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/routers/items.py hl[30:31] title["app/routers/items.py"] *}
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
Esta última *path operation* tendrá la combinación de tags: `["items", "custom"]`.
|
||||
Esta última path operation tendrá la combinación de tags: `["items", "custom"]`.
|
||||
|
||||
Y también tendrá ambas responses en la documentación, una para `404` y otra para `403`.
|
||||
|
||||
///
|
||||
|
||||
## El `FastAPI` principal
|
||||
## El `FastAPI` principal { #the-main-fastapi }
|
||||
|
||||
Ahora, veamos el módulo en `app/main.py`.
|
||||
|
||||
@@ -335,27 +297,25 @@ Aquí es donde importas y usas la clase `FastAPI`.
|
||||
|
||||
Este será el archivo principal en tu aplicación que conecta todo.
|
||||
|
||||
### Importar `FastAPI`
|
||||
Y como la mayor parte de tu lógica ahora vivirá en su propio módulo específico, el archivo principal será bastante simple.
|
||||
|
||||
Importas y creas una clase `FastAPI` como de costumbre.
|
||||
### Importar `FastAPI` { #import-fastapi }
|
||||
|
||||
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`:
|
||||
|
||||
```Python hl_lines="1 3 7" title="app/main.py"
|
||||
{!../../docs_src/bigger_applications/app/main.py!}
|
||||
```
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/main.py hl[1,3,7] title["app/main.py"] *}
|
||||
|
||||
### Importar el `APIRouter`
|
||||
### Importar el `APIRouter` { #import-the-apirouter }
|
||||
|
||||
Ahora importamos los otros submódulos que tienen `APIRouter`s:
|
||||
|
||||
```Python hl_lines="4-5" title="app/main.py"
|
||||
{!../../docs_src/bigger_applications/app/main.py!}
|
||||
```
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/main.py hl[4:5] title["app/main.py"] *}
|
||||
|
||||
Como los archivos `app/routers/users.py` y `app/routers/items.py` son submódulos que son parte del mismo paquete de Python `app`, podemos usar un solo punto `.` para importarlos usando "imports relativos".
|
||||
|
||||
### Cómo funciona la importación
|
||||
### Cómo funciona la importación { #how-the-importing-works }
|
||||
|
||||
La sección:
|
||||
|
||||
@@ -397,7 +357,7 @@ Para aprender más sobre Paquetes y Módulos de Python, lee <a href="https://doc
|
||||
|
||||
///
|
||||
|
||||
### Evitar colisiones de nombres
|
||||
### Evitar colisiones de nombres { #avoid-name-collisions }
|
||||
|
||||
Estamos importando el submódulo `items` directamente, en lugar de importar solo su variable `router`.
|
||||
|
||||
@@ -414,17 +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:
|
||||
|
||||
```Python hl_lines="5" title="app/main.py"
|
||||
{!../../docs_src/bigger_applications/app/main.py!}
|
||||
```
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/main.py hl[5] title["app/main.py"] *}
|
||||
|
||||
### Incluir los `APIRouter`s para `users` y `items`
|
||||
### Incluir los `APIRouter`s para `users` y `items` { #include-the-apirouters-for-users-and-items }
|
||||
|
||||
Ahora, incluyamos los `router`s de los submódulos `users` y `items`:
|
||||
|
||||
```Python hl_lines="10-11" title="app/main.py"
|
||||
{!../../docs_src/bigger_applications/app/main.py!}
|
||||
```
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/main.py hl[10:11] title["app/main.py"] *}
|
||||
|
||||
/// info | Información
|
||||
|
||||
@@ -442,7 +398,7 @@ Incluirá todas las rutas de ese router como parte de ella.
|
||||
|
||||
En realidad creará internamente una *path operation* para cada *path operation* que fue declarada en el `APIRouter`.
|
||||
|
||||
Así, detrás de escena, funcionará como si todo fuera la misma única aplicación.
|
||||
Así, detrás de escena, funcionará como si todo fuera la misma única app.
|
||||
|
||||
///
|
||||
|
||||
@@ -456,7 +412,7 @@ Así que no afectará el rendimiento. ⚡
|
||||
|
||||
///
|
||||
|
||||
### Incluir un `APIRouter` con un `prefix`, `tags`, `responses`, y `dependencies` personalizados
|
||||
### Incluir un `APIRouter` con un `prefix`, `tags`, `responses`, y `dependencies` personalizados { #include-an-apirouter-with-a-custom-prefix-tags-responses-and-dependencies }
|
||||
|
||||
Ahora, imaginemos que tu organización te dio el archivo `app/internal/admin.py`.
|
||||
|
||||
@@ -464,40 +420,34 @@ 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`:
|
||||
|
||||
```Python hl_lines="3" title="app/internal/admin.py"
|
||||
{!../../docs_src/bigger_applications/app/internal/admin.py!}
|
||||
```
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/internal/admin.py hl[3] title["app/internal/admin.py"] *}
|
||||
|
||||
Pero aún queremos configurar un `prefix` personalizado al incluir el `APIRouter` para que todas sus *path operations* comiencen con `/admin`, queremos asegurarlo con las `dependencies` que ya tenemos para este proyecto, y queremos incluir `tags` y `responses`.
|
||||
|
||||
Podemos declarar todo eso sin tener que modificar el `APIRouter` original pasando esos parámetros a `app.include_router()`:
|
||||
|
||||
```Python hl_lines="14-17" title="app/main.py"
|
||||
{!../../docs_src/bigger_applications/app/main.py!}
|
||||
```
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/main.py hl[14:17] title["app/main.py"] *}
|
||||
|
||||
De esa manera, el `APIRouter` original permanecerá sin modificar, por lo que aún podemos compartir ese mismo archivo `app/internal/admin.py` con otros proyectos en la organización.
|
||||
|
||||
El resultado es que, en nuestra aplicación, cada una de las *path operations* del módulo `admin` tendrá:
|
||||
El resultado es que, en nuestra app, cada una de las *path operations* del módulo `admin` tendrá:
|
||||
|
||||
* El prefix `/admin`.
|
||||
* El tag `admin`.
|
||||
* La dependencia `get_token_header`.
|
||||
* La response `418`. 🍵
|
||||
|
||||
Pero eso solo afectará a ese `APIRouter` en nuestra aplicación, no en ningún otro código que lo utilice.
|
||||
Pero eso solo afectará a ese `APIRouter` en nuestra app, no en ningún otro código que lo utilice.
|
||||
|
||||
Así, por ejemplo, otros proyectos podrían usar el mismo `APIRouter` con un método de autenticación diferente.
|
||||
|
||||
### Incluir una *path operation*
|
||||
### Incluir una *path operation* { #include-a-path-operation }
|
||||
|
||||
También podemos agregar *path operations* directamente a la aplicación de `FastAPI`.
|
||||
También podemos agregar *path operations* directamente a la app de `FastAPI`.
|
||||
|
||||
Aquí lo hacemos... solo para mostrar que podemos 🤷:
|
||||
|
||||
```Python hl_lines="21-23" title="app/main.py"
|
||||
{!../../docs_src/bigger_applications/app/main.py!}
|
||||
```
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/main.py hl[21:23] title["app/main.py"] *}
|
||||
|
||||
y funcionará correctamente, junto con todas las otras *path operations* añadidas con `app.include_router()`.
|
||||
|
||||
@@ -511,13 +461,13 @@ Los `APIRouter`s no están "montados", no están aislados del resto de la aplica
|
||||
|
||||
Esto se debe a que queremos incluir sus *path operations* en el esquema de OpenAPI y las interfaces de usuario.
|
||||
|
||||
Como no podemos simplemente aislarlos y "montarlos" independientemente del resto, se "clonan" las *path operations* (se vuelven a crear), no se incluyen directamente.
|
||||
Como no podemos simplemente aislarlos y "montarlos" independientemente del resto, las *path operations* se "clonan" (se vuelven a crear), no se incluyen directamente.
|
||||
|
||||
///
|
||||
|
||||
## Revisa la documentación automática de la API
|
||||
## Revisa la documentación automática de la API { #check-the-automatic-api-docs }
|
||||
|
||||
Ahora, ejecuta tu aplicación:
|
||||
Ahora, ejecuta tu app:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
@@ -531,11 +481,11 @@ $ fastapi dev app/main.py
|
||||
|
||||
Y abre la documentación en <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
|
||||
|
||||
Verás la documentación automática de la API, incluyendo los paths de todos los submódulos, usando los paths correctos (y prefijos) y las tags correctas:
|
||||
Verás la documentación automática de la API, incluyendo los paths de todos los submódulos, usando los paths correctos (y prefijos) y los tags correctos:
|
||||
|
||||
<img src="/img/tutorial/bigger-applications/image01.png">
|
||||
|
||||
## Incluir el mismo router múltiples veces con diferentes `prefix`
|
||||
## Incluir el mismo router múltiples veces con diferentes `prefix` { #include-the-same-router-multiple-times-with-different-prefix }
|
||||
|
||||
También puedes usar `.include_router()` múltiples veces con el *mismo* router usando diferentes prefijos.
|
||||
|
||||
@@ -543,7 +493,7 @@ Esto podría ser útil, por ejemplo, para exponer la misma API bajo diferentes p
|
||||
|
||||
Este es un uso avanzado que quizás no necesites realmente, pero está allí en caso de que lo necesites.
|
||||
|
||||
## Incluir un `APIRouter` en otro
|
||||
## Incluir un `APIRouter` en otro { #include-an-apirouter-in-another }
|
||||
|
||||
De la misma manera que puedes incluir un `APIRouter` en una aplicación `FastAPI`, puedes incluir un `APIRouter` en otro `APIRouter` usando:
|
||||
|
||||
@@ -551,4 +501,4 @@ De la misma manera que puedes incluir un `APIRouter` en una aplicación `FastAPI
|
||||
router.include_router(other_router)
|
||||
```
|
||||
|
||||
Asegúrate de hacerlo antes de incluir `router` en la aplicación de `FastAPI`, para que las *path operations* de `other_router` también se incluyan.
|
||||
Asegúrate de hacerlo antes de incluir `router` en la app de `FastAPI`, para que las *path operations* de `other_router` también se incluyan.
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# Body - Campos
|
||||
# Body - Campos { #body-fields }
|
||||
|
||||
De la misma manera que puedes declarar validaciones adicionales y metadatos en los parámetros de las *path operation function* con `Query`, `Path` y `Body`, puedes declarar validaciones y metadatos dentro de los modelos de Pydantic usando `Field` de Pydantic.
|
||||
|
||||
## Importar `Field`
|
||||
## Importar `Field` { #import-field }
|
||||
|
||||
Primero, tienes que importarlo:
|
||||
|
||||
@@ -14,7 +14,7 @@ Fíjate que `Field` se importa directamente desde `pydantic`, no desde `fastapi`
|
||||
|
||||
///
|
||||
|
||||
## Declarar atributos del modelo
|
||||
## Declarar atributos del modelo { #declare-model-attributes }
|
||||
|
||||
Después puedes utilizar `Field` con los atributos del modelo:
|
||||
|
||||
@@ -40,7 +40,7 @@ Observa cómo cada atributo del modelo con un tipo, un valor por defecto y `Fiel
|
||||
|
||||
///
|
||||
|
||||
## Agregar información extra
|
||||
## Agregar información extra { #add-extra-information }
|
||||
|
||||
Puedes declarar información extra en `Field`, `Query`, `Body`, etc. Y será incluida en el JSON Schema generado.
|
||||
|
||||
@@ -53,7 +53,7 @@ Como estas claves no necesariamente tienen que ser parte de la especificación d
|
||||
|
||||
///
|
||||
|
||||
## Resumen
|
||||
## Resumen { #recap }
|
||||
|
||||
Puedes utilizar `Field` de Pydantic para declarar validaciones adicionales y metadatos para los atributos del modelo.
|
||||
|
||||
|
||||
@@ -1,24 +1,22 @@
|
||||
# Cuerpo - Múltiples Parámetros
|
||||
# Cuerpo - Múltiples Parámetros { #body-multiple-parameters }
|
||||
|
||||
Ahora que hemos visto cómo usar `Path` y `Query`, veamos usos más avanzados de las declaraciones del request body.
|
||||
|
||||
## Mezclar `Path`, `Query` y parámetros del cuerpo
|
||||
## Mezclar `Path`, `Query` y parámetros del cuerpo { #mix-path-query-and-body-parameters }
|
||||
|
||||
Primero, por supuesto, puedes mezclar las declaraciones de parámetros de `Path`, `Query` y del request body libremente y **FastAPI** sabrá qué hacer.
|
||||
|
||||
Y también puedes declarar parámetros del cuerpo como opcionales, estableciendo el valor predeterminado a `None`:
|
||||
Y también puedes declarar parámetros del cuerpo como opcionales, estableciendo el valor por defecto a `None`:
|
||||
|
||||
{* ../../docs_src/body_multiple_params/tutorial001_an_py310.py hl[18:20] *}
|
||||
|
||||
## Múltiples parámetros del cuerpo
|
||||
|
||||
/// note | Nota
|
||||
|
||||
Ten en cuenta que, en este caso, el `item` que se tomaría del cuerpo es opcional. Ya que tiene un valor por defecto de `None`.
|
||||
|
||||
///
|
||||
|
||||
## Múltiples parámetros del cuerpo
|
||||
## Múltiples parámetros del cuerpo { #multiple-body-parameters }
|
||||
|
||||
En el ejemplo anterior, las *path operations* esperarían un cuerpo JSON con los atributos de un `Item`, como:
|
||||
|
||||
@@ -64,7 +62,7 @@ Ten en cuenta que aunque el `item` se declaró de la misma manera que antes, aho
|
||||
|
||||
Realizará la validación de los datos compuestos, y los documentará así para el esquema de OpenAPI y la documentación automática.
|
||||
|
||||
## Valores singulares en el cuerpo
|
||||
## Valores singulares en el cuerpo { #singular-values-in-body }
|
||||
|
||||
De la misma manera que hay un `Query` y `Path` para definir datos extra para parámetros de query y path, **FastAPI** proporciona un equivalente `Body`.
|
||||
|
||||
@@ -96,7 +94,7 @@ En este caso, **FastAPI** esperará un cuerpo como:
|
||||
|
||||
Nuevamente, convertirá los tipos de datos, validará, documentará, etc.
|
||||
|
||||
## Múltiples parámetros de cuerpo y query
|
||||
## Múltiples parámetros de cuerpo y query { #multiple-body-params-and-query }
|
||||
|
||||
Por supuesto, también puedes declarar parámetros adicionales de query siempre que lo necesites, además de cualquier parámetro del cuerpo.
|
||||
|
||||
@@ -122,7 +120,7 @@ Por ejemplo:
|
||||
|
||||
///
|
||||
|
||||
## Embeber un solo parámetro de cuerpo
|
||||
## Embeber un solo parámetro de cuerpo { #embed-a-single-body-parameter }
|
||||
|
||||
Supongamos que solo tienes un único parámetro de cuerpo `item` de un modelo Pydantic `Item`.
|
||||
|
||||
@@ -162,7 +160,7 @@ en lugar de:
|
||||
}
|
||||
```
|
||||
|
||||
## Resumen
|
||||
## Resumen { #recap }
|
||||
|
||||
Puedes añadir múltiples parámetros de cuerpo a tu *path operation function*, aunque un request solo puede tener un único cuerpo.
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# Cuerpo - Modelos Anidados
|
||||
# Cuerpo - Modelos Anidados { #body-nested-models }
|
||||
|
||||
Con **FastAPI**, puedes definir, validar, documentar y usar modelos anidados de manera arbitraria (gracias a Pydantic).
|
||||
|
||||
## Campos de lista
|
||||
## Campos de lista { #list-fields }
|
||||
|
||||
Puedes definir un atributo como un subtipo. Por ejemplo, una `list` en Python:
|
||||
|
||||
@@ -10,39 +10,19 @@ Puedes definir un atributo como un subtipo. Por ejemplo, una `list` en Python:
|
||||
|
||||
Esto hará que `tags` sea una lista, aunque no declare el tipo de los elementos de la lista.
|
||||
|
||||
## Campos de lista con parámetro de tipo
|
||||
## Campos de lista con parámetro de tipo { #list-fields-with-type-parameter }
|
||||
|
||||
Pero Python tiene una forma específica de declarar listas con tipos internos, o "parámetros de tipo":
|
||||
|
||||
### Importar `List` de typing
|
||||
### Declarar una `list` con un parámetro de tipo { #declare-a-list-with-a-type-parameter }
|
||||
|
||||
En Python 3.9 y superior, puedes usar el `list` estándar para declarar estas anotaciones de tipo como veremos a continuación. 💡
|
||||
|
||||
Pero en versiones de Python anteriores a 3.9 (desde 3.6 en adelante), primero necesitas importar `List` del módulo `typing` estándar de Python:
|
||||
|
||||
{* ../../docs_src/body_nested_models/tutorial002.py hl[1] *}
|
||||
|
||||
### Declarar una `list` con un parámetro de tipo
|
||||
|
||||
Para declarar tipos que tienen parámetros de tipo (tipos internos), como `list`, `dict`, `tuple`:
|
||||
|
||||
* Si estás en una versión de Python inferior a 3.9, importa su versión equivalente del módulo `typing`
|
||||
* Pasa el/los tipo(s) interno(s) como "parámetros de tipo" usando corchetes: `[` y `]`
|
||||
|
||||
En Python 3.9 sería:
|
||||
Para declarar tipos que tienen parámetros de tipo (tipos internos), como `list`, `dict`, `tuple`,
|
||||
pasa el/los tipo(s) interno(s) como "parámetros de tipo" usando corchetes: `[` y `]`
|
||||
|
||||
```Python
|
||||
my_list: list[str]
|
||||
```
|
||||
|
||||
En versiones de Python anteriores a 3.9, sería:
|
||||
|
||||
```Python
|
||||
from typing import List
|
||||
|
||||
my_list: List[str]
|
||||
```
|
||||
|
||||
Eso es toda la sintaxis estándar de Python para declaraciones de tipo.
|
||||
|
||||
Usa esa misma sintaxis estándar para atributos de modelos con tipos internos.
|
||||
@@ -51,7 +31,7 @@ Así, en nuestro ejemplo, podemos hacer que `tags` sea específicamente una "lis
|
||||
|
||||
{* ../../docs_src/body_nested_models/tutorial002_py310.py hl[12] *}
|
||||
|
||||
## Tipos de conjunto
|
||||
## Tipos de conjunto { #set-types }
|
||||
|
||||
Pero luego pensamos en ello, y nos damos cuenta de que los tags no deberían repetirse, probablemente serían strings únicos.
|
||||
|
||||
@@ -67,7 +47,7 @@ Y siempre que emitas esos datos, incluso si la fuente tenía duplicados, se emit
|
||||
|
||||
Y también se anotará/documentará en consecuencia.
|
||||
|
||||
## Modelos Anidados
|
||||
## Modelos Anidados { #nested-models }
|
||||
|
||||
Cada atributo de un modelo Pydantic tiene un tipo.
|
||||
|
||||
@@ -77,13 +57,13 @@ Así que, puedes declarar "objetos" JSON anidados profundamente con nombres de a
|
||||
|
||||
Todo eso, de manera arbitraria.
|
||||
|
||||
### Definir un submodelo
|
||||
### Definir un submodelo { #define-a-submodel }
|
||||
|
||||
Por ejemplo, podemos definir un modelo `Image`:
|
||||
|
||||
{* ../../docs_src/body_nested_models/tutorial004_py310.py hl[7:9] *}
|
||||
|
||||
### Usar el submodelo como tipo
|
||||
### Usar el submodelo como tipo { #use-the-submodel-as-a-type }
|
||||
|
||||
Y luego podemos usarlo como el tipo de un atributo:
|
||||
|
||||
@@ -112,7 +92,7 @@ Nuevamente, haciendo solo esa declaración, con **FastAPI** obtienes:
|
||||
* Validación de datos
|
||||
* Documentación automática
|
||||
|
||||
## Tipos especiales y validación
|
||||
## Tipos especiales y validación { #special-types-and-validation }
|
||||
|
||||
Además de tipos singulares normales como `str`, `int`, `float`, etc., puedes usar tipos singulares más complejos que heredan de `str`.
|
||||
|
||||
@@ -124,7 +104,7 @@ Por ejemplo, como en el modelo `Image` tenemos un campo `url`, podemos declararl
|
||||
|
||||
El string será verificado para ser una URL válida, y documentado en JSON Schema / OpenAPI como tal.
|
||||
|
||||
## Atributos con listas de submodelos
|
||||
## Atributos con listas de submodelos { #attributes-with-lists-of-submodels }
|
||||
|
||||
También puedes usar modelos Pydantic como subtipos de `list`, `set`, etc.:
|
||||
|
||||
@@ -162,7 +142,7 @@ Nota cómo la clave `images` ahora tiene una lista de objetos de imagen.
|
||||
|
||||
///
|
||||
|
||||
## Modelos anidados profundamente
|
||||
## Modelos anidados profundamente { #deeply-nested-models }
|
||||
|
||||
Puedes definir modelos anidados tan profundamente como desees:
|
||||
|
||||
@@ -174,16 +154,10 @@ Observa cómo `Offer` tiene una lista de `Item`s, que a su vez tienen una lista
|
||||
|
||||
///
|
||||
|
||||
## Cuerpos de listas puras
|
||||
## Cuerpos de listas puras { #bodies-of-pure-lists }
|
||||
|
||||
Si el valor superior del cuerpo JSON que esperas es un `array` JSON (una `list` en Python), puedes declarar el tipo en el parámetro de la función, al igual que en los modelos Pydantic:
|
||||
|
||||
```Python
|
||||
images: List[Image]
|
||||
```
|
||||
|
||||
o en Python 3.9 y superior:
|
||||
|
||||
```Python
|
||||
images: list[Image]
|
||||
```
|
||||
@@ -192,7 +166,7 @@ como en:
|
||||
|
||||
{* ../../docs_src/body_nested_models/tutorial008_py39.py hl[13] *}
|
||||
|
||||
## Soporte de editor en todas partes
|
||||
## Soporte de editor en todas partes { #editor-support-everywhere }
|
||||
|
||||
Y obtienes soporte de editor en todas partes.
|
||||
|
||||
@@ -204,7 +178,7 @@ No podrías obtener este tipo de soporte de editor si estuvieras trabajando dire
|
||||
|
||||
Pero tampoco tienes que preocuparte por ellos, los `dicts` entrantes se convierten automáticamente y tu salida se convierte automáticamente a JSON también.
|
||||
|
||||
## Cuerpos de `dict`s arbitrarios
|
||||
## Cuerpos de `dict`s arbitrarios { #bodies-of-arbitrary-dicts }
|
||||
|
||||
También puedes declarar un cuerpo como un `dict` con claves de algún tipo y valores de algún otro tipo.
|
||||
|
||||
@@ -234,7 +208,7 @@ Y el `dict` que recibas como `weights` tendrá realmente claves `int` y valores
|
||||
|
||||
///
|
||||
|
||||
## Resumen
|
||||
## Resumen { #recap }
|
||||
|
||||
Con **FastAPI** tienes la máxima flexibilidad proporcionada por los modelos Pydantic, manteniendo tu código simple, corto y elegante.
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Cuerpo - Actualizaciones
|
||||
# Body - Actualizaciones { #body-updates }
|
||||
|
||||
## Actualización reemplazando con `PUT`
|
||||
## Actualización reemplazando con `PUT` { #update-replacing-with-put }
|
||||
|
||||
Para actualizar un ítem puedes utilizar la operación de <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PUT" class="external-link" target="_blank">HTTP `PUT`</a>.
|
||||
|
||||
@@ -10,7 +10,7 @@ Puedes usar el `jsonable_encoder` para convertir los datos de entrada en datos q
|
||||
|
||||
`PUT` se usa para recibir datos que deben reemplazar los datos existentes.
|
||||
|
||||
### Advertencia sobre el reemplazo
|
||||
### Advertencia sobre el reemplazo { #warning-about-replacing }
|
||||
|
||||
Esto significa que si quieres actualizar el ítem `bar` usando `PUT` con un body que contenga:
|
||||
|
||||
@@ -26,7 +26,7 @@ debido a que no incluye el atributo ya almacenado `"tax": 20.2`, el modelo de en
|
||||
|
||||
Y los datos se guardarían con ese "nuevo" `tax` de `10.5`.
|
||||
|
||||
## Actualizaciones parciales con `PATCH`
|
||||
## Actualizaciones parciales con `PATCH` { #partial-updates-with-patch }
|
||||
|
||||
También puedes usar la operación de <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PATCH" class="external-link" target="_blank">HTTP `PATCH`</a> para actualizar *parcialmente* datos.
|
||||
|
||||
@@ -44,43 +44,27 @@ Pero esta guía te muestra, más o menos, cómo se pretende que se usen.
|
||||
|
||||
///
|
||||
|
||||
### Uso del parámetro `exclude_unset` de Pydantic
|
||||
### Uso del parámetro `exclude_unset` de Pydantic { #using-pydantics-exclude-unset-parameter }
|
||||
|
||||
Si quieres recibir actualizaciones parciales, es muy útil usar el parámetro `exclude_unset` en el `.model_dump()` del modelo de Pydantic.
|
||||
|
||||
Como `item.model_dump(exclude_unset=True)`.
|
||||
|
||||
/// info | Información
|
||||
|
||||
En Pydantic v1 el método se llamaba `.dict()`, fue deprecado (pero aún soportado) en Pydantic v2, y renombrado a `.model_dump()`.
|
||||
|
||||
Los ejemplos aquí usan `.dict()` para compatibilidad con Pydantic v1, pero deberías usar `.model_dump()` si puedes usar Pydantic v2.
|
||||
|
||||
///
|
||||
|
||||
Eso generaría un `dict` solo con los datos que se establecieron al crear el modelo `item`, excluyendo los valores por defecto.
|
||||
|
||||
Luego puedes usar esto para generar un `dict` solo con los datos que se establecieron (enviados en el request), omitiendo los valores por defecto:
|
||||
|
||||
{* ../../docs_src/body_updates/tutorial002_py310.py hl[32] *}
|
||||
|
||||
### Uso del parámetro `update` de Pydantic
|
||||
### Uso del parámetro `update` de Pydantic { #using-pydantics-update-parameter }
|
||||
|
||||
Ahora, puedes crear una copia del modelo existente usando `.model_copy()`, y pasar el parámetro `update` con un `dict` que contenga los datos a actualizar.
|
||||
|
||||
/// info | Información
|
||||
|
||||
En Pydantic v1 el método se llamaba `.copy()`, fue deprecado (pero aún soportado) en Pydantic v2, y renombrado a `.model_copy()`.
|
||||
|
||||
Los ejemplos aquí usan `.copy()` para compatibilidad con Pydantic v1, pero deberías usar `.model_copy()` si puedes usar Pydantic v2.
|
||||
|
||||
///
|
||||
|
||||
Como `stored_item_model.model_copy(update=update_data)`:
|
||||
|
||||
{* ../../docs_src/body_updates/tutorial002_py310.py hl[33] *}
|
||||
|
||||
### Resumen de actualizaciones parciales
|
||||
### Resumen de actualizaciones parciales { #partial-updates-recap }
|
||||
|
||||
En resumen, para aplicar actualizaciones parciales deberías:
|
||||
|
||||
@@ -90,9 +74,9 @@ En resumen, para aplicar actualizaciones parciales deberías:
|
||||
* Generar un `dict` sin valores por defecto del modelo de entrada (usando `exclude_unset`).
|
||||
* De esta manera puedes actualizar solo los valores realmente establecidos por el usuario, en lugar de sobrescribir valores ya almacenados con valores por defecto en tu modelo.
|
||||
* Crear una copia del modelo almacenado, actualizando sus atributos con las actualizaciones parciales recibidas (usando el parámetro `update`).
|
||||
* Convertir el modelo copiado en algo que pueda almacenarse en tu base de datos (por ejemplo, usando el `jsonable_encoder`).
|
||||
* Convertir el modelo copiado en algo que pueda almacenarse en tu DB (por ejemplo, usando el `jsonable_encoder`).
|
||||
* Esto es comparable a usar el método `.model_dump()` del modelo de nuevo, pero asegura (y convierte) los valores a tipos de datos que pueden convertirse a JSON, por ejemplo, `datetime` a `str`.
|
||||
* Guardar los datos en tu base de datos.
|
||||
* Guardar los datos en tu DB.
|
||||
* Devolver el modelo actualizado.
|
||||
|
||||
{* ../../docs_src/body_updates/tutorial002_py310.py hl[28:35] *}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Request Body
|
||||
# Request Body { #request-body }
|
||||
|
||||
Cuando necesitas enviar datos desde un cliente (digamos, un navegador) a tu API, los envías como un **request body**.
|
||||
|
||||
@@ -18,13 +18,13 @@ Como no se recomienda, la documentación interactiva con Swagger UI no mostrará
|
||||
|
||||
///
|
||||
|
||||
## Importar `BaseModel` de Pydantic
|
||||
## Importar `BaseModel` de Pydantic { #import-pydantics-basemodel }
|
||||
|
||||
Primero, necesitas importar `BaseModel` de `pydantic`:
|
||||
|
||||
{* ../../docs_src/body/tutorial001_py310.py hl[2] *}
|
||||
|
||||
## Crea tu modelo de datos
|
||||
## Crea tu modelo de datos { #create-your-data-model }
|
||||
|
||||
Luego, declaras tu modelo de datos como una clase que hereda de `BaseModel`.
|
||||
|
||||
@@ -32,7 +32,8 @@ Usa tipos estándar de Python para todos los atributos:
|
||||
|
||||
{* ../../docs_src/body/tutorial001_py310.py hl[5:9] *}
|
||||
|
||||
Al igual que al declarar parámetros de query, cuando un atributo del modelo tiene un valor por defecto, no es obligatorio. De lo contrario, es obligatorio. Usa `None` para hacerlo opcional.
|
||||
|
||||
Al igual que al declarar parámetros de query, cuando un atributo del modelo tiene un valor por defecto, no es obligatorio. De lo contrario, es obligatorio. Usa `None` para hacerlo solo opcional.
|
||||
|
||||
Por ejemplo, el modelo anterior declara un “`object`” JSON (o `dict` en Python) como:
|
||||
|
||||
@@ -54,7 +55,7 @@ Por ejemplo, el modelo anterior declara un “`object`” JSON (o `dict` en Pyth
|
||||
}
|
||||
```
|
||||
|
||||
## Decláralo como un parámetro
|
||||
## Decláralo como un parámetro { #declare-it-as-a-parameter }
|
||||
|
||||
Para añadirlo a tu *path operation*, decláralo de la misma manera que declaraste parámetros de path y query:
|
||||
|
||||
@@ -62,7 +63,7 @@ Para añadirlo a tu *path operation*, decláralo de la misma manera que declaras
|
||||
|
||||
...y declara su tipo como el modelo que creaste, `Item`.
|
||||
|
||||
## Resultados
|
||||
## Resultados { #results }
|
||||
|
||||
Con solo esa declaración de tipo en Python, **FastAPI** hará lo siguiente:
|
||||
|
||||
@@ -73,9 +74,9 @@ Con solo esa declaración de tipo en Python, **FastAPI** hará lo siguiente:
|
||||
* Proporcionar los datos recibidos en el parámetro `item`.
|
||||
* Como lo declaraste en la función como de tipo `Item`, también tendrás todo el soporte del editor (autocompletado, etc.) para todos los atributos y sus tipos.
|
||||
* Generar definiciones de <a href="https://json-schema.org" class="external-link" target="_blank">JSON Schema</a> para tu modelo, que también puedes usar en cualquier otro lugar si tiene sentido para tu proyecto.
|
||||
* Esquemas que serán parte del esquema de OpenAPI generado y usados por la <abbr title="User Interfaces">UIs</abbr> de documentación automática.
|
||||
* Esos esquemas serán parte del esquema de OpenAPI generado y usados por las <abbr title="User Interfaces – Interfaces de usuario">UIs</abbr> de documentación automática.
|
||||
|
||||
## Documentación automática
|
||||
## Documentación automática { #automatic-docs }
|
||||
|
||||
Los JSON Schemas de tus modelos serán parte del esquema OpenAPI generado y se mostrarán en la documentación API interactiva:
|
||||
|
||||
@@ -85,7 +86,7 @@ Y también se utilizarán en la documentación API dentro de cada *path operatio
|
||||
|
||||
<img src="/img/tutorial/body/image02.png">
|
||||
|
||||
## Soporte del editor
|
||||
## Soporte del editor { #editor-support }
|
||||
|
||||
En tu editor, dentro de tu función, obtendrás anotaciones de tipos y autocompletado en todas partes (esto no sucedería si recibieras un `dict` en lugar de un modelo de Pydantic):
|
||||
|
||||
@@ -121,13 +122,13 @@ Mejora el soporte del editor para modelos de Pydantic, con:
|
||||
|
||||
///
|
||||
|
||||
## Usa el modelo
|
||||
## Usa el modelo { #use-the-model }
|
||||
|
||||
Dentro de la función, puedes acceder a todos los atributos del objeto modelo directamente:
|
||||
|
||||
{* ../../docs_src/body/tutorial002_py310.py *}
|
||||
|
||||
## Request body + parámetros de path
|
||||
## Request body + parámetros de path { #request-body-path-parameters }
|
||||
|
||||
Puedes declarar parámetros de path y request body al mismo tiempo.
|
||||
|
||||
@@ -135,7 +136,8 @@ Puedes declarar parámetros de path y request body al mismo tiempo.
|
||||
|
||||
{* ../../docs_src/body/tutorial003_py310.py hl[15:16] *}
|
||||
|
||||
## Request body + path + parámetros de query
|
||||
|
||||
## Request body + path + parámetros de query { #request-body-path-query-parameters }
|
||||
|
||||
También puedes declarar parámetros de **body**, **path** y **query**, todos al mismo tiempo.
|
||||
|
||||
@@ -147,18 +149,18 @@ Los parámetros de la función se reconocerán de la siguiente manera:
|
||||
|
||||
* Si el parámetro también se declara en el **path**, se utilizará como un parámetro de path.
|
||||
* Si el parámetro es de un **tipo singular** (como `int`, `float`, `str`, `bool`, etc.), se interpretará como un parámetro de **query**.
|
||||
* Si el parámetro se declara como del tipo de un **modelo de Pydantic**, se interpretará como un **request body**.
|
||||
* Si el parámetro se declara como del tipo de un **modelo de Pydantic**, se interpretará como un **body** de request.
|
||||
|
||||
/// note | Nota
|
||||
|
||||
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.8+) 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` (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`.
|
||||
|
||||
Pero agregar las anotaciones de tipos permitirá que tu editor te brinde un mejor soporte y detecte errores.
|
||||
|
||||
///
|
||||
|
||||
## Sin Pydantic
|
||||
## Sin Pydantic { #without-pydantic }
|
||||
|
||||
Si no quieres usar modelos de Pydantic, también puedes usar parámetros **Body**. Consulta la documentación para [Body - Multiples Parametros: Valores singulares en 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 - Multiple Parameters: Singular values in body](body-multiple-params.md#singular-values-in-body){.internal-link target=_blank}.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Modelos de Cookies
|
||||
# Modelos de Cookies { #cookie-parameter-models }
|
||||
|
||||
Si tienes un grupo de **cookies** que están relacionadas, puedes crear un **modelo de Pydantic** para declararlas. 🍪
|
||||
|
||||
@@ -16,7 +16,7 @@ Esta misma técnica se aplica a `Query`, `Cookie`, y `Header`. 😎
|
||||
|
||||
///
|
||||
|
||||
## Cookies con un Modelo de Pydantic
|
||||
## Cookies con un Modelo de Pydantic { #cookies-with-a-pydantic-model }
|
||||
|
||||
Declara los parámetros de **cookie** que necesites en un **modelo de Pydantic**, y luego declara el parámetro como `Cookie`:
|
||||
|
||||
@@ -24,7 +24,7 @@ Declara los parámetros de **cookie** que necesites en un **modelo de Pydantic**
|
||||
|
||||
**FastAPI** **extraerá** los datos para **cada campo** de las **cookies** recibidas en el request y te entregará el modelo de Pydantic que definiste.
|
||||
|
||||
## Revisa la Documentación
|
||||
## Revisa la Documentación { #check-the-docs }
|
||||
|
||||
Puedes ver las cookies definidas en la UI de la documentación en `/docs`:
|
||||
|
||||
@@ -42,7 +42,7 @@ Pero incluso si **rellenas los datos** y haces clic en "Execute", como la UI de
|
||||
|
||||
///
|
||||
|
||||
## Prohibir Cookies Extra
|
||||
## Prohibir Cookies Extra { #forbid-extra-cookies }
|
||||
|
||||
En algunos casos de uso especiales (probablemente no muy comunes), podrías querer **restringir** las cookies que deseas recibir.
|
||||
|
||||
@@ -50,7 +50,7 @@ Tu API ahora tiene el poder de controlar su propio <abbr title="Esto es una brom
|
||||
|
||||
Puedes usar la configuración del modelo de Pydantic para `prohibir` cualquier campo `extra`:
|
||||
|
||||
{* ../../docs_src/cookie_param_models/tutorial002_an_py39.py hl[10] *}
|
||||
{* ../../docs_src/cookie_param_models/tutorial002_an_py310.py hl[10] *}
|
||||
|
||||
Si un cliente intenta enviar algunas **cookies extra**, recibirán un response de **error**.
|
||||
|
||||
@@ -71,6 +71,6 @@ Por ejemplo, si el cliente intenta enviar una cookie `santa_tracker` con un valo
|
||||
}
|
||||
```
|
||||
|
||||
## Resumen
|
||||
## Resumen { #summary }
|
||||
|
||||
Puedes usar **modelos de Pydantic** para declarar <abbr title="Toma una última cookie antes de irte. 🍪">**cookies**</abbr> en **FastAPI**. 😎
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
# Parámetros de Cookie
|
||||
# Parámetros de Cookie { #cookie-parameters }
|
||||
|
||||
Puedes definir parámetros de Cookie de la misma manera que defines los parámetros `Query` y `Path`.
|
||||
|
||||
## Importar `Cookie`
|
||||
## Importar `Cookie` { #import-cookie }
|
||||
|
||||
Primero importa `Cookie`:
|
||||
|
||||
{* ../../docs_src/cookie_params/tutorial001_an_py310.py hl[3] *}
|
||||
|
||||
## Declarar parámetros de `Cookie`
|
||||
## Declarar parámetros de `Cookie` { #declare-cookie-parameters }
|
||||
|
||||
Luego declara los parámetros de cookie usando la misma estructura que con `Path` y `Query`.
|
||||
|
||||
@@ -30,6 +30,16 @@ Para declarar cookies, necesitas usar `Cookie`, porque de lo contrario los pará
|
||||
|
||||
///
|
||||
|
||||
## Resumen
|
||||
/// info | Información
|
||||
|
||||
Ten en cuenta que, como **los navegadores manejan las cookies** de formas especiales y por detrás, **no** permiten fácilmente que **JavaScript** las toque.
|
||||
|
||||
Si vas a la **UI de la documentación de la API** en `/docs` podrás ver la **documentación** de cookies para tus *path operations*.
|
||||
|
||||
Pero incluso si **rellenas los datos** y haces clic en "Execute", como la UI de la documentación funciona con **JavaScript**, las cookies no se enviarán y verás un mensaje de **error** como si no hubieras escrito ningún valor.
|
||||
|
||||
///
|
||||
|
||||
## Resumen { #recap }
|
||||
|
||||
Declara cookies con `Cookie`, usando el mismo patrón común que `Query` y `Path`.
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# CORS (Cross-Origin Resource Sharing)
|
||||
# CORS (Cross-Origin Resource Sharing) { #cors-cross-origin-resource-sharing }
|
||||
|
||||
<a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS" class="external-link" target="_blank">CORS o "Cross-Origin Resource Sharing"</a> se refiere a situaciones en las que un frontend que se ejecuta en un navegador tiene código JavaScript que se comunica con un backend, y el backend está en un "origen" diferente al frontend.
|
||||
|
||||
## Origen
|
||||
## Origen { #origin }
|
||||
|
||||
Un origen es la combinación de protocolo (`http`, `https`), dominio (`myapp.com`, `localhost`, `localhost.tiangolo.com`) y puerto (`80`, `443`, `8080`).
|
||||
|
||||
@@ -14,7 +14,7 @@ Así que, todos estos son orígenes diferentes:
|
||||
|
||||
Aunque todos están en `localhost`, usan protocolos o puertos diferentes, por lo tanto, son "orígenes" diferentes.
|
||||
|
||||
## Pasos
|
||||
## Pasos { #steps }
|
||||
|
||||
Entonces, digamos que tienes un frontend corriendo en tu navegador en `http://localhost:8080`, y su JavaScript está tratando de comunicarse con un backend corriendo en `http://localhost` (porque no especificamos un puerto, el navegador asumirá el puerto por defecto `80`).
|
||||
|
||||
@@ -24,7 +24,7 @@ Para lograr esto, el backend `:80` debe tener una lista de "orígenes permitidos
|
||||
|
||||
En este caso, la lista tendría que incluir `http://localhost:8080` para que el frontend `:8080` funcione correctamente.
|
||||
|
||||
## Comodines
|
||||
## Comodines { #wildcards }
|
||||
|
||||
También es posible declarar la lista como `"*"` (un "comodín") para decir que todos están permitidos.
|
||||
|
||||
@@ -32,7 +32,7 @@ Pero eso solo permitirá ciertos tipos de comunicación, excluyendo todo lo que
|
||||
|
||||
Así que, para que todo funcione correctamente, es mejor especificar explícitamente los orígenes permitidos.
|
||||
|
||||
## Usa `CORSMiddleware`
|
||||
## Usa `CORSMiddleware` { #use-corsmiddleware }
|
||||
|
||||
Puedes configurarlo en tu aplicación **FastAPI** usando el `CORSMiddleware`.
|
||||
|
||||
@@ -46,7 +46,7 @@ También puedes especificar si tu backend permite:
|
||||
* Métodos HTTP específicos (`POST`, `PUT`) o todos ellos con el comodín `"*"`.
|
||||
* Headers HTTP específicos o todos ellos con el comodín `"*"`.
|
||||
|
||||
{* ../../docs_src/cors/tutorial001.py hl[2,6:11,13:19] *}
|
||||
{* ../../docs_src/cors/tutorial001_py39.py hl[2,6:11,13:19] *}
|
||||
|
||||
Los parámetros predeterminados utilizados por la implementación de `CORSMiddleware` son restrictivos por defecto, por lo que necesitarás habilitar explícitamente orígenes, métodos o headers particulares para que los navegadores estén permitidos de usarlos en un contexto de Cross-Domain.
|
||||
|
||||
@@ -56,23 +56,26 @@ Se admiten los siguientes argumentos:
|
||||
* `allow_origin_regex` - Una cadena regex para coincidir con orígenes que deberían estar permitidos para hacer requests cross-origin. por ejemplo, `'https://.*\.example\.org'`.
|
||||
* `allow_methods` - Una lista de métodos HTTP que deberían estar permitidos para requests cross-origin. Por defecto es `['GET']`. Puedes usar `['*']` para permitir todos los métodos estándar.
|
||||
* `allow_headers` - Una lista de headers de request HTTP que deberían estar soportados para requests cross-origin. Por defecto es `[]`. Puedes usar `['*']` para permitir todos los headers. Los headers `Accept`, `Accept-Language`, `Content-Language` y `Content-Type` siempre están permitidos para <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#simple_requests" class="external-link" rel="noopener" target="_blank">requests CORS simples</a>.
|
||||
* `allow_credentials` - Indica que las cookies deberían estar soportadas para requests cross-origin. Por defecto es `False`. Además, `allow_origins` no puede ser configurado a `['*']` para que las credenciales estén permitidas, los orígenes deben ser especificados.
|
||||
* `allow_credentials` - Indica que las cookies deberían estar soportadas para requests cross-origin. Por defecto es `False`.
|
||||
|
||||
Ninguno de `allow_origins`, `allow_methods` y `allow_headers` puede establecerse a `['*']` si `allow_credentials` está configurado a `True`. Todos deben ser <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#credentialed_requests_and_wildcards" class="external-link" rel="noopener" target="_blank">especificados explícitamente</a>.
|
||||
|
||||
* `expose_headers` - Indica cualquier header de response que debería ser accesible para el navegador. Por defecto es `[]`.
|
||||
* `max_age` - Establece un tiempo máximo en segundos para que los navegadores almacenen en caché los responses CORS. Por defecto es `600`.
|
||||
|
||||
El middleware responde a dos tipos particulares de request HTTP...
|
||||
|
||||
### Requests de preflight CORS
|
||||
### Requests de preflight CORS { #cors-preflight-requests }
|
||||
|
||||
Estos son cualquier request `OPTIONS` con headers `Origin` y `Access-Control-Request-Method`.
|
||||
|
||||
En este caso, el middleware interceptará el request entrante y responderá con los headers CORS adecuados, y un response `200` o `400` con fines informativos.
|
||||
|
||||
### Requests simples
|
||||
### Requests simples { #simple-requests }
|
||||
|
||||
Cualquier request con un header `Origin`. En este caso, el middleware pasará el request a través de lo normal, pero incluirá los headers CORS adecuados en el response.
|
||||
|
||||
## Más info
|
||||
## Más info { #more-info }
|
||||
|
||||
Para más información sobre <abbr title="Cross-Origin Resource Sharing">CORS</abbr>, revisa la <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS" class="external-link" target="_blank">documentación de CORS de Mozilla</a>.
|
||||
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
# Depuración
|
||||
# Depuración { #debugging }
|
||||
|
||||
Puedes conectar el depurador en tu editor, por ejemplo con Visual Studio Code o PyCharm.
|
||||
|
||||
## Llama a `uvicorn`
|
||||
## Llama a `uvicorn` { #call-uvicorn }
|
||||
|
||||
En tu aplicación de FastAPI, importa y ejecuta `uvicorn` directamente:
|
||||
|
||||
{* ../../docs_src/debugging/tutorial001.py hl[1,15] *}
|
||||
{* ../../docs_src/debugging/tutorial001_py39.py hl[1,15] *}
|
||||
|
||||
### Acerca de `__name__ == "__main__"`
|
||||
### Acerca de `__name__ == "__main__"` { #about-name-main }
|
||||
|
||||
El objetivo principal de `__name__ == "__main__"` es tener algo de código que se ejecute cuando tu archivo es llamado con:
|
||||
|
||||
@@ -26,7 +26,7 @@ pero no es llamado cuando otro archivo lo importa, como en:
|
||||
from myapp import app
|
||||
```
|
||||
|
||||
#### Más detalles
|
||||
#### Más detalles { #more-details }
|
||||
|
||||
Supongamos que tu archivo se llama `myapp.py`.
|
||||
|
||||
@@ -78,7 +78,7 @@ Para más información, revisa <a href="https://docs.python.org/3/library/__main
|
||||
|
||||
///
|
||||
|
||||
## Ejecuta tu código con tu depurador
|
||||
## Ejecuta tu código con tu depurador { #run-your-code-with-your-debugger }
|
||||
|
||||
Dado que estás ejecutando el servidor Uvicorn directamente desde tu código, puedes llamar a tu programa de Python (tu aplicación FastAPI) directamente desde el depurador.
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# Clases como dependencias
|
||||
# Clases como dependencias { #classes-as-dependencies }
|
||||
|
||||
Antes de profundizar en el sistema de **Inyección de Dependencias**, vamos a mejorar el ejemplo anterior.
|
||||
|
||||
## Un `dict` del ejemplo anterior
|
||||
## Un `dict` del ejemplo anterior { #a-dict-from-the-previous-example }
|
||||
|
||||
En el ejemplo anterior, estábamos devolviendo un `dict` de nuestra dependencia ("dependable"):
|
||||
|
||||
@@ -14,7 +14,7 @@ Y sabemos que los editores no pueden proporcionar mucho soporte (como autocomple
|
||||
|
||||
Podemos hacerlo mejor...
|
||||
|
||||
## Qué hace a una dependencia
|
||||
## Qué hace a una dependencia { #what-makes-a-dependency }
|
||||
|
||||
Hasta ahora has visto dependencias declaradas como funciones.
|
||||
|
||||
@@ -38,7 +38,7 @@ something(some_argument, some_keyword_argument="foo")
|
||||
|
||||
entonces es un "callable".
|
||||
|
||||
## Clases como dependencias
|
||||
## Clases como dependencias { #classes-as-dependencies_1 }
|
||||
|
||||
Puedes notar que para crear una instance de una clase en Python, utilizas esa misma sintaxis.
|
||||
|
||||
@@ -89,7 +89,7 @@ En ambos casos, tendrá:
|
||||
|
||||
En ambos casos, los datos serán convertidos, validados, documentados en el esquema de OpenAPI, etc.
|
||||
|
||||
## Úsalo
|
||||
## Úsalo { #use-it }
|
||||
|
||||
Ahora puedes declarar tu dependencia usando esta clase.
|
||||
|
||||
@@ -97,11 +97,11 @@ Ahora puedes declarar tu dependencia usando esta clase.
|
||||
|
||||
**FastAPI** llama a la clase `CommonQueryParams`. Esto crea una "instance" de esa clase y la instance será pasada como el parámetro `commons` a tu función.
|
||||
|
||||
## Anotación de tipos vs `Depends`
|
||||
## Anotación de tipos vs `Depends` { #type-annotation-vs-depends }
|
||||
|
||||
Nota cómo escribimos `CommonQueryParams` dos veces en el código anterior:
|
||||
|
||||
//// tab | Python 3.8+
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python
|
||||
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
|
||||
@@ -109,7 +109,7 @@ commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+ sin `Annotated`
|
||||
//// tab | Python 3.9+ sin `Annotated`
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
@@ -137,7 +137,7 @@ Es a partir de este que **FastAPI** extraerá los parámetros declarados y es lo
|
||||
|
||||
En este caso, el primer `CommonQueryParams`, en:
|
||||
|
||||
//// tab | Python 3.8+
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python
|
||||
commons: Annotated[CommonQueryParams, ...
|
||||
@@ -145,7 +145,7 @@ commons: Annotated[CommonQueryParams, ...
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+ sin `Annotated`
|
||||
//// tab | Python 3.9+ sin `Annotated`
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
@@ -163,7 +163,7 @@ commons: CommonQueryParams ...
|
||||
|
||||
De hecho, podrías escribir simplemente:
|
||||
|
||||
//// tab | Python 3.8+
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python
|
||||
commons: Annotated[Any, Depends(CommonQueryParams)]
|
||||
@@ -171,7 +171,7 @@ commons: Annotated[Any, Depends(CommonQueryParams)]
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+ sin `Annotated`
|
||||
//// tab | Python 3.9+ sin `Annotated`
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
@@ -193,11 +193,11 @@ Pero declarar el tipo es recomendable, ya que de esa manera tu editor sabrá lo
|
||||
|
||||
<img src="/img/tutorial/dependencies/image02.png">
|
||||
|
||||
## Atajo
|
||||
## Atajo { #shortcut }
|
||||
|
||||
Pero ves que estamos teniendo algo de repetición de código aquí, escribiendo `CommonQueryParams` dos veces:
|
||||
|
||||
//// tab | Python 3.8+
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python
|
||||
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
|
||||
@@ -205,7 +205,7 @@ commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+ sin `Annotated`
|
||||
//// tab | Python 3.9+ sin `Annotated`
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
@@ -225,7 +225,7 @@ Para esos casos específicos, puedes hacer lo siguiente:
|
||||
|
||||
En lugar de escribir:
|
||||
|
||||
//// tab | Python 3.8+
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python
|
||||
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
|
||||
@@ -233,7 +233,7 @@ commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+ sin `Annotated`
|
||||
//// tab | Python 3.9+ sin `Annotated`
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
@@ -249,7 +249,7 @@ commons: CommonQueryParams = Depends(CommonQueryParams)
|
||||
|
||||
...escribes:
|
||||
|
||||
//// tab | Python 3.8+
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python
|
||||
commons: Annotated[CommonQueryParams, Depends()]
|
||||
@@ -257,7 +257,7 @@ commons: Annotated[CommonQueryParams, Depends()]
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8 sin `Annotated`
|
||||
//// tab | Python 3.9+ sin `Annotated`
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Dependencias en decoradores de *path operation*
|
||||
# Dependencias en decoradores de *path operation* { #dependencies-in-path-operation-decorators }
|
||||
|
||||
En algunos casos realmente no necesitas el valor de retorno de una dependencia dentro de tu *path operation function*.
|
||||
|
||||
@@ -8,7 +8,7 @@ Pero aún necesitas que sea ejecutada/resuelta.
|
||||
|
||||
Para esos casos, en lugar de declarar un parámetro de *path operation function* con `Depends`, puedes añadir una `list` de `dependencies` al decorador de *path operation*.
|
||||
|
||||
## Agregar `dependencies` al decorador de *path operation*
|
||||
## Agregar `dependencies` al decorador de *path operation* { #add-dependencies-to-the-path-operation-decorator }
|
||||
|
||||
El decorador de *path operation* recibe un argumento opcional `dependencies`.
|
||||
|
||||
@@ -36,23 +36,23 @@ Pero en casos reales, al implementar seguridad, obtendrías más beneficios usan
|
||||
|
||||
///
|
||||
|
||||
## Errores de dependencias y valores de retorno
|
||||
## Errores de dependencias y valores de retorno { #dependencies-errors-and-return-values }
|
||||
|
||||
Puedes usar las mismas *funciones* de dependencia que usas normalmente.
|
||||
|
||||
### Requisitos de dependencia
|
||||
### Requisitos de dependencia { #dependency-requirements }
|
||||
|
||||
Pueden declarar requisitos de request (como headers) u otras sub-dependencias:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial006_an_py39.py hl[8,13] *}
|
||||
|
||||
### Lanzar excepciones
|
||||
### Lanzar excepciones { #raise-exceptions }
|
||||
|
||||
Estas dependencias pueden `raise` excepciones, igual que las dependencias normales:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial006_an_py39.py hl[10,15] *}
|
||||
|
||||
### Valores de retorno
|
||||
### Valores de retorno { #return-values }
|
||||
|
||||
Y pueden devolver valores o no, los valores no serán usados.
|
||||
|
||||
@@ -60,10 +60,10 @@ Así que, puedes reutilizar una dependencia normal (que devuelve un valor) que y
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial006_an_py39.py hl[11,16] *}
|
||||
|
||||
## Dependencias para un grupo de *path operations*
|
||||
## Dependencias para un grupo de *path operations* { #dependencies-for-a-group-of-path-operations }
|
||||
|
||||
Más adelante, cuando leas sobre cómo estructurar aplicaciones más grandes ([Aplicaciones Más Grandes - Múltiples Archivos](../../tutorial/bigger-applications.md){.internal-link target=_blank}), posiblemente con múltiples archivos, aprenderás cómo declarar un único parámetro `dependencies` para un grupo de *path operations*.
|
||||
|
||||
## Dependencias Globales
|
||||
## Dependencias Globales { #global-dependencies }
|
||||
|
||||
A continuación veremos cómo añadir dependencias a toda la aplicación `FastAPI`, de modo que se apliquen a cada *path operation*.
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# Dependencias con yield
|
||||
# Dependencias con yield { #dependencies-with-yield }
|
||||
|
||||
FastAPI admite dependencias que realizan algunos <abbr title='sometimes also called "exit code", "cleanup code", "teardown code", "closing code", "context manager exit code", etc.'>pasos adicionales después de finalizar</abbr>.
|
||||
FastAPI admite dependencias que realizan algunos <abbr title='a veces también llamado "código de salida", "código de limpieza", "código de teardown", "código de cierre", "código de salida del context manager", etc.'>pasos adicionales después de finalizar</abbr>.
|
||||
|
||||
Para hacer esto, usa `yield` en lugar de `return` y escribe los pasos adicionales (código) después.
|
||||
Para hacer esto, usa `yield` en lugar de `return`, y escribe los pasos adicionales (código) después.
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
@@ -10,7 +10,7 @@ Asegúrate de usar `yield` una sola vez por dependencia.
|
||||
|
||||
///
|
||||
|
||||
/// note | Nota técnica
|
||||
/// note | Detalles técnicos
|
||||
|
||||
Cualquier función que sea válida para usar con:
|
||||
|
||||
@@ -23,21 +23,21 @@ De hecho, FastAPI usa esos dos decoradores internamente.
|
||||
|
||||
///
|
||||
|
||||
## Una dependencia de base de datos con `yield`
|
||||
## Una dependencia de base de datos con `yield` { #a-database-dependency-with-yield }
|
||||
|
||||
Por ejemplo, podrías usar esto para crear una sesión de base de datos y cerrarla después de finalizar.
|
||||
|
||||
Solo el código anterior e incluyendo la declaración `yield` se ejecuta antes de crear un response:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial007.py hl[2:4] *}
|
||||
{* ../../docs_src/dependencies/tutorial007_py39.py hl[2:4] *}
|
||||
|
||||
El valor generado es lo que se inyecta en *path operations* y otras dependencias:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial007.py hl[4] *}
|
||||
{* ../../docs_src/dependencies/tutorial007_py39.py hl[4] *}
|
||||
|
||||
El código posterior a la declaración `yield` se ejecuta después de crear el response pero antes de enviarla:
|
||||
El código posterior a la declaración `yield` se ejecuta después del response:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial007.py hl[5:6] *}
|
||||
{* ../../docs_src/dependencies/tutorial007_py39.py hl[5:6] *}
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
@@ -47,7 +47,7 @@ Puedes usar funciones `async` o regulares.
|
||||
|
||||
///
|
||||
|
||||
## Una dependencia con `yield` y `try`
|
||||
## Una dependencia con `yield` y `try` { #a-dependency-with-yield-and-try }
|
||||
|
||||
Si usas un bloque `try` en una dependencia con `yield`, recibirás cualquier excepción que se haya lanzado al usar la dependencia.
|
||||
|
||||
@@ -57,9 +57,9 @@ Por lo tanto, puedes buscar esa excepción específica dentro de la dependencia
|
||||
|
||||
Del mismo modo, puedes usar `finally` para asegurarte de que los pasos de salida se ejecuten, sin importar si hubo una excepción o no.
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial007.py hl[3,5] *}
|
||||
{* ../../docs_src/dependencies/tutorial007_py39.py hl[3,5] *}
|
||||
|
||||
## Sub-dependencias con `yield`
|
||||
## Sub-dependencias con `yield` { #sub-dependencies-with-yield }
|
||||
|
||||
Puedes tener sub-dependencias y "árboles" de sub-dependencias de cualquier tamaño y forma, y cualquiera o todas ellas pueden usar `yield`.
|
||||
|
||||
@@ -85,7 +85,7 @@ Puedes tener cualquier combinación de dependencias que quieras.
|
||||
|
||||
**FastAPI** se asegurará de que todo se ejecute en el orden correcto.
|
||||
|
||||
/// note | Nota técnica
|
||||
/// note | Detalles técnicos
|
||||
|
||||
Esto funciona gracias a los <a href="https://docs.python.org/3/library/contextlib.html" class="external-link" target="_blank">Context Managers</a> de Python.
|
||||
|
||||
@@ -93,15 +93,17 @@ Esto funciona gracias a los <a href="https://docs.python.org/3/library/contextli
|
||||
|
||||
///
|
||||
|
||||
## Dependencias con `yield` y `HTTPException`
|
||||
## Dependencias con `yield` y `HTTPException` { #dependencies-with-yield-and-httpexception }
|
||||
|
||||
Viste que puedes usar dependencias con `yield` y tener bloques `try` que capturen excepciones.
|
||||
Viste que puedes usar dependencias con `yield` y tener bloques `try` que intentan ejecutar algo de código y luego ejecutar código de salida después de `finally`.
|
||||
|
||||
De la misma manera, podrías lanzar una `HTTPException` o similar en el código de salida, después del `yield`.
|
||||
También puedes usar `except` para capturar la excepción que se lanzó y hacer algo con ella.
|
||||
|
||||
Por ejemplo, puedes lanzar una excepción diferente, como `HTTPException`.
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
Esta es una técnica algo avanzada, y en la mayoría de los casos realmente no lo necesitarás, ya que puedes lanzar excepciones (incluyendo `HTTPException`) desde dentro del resto del código de tu aplicación, por ejemplo, en la *path operation function*.
|
||||
Esta es una técnica algo avanzada, y en la mayoría de los casos realmente no la necesitarás, ya que puedes lanzar excepciones (incluyendo `HTTPException`) desde dentro del resto del código de tu aplicación, por ejemplo, en la *path operation function*.
|
||||
|
||||
Pero está ahí para ti si la necesitas. 🤓
|
||||
|
||||
@@ -109,9 +111,9 @@ Pero está ahí para ti si la necesitas. 🤓
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial008b_an_py39.py hl[18:22,31] *}
|
||||
|
||||
Una alternativa que podrías usar para capturar excepciones (y posiblemente también lanzar otra `HTTPException`) es crear un [Manejador de Excepciones Personalizado](../handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank}.
|
||||
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}.
|
||||
|
||||
## Dependencias con `yield` y `except`
|
||||
## Dependencias con `yield` y `except` { #dependencies-with-yield-and-except }
|
||||
|
||||
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:
|
||||
|
||||
@@ -119,9 +121,9 @@ Si capturas una excepción usando `except` en una dependencia con `yield` y no l
|
||||
|
||||
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. 😱
|
||||
|
||||
### Siempre `raise` en Dependencias con `yield` y `except`
|
||||
### Siempre `raise` en Dependencias con `yield` y `except` { #always-raise-in-dependencies-with-yield-and-except }
|
||||
|
||||
Si capturas una excepción en una dependencia con `yield`, a menos que estés lanzando otra `HTTPException` o similar, deberías volver a lanzar la excepción original.
|
||||
Si capturas una excepción en una dependencia con `yield`, a menos que estés lanzando otra `HTTPException` o similar, **deberías volver a lanzar la excepción original**.
|
||||
|
||||
Puedes volver a lanzar la misma excepción usando `raise`:
|
||||
|
||||
@@ -129,7 +131,7 @@ 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. 😎
|
||||
|
||||
## Ejecución de dependencias con `yield`
|
||||
## Ejecución de dependencias con `yield` { #execution-of-dependencies-with-yield }
|
||||
|
||||
La secuencia de ejecución es más o menos como este diagrama. El tiempo fluye de arriba a abajo. Y cada columna es una de las partes que interactúa o ejecuta código.
|
||||
|
||||
@@ -178,51 +180,64 @@ Después de que se envíe uno de esos responses, no se podrá enviar ningún otr
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
Este diagrama muestra `HTTPException`, pero también podrías lanzar cualquier otra excepción que captures en una dependencia con `yield` o con un [Manejador de Excepciones Personalizado](../handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank}.
|
||||
|
||||
Si lanzas alguna excepción, será pasada a las dependencias con yield, incluyendo `HTTPException`. En la mayoría de los casos querrás volver a lanzar esa misma excepción o una nueva desde la dependencia con `yield` para asegurarte de que se maneje correctamente.
|
||||
Si lanzas cualquier excepción en el código de la *path operation function*, se pasará a las dependencias con `yield`, incluyendo `HTTPException`. En la mayoría de los casos querrás volver a lanzar esa misma excepción o una nueva desde la dependencia con `yield` para asegurarte de que se maneje correctamente.
|
||||
|
||||
///
|
||||
|
||||
## Dependencias con `yield`, `HTTPException`, `except` y Tareas en Background
|
||||
## Salida temprana y `scope` { #early-exit-and-scope }
|
||||
|
||||
/// warning | Advertencia
|
||||
Normalmente, el código de salida de las dependencias con `yield` se ejecuta **después de que el response** se envía al cliente.
|
||||
|
||||
Probablemente no necesites estos detalles técnicos, puedes omitir esta sección y continuar abajo.
|
||||
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**.
|
||||
|
||||
Estos detalles son útiles principalmente si estabas usando una versión de FastAPI anterior a 0.106.0 y usabas recursos de dependencias con `yield` en tareas en background.
|
||||
{* ../../docs_src/dependencies/tutorial008e_an_py39.py hl[12,16] *}
|
||||
|
||||
///
|
||||
`Depends()` recibe un parámetro `scope` que puede ser:
|
||||
|
||||
### Dependencias con `yield` y `except`, Detalles Técnicos
|
||||
* `"function"`: iniciar la dependencia antes de la *path operation function* que maneja el request, terminar la dependencia después de que termine la *path operation function*, pero **antes** de que el response se envíe de vuelta al cliente. Entonces, la función de dependencia se ejecutará **alrededor** de la *path operation **function***.
|
||||
* `"request"`: iniciar la dependencia antes de la *path operation function* que maneja el request (similar a cuando se usa `"function"`), pero terminar **después** de que el response se envíe de vuelta al cliente. Entonces, la función de dependencia se ejecutará **alrededor** del **request** y del ciclo del response.
|
||||
|
||||
Antes de FastAPI 0.110.0, si usabas una dependencia con `yield`, y luego capturabas una excepción con `except` en esa dependencia, y no volvías a lanzar la excepción, la excepción se lanzaría automáticamente/transmitiría a cualquier manejador de excepciones o al manejador de errores interno del servidor.
|
||||
Si no se especifica y la dependencia tiene `yield`, tendrá un `scope` de `"request"` por defecto.
|
||||
|
||||
Esto se cambió en la versión 0.110.0 para corregir el consumo no gestionado de memoria de excepciones transmitidas sin un manejador (errores internos del servidor), y para que sea consistente con el comportamiento del código regular de Python.
|
||||
### `scope` para sub-dependencias { #scope-for-sub-dependencies }
|
||||
|
||||
### Tareas en Background y Dependencias con `yield`, Detalles Técnicos
|
||||
Cuando declaras una dependencia con `scope="request"` (el valor por defecto), cualquier sub-dependencia también necesita tener un `scope` de `"request"`.
|
||||
|
||||
Antes de FastAPI 0.106.0, lanzar excepciones después de `yield` no era posible, el código de salida en dependencias con `yield` se ejecutaba *después* de que el response se enviara, por lo que los [Manejadores de Excepciones](../handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank} ya se habrían ejecutado.
|
||||
Pero una dependencia con `scope` de `"function"` puede tener dependencias con `scope` de `"function"` y `scope` de `"request"`.
|
||||
|
||||
Esto se diseñó de esta manera principalmente para permitir usar los mismos objetos "extraídos" por dependencias dentro de tareas en background, porque el código de salida se ejecutaría después de que las tareas en background terminaran.
|
||||
Esto es porque cualquier dependencia necesita poder ejecutar su código de salida antes que las sub-dependencias, ya que podría necesitar seguir usándolas durante su código de salida.
|
||||
|
||||
Sin embargo, ya que esto significaría esperar a que el response viaje a través de la red mientras se retiene innecesariamente un recurso en una dependencia con yield (por ejemplo, una conexión a base de datos), esto se cambió en FastAPI 0.106.0.
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
|
||||
/// tip | Consejo
|
||||
participant client as Client
|
||||
participant dep_req as Dep scope="request"
|
||||
participant dep_func as Dep scope="function"
|
||||
participant operation as Path Operation
|
||||
|
||||
Además, una tarea en background es normalmente un conjunto independiente de lógica que debería manejarse por separado, con sus propios recursos (por ejemplo, su propia conexión a base de datos).
|
||||
client ->> dep_req: Start request
|
||||
Note over dep_req: Run code up to yield
|
||||
dep_req ->> dep_func: Pass dependency
|
||||
Note over dep_func: Run code up to yield
|
||||
dep_func ->> operation: Run path operation with dependency
|
||||
operation ->> dep_func: Return from path operation
|
||||
Note over dep_func: Run code after yield
|
||||
Note over dep_func: ✅ Dependency closed
|
||||
dep_func ->> client: Send response to client
|
||||
Note over client: Response sent
|
||||
Note over dep_req: Run code after yield
|
||||
Note over dep_req: ✅ Dependency closed
|
||||
```
|
||||
|
||||
De esta manera probablemente tendrás un código más limpio.
|
||||
## Dependencias con `yield`, `HTTPException`, `except` y Tareas en Background { #dependencies-with-yield-httpexception-except-and-background-tasks }
|
||||
|
||||
///
|
||||
Las dependencias con `yield` han evolucionado con el tiempo para cubrir diferentes casos de uso y corregir algunos problemas.
|
||||
|
||||
Si solías depender de este comportamiento, ahora deberías crear los recursos para tareas en background dentro de la propia tarea en background, y usar internamente solo datos que no dependan de los recursos de las dependencias con `yield`.
|
||||
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}.
|
||||
|
||||
Por ejemplo, en lugar de usar la misma sesión de base de datos, crearías una nueva sesión de base de datos dentro de la tarea en background, y obtendrías los objetos de la base de datos usando esta nueva sesión. Y luego, en lugar de pasar el objeto de la base de datos como parámetro a la función de tarea en background, pasarías el ID de ese objeto y luego obtendrías el objeto nuevamente dentro de la función de tarea en background.
|
||||
## Context Managers { #context-managers }
|
||||
|
||||
## Context Managers
|
||||
|
||||
### Qué son los "Context Managers"
|
||||
### Qué son los "Context Managers" { #what-are-context-managers }
|
||||
|
||||
Los "Context Managers" son aquellos objetos de Python que puedes usar en una declaración `with`.
|
||||
|
||||
@@ -240,7 +255,7 @@ Cuando el bloque `with` termina, se asegura de cerrar el archivo, incluso si hub
|
||||
|
||||
Cuando creas una dependencia con `yield`, **FastAPI** creará internamente un context manager para ella y lo combinará con algunas otras herramientas relacionadas.
|
||||
|
||||
### Usando context managers en dependencias con `yield`
|
||||
### Usando context managers en dependencias con `yield` { #using-context-managers-in-dependencies-with-yield }
|
||||
|
||||
/// warning | Advertencia
|
||||
|
||||
@@ -255,7 +270,7 @@ En Python, puedes crear Context Managers <a href="https://docs.python.org/3/refe
|
||||
También puedes usarlos dentro de las dependencias de **FastAPI** con `yield` usando
|
||||
`with` o `async with` en la función de dependencia:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial010.py hl[1:9,13] *}
|
||||
{* ../../docs_src/dependencies/tutorial010_py39.py hl[1:9,13] *}
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Dependencias Globales
|
||||
# Dependencias Globales { #global-dependencies }
|
||||
|
||||
Para algunos tipos de aplicaciones, podrías querer agregar dependencias a toda la aplicación.
|
||||
|
||||
@@ -6,10 +6,10 @@ Similar a como puedes [agregar `dependencies` a los *path operation decorators*]
|
||||
|
||||
En ese caso, se aplicarán a todas las *path operations* en la aplicación:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial012_an_py39.py hl[16] *}
|
||||
{* ../../docs_src/dependencies/tutorial012_an_py39.py hl[17] *}
|
||||
|
||||
Y todas las ideas en la sección sobre [agregar `dependencies` a los *path operation decorators*](dependencies-in-path-operation-decorators.md){.internal-link target=_blank} siguen aplicándose, pero en este caso, a todas las *path operations* en la app.
|
||||
|
||||
## Dependencias para grupos de *path operations*
|
||||
## Dependencias para grupos de *path operations* { #dependencies-for-groups-of-path-operations }
|
||||
|
||||
Más adelante, al leer sobre cómo estructurar aplicaciones más grandes ([Aplicaciones Más Grandes - Múltiples Archivos](../../tutorial/bigger-applications.md){.internal-link target=_blank}), posiblemente con múltiples archivos, aprenderás cómo declarar un solo parámetro de `dependencies` para un grupo de *path operations*.
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# Dependencias
|
||||
# Dependencias { #dependencies }
|
||||
|
||||
**FastAPI** tiene un sistema de **<abbr title="también conocido como componentes, recursos, proveedores, servicios, inyectables">Inyección de Dependencias</abbr>** muy poderoso pero intuitivo.
|
||||
|
||||
Está diseñado para ser muy simple de usar, y para hacer que cualquier desarrollador integre otros componentes con **FastAPI** de forma muy sencilla.
|
||||
|
||||
## Qué es la "Inyección de Dependencias"
|
||||
## Qué es la "Inyección de Dependencias" { #what-is-dependency-injection }
|
||||
|
||||
**"Inyección de Dependencias"** significa, en programación, que hay una manera para que tu código (en este caso, tus *path operation functions*) declare las cosas que necesita para funcionar y utilizar: "dependencias".
|
||||
|
||||
@@ -19,13 +19,13 @@ Esto es muy útil cuando necesitas:
|
||||
|
||||
Todo esto, mientras minimizas la repetición de código.
|
||||
|
||||
## Primeros Pasos
|
||||
## Primeros Pasos { #first-steps }
|
||||
|
||||
Veamos un ejemplo muy simple. Será tan simple que no es muy útil, por ahora.
|
||||
|
||||
Pero de esta manera podemos enfocarnos en cómo funciona el sistema de **Inyección de Dependencias**.
|
||||
|
||||
### Crear una dependencia, o "dependable"
|
||||
### Crear una dependencia, o "dependable" { #create-a-dependency-or-dependable }
|
||||
|
||||
Primero enfoquémonos en la dependencia.
|
||||
|
||||
@@ -61,11 +61,11 @@ Asegúrate de [Actualizar la versión de FastAPI](../../deployment/versions.md#u
|
||||
|
||||
///
|
||||
|
||||
### Importar `Depends`
|
||||
### Importar `Depends` { #import-depends }
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[3] *}
|
||||
|
||||
### Declarar la dependencia, en el "dependant"
|
||||
### Declarar la dependencia, en el "dependant" { #declare-the-dependency-in-the-dependant }
|
||||
|
||||
De la misma forma en que usas `Body`, `Query`, etc. con los parámetros de tu *path operation function*, usa `Depends` con un nuevo parámetro:
|
||||
|
||||
@@ -114,7 +114,7 @@ Solo la pasas a `Depends` y **FastAPI** sabe cómo hacer el resto.
|
||||
|
||||
///
|
||||
|
||||
## Compartir dependencias `Annotated`
|
||||
## Compartir dependencias `Annotated` { #share-annotated-dependencies }
|
||||
|
||||
En los ejemplos anteriores, ves que hay un poquito de **duplicación de código**.
|
||||
|
||||
@@ -138,9 +138,9 @@ Pero porque **FastAPI** está basado en los estándares de Python, incluido `Ann
|
||||
|
||||
Las dependencias seguirán funcionando como se esperaba, y la **mejor parte** es que la **información de tipo se preservará**, lo que significa que tu editor podrá seguir proporcionándote **autocompletado**, **errores en línea**, etc. Lo mismo para otras herramientas como `mypy`.
|
||||
|
||||
Esto será especialmente útil cuando lo uses en una **gran base de código** donde uses **las mismas dependencias** una y otra vez en **muchas *path operations***.
|
||||
Esto será especialmente útil cuando lo uses en una **gran code base** donde uses **las mismas dependencias** una y otra vez en **muchas *path operations***.
|
||||
|
||||
## Usar `async` o no usar `async`
|
||||
## Usar `async` o no usar `async` { #to-async-or-not-to-async }
|
||||
|
||||
Como las dependencias también serán llamadas por **FastAPI** (lo mismo que tus *path operation functions*), las mismas reglas aplican al definir tus funciones.
|
||||
|
||||
@@ -156,7 +156,7 @@ Si no lo sabes, revisa la sección [Async: *"¿Con prisa?"*](../../async.md#in-a
|
||||
|
||||
///
|
||||
|
||||
## Integración con OpenAPI
|
||||
## Integración con OpenAPI { #integrated-with-openapi }
|
||||
|
||||
Todas las declaraciones de request, validaciones y requisitos de tus dependencias (y sub-dependencias) se integrarán en el mismo esquema de OpenAPI.
|
||||
|
||||
@@ -164,7 +164,7 @@ Así, la documentación interactiva tendrá toda la información de estas depend
|
||||
|
||||
<img src="/img/tutorial/dependencies/image01.png">
|
||||
|
||||
## Uso simple
|
||||
## Uso simple { #simple-usage }
|
||||
|
||||
Si lo ves, las *path operation functions* se declaran para ser usadas siempre que un *path* y una *operación* coincidan, y luego **FastAPI** se encarga de llamar la función con los parámetros correctos, extrayendo los datos del request.
|
||||
|
||||
@@ -182,7 +182,7 @@ Otros términos comunes para esta misma idea de "inyección de dependencias" son
|
||||
* inyectables
|
||||
* componentes
|
||||
|
||||
## Plug-ins de **FastAPI**
|
||||
## Plug-ins de **FastAPI** { #fastapi-plug-ins }
|
||||
|
||||
Las integraciones y "plug-ins" pueden construirse usando el sistema de **Inyección de Dependencias**. Pero, de hecho, en realidad **no hay necesidad de crear "plug-ins"**, ya que al usar dependencias es posible declarar una cantidad infinita de integraciones e interacciones que se vuelven disponibles para tus *path operation functions*.
|
||||
|
||||
@@ -190,7 +190,7 @@ Y las dependencias se pueden crear de una manera muy simple e intuitiva que te p
|
||||
|
||||
Verás ejemplos de esto en los próximos capítulos, sobre bases de datos relacionales y NoSQL, seguridad, etc.
|
||||
|
||||
## Compatibilidad de **FastAPI**
|
||||
## Compatibilidad de **FastAPI** { #fastapi-compatibility }
|
||||
|
||||
La simplicidad del sistema de inyección de dependencias hace que **FastAPI** sea compatible con:
|
||||
|
||||
@@ -203,7 +203,7 @@ La simplicidad del sistema de inyección de dependencias hace que **FastAPI** se
|
||||
* sistemas de inyección de datos de response
|
||||
* etc.
|
||||
|
||||
## Simple y Poderoso
|
||||
## Simple y Poderoso { #simple-and-powerful }
|
||||
|
||||
Aunque el sistema de inyección de dependencias jerárquico es muy simple de definir y usar, sigue siendo muy poderoso.
|
||||
|
||||
@@ -243,7 +243,7 @@ admin_user --> activate_user
|
||||
paying_user --> pro_items
|
||||
```
|
||||
|
||||
## Integrado con **OpenAPI**
|
||||
## Integrado con **OpenAPI** { #integrated-with-openapi_1 }
|
||||
|
||||
Todas estas dependencias, al declarar sus requisitos, también añaden parámetros, validaciones, etc. a tus *path operations*.
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Sub-dependencias
|
||||
# Sub-dependencias { #sub-dependencies }
|
||||
|
||||
Puedes crear dependencias que tengan **sub-dependencias**.
|
||||
|
||||
@@ -6,7 +6,7 @@ Pueden ser tan **profundas** como necesites.
|
||||
|
||||
**FastAPI** se encargará de resolverlas.
|
||||
|
||||
## Primera dependencia "dependable"
|
||||
## Primera dependencia "dependable" { #first-dependency-dependable }
|
||||
|
||||
Podrías crear una primera dependencia ("dependable") así:
|
||||
|
||||
@@ -16,7 +16,7 @@ Declara un parámetro de query opcional `q` como un `str`, y luego simplemente l
|
||||
|
||||
Esto es bastante simple (no muy útil), pero nos ayudará a centrarnos en cómo funcionan las sub-dependencias.
|
||||
|
||||
## Segunda dependencia, "dependable" y "dependant"
|
||||
## Segunda dependencia, "dependable" y "dependant" { #second-dependency-dependable-and-dependant }
|
||||
|
||||
Luego puedes crear otra función de dependencia (un "dependable") que al mismo tiempo declare una dependencia propia (por lo que también es un "dependant"):
|
||||
|
||||
@@ -29,7 +29,7 @@ Centrémonos en los parámetros declarados:
|
||||
* También declara una `last_query` cookie opcional, como un `str`.
|
||||
* Si el usuario no proporcionó ningún query `q`, usamos el último query utilizado, que guardamos previamente en una cookie.
|
||||
|
||||
## Usa la dependencia
|
||||
## Usa la dependencia { #use-the-dependency }
|
||||
|
||||
Entonces podemos usar la dependencia con:
|
||||
|
||||
@@ -54,7 +54,7 @@ read_query["/items/"]
|
||||
query_extractor --> query_or_cookie_extractor --> read_query
|
||||
```
|
||||
|
||||
## Usando la misma dependencia múltiples veces
|
||||
## Usando la misma dependencia múltiples veces { #using-the-same-dependency-multiple-times }
|
||||
|
||||
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.
|
||||
|
||||
@@ -62,7 +62,7 @@ Y guardará el valor devuelto en un <abbr title="Una utilidad/sistema para almac
|
||||
|
||||
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`:
|
||||
|
||||
//// tab | Python 3.8+
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python hl_lines="1"
|
||||
async def needy_dependency(fresh_value: Annotated[str, Depends(get_value, use_cache=False)]):
|
||||
@@ -71,7 +71,7 @@ async def needy_dependency(fresh_value: Annotated[str, Depends(get_value, use_ca
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+ sin Anotaciones
|
||||
//// tab | Python 3.9+ sin Anotaciones
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
@@ -86,7 +86,7 @@ async def needy_dependency(fresh_value: str = Depends(get_value, use_cache=False
|
||||
|
||||
////
|
||||
|
||||
## Resumen
|
||||
## Resumen { #recap }
|
||||
|
||||
Aparte de todas las palabras rimbombantes usadas aquí, el sistema de **Inyección de Dependencias** es bastante simple.
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# JSON Compatible Encoder
|
||||
# Codificador compatible con JSON { #json-compatible-encoder }
|
||||
|
||||
Hay algunos casos en los que podrías necesitar convertir un tipo de dato (como un modelo de Pydantic) a algo compatible con JSON (como un `dict`, `list`, etc).
|
||||
|
||||
@@ -6,13 +6,13 @@ Por ejemplo, si necesitas almacenarlo en una base de datos.
|
||||
|
||||
Para eso, **FastAPI** proporciona una función `jsonable_encoder()`.
|
||||
|
||||
## Usando el `jsonable_encoder`
|
||||
## Usando el `jsonable_encoder` { #using-the-jsonable-encoder }
|
||||
|
||||
Imaginemos que tienes una base de datos `fake_db` que solo recibe datos compatibles con JSON.
|
||||
|
||||
Por ejemplo, no recibe objetos `datetime`, ya que no son compatibles con JSON.
|
||||
|
||||
Entonces, un objeto `datetime` tendría que ser convertido a un `str` que contenga los datos en formato <a href="https://en.wikipedia.org/wiki/ISO_8601" class="external-link" target="_blank">ISO</a>.
|
||||
Entonces, un objeto `datetime` tendría que ser convertido a un `str` que contenga los datos en <a href="https://en.wikipedia.org/wiki/ISO_8601" class="external-link" target="_blank">formato ISO</a>.
|
||||
|
||||
De la misma manera, esta base de datos no recibiría un modelo de Pydantic (un objeto con atributos), solo un `dict`.
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Tipos de Datos Extra
|
||||
# Tipos de Datos Extra { #extra-data-types }
|
||||
|
||||
Hasta ahora, has estado usando tipos de datos comunes, como:
|
||||
|
||||
@@ -17,7 +17,7 @@ Y seguirás teniendo las mismas funcionalidades como hasta ahora:
|
||||
* Validación de datos.
|
||||
* Anotación y documentación automática.
|
||||
|
||||
## Otros tipos de datos
|
||||
## Otros tipos de datos { #other-data-types }
|
||||
|
||||
Aquí hay algunos de los tipos de datos adicionales que puedes usar:
|
||||
|
||||
@@ -51,7 +51,7 @@ Aquí hay algunos de los tipos de datos adicionales que puedes usar:
|
||||
* En requests y responses, manejado igual que un `float`.
|
||||
* Puedes revisar todos los tipos de datos válidos de Pydantic aquí: <a href="https://docs.pydantic.dev/latest/usage/types/types/" class="external-link" target="_blank">Tipos de datos de Pydantic</a>.
|
||||
|
||||
## Ejemplo
|
||||
## Ejemplo { #example }
|
||||
|
||||
Aquí tienes un ejemplo de una *path operation* con parámetros usando algunos de los tipos anteriores.
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Modelos Extra
|
||||
# Modelos Extra { #extra-models }
|
||||
|
||||
Continuando con el ejemplo anterior, será común tener más de un modelo relacionado.
|
||||
|
||||
@@ -16,27 +16,19 @@ Si no lo sabes, aprenderás qué es un "hash de contraseña" en los [capítulos
|
||||
|
||||
///
|
||||
|
||||
## Múltiples modelos
|
||||
## Múltiples modelos { #multiple-models }
|
||||
|
||||
Aquí tienes una idea general de cómo podrían ser los modelos con sus campos de contraseña y los lugares donde se utilizan:
|
||||
|
||||
{* ../../docs_src/extra_models/tutorial001_py310.py hl[7,9,14,20,22,27:28,31:33,38:39] *}
|
||||
|
||||
/// info | Información
|
||||
### Acerca de `**user_in.model_dump()` { #about-user-in-model-dump }
|
||||
|
||||
En Pydantic v1 el método se llamaba `.dict()`, fue deprecado (pero aún soportado) en Pydantic v2, y renombrado a `.model_dump()`.
|
||||
|
||||
Los ejemplos aquí usan `.dict()` para compatibilidad con Pydantic v1, pero deberías usar `.model_dump()` en su lugar si puedes usar Pydantic v2.
|
||||
|
||||
///
|
||||
|
||||
### Acerca de `**user_in.dict()`
|
||||
|
||||
#### `.dict()` de Pydantic
|
||||
#### `.model_dump()` de Pydantic { #pydantics-model-dump }
|
||||
|
||||
`user_in` es un modelo Pydantic de la clase `UserIn`.
|
||||
|
||||
Los modelos Pydantic tienen un método `.dict()` que devuelve un `dict` con los datos del modelo.
|
||||
Los modelos Pydantic tienen un método `.model_dump()` que devuelve un `dict` con los datos del modelo.
|
||||
|
||||
Así que, si creamos un objeto Pydantic `user_in` como:
|
||||
|
||||
@@ -47,7 +39,7 @@ user_in = UserIn(username="john", password="secret", email="john.doe@example.com
|
||||
y luego llamamos a:
|
||||
|
||||
```Python
|
||||
user_dict = user_in.dict()
|
||||
user_dict = user_in.model_dump()
|
||||
```
|
||||
|
||||
ahora tenemos un `dict` con los datos en la variable `user_dict` (es un `dict` en lugar de un objeto modelo Pydantic).
|
||||
@@ -58,7 +50,7 @@ Y si llamamos a:
|
||||
print(user_dict)
|
||||
```
|
||||
|
||||
obtendremos un `dict` de Python con:
|
||||
obtendríamos un `dict` de Python con:
|
||||
|
||||
```Python
|
||||
{
|
||||
@@ -69,7 +61,7 @@ obtendremos un `dict` de Python con:
|
||||
}
|
||||
```
|
||||
|
||||
#### Desempaquetando un `dict`
|
||||
#### Desempaquetando un `dict` { #unpacking-a-dict }
|
||||
|
||||
Si tomamos un `dict` como `user_dict` y lo pasamos a una función (o clase) con `**user_dict`, Python lo "desempaquetará". Pasará las claves y valores del `user_dict` directamente como argumentos clave-valor.
|
||||
|
||||
@@ -101,31 +93,31 @@ UserInDB(
|
||||
)
|
||||
```
|
||||
|
||||
#### Un modelo Pydantic a partir del contenido de otro
|
||||
#### Un modelo Pydantic a partir del contenido de otro { #a-pydantic-model-from-the-contents-of-another }
|
||||
|
||||
Como en el ejemplo anterior obtuvimos `user_dict` de `user_in.dict()`, este código:
|
||||
Como en el ejemplo anterior obtuvimos `user_dict` de `user_in.model_dump()`, este código:
|
||||
|
||||
```Python
|
||||
user_dict = user_in.dict()
|
||||
user_dict = user_in.model_dump()
|
||||
UserInDB(**user_dict)
|
||||
```
|
||||
|
||||
sería equivalente a:
|
||||
|
||||
```Python
|
||||
UserInDB(**user_in.dict())
|
||||
UserInDB(**user_in.model_dump())
|
||||
```
|
||||
|
||||
...porque `user_in.dict()` es un `dict`, y luego hacemos que Python lo "desempaquete" al pasarlo a `UserInDB` con el prefijo `**`.
|
||||
...porque `user_in.model_dump()` es un `dict`, y luego hacemos que Python lo "desempaquete" al pasarlo a `UserInDB` con el prefijo `**`.
|
||||
|
||||
Así, obtenemos un modelo Pydantic a partir de los datos en otro modelo Pydantic.
|
||||
|
||||
#### Desempaquetando un `dict` y palabras clave adicionales
|
||||
#### Desempaquetando un `dict` y palabras clave adicionales { #unpacking-a-dict-and-extra-keywords }
|
||||
|
||||
Y luego agregando el argumento de palabra clave adicional `hashed_password=hashed_password`, como en:
|
||||
|
||||
```Python
|
||||
UserInDB(**user_in.dict(), hashed_password=hashed_password)
|
||||
UserInDB(**user_in.model_dump(), hashed_password=hashed_password)
|
||||
```
|
||||
|
||||
...termina siendo como:
|
||||
@@ -146,7 +138,7 @@ Las funciones adicionales de soporte `fake_password_hasher` y `fake_save_user` s
|
||||
|
||||
///
|
||||
|
||||
## Reducir duplicación
|
||||
## Reducir duplicación { #reduce-duplication }
|
||||
|
||||
Reducir la duplicación de código es una de las ideas centrales en **FastAPI**.
|
||||
|
||||
@@ -156,7 +148,7 @@ Y estos modelos están compartiendo muchos de los datos y duplicando nombres y t
|
||||
|
||||
Podríamos hacerlo mejor.
|
||||
|
||||
Podemos declarar un modelo `UserBase` que sirva como base para nuestros otros modelos. Y luego podemos hacer subclases de ese modelo que heredan sus atributos (declaraciones de tipo, validación, etc).
|
||||
Podemos declarar un modelo `UserBase` que sirva como base para nuestros otros modelos. Y luego podemos hacer subclases de ese modelo que heredan sus atributos (declaraciones de tipos, validación, etc).
|
||||
|
||||
Toda la conversión de datos, validación, documentación, etc. seguirá funcionando normalmente.
|
||||
|
||||
@@ -164,13 +156,13 @@ De esa manera, podemos declarar solo las diferencias entre los modelos (con `pas
|
||||
|
||||
{* ../../docs_src/extra_models/tutorial002_py310.py hl[7,13:14,17:18,21:22] *}
|
||||
|
||||
## `Union` o `anyOf`
|
||||
## `Union` o `anyOf` { #union-or-anyof }
|
||||
|
||||
Puedes declarar un response que sea la `Union` de dos o más tipos, eso significa que el response sería cualquiera de ellos.
|
||||
|
||||
Se definirá en OpenAPI con `anyOf`.
|
||||
|
||||
Para hacerlo, usa el type hint estándar de Python <a href="https://docs.python.org/3/library/typing.html#typing.Union" class="external-link" target="_blank">`typing.Union`</a>:
|
||||
Para hacerlo, usa la anotación de tipos estándar de Python <a href="https://docs.python.org/3/library/typing.html#typing.Union" class="external-link" target="_blank">`typing.Union`</a>:
|
||||
|
||||
/// note | Nota
|
||||
|
||||
@@ -180,22 +172,21 @@ Al definir una <a href="https://docs.pydantic.dev/latest/concepts/types/#unions"
|
||||
|
||||
{* ../../docs_src/extra_models/tutorial003_py310.py hl[1,14:15,18:20,33] *}
|
||||
|
||||
|
||||
### `Union` en Python 3.10
|
||||
### `Union` en Python 3.10 { #union-in-python-3-10 }
|
||||
|
||||
En este ejemplo pasamos `Union[PlaneItem, CarItem]` como el valor del argumento `response_model`.
|
||||
|
||||
Porque lo estamos pasando como un **valor a un argumento** en lugar de ponerlo en una **anotación de tipo**, tenemos que usar `Union` incluso en Python 3.10.
|
||||
Porque lo estamos pasando como un **valor a un argumento** en lugar de ponerlo en una **anotación de tipos**, tenemos que usar `Union` incluso en Python 3.10.
|
||||
|
||||
Si estuviera en una anotación de tipo podríamos haber usado la barra vertical, como:
|
||||
Si estuviera en una anotación de tipos podríamos haber usado la barra vertical, como:
|
||||
|
||||
```Python
|
||||
some_variable: PlaneItem | CarItem
|
||||
```
|
||||
|
||||
Pero si ponemos eso en la asignación `response_model=PlaneItem | CarItem` obtendríamos un error, porque Python intentaría realizar una **operación inválida** entre `PlaneItem` y `CarItem` en lugar de interpretar eso como una anotación de tipo.
|
||||
Pero si ponemos eso en la asignación `response_model=PlaneItem | CarItem` obtendríamos un error, porque Python intentaría realizar una **operación inválida** entre `PlaneItem` y `CarItem` en lugar de interpretar eso como una anotación de tipos.
|
||||
|
||||
## Lista de modelos
|
||||
## Lista de modelos { #list-of-models }
|
||||
|
||||
De la misma manera, puedes declarar responses de listas de objetos.
|
||||
|
||||
@@ -203,8 +194,7 @@ Para eso, usa el `typing.List` estándar de Python (o simplemente `list` en Pyth
|
||||
|
||||
{* ../../docs_src/extra_models/tutorial004_py39.py hl[18] *}
|
||||
|
||||
|
||||
## Response con `dict` arbitrario
|
||||
## Response con `dict` arbitrario { #response-with-arbitrary-dict }
|
||||
|
||||
También puedes declarar un response usando un `dict` arbitrario plano, declarando solo el tipo de las claves y valores, sin usar un modelo Pydantic.
|
||||
|
||||
@@ -214,8 +204,7 @@ En este caso, puedes usar `typing.Dict` (o solo `dict` en Python 3.9 y posterior
|
||||
|
||||
{* ../../docs_src/extra_models/tutorial005_py39.py hl[6] *}
|
||||
|
||||
|
||||
## Recapitulación
|
||||
## Recapitulación { #recap }
|
||||
|
||||
Usa múltiples modelos Pydantic y hereda libremente para cada caso.
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# Primeros Pasos
|
||||
# Primeros Pasos { #first-steps }
|
||||
|
||||
El archivo FastAPI más simple podría verse así:
|
||||
|
||||
{* ../../docs_src/first_steps/tutorial001.py *}
|
||||
{* ../../docs_src/first_steps/tutorial001_py39.py *}
|
||||
|
||||
Copia eso en un archivo `main.py`.
|
||||
|
||||
@@ -11,47 +11,39 @@ Ejecuta el servidor en vivo:
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ <font color="#4E9A06">fastapi</font> dev <u style="text-decoration-style:single">main.py</u>
|
||||
<font color="#3465A4">INFO </font> Using path <font color="#3465A4">main.py</font>
|
||||
<font color="#3465A4">INFO </font> Resolved absolute path <font color="#75507B">/home/user/code/awesomeapp/</font><font color="#AD7FA8">main.py</font>
|
||||
<font color="#3465A4">INFO </font> Searching for package file structure from directories with <font color="#3465A4">__init__.py</font> files
|
||||
<font color="#3465A4">INFO </font> Importing from <font color="#75507B">/home/user/code/</font><font color="#AD7FA8">awesomeapp</font>
|
||||
$ <font color="#4E9A06">fastapi</font> dev <u style="text-decoration-style:solid">main.py</u>
|
||||
|
||||
╭─ <font color="#8AE234"><b>Python module file</b></font> ─╮
|
||||
│ │
|
||||
│ 🐍 main.py │
|
||||
│ │
|
||||
╰──────────────────────╯
|
||||
<span style="background-color:#009485"><font color="#D3D7CF"> FastAPI </font></span> Starting development server 🚀
|
||||
|
||||
<font color="#3465A4">INFO </font> Importing module <font color="#4E9A06">main</font>
|
||||
<font color="#3465A4">INFO </font> Found importable FastAPI app
|
||||
Searching for package file structure from directories
|
||||
with <font color="#3465A4">__init__.py</font> files
|
||||
Importing from <font color="#75507B">/home/user/code/</font><font color="#AD7FA8">awesomeapp</font>
|
||||
|
||||
╭─ <font color="#8AE234"><b>Importable FastAPI app</b></font> ─╮
|
||||
│ │
|
||||
│ <span style="background-color:#272822"><font color="#FF4689">from</font></span><span style="background-color:#272822"><font color="#F8F8F2"> main </font></span><span style="background-color:#272822"><font color="#FF4689">import</font></span><span style="background-color:#272822"><font color="#F8F8F2"> app</font></span><span style="background-color:#272822"> </span> │
|
||||
│ │
|
||||
╰──────────────────────────╯
|
||||
<span style="background-color:#007166"><font color="#D3D7CF"> module </font></span> 🐍 main.py
|
||||
|
||||
<font color="#3465A4">INFO </font> Using import string <font color="#8AE234"><b>main:app</b></font>
|
||||
<span style="background-color:#007166"><font color="#D3D7CF"> code </font></span> Importing the FastAPI app object from the module with
|
||||
the following code:
|
||||
|
||||
<span style="background-color:#C4A000"><font color="#2E3436">╭────────── FastAPI CLI - Development mode ───────────╮</font></span>
|
||||
<span style="background-color:#C4A000"><font color="#2E3436">│ │</font></span>
|
||||
<span style="background-color:#C4A000"><font color="#2E3436">│ Serving at: http://127.0.0.1:8000 │</font></span>
|
||||
<span style="background-color:#C4A000"><font color="#2E3436">│ │</font></span>
|
||||
<span style="background-color:#C4A000"><font color="#2E3436">│ API docs: http://127.0.0.1:8000/docs │</font></span>
|
||||
<span style="background-color:#C4A000"><font color="#2E3436">│ │</font></span>
|
||||
<span style="background-color:#C4A000"><font color="#2E3436">│ Running in development mode, for production use: │</font></span>
|
||||
<span style="background-color:#C4A000"><font color="#2E3436">│ │</font></span>
|
||||
<span style="background-color:#C4A000"><font color="#2E3436">│ </font></span><span style="background-color:#C4A000"><font color="#555753"><b>fastapi run</b></font></span><span style="background-color:#C4A000"><font color="#2E3436"> │</font></span>
|
||||
<span style="background-color:#C4A000"><font color="#2E3436">│ │</font></span>
|
||||
<span style="background-color:#C4A000"><font color="#2E3436">╰─────────────────────────────────────────────────────╯</font></span>
|
||||
<u style="text-decoration-style:solid">from </u><u style="text-decoration-style:solid"><b>main</b></u><u style="text-decoration-style:solid"> import </u><u style="text-decoration-style:solid"><b>app</b></u>
|
||||
|
||||
<font color="#4E9A06">INFO</font>: Will watch for changes in these directories: ['/home/user/code/awesomeapp']
|
||||
<font color="#4E9A06">INFO</font>: Uvicorn running on <b>http://127.0.0.1:8000</b> (Press CTRL+C to quit)
|
||||
<font color="#4E9A06">INFO</font>: Started reloader process [<font color="#34E2E2"><b>2265862</b></font>] using <font color="#34E2E2"><b>WatchFiles</b></font>
|
||||
<font color="#4E9A06">INFO</font>: Started server process [<font color="#06989A">2265873</font>]
|
||||
<font color="#4E9A06">INFO</font>: Waiting for application startup.
|
||||
<font color="#4E9A06">INFO</font>: Application startup complete.
|
||||
<span style="background-color:#007166"><font color="#D3D7CF"> app </font></span> Using import string: <font color="#3465A4">main:app</font>
|
||||
|
||||
<span style="background-color:#007166"><font color="#D3D7CF"> server </font></span> Server started at <font color="#729FCF"><u style="text-decoration-style:solid">http://127.0.0.1:8000</u></font>
|
||||
<span style="background-color:#007166"><font color="#D3D7CF"> server </font></span> Documentation at <font color="#729FCF"><u style="text-decoration-style:solid">http://127.0.0.1:8000/docs</u></font>
|
||||
|
||||
<span style="background-color:#007166"><font color="#D3D7CF"> tip </font></span> Running in development mode, for production use:
|
||||
<b>fastapi run</b>
|
||||
|
||||
Logs:
|
||||
|
||||
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Will watch for changes in these directories:
|
||||
<b>[</b><font color="#4E9A06">'/home/user/code/awesomeapp'</font><b>]</b>
|
||||
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Uvicorn running on <font color="#729FCF"><u style="text-decoration-style:solid">http://127.0.0.1:8000</u></font> <b>(</b>Press CTRL+C
|
||||
to quit<b>)</b>
|
||||
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Started reloader process <b>[</b><font color="#34E2E2"><b>383138</b></font><b>]</b> using WatchFiles
|
||||
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Started server process <b>[</b><font color="#34E2E2"><b>383153</b></font><b>]</b>
|
||||
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Waiting for application startup.
|
||||
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Application startup complete.
|
||||
```
|
||||
|
||||
</div>
|
||||
@@ -64,7 +56,7 @@ INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
|
||||
|
||||
Esa línea muestra la URL donde tu aplicación está siendo servida, en tu máquina local.
|
||||
|
||||
### Compruébalo
|
||||
### Compruébalo { #check-it }
|
||||
|
||||
Abre tu navegador en <a href="http://127.0.0.1:8000" class="external-link" target="_blank">http://127.0.0.1:8000</a>.
|
||||
|
||||
@@ -74,7 +66,7 @@ Verás el response JSON como:
|
||||
{"message": "Hello World"}
|
||||
```
|
||||
|
||||
### Documentación interactiva de la API
|
||||
### Documentación interactiva de la API { #interactive-api-docs }
|
||||
|
||||
Ahora ve a <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
|
||||
|
||||
@@ -82,7 +74,7 @@ Verás la documentación interactiva automática de la API (proporcionada por <a
|
||||
|
||||

|
||||
|
||||
### Documentación alternativa de la API
|
||||
### Documentación alternativa de la API { #alternative-api-docs }
|
||||
|
||||
Y ahora, ve a <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>.
|
||||
|
||||
@@ -90,31 +82,31 @@ Verás la documentación alternativa automática (proporcionada por <a href="htt
|
||||
|
||||

|
||||
|
||||
### OpenAPI
|
||||
### OpenAPI { #openapi }
|
||||
|
||||
**FastAPI** genera un "esquema" con toda tu API utilizando el estándar **OpenAPI** para definir APIs.
|
||||
|
||||
#### "Esquema"
|
||||
#### "Esquema" { #schema }
|
||||
|
||||
Un "esquema" es una definición o descripción de algo. No el código que lo implementa, sino solo una descripción abstracta.
|
||||
|
||||
#### Esquema de la API
|
||||
#### Esquema de la API { #api-schema }
|
||||
|
||||
En este caso, <a href="https://github.com/OAI/OpenAPI-Specification" class="external-link" target="_blank">OpenAPI</a> es una especificación que dicta cómo definir un esquema de tu API.
|
||||
|
||||
Esta definición de esquema incluye los paths de tu API, los posibles parámetros que toman, etc.
|
||||
|
||||
#### Esquema de Datos
|
||||
#### Esquema de Datos { #data-schema }
|
||||
|
||||
El término "esquema" también podría referirse a la forma de algunos datos, como el contenido JSON.
|
||||
|
||||
En ese caso, significaría los atributos del JSON, los tipos de datos que tienen, etc.
|
||||
|
||||
#### OpenAPI y JSON Schema
|
||||
#### OpenAPI y JSON Schema { #openapi-and-json-schema }
|
||||
|
||||
OpenAPI define un esquema de API para tu API. Y ese esquema incluye definiciones (o "esquemas") de los datos enviados y recibidos por tu API utilizando **JSON Schema**, el estándar para esquemas de datos JSON.
|
||||
|
||||
#### Revisa el `openapi.json`
|
||||
#### Revisa el `openapi.json` { #check-the-openapi-json }
|
||||
|
||||
Si tienes curiosidad por cómo se ve el esquema OpenAPI en bruto, FastAPI automáticamente genera un JSON (esquema) con las descripciones de toda tu API.
|
||||
|
||||
@@ -143,7 +135,7 @@ Mostrará un JSON que empieza con algo como:
|
||||
...
|
||||
```
|
||||
|
||||
#### Para qué sirve OpenAPI
|
||||
#### Para qué sirve OpenAPI { #what-is-openapi-for }
|
||||
|
||||
El esquema OpenAPI es lo que impulsa los dos sistemas de documentación interactiva incluidos.
|
||||
|
||||
@@ -151,15 +143,51 @@ Y hay docenas de alternativas, todas basadas en OpenAPI. Podrías añadir fácil
|
||||
|
||||
También podrías usarlo para generar código automáticamente, para clientes que se comuniquen con tu API. Por ejemplo, aplicaciones frontend, móviles o IoT.
|
||||
|
||||
## Recapitulación, paso a paso
|
||||
### Despliega tu app (opcional) { #deploy-your-app-optional }
|
||||
|
||||
### Paso 1: importa `FastAPI`
|
||||
Opcionalmente puedes desplegar tu app de FastAPI en <a href="https://fastapicloud.com" class="external-link" target="_blank">FastAPI Cloud</a>, ve y únete a la lista de espera si aún no lo has hecho. 🚀
|
||||
|
||||
{* ../../docs_src/first_steps/tutorial001.py hl[1] *}
|
||||
Si ya tienes una cuenta de **FastAPI Cloud** (te invitamos desde la lista de espera 😉), puedes desplegar tu aplicación con un solo comando.
|
||||
|
||||
Antes de desplegar, asegúrate de haber iniciado sesión:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ fastapi login
|
||||
|
||||
You are logged in to FastAPI Cloud 🚀
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
Luego despliega tu app:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ fastapi deploy
|
||||
|
||||
Deploying to FastAPI Cloud...
|
||||
|
||||
✅ Deployment successful!
|
||||
|
||||
🐔 Ready the chicken! Your app is ready at https://myapp.fastapicloud.dev
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
¡Eso es todo! Ahora puedes acceder a tu app en esa URL. ✨
|
||||
|
||||
## Recapitulación, paso a paso { #recap-step-by-step }
|
||||
|
||||
### Paso 1: importa `FastAPI` { #step-1-import-fastapi }
|
||||
|
||||
{* ../../docs_src/first_steps/tutorial001_py39.py hl[1] *}
|
||||
|
||||
`FastAPI` es una clase de Python que proporciona toda la funcionalidad para tu API.
|
||||
|
||||
/// note | Detalles Técnicos
|
||||
/// note | Detalles técnicos
|
||||
|
||||
`FastAPI` es una clase que hereda directamente de `Starlette`.
|
||||
|
||||
@@ -167,17 +195,17 @@ Puedes usar toda la funcionalidad de <a href="https://www.starlette.dev/" class=
|
||||
|
||||
///
|
||||
|
||||
### Paso 2: crea una "instance" de `FastAPI`
|
||||
### Paso 2: crea una "instance" de `FastAPI` { #step-2-create-a-fastapi-instance }
|
||||
|
||||
{* ../../docs_src/first_steps/tutorial001.py hl[3] *}
|
||||
{* ../../docs_src/first_steps/tutorial001_py39.py hl[3] *}
|
||||
|
||||
Aquí la variable `app` será una "instance" de la clase `FastAPI`.
|
||||
|
||||
Este será el punto principal de interacción para crear toda tu API.
|
||||
|
||||
### Paso 3: crea una *path operation*
|
||||
### Paso 3: crea una *path operation* { #step-3-create-a-path-operation }
|
||||
|
||||
#### Path
|
||||
#### Path { #path }
|
||||
|
||||
"Path" aquí se refiere a la última parte de la URL empezando desde la primera `/`.
|
||||
|
||||
@@ -193,7 +221,7 @@ https://example.com/items/foo
|
||||
/items/foo
|
||||
```
|
||||
|
||||
/// info
|
||||
/// info | Información
|
||||
|
||||
Un "path" también es comúnmente llamado "endpoint" o "ruta".
|
||||
|
||||
@@ -201,7 +229,7 @@ Un "path" también es comúnmente llamado "endpoint" o "ruta".
|
||||
|
||||
Mientras construyes una API, el "path" es la forma principal de separar "concerns" y "resources".
|
||||
|
||||
#### Operación
|
||||
#### Operación { #operation }
|
||||
|
||||
"Operación" aquí se refiere a uno de los "métodos" HTTP.
|
||||
|
||||
@@ -236,9 +264,9 @@ Así que, en OpenAPI, cada uno de los métodos HTTP se llama una "operation".
|
||||
|
||||
Vamos a llamarlas "**operaciones**" también.
|
||||
|
||||
#### Define un *path operation decorator*
|
||||
#### Define un *path operation decorator* { #define-a-path-operation-decorator }
|
||||
|
||||
{* ../../docs_src/first_steps/tutorial001.py hl[6] *}
|
||||
{* ../../docs_src/first_steps/tutorial001_py39.py hl[6] *}
|
||||
|
||||
El `@app.get("/")` le dice a **FastAPI** que la función justo debajo se encarga de manejar requests que vayan a:
|
||||
|
||||
@@ -272,7 +300,7 @@ Y los más exóticos:
|
||||
* `@app.patch()`
|
||||
* `@app.trace()`
|
||||
|
||||
/// tip
|
||||
/// tip | Consejo
|
||||
|
||||
Eres libre de usar cada operación (método HTTP) como quieras.
|
||||
|
||||
@@ -284,7 +312,7 @@ Por ejemplo, cuando usas GraphQL normalmente realizas todas las acciones usando
|
||||
|
||||
///
|
||||
|
||||
### Paso 4: define la **path operation function**
|
||||
### Paso 4: define la **path operation function** { #step-4-define-the-path-operation-function }
|
||||
|
||||
Esta es nuestra "**path operation function**":
|
||||
|
||||
@@ -292,7 +320,7 @@ Esta es nuestra "**path operation function**":
|
||||
* **operation**: es `get`.
|
||||
* **function**: es la función debajo del "decorador" (debajo de `@app.get("/")`).
|
||||
|
||||
{* ../../docs_src/first_steps/tutorial001.py hl[7] *}
|
||||
{* ../../docs_src/first_steps/tutorial001_py39.py hl[7] *}
|
||||
|
||||
Esta es una función de Python.
|
||||
|
||||
@@ -304,17 +332,17 @@ En este caso, es una función `async`.
|
||||
|
||||
También podrías definirla como una función normal en lugar de `async def`:
|
||||
|
||||
{* ../../docs_src/first_steps/tutorial003.py hl[7] *}
|
||||
{* ../../docs_src/first_steps/tutorial003_py39.py hl[7] *}
|
||||
|
||||
/// note | Nota
|
||||
|
||||
Si no sabes la diferencia, revisa la sección [Async: *"¿Tienes prisa?"*](../async.md#in-a-hurry){.internal-link target=_blank}.
|
||||
Si no sabes la diferencia, Revisa la sección [Async: *"¿Tienes prisa?"*](../async.md#in-a-hurry){.internal-link target=_blank}.
|
||||
|
||||
///
|
||||
|
||||
### Paso 5: retorna el contenido
|
||||
### Paso 5: retorna el contenido { #step-5-return-the-content }
|
||||
|
||||
{* ../../docs_src/first_steps/tutorial001.py hl[8] *}
|
||||
{* ../../docs_src/first_steps/tutorial001_py39.py hl[8] *}
|
||||
|
||||
Puedes retornar un `dict`, `list`, valores singulares como `str`, `int`, etc.
|
||||
|
||||
@@ -322,10 +350,31 @@ También puedes retornar modelos de Pydantic (verás más sobre eso más adelant
|
||||
|
||||
Hay muchos otros objetos y modelos que serán automáticamente convertidos a JSON (incluyendo ORMs, etc). Intenta usar tus favoritos, es altamente probable que ya sean compatibles.
|
||||
|
||||
## Recapitulación
|
||||
### Paso 6: Despliégalo { #step-6-deploy-it }
|
||||
|
||||
Despliega tu app en **<a href="https://fastapicloud.com" class="external-link" target="_blank">FastAPI Cloud</a>** con un solo comando: `fastapi deploy`. 🎉
|
||||
|
||||
#### Sobre FastAPI Cloud { #about-fastapi-cloud }
|
||||
|
||||
**<a href="https://fastapicloud.com" class="external-link" target="_blank">FastAPI Cloud</a>** está construido por el mismo autor y equipo detrás de **FastAPI**.
|
||||
|
||||
Agiliza el proceso de **construir**, **desplegar** y **acceder** a una API con el mínimo esfuerzo.
|
||||
|
||||
Trae la misma **experiencia de desarrollador** de construir apps con FastAPI a **desplegarlas** en la nube. 🎉
|
||||
|
||||
FastAPI Cloud es el sponsor principal y proveedor de financiación para los proyectos open source de *FastAPI and friends*. ✨
|
||||
|
||||
#### Despliega en otros proveedores cloud { #deploy-to-other-cloud-providers }
|
||||
|
||||
FastAPI es open source y basado en estándares. Puedes desplegar apps de FastAPI en cualquier proveedor cloud que elijas.
|
||||
|
||||
Sigue las guías de tu proveedor cloud para desplegar apps de FastAPI con ellos. 🤓
|
||||
|
||||
## Recapitulación { #recap }
|
||||
|
||||
* Importa `FastAPI`.
|
||||
* Crea una instancia `app`.
|
||||
* Crea una instance `app`.
|
||||
* Escribe un **path operation decorator** usando decoradores como `@app.get("/")`.
|
||||
* Define una **path operation function**; por ejemplo, `def root(): ...`.
|
||||
* Ejecuta el servidor de desarrollo usando el comando `fastapi dev`.
|
||||
* Opcionalmente, despliega tu app con `fastapi deploy`.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Manejo de Errores
|
||||
# Manejo de Errores { #handling-errors }
|
||||
|
||||
Existen muchas situaciones en las que necesitas notificar un error a un cliente que está usando tu API.
|
||||
|
||||
@@ -19,15 +19,15 @@ Los códigos de estado en el rango de 400 significan que hubo un error por parte
|
||||
|
||||
¿Recuerdas todos esos errores de **"404 Not Found"** (y chistes)?
|
||||
|
||||
## Usa `HTTPException`
|
||||
## Usa `HTTPException` { #use-httpexception }
|
||||
|
||||
Para devolver responses HTTP con errores al cliente, usa `HTTPException`.
|
||||
|
||||
### Importa `HTTPException`
|
||||
### Importa `HTTPException` { #import-httpexception }
|
||||
|
||||
{* ../../docs_src/handling_errors/tutorial001.py hl[1] *}
|
||||
{* ../../docs_src/handling_errors/tutorial001_py39.py hl[1] *}
|
||||
|
||||
### Lanza un `HTTPException` en tu código
|
||||
### Lanza un `HTTPException` en tu código { #raise-an-httpexception-in-your-code }
|
||||
|
||||
`HTTPException` es una excepción de Python normal con datos adicionales relevantes para APIs.
|
||||
|
||||
@@ -39,9 +39,9 @@ El beneficio de lanzar una excepción en lugar de `return`ar un valor será más
|
||||
|
||||
En este ejemplo, cuando el cliente solicita un ítem por un ID que no existe, lanza una excepción con un código de estado de `404`:
|
||||
|
||||
{* ../../docs_src/handling_errors/tutorial001.py hl[11] *}
|
||||
{* ../../docs_src/handling_errors/tutorial001_py39.py hl[11] *}
|
||||
|
||||
### El response resultante
|
||||
### El response resultante { #the-resulting-response }
|
||||
|
||||
Si el cliente solicita `http://example.com/items/foo` (un `item_id` `"foo"`), ese cliente recibirá un código de estado HTTP de 200, y un response JSON de:
|
||||
|
||||
@@ -69,7 +69,7 @@ Son manejados automáticamente por **FastAPI** y convertidos a JSON.
|
||||
|
||||
///
|
||||
|
||||
## Agrega headers personalizados
|
||||
## Agrega headers personalizados { #add-custom-headers }
|
||||
|
||||
Existen algunas situaciones en las que es útil poder agregar headers personalizados al error HTTP. Por ejemplo, para algunos tipos de seguridad.
|
||||
|
||||
@@ -77,19 +77,19 @@ Probablemente no necesitarás usarlos directamente en tu código.
|
||||
|
||||
Pero en caso de que los necesites para un escenario avanzado, puedes agregar headers personalizados:
|
||||
|
||||
{* ../../docs_src/handling_errors/tutorial002.py hl[14] *}
|
||||
{* ../../docs_src/handling_errors/tutorial002_py39.py hl[14] *}
|
||||
|
||||
## Instalar manejadores de excepciones personalizados
|
||||
## Instalar manejadores de excepciones personalizados { #install-custom-exception-handlers }
|
||||
|
||||
Puedes agregar manejadores de excepciones personalizados con <a href="https://www.starlette.dev/exceptions/" class="external-link" target="_blank">las mismas utilidades de excepciones de Starlette</a>.
|
||||
|
||||
Supongamos que tienes una excepción personalizada `UnicornException` que tú (o un paquete que usas) podría lanzar.
|
||||
Supongamos que tienes una excepción personalizada `UnicornException` que tú (o un paquete que usas) podrías lanzar.
|
||||
|
||||
Y quieres manejar esta excepción globalmente con FastAPI.
|
||||
|
||||
Podrías agregar un manejador de excepciones personalizado con `@app.exception_handler()`:
|
||||
|
||||
{* ../../docs_src/handling_errors/tutorial003.py hl[5:7,13:18,24] *}
|
||||
{* ../../docs_src/handling_errors/tutorial003_py39.py hl[5:7,13:18,24] *}
|
||||
|
||||
Aquí, si solicitas `/unicorns/yolo`, la *path operation* lanzará un `UnicornException`.
|
||||
|
||||
@@ -109,7 +109,7 @@ También podrías usar `from starlette.requests import Request` y `from starlett
|
||||
|
||||
///
|
||||
|
||||
## Sobrescribir los manejadores de excepciones predeterminados
|
||||
## Sobrescribir los manejadores de excepciones predeterminados { #override-the-default-exception-handlers }
|
||||
|
||||
**FastAPI** tiene algunos manejadores de excepciones predeterminados.
|
||||
|
||||
@@ -117,7 +117,7 @@ Estos manejadores se encargan de devolver los responses JSON predeterminadas cua
|
||||
|
||||
Puedes sobrescribir estos manejadores de excepciones con los tuyos propios.
|
||||
|
||||
### Sobrescribir excepciones de validación de request
|
||||
### Sobrescribir excepciones de validación de request { #override-request-validation-exceptions }
|
||||
|
||||
Cuando un request contiene datos inválidos, **FastAPI** lanza internamente un `RequestValidationError`.
|
||||
|
||||
@@ -127,7 +127,7 @@ Para sobrescribirlo, importa el `RequestValidationError` y úsalo con `@app.exce
|
||||
|
||||
El manejador de excepciones recibirá un `Request` y la excepción.
|
||||
|
||||
{* ../../docs_src/handling_errors/tutorial004.py hl[2,14:16] *}
|
||||
{* ../../docs_src/handling_errors/tutorial004_py39.py hl[2,14:19] *}
|
||||
|
||||
Ahora, si vas a `/items/foo`, en lugar de obtener el error JSON por defecto con:
|
||||
|
||||
@@ -149,36 +149,17 @@ Ahora, si vas a `/items/foo`, en lugar de obtener el error JSON por defecto con:
|
||||
obtendrás una versión en texto, con:
|
||||
|
||||
```
|
||||
1 validation error
|
||||
path -> item_id
|
||||
value is not a valid integer (type=type_error.integer)
|
||||
Validation errors:
|
||||
Field: ('path', 'item_id'), Error: Input should be a valid integer, unable to parse string as an integer
|
||||
```
|
||||
|
||||
#### `RequestValidationError` vs `ValidationError`
|
||||
|
||||
/// warning | Advertencia
|
||||
|
||||
Estos son detalles técnicos que podrías omitir si no es importante para ti en este momento.
|
||||
|
||||
///
|
||||
|
||||
`RequestValidationError` es una subclase de <a href="https://docs.pydantic.dev/latest/concepts/models/#error-handling" class="external-link" target="_blank">`ValidationError`</a> de Pydantic.
|
||||
|
||||
**FastAPI** la usa para que, si usas un modelo Pydantic en `response_model`, y tus datos tienen un error, lo verás en tu log.
|
||||
|
||||
Pero el cliente/usuario no lo verá. En su lugar, el cliente recibirá un "Error Interno del Servidor" con un código de estado HTTP `500`.
|
||||
|
||||
Debería ser así porque si tienes un `ValidationError` de Pydantic en tu *response* o en cualquier lugar de tu código (no en el *request* del cliente), en realidad es un bug en tu código.
|
||||
|
||||
Y mientras lo arreglas, tus clientes/usuarios no deberían tener acceso a información interna sobre el error, ya que eso podría exponer una vulnerabilidad de seguridad.
|
||||
|
||||
### Sobrescribir el manejador de errores de `HTTPException`
|
||||
### Sobrescribir el manejador de errores de `HTTPException` { #override-the-httpexception-error-handler }
|
||||
|
||||
De la misma manera, puedes sobrescribir el manejador de `HTTPException`.
|
||||
|
||||
Por ejemplo, podrías querer devolver un response de texto plano en lugar de JSON para estos errores:
|
||||
|
||||
{* ../../docs_src/handling_errors/tutorial004.py hl[3:4,9:11,22] *}
|
||||
{* ../../docs_src/handling_errors/tutorial004_py39.py hl[3:4,9:11,25] *}
|
||||
|
||||
/// note | Nota Técnica
|
||||
|
||||
@@ -188,13 +169,21 @@ También podrías usar `from starlette.responses import PlainTextResponse`.
|
||||
|
||||
///
|
||||
|
||||
### Usar el body de `RequestValidationError`
|
||||
/// warning | Advertencia
|
||||
|
||||
Ten en cuenta que `RequestValidationError` contiene la información del nombre de archivo y la línea donde ocurre el error de validación, para que puedas mostrarla en tus logs con la información relevante si quieres.
|
||||
|
||||
Pero eso significa que si simplemente lo conviertes a un string y devuelves esa información directamente, podrías estar filtrando un poquito de información sobre tu sistema, por eso aquí el código extrae y muestra cada error de forma independiente.
|
||||
|
||||
///
|
||||
|
||||
### Usar el body de `RequestValidationError` { #use-the-requestvalidationerror-body }
|
||||
|
||||
El `RequestValidationError` contiene el `body` que recibió con datos inválidos.
|
||||
|
||||
Podrías usarlo mientras desarrollas tu aplicación para registrar el body y depurarlo, devolverlo al usuario, etc.
|
||||
|
||||
{* ../../docs_src/handling_errors/tutorial005.py hl[14] *}
|
||||
{* ../../docs_src/handling_errors/tutorial005_py39.py hl[14] *}
|
||||
|
||||
Ahora intenta enviar un ítem inválido como:
|
||||
|
||||
@@ -226,7 +215,7 @@ Recibirás un response que te dirá que los datos son inválidos conteniendo el
|
||||
}
|
||||
```
|
||||
|
||||
#### `HTTPException` de FastAPI vs `HTTPException` de Starlette
|
||||
#### `HTTPException` de FastAPI vs `HTTPException` de Starlette { #fastapis-httpexception-vs-starlettes-httpexception }
|
||||
|
||||
**FastAPI** tiene su propio `HTTPException`.
|
||||
|
||||
@@ -238,7 +227,7 @@ Así que puedes seguir lanzando un `HTTPException` de **FastAPI** como de costum
|
||||
|
||||
Pero cuando registras un manejador de excepciones, deberías registrarlo para el `HTTPException` de Starlette.
|
||||
|
||||
De esta manera, si alguna parte del código interno de Starlette, o una extensión o complemento de Starlette, lanza un `HTTPException` de Starlette, tu manejador podrá capturarlo y manejarlo.
|
||||
De esta manera, si alguna parte del código interno de Starlette, o una extensión o plug-in de Starlette, lanza un `HTTPException` de Starlette, tu manejador podrá capturarlo y manejarlo.
|
||||
|
||||
En este ejemplo, para poder tener ambos `HTTPException` en el mismo código, las excepciones de Starlette son renombradas a `StarletteHTTPException`:
|
||||
|
||||
@@ -246,10 +235,10 @@ En este ejemplo, para poder tener ambos `HTTPException` en el mismo código, las
|
||||
from starlette.exceptions import HTTPException as StarletteHTTPException
|
||||
```
|
||||
|
||||
### Reutilizar los manejadores de excepciones de **FastAPI**
|
||||
### Reutilizar los manejadores de excepciones de **FastAPI** { #reuse-fastapis-exception-handlers }
|
||||
|
||||
Si quieres usar la excepción junto con los mismos manejadores de excepciones predeterminados de **FastAPI**, puedes importar y reutilizar los manejadores de excepciones predeterminados de `fastapi.exception_handlers`:
|
||||
|
||||
{* ../../docs_src/handling_errors/tutorial006.py hl[2:5,15,21] *}
|
||||
{* ../../docs_src/handling_errors/tutorial006_py39.py hl[2:5,15,21] *}
|
||||
|
||||
En este ejemplo solo estás `print`eando el error con un mensaje muy expresivo, pero te haces una idea. Puedes usar la excepción y luego simplemente reutilizar los manejadores de excepciones predeterminados.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Modelos de Parámetros de Header
|
||||
# Modelos de Parámetros de Header { #header-parameter-models }
|
||||
|
||||
Si tienes un grupo de **parámetros de header** relacionados, puedes crear un **modelo Pydantic** para declararlos.
|
||||
|
||||
@@ -10,7 +10,7 @@ Esto es compatible desde la versión `0.115.0` de FastAPI. 🤓
|
||||
|
||||
///
|
||||
|
||||
## Parámetros de Header con un Modelo Pydantic
|
||||
## Parámetros de Header con un Modelo Pydantic { #header-parameters-with-a-pydantic-model }
|
||||
|
||||
Declara los **parámetros de header** que necesitas en un **modelo Pydantic**, y luego declara el parámetro como `Header`:
|
||||
|
||||
@@ -18,7 +18,7 @@ Declara los **parámetros de header** que necesitas en un **modelo Pydantic**, y
|
||||
|
||||
**FastAPI** **extraerá** los datos para **cada campo** de los **headers** en el request y te dará el modelo Pydantic que definiste.
|
||||
|
||||
## Revisa la Documentación
|
||||
## Revisa la Documentación { #check-the-docs }
|
||||
|
||||
Puedes ver los headers requeridos en la interfaz de documentación en `/docs`:
|
||||
|
||||
@@ -26,7 +26,7 @@ Puedes ver los headers requeridos en la interfaz de documentación en `/docs`:
|
||||
<img src="/img/tutorial/header-param-models/image01.png">
|
||||
</div>
|
||||
|
||||
## Prohibir Headers Extra
|
||||
## Prohibir Headers Extra { #forbid-extra-headers }
|
||||
|
||||
En algunos casos de uso especiales (probablemente no muy comunes), podrías querer **restringir** los headers que deseas recibir.
|
||||
|
||||
@@ -51,6 +51,22 @@ Por ejemplo, si el cliente intenta enviar un header `tool` con un valor de `plum
|
||||
}
|
||||
```
|
||||
|
||||
## Resumen
|
||||
## Desactivar la conversión de guiones bajos { #disable-convert-underscores }
|
||||
|
||||
De la misma forma que con los parámetros de header normales, cuando tienes caracteres de guion bajo en los nombres de los parámetros, se **convierten automáticamente en guiones**.
|
||||
|
||||
Por ejemplo, si tienes un parámetro de header `save_data` en el código, el header HTTP esperado será `save-data`, y aparecerá así en la documentación.
|
||||
|
||||
Si por alguna razón necesitas desactivar esta conversión automática, también puedes hacerlo para los modelos Pydantic de parámetros de header.
|
||||
|
||||
{* ../../docs_src/header_param_models/tutorial003_an_py310.py hl[19] *}
|
||||
|
||||
/// warning | Advertencia
|
||||
|
||||
Antes de establecer `convert_underscores` a `False`, ten en cuenta que algunos proxies y servidores HTTP no permiten el uso de headers con guiones bajos.
|
||||
|
||||
///
|
||||
|
||||
## Resumen { #summary }
|
||||
|
||||
Puedes usar **modelos Pydantic** para declarar **headers** en **FastAPI**. 😎
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
# Parámetros de Header
|
||||
# Parámetros de Header { #header-parameters }
|
||||
|
||||
Puedes definir los parámetros de Header de la misma manera que defines los parámetros de `Query`, `Path` y `Cookie`.
|
||||
|
||||
## Importar `Header`
|
||||
## Importar `Header` { #import-header }
|
||||
|
||||
Primero importa `Header`:
|
||||
|
||||
{* ../../docs_src/header_params/tutorial001_an_py310.py hl[3] *}
|
||||
|
||||
## Declarar parámetros de `Header`
|
||||
## Declarar parámetros de `Header` { #declare-header-parameters }
|
||||
|
||||
Luego declara los parámetros de header usando la misma estructura que con `Path`, `Query` y `Cookie`.
|
||||
|
||||
@@ -30,7 +30,7 @@ Para declarar headers, necesitas usar `Header`, porque de otra forma los paráme
|
||||
|
||||
///
|
||||
|
||||
## Conversión automática
|
||||
## Conversión automática { #automatic-conversion }
|
||||
|
||||
`Header` tiene un poquito de funcionalidad extra además de lo que proporcionan `Path`, `Query` y `Cookie`.
|
||||
|
||||
@@ -54,7 +54,7 @@ Antes de establecer `convert_underscores` a `False`, ten en cuenta que algunos p
|
||||
|
||||
///
|
||||
|
||||
## Headers duplicados
|
||||
## Headers duplicados { #duplicate-headers }
|
||||
|
||||
Es posible recibir headers duplicados. Eso significa, el mismo header con múltiples valores.
|
||||
|
||||
@@ -84,7 +84,7 @@ El response sería como:
|
||||
}
|
||||
```
|
||||
|
||||
## Recapitulación
|
||||
## Recapitulación { #recap }
|
||||
|
||||
Declara headers con `Header`, usando el mismo patrón común que `Query`, `Path` y `Cookie`.
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Tutorial - Guía del Usuario
|
||||
# Tutorial - Guía del Usuario { #tutorial-user-guide }
|
||||
|
||||
Este tutorial te muestra cómo usar **FastAPI** con la mayoría de sus funcionalidades, paso a paso.
|
||||
|
||||
@@ -6,7 +6,7 @@ Cada sección se basa gradualmente en las anteriores, pero está estructurada pa
|
||||
|
||||
También está diseñado para funcionar como una referencia futura para que puedas volver y ver exactamente lo que necesitas.
|
||||
|
||||
## Ejecuta el código
|
||||
## Ejecuta el código { #run-the-code }
|
||||
|
||||
Todos los bloques de código pueden ser copiados y usados directamente (de hecho, son archivos Python probados).
|
||||
|
||||
@@ -15,48 +15,39 @@ Para ejecutar cualquiera de los ejemplos, copia el código a un archivo `main.py
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ <font color="#4E9A06">fastapi</font> dev <u style="text-decoration-style:single">main.py</u>
|
||||
<font color="#3465A4">INFO </font> Using path <font color="#3465A4">main.py</font>
|
||||
<font color="#3465A4">INFO </font> Resolved absolute path <font color="#75507B">/home/user/code/awesomeapp/</font><font color="#AD7FA8">main.py</font>
|
||||
<font color="#3465A4">INFO </font> Searching for package file structure from directories with <font color="#3465A4">__init__.py</font> files
|
||||
<font color="#3465A4">INFO </font> Importing from <font color="#75507B">/home/user/code/</font><font color="#AD7FA8">awesomeapp</font>
|
||||
$ <font color="#4E9A06">fastapi</font> dev <u style="text-decoration-style:solid">main.py</u>
|
||||
|
||||
╭─ <font color="#8AE234"><b>Python module file</b></font> ─╮
|
||||
│ │
|
||||
│ 🐍 main.py │
|
||||
│ │
|
||||
╰──────────────────────╯
|
||||
<span style="background-color:#009485"><font color="#D3D7CF"> FastAPI </font></span> Starting development server 🚀
|
||||
|
||||
<font color="#3465A4">INFO </font> Importing module <font color="#4E9A06">main</font>
|
||||
<font color="#3465A4">INFO </font> Found importable FastAPI app
|
||||
Searching for package file structure from directories
|
||||
with <font color="#3465A4">__init__.py</font> files
|
||||
Importing from <font color="#75507B">/home/user/code/</font><font color="#AD7FA8">awesomeapp</font>
|
||||
|
||||
╭─ <font color="#8AE234"><b>Importable FastAPI app</b></font> ─╮
|
||||
│ │
|
||||
│ <span style="background-color:#272822"><font color="#FF4689">from</font></span><span style="background-color:#272822"><font color="#F8F8F2"> main </font></span><span style="background-color:#272822"><font color="#FF4689">import</font></span><span style="background-color:#272822"><font color="#F8F8F2"> app</font></span><span style="background-color:#272822"> </span> │
|
||||
│ │
|
||||
╰──────────────────────────╯
|
||||
<span style="background-color:#007166"><font color="#D3D7CF"> module </font></span> 🐍 main.py
|
||||
|
||||
<font color="#3465A4">INFO </font> Using import string <font color="#8AE234"><b>main:app</b></font>
|
||||
<span style="background-color:#007166"><font color="#D3D7CF"> code </font></span> Importing the FastAPI app object from the module with
|
||||
the following code:
|
||||
|
||||
<span style="background-color:#C4A000"><font color="#2E3436">╭────────── FastAPI CLI - Development mode ───────────╮</font></span>
|
||||
<span style="background-color:#C4A000"><font color="#2E3436">│ │</font></span>
|
||||
<span style="background-color:#C4A000"><font color="#2E3436">│ Serving at: http://127.0.0.1:8000 │</font></span>
|
||||
<span style="background-color:#C4A000"><font color="#2E3436">│ │</font></span>
|
||||
<span style="background-color:#C4A000"><font color="#2E3436">│ API docs: http://127.0.0.1:8000/docs │</font></span>
|
||||
<span style="background-color:#C4A000"><font color="#2E3436">│ │</font></span>
|
||||
<span style="background-color:#C4A000"><font color="#2E3436">│ Running in development mode, for production use: │</font></span>
|
||||
<span style="background-color:#C4A000"><font color="#2E3436">│ │</font></span>
|
||||
<span style="background-color:#C4A000"><font color="#2E3436">│ </font></span><span style="background-color:#C4A000"><font color="#555753"><b>fastapi run</b></font></span><span style="background-color:#C4A000"><font color="#2E3436"> │</font></span>
|
||||
<span style="background-color:#C4A000"><font color="#2E3436">│ │</font></span>
|
||||
<span style="background-color:#C4A000"><font color="#2E3436">╰─────────────────────────────────────────────────────╯</font></span>
|
||||
<u style="text-decoration-style:solid">from </u><u style="text-decoration-style:solid"><b>main</b></u><u style="text-decoration-style:solid"> import </u><u style="text-decoration-style:solid"><b>app</b></u>
|
||||
|
||||
<font color="#4E9A06">INFO</font>: Will watch for changes in these directories: ['/home/user/code/awesomeapp']
|
||||
<font color="#4E9A06">INFO</font>: Uvicorn running on <b>http://127.0.0.1:8000</b> (Press CTRL+C to quit)
|
||||
<font color="#4E9A06">INFO</font>: Started reloader process [<font color="#34E2E2"><b>2265862</b></font>] using <font color="#34E2E2"><b>WatchFiles</b></font>
|
||||
<font color="#4E9A06">INFO</font>: Started server process [<font color="#06989A">2265873</font>]
|
||||
<font color="#4E9A06">INFO</font>: Waiting for application startup.
|
||||
<font color="#4E9A06">INFO</font>: Application startup complete.
|
||||
</pre>
|
||||
<span style="background-color:#007166"><font color="#D3D7CF"> app </font></span> Using import string: <font color="#3465A4">main:app</font>
|
||||
|
||||
<span style="background-color:#007166"><font color="#D3D7CF"> server </font></span> Server started at <font color="#729FCF"><u style="text-decoration-style:solid">http://127.0.0.1:8000</u></font>
|
||||
<span style="background-color:#007166"><font color="#D3D7CF"> server </font></span> Documentation at <font color="#729FCF"><u style="text-decoration-style:solid">http://127.0.0.1:8000/docs</u></font>
|
||||
|
||||
<span style="background-color:#007166"><font color="#D3D7CF"> tip </font></span> Running in development mode, for production use:
|
||||
<b>fastapi run</b>
|
||||
|
||||
Logs:
|
||||
|
||||
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Will watch for changes in these directories:
|
||||
<b>[</b><font color="#4E9A06">'/home/user/code/awesomeapp'</font><b>]</b>
|
||||
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Uvicorn running on <font color="#729FCF"><u style="text-decoration-style:solid">http://127.0.0.1:8000</u></font> <b>(</b>Press CTRL+C
|
||||
to quit<b>)</b>
|
||||
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Started reloader process <b>[</b><font color="#34E2E2"><b>383138</b></font><b>]</b> using WatchFiles
|
||||
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Started server process <b>[</b><font color="#34E2E2"><b>383153</b></font><b>]</b>
|
||||
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Waiting for application startup.
|
||||
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Application startup complete.
|
||||
```
|
||||
|
||||
</div>
|
||||
@@ -67,7 +58,7 @@ Usarlo en tu editor es lo que realmente te muestra los beneficios de FastAPI, al
|
||||
|
||||
---
|
||||
|
||||
## Instalar FastAPI
|
||||
## Instalar FastAPI { #install-fastapi }
|
||||
|
||||
El primer paso es instalar FastAPI.
|
||||
|
||||
@@ -85,13 +76,15 @@ $ pip install "fastapi[standard]"
|
||||
|
||||
/// note | Nota
|
||||
|
||||
Cuando instalas con `pip install "fastapi[standard]"` viene con algunas dependencias opcionales estándar por defecto.
|
||||
Cuando instalas con `pip install "fastapi[standard]"` viene con algunas dependencias opcionales estándar por defecto, incluyendo `fastapi-cloud-cli`, que te permite hacer deploy a <a href="https://fastapicloud.com" class="external-link" target="_blank">FastAPI Cloud</a>.
|
||||
|
||||
Si no quieres tener esas dependencias opcionales, en su lugar puedes instalar `pip install fastapi`.
|
||||
|
||||
Si quieres instalar las dependencias estándar pero sin `fastapi-cloud-cli`, puedes instalar con `pip install "fastapi[standard-no-fastapi-cloud-cli]"`.
|
||||
|
||||
///
|
||||
|
||||
## Guía Avanzada del Usuario
|
||||
## Guía Avanzada del Usuario { #advanced-user-guide }
|
||||
|
||||
También hay una **Guía Avanzada del Usuario** que puedes leer después de esta **Tutorial - Guía del Usuario**.
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# Metadata y URLs de Docs
|
||||
# Metadata y URLs de Docs { #metadata-and-docs-urls }
|
||||
|
||||
Puedes personalizar varias configuraciones de metadata en tu aplicación **FastAPI**.
|
||||
|
||||
## Metadata para la API
|
||||
## Metadata para la API { #metadata-for-api }
|
||||
|
||||
Puedes establecer los siguientes campos que se usan en la especificación OpenAPI y en las interfaces automáticas de documentación de la API:
|
||||
|
||||
@@ -18,7 +18,7 @@ Puedes establecer los siguientes campos que se usan en la especificación OpenAP
|
||||
|
||||
Puedes configurarlos de la siguiente manera:
|
||||
|
||||
{* ../../docs_src/metadata/tutorial001.py hl[3:16, 19:32] *}
|
||||
{* ../../docs_src/metadata/tutorial001_py39.py hl[3:16, 19:32] *}
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
@@ -30,15 +30,15 @@ Con esta configuración, la documentación automática de la API se vería así:
|
||||
|
||||
<img src="/img/tutorial/metadata/image01.png">
|
||||
|
||||
## Identificador de licencia
|
||||
## Identificador de licencia { #license-identifier }
|
||||
|
||||
Desde OpenAPI 3.1.0 y FastAPI 0.99.0, también puedes establecer la `license_info` con un `identifier` en lugar de una `url`.
|
||||
|
||||
Por ejemplo:
|
||||
|
||||
{* ../../docs_src/metadata/tutorial001_1.py hl[31] *}
|
||||
{* ../../docs_src/metadata/tutorial001_1_py39.py hl[31] *}
|
||||
|
||||
## Metadata para etiquetas
|
||||
## Metadata para etiquetas { #metadata-for-tags }
|
||||
|
||||
También puedes agregar metadata adicional para las diferentes etiquetas usadas para agrupar tus path operations con el parámetro `openapi_tags`.
|
||||
|
||||
@@ -52,13 +52,13 @@ Cada diccionario puede contener:
|
||||
* `description`: un `str` con una breve descripción para la documentación externa.
|
||||
* `url` (**requerido**): un `str` con la URL para la documentación externa.
|
||||
|
||||
### Crear metadata para etiquetas
|
||||
### Crear metadata para etiquetas { #create-metadata-for-tags }
|
||||
|
||||
Probemos eso en un ejemplo con etiquetas para `users` y `items`.
|
||||
|
||||
Crea metadata para tus etiquetas y pásala al parámetro `openapi_tags`:
|
||||
|
||||
{* ../../docs_src/metadata/tutorial004.py hl[3:16,18] *}
|
||||
{* ../../docs_src/metadata/tutorial004_py39.py hl[3:16,18] *}
|
||||
|
||||
Nota que puedes utilizar Markdown dentro de las descripciones, por ejemplo "login" se mostrará en negrita (**login**) y "fancy" se mostrará en cursiva (_fancy_).
|
||||
|
||||
@@ -68,11 +68,11 @@ No tienes que agregar metadata para todas las etiquetas que uses.
|
||||
|
||||
///
|
||||
|
||||
### Usar tus etiquetas
|
||||
### Usar tus etiquetas { #use-your-tags }
|
||||
|
||||
Usa el parámetro `tags` con tus *path operations* (y `APIRouter`s) para asignarlas a diferentes etiquetas:
|
||||
|
||||
{* ../../docs_src/metadata/tutorial004.py hl[21,26] *}
|
||||
{* ../../docs_src/metadata/tutorial004_py39.py hl[21,26] *}
|
||||
|
||||
/// info | Información
|
||||
|
||||
@@ -80,19 +80,19 @@ Lee más sobre etiquetas en [Configuración de Path Operation](path-operation-co
|
||||
|
||||
///
|
||||
|
||||
### Revisa la documentación
|
||||
### Revisa la documentación { #check-the-docs }
|
||||
|
||||
Ahora, si revisas la documentación, mostrará toda la metadata adicional:
|
||||
|
||||
<img src="/img/tutorial/metadata/image02.png">
|
||||
|
||||
### Orden de las etiquetas
|
||||
### Orden de las etiquetas { #order-of-tags }
|
||||
|
||||
El orden de cada diccionario de metadata de etiqueta también define el orden mostrado en la interfaz de documentación.
|
||||
|
||||
Por ejemplo, aunque `users` iría después de `items` en orden alfabético, se muestra antes porque agregamos su metadata como el primer diccionario en la list.
|
||||
|
||||
## URL de OpenAPI
|
||||
## URL de OpenAPI { #openapi-url }
|
||||
|
||||
Por defecto, el esquema OpenAPI se sirve en `/openapi.json`.
|
||||
|
||||
@@ -100,11 +100,11 @@ Pero puedes configurarlo con el parámetro `openapi_url`.
|
||||
|
||||
Por ejemplo, para configurarlo para que se sirva en `/api/v1/openapi.json`:
|
||||
|
||||
{* ../../docs_src/metadata/tutorial002.py hl[3] *}
|
||||
{* ../../docs_src/metadata/tutorial002_py39.py hl[3] *}
|
||||
|
||||
Si quieres deshabilitar el esquema OpenAPI completamente, puedes establecer `openapi_url=None`, eso también deshabilitará las interfaces de usuario de documentación que lo usan.
|
||||
|
||||
## URLs de Docs
|
||||
## URLs de Docs { #docs-urls }
|
||||
|
||||
Puedes configurar las dos interfaces de usuario de documentación incluidas:
|
||||
|
||||
@@ -117,4 +117,4 @@ Puedes configurar las dos interfaces de usuario de documentación incluidas:
|
||||
|
||||
Por ejemplo, para configurar Swagger UI para que se sirva en `/documentation` y deshabilitar ReDoc:
|
||||
|
||||
{* ../../docs_src/metadata/tutorial003.py hl[3] *}
|
||||
{* ../../docs_src/metadata/tutorial003_py39.py hl[3] *}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Middleware
|
||||
# Middleware { #middleware }
|
||||
|
||||
Puedes añadir middleware a las aplicaciones de **FastAPI**.
|
||||
|
||||
@@ -15,11 +15,11 @@ Un "middleware" es una función que trabaja con cada **request** antes de que se
|
||||
|
||||
Si tienes dependencias con `yield`, el código de salida se ejecutará *después* del middleware.
|
||||
|
||||
Si hubiera alguna tarea en segundo plano (documentada más adelante), se ejecutará *después* de todo el middleware.
|
||||
Si hubiera tareas en segundo plano (cubiertas en la sección [Tareas en segundo plano](background-tasks.md){.internal-link target=_blank}, lo verás más adelante), se ejecutarán *después* de todo el middleware.
|
||||
|
||||
///
|
||||
|
||||
## Crear un middleware
|
||||
## Crear un middleware { #create-a-middleware }
|
||||
|
||||
Para crear un middleware usas el decorador `@app.middleware("http")` encima de una función.
|
||||
|
||||
@@ -31,11 +31,11 @@ La función middleware recibe:
|
||||
* Luego devuelve la `response` generada por la correspondiente *path operation*.
|
||||
* Puedes entonces modificar aún más la `response` antes de devolverla.
|
||||
|
||||
{* ../../docs_src/middleware/tutorial001.py hl[8:9,11,14] *}
|
||||
{* ../../docs_src/middleware/tutorial001_py39.py hl[8:9,11,14] *}
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
Ten en cuenta que los custom proprietary headers se pueden añadir <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers" class="external-link" target="_blank">usando el prefijo 'X-'</a>.
|
||||
Ten en cuenta que los custom proprietary headers se pueden añadir <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers" class="external-link" target="_blank">usando el prefijo `X-`</a>.
|
||||
|
||||
Pero si tienes custom headers que deseas que un cliente en un navegador pueda ver, necesitas añadirlos a tus configuraciones de CORS ([CORS (Cross-Origin Resource Sharing)](cors.md){.internal-link target=_blank}) usando el parámetro `expose_headers` documentado en <a href="https://www.starlette.dev/middleware/#corsmiddleware" class="external-link" target="_blank">la documentación de CORS de Starlette</a>.
|
||||
|
||||
@@ -49,7 +49,7 @@ También podrías usar `from starlette.requests import Request`.
|
||||
|
||||
///
|
||||
|
||||
### Antes y después de la `response`
|
||||
### Antes y después de la `response` { #before-and-after-the-response }
|
||||
|
||||
Puedes añadir código que se ejecute con la `request`, antes de que cualquier *path operation* la reciba.
|
||||
|
||||
@@ -57,7 +57,7 @@ Y también después de que se genere la `response`, antes de devolverla.
|
||||
|
||||
Por ejemplo, podrías añadir un custom header `X-Process-Time` que contenga el tiempo en segundos que tomó procesar la request y generar una response:
|
||||
|
||||
{* ../../docs_src/middleware/tutorial001.py hl[10,12:13] *}
|
||||
{* ../../docs_src/middleware/tutorial001_py39.py hl[10,12:13] *}
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
@@ -65,7 +65,30 @@ Aquí usamos <a href="https://docs.python.org/3/library/time.html#time.perf_coun
|
||||
|
||||
///
|
||||
|
||||
## Otros middlewares
|
||||
## Orden de ejecución con múltiples middlewares { #multiple-middleware-execution-order }
|
||||
|
||||
Cuando añades múltiples middlewares usando ya sea el decorador `@app.middleware()` o el método `app.add_middleware()`, cada nuevo middleware envuelve la aplicación, formando un stack. El último middleware añadido es el más externo, y el primero es el más interno.
|
||||
|
||||
En el camino de la request, el middleware más externo se ejecuta primero.
|
||||
|
||||
En el camino de la response, se ejecuta al final.
|
||||
|
||||
Por ejemplo:
|
||||
|
||||
```Python
|
||||
app.add_middleware(MiddlewareA)
|
||||
app.add_middleware(MiddlewareB)
|
||||
```
|
||||
|
||||
Esto da como resultado el siguiente orden de ejecución:
|
||||
|
||||
* **Request**: MiddlewareB → MiddlewareA → ruta
|
||||
|
||||
* **Response**: ruta → MiddlewareA → MiddlewareB
|
||||
|
||||
Este comportamiento de apilamiento asegura que los middlewares se ejecuten en un orden predecible y controlable.
|
||||
|
||||
## Otros middlewares { #other-middlewares }
|
||||
|
||||
Más adelante puedes leer sobre otros middlewares en la [Guía del Usuario Avanzado: Middleware Avanzado](../advanced/middleware.md){.internal-link target=_blank}.
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Configuración de Path Operation
|
||||
# Configuración de Path Operation { #path-operation-configuration }
|
||||
|
||||
Hay varios parámetros que puedes pasar a tu *path operation decorator* para configurarlo.
|
||||
|
||||
@@ -8,7 +8,7 @@ Ten en cuenta que estos parámetros se pasan directamente al *path operation dec
|
||||
|
||||
///
|
||||
|
||||
## Código de Estado del Response
|
||||
## Código de Estado del Response { #response-status-code }
|
||||
|
||||
Puedes definir el `status_code` (HTTP) que se utilizará en el response de tu *path operation*.
|
||||
|
||||
@@ -28,7 +28,7 @@ También podrías usar `from starlette import status`.
|
||||
|
||||
///
|
||||
|
||||
## Tags
|
||||
## Tags { #tags }
|
||||
|
||||
Puedes añadir tags a tu *path operation*, pasando el parámetro `tags` con un `list` de `str` (comúnmente solo una `str`):
|
||||
|
||||
@@ -38,7 +38,7 @@ Serán añadidas al esquema de OpenAPI y usadas por las interfaces de documentac
|
||||
|
||||
<img src="/img/tutorial/path-operation-configuration/image01.png">
|
||||
|
||||
### Tags con Enums
|
||||
### Tags con Enums { #tags-with-enums }
|
||||
|
||||
Si tienes una gran aplicación, podrías terminar acumulando **varias tags**, y querrías asegurarte de que siempre uses la **misma tag** para *path operations* relacionadas.
|
||||
|
||||
@@ -46,15 +46,15 @@ En estos casos, podría tener sentido almacenar las tags en un `Enum`.
|
||||
|
||||
**FastAPI** soporta eso de la misma manera que con strings normales:
|
||||
|
||||
{* ../../docs_src/path_operation_configuration/tutorial002b.py hl[1,8:10,13,18] *}
|
||||
{* ../../docs_src/path_operation_configuration/tutorial002b_py39.py hl[1,8:10,13,18] *}
|
||||
|
||||
## Resumen y Descripción
|
||||
## Resumen y Descripción { #summary-and-description }
|
||||
|
||||
Puedes añadir un `summary` y `description`:
|
||||
|
||||
{* ../../docs_src/path_operation_configuration/tutorial003_py310.py hl[18:19] *}
|
||||
|
||||
## Descripción desde docstring
|
||||
## Descripción desde docstring { #description-from-docstring }
|
||||
|
||||
Como las descripciones tienden a ser largas y cubrir múltiples líneas, puedes declarar la descripción de la *path operation* en la <abbr title="un string de múltiples líneas como la primera expresión dentro de una función (no asignada a ninguna variable) usada para documentación">docstring</abbr> de la función y **FastAPI** la leerá desde allí.
|
||||
|
||||
@@ -66,7 +66,7 @@ Será usado en la documentación interactiva:
|
||||
|
||||
<img src="/img/tutorial/path-operation-configuration/image02.png">
|
||||
|
||||
## Descripción del Response
|
||||
## Descripción del Response { #response-description }
|
||||
|
||||
Puedes especificar la descripción del response con el parámetro `response_description`:
|
||||
|
||||
@@ -88,11 +88,11 @@ Entonces, si no proporcionas una, **FastAPI** generará automáticamente una de
|
||||
|
||||
<img src="/img/tutorial/path-operation-configuration/image03.png">
|
||||
|
||||
## Deprecar una *path operation*
|
||||
## Deprecar una *path operation* { #deprecate-a-path-operation }
|
||||
|
||||
Si necesitas marcar una *path operation* como <abbr title="obsoleta, se recomienda no usarla">deprecated</abbr>, pero sin eliminarla, pasa el parámetro `deprecated`:
|
||||
|
||||
{* ../../docs_src/path_operation_configuration/tutorial006.py hl[16] *}
|
||||
{* ../../docs_src/path_operation_configuration/tutorial006_py39.py hl[16] *}
|
||||
|
||||
Se marcará claramente como deprecado en la documentación interactiva:
|
||||
|
||||
@@ -102,6 +102,6 @@ Revisa cómo lucen las *path operations* deprecadas y no deprecadas:
|
||||
|
||||
<img src="/img/tutorial/path-operation-configuration/image05.png">
|
||||
|
||||
## Resumen
|
||||
## Resumen { #recap }
|
||||
|
||||
Puedes configurar y añadir metadatos a tus *path operations* fácilmente pasando parámetros a los *path operation decorators*.
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# Parámetros de Path y Validaciones Numéricas
|
||||
# Parámetros de Path y Validaciones Numéricas { #path-parameters-and-numeric-validations }
|
||||
|
||||
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
|
||||
## Importar Path { #import-path }
|
||||
|
||||
Primero, importa `Path` de `fastapi`, e importa `Annotated`:
|
||||
|
||||
@@ -18,7 +18,7 @@ Asegúrate de [Actualizar la versión de FastAPI](../deployment/versions.md#upgr
|
||||
|
||||
///
|
||||
|
||||
## Declarar metadatos
|
||||
## Declarar metadatos { #declare-metadata }
|
||||
|
||||
Puedes declarar todos los mismos parámetros que para `Query`.
|
||||
|
||||
@@ -32,7 +32,7 @@ Un parámetro de path siempre es requerido ya que tiene que formar parte del pat
|
||||
|
||||
///
|
||||
|
||||
## Ordena los parámetros como necesites
|
||||
## Ordena los parámetros como necesites { #order-the-parameters-as-you-need }
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
@@ -54,13 +54,13 @@ No importa para **FastAPI**. Detectará los parámetros por sus nombres, tipos y
|
||||
|
||||
Así que puedes declarar tu función como:
|
||||
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial002.py hl[7] *}
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial002_py39.py hl[7] *}
|
||||
|
||||
Pero ten en cuenta que si usas `Annotated`, no tendrás este problema, no importará ya que no estás usando los valores por defecto de los parámetros de la función para `Query()` o `Path()`.
|
||||
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial002_an_py39.py *}
|
||||
|
||||
## Ordena los parámetros como necesites, trucos
|
||||
## Ordena los parámetros como necesites, trucos { #order-the-parameters-as-you-need-tricks }
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
@@ -83,15 +83,15 @@ Pasa `*`, como el primer parámetro de la función.
|
||||
|
||||
Python no hará nada con ese `*`, pero sabrá que todos los parámetros siguientes deben ser llamados como argumentos de palabras clave (parejas key-value), también conocidos como <abbr title="De: K-ey W-ord Arg-uments"><code>kwargs</code></abbr>. Incluso si no tienen un valor por defecto.
|
||||
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial003.py hl[7] *}
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial003_py39.py hl[7] *}
|
||||
|
||||
### Mejor con `Annotated`
|
||||
### Mejor con `Annotated` { #better-with-annotated }
|
||||
|
||||
Ten en cuenta que si usas `Annotated`, como no estás usando valores por defecto de los parámetros de la función, no tendrás este problema y probablemente no necesitarás usar `*`.
|
||||
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial003_an_py39.py hl[10] *}
|
||||
|
||||
## Validaciones numéricas: mayor o igual
|
||||
## Validaciones numéricas: mayor o igual { #number-validations-greater-than-or-equal }
|
||||
|
||||
Con `Query` y `Path` (y otros que verás más adelante) puedes declarar restricciones numéricas.
|
||||
|
||||
@@ -99,7 +99,7 @@ Aquí, con `ge=1`, `item_id` necesitará ser un número entero "`g`reater than o
|
||||
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial004_an_py39.py hl[10] *}
|
||||
|
||||
## Validaciones numéricas: mayor que y menor o igual
|
||||
## Validaciones numéricas: mayor que y menor o igual { #number-validations-greater-than-and-less-than-or-equal }
|
||||
|
||||
Lo mismo aplica para:
|
||||
|
||||
@@ -108,19 +108,19 @@ Lo mismo aplica para:
|
||||
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial005_an_py39.py hl[10] *}
|
||||
|
||||
## Validaciones numéricas: flotantes, mayor y menor
|
||||
## Validaciones numéricas: flotantes, mayor y menor { #number-validations-floats-greater-than-and-less-than }
|
||||
|
||||
Las validaciones numéricas también funcionan para valores `float`.
|
||||
|
||||
Aquí es donde se convierte en importante poder declarar <abbr title="greater than"><code>gt</code></abbr> y no solo <abbr title="greater than or equal"><code>ge</code></abbr>. Ya que con esto puedes requerir, por ejemplo, que un valor sea mayor que `0`, incluso si es menor que `1`.
|
||||
Aquí es donde se convierte en importante poder declarar <abbr title="greater than – mayor que"><code>gt</code></abbr> y no solo <abbr title="greater than or equal – mayor o igual que"><code>ge</code></abbr>. Ya que con esto puedes requerir, por ejemplo, que un valor sea mayor que `0`, incluso si es menor que `1`.
|
||||
|
||||
Así, `0.5` sería un valor válido. Pero `0.0` o `0` no lo serían.
|
||||
|
||||
Y lo mismo para <abbr title="less than"><code>lt</code></abbr>.
|
||||
Y lo mismo para <abbr title="less than – menor que"><code>lt</code></abbr>.
|
||||
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial006_an_py39.py hl[13] *}
|
||||
|
||||
## Resumen
|
||||
## Resumen { #recap }
|
||||
|
||||
Con `Query`, `Path` (y otros que aún no has visto) puedes declarar metadatos y validaciones de string de las mismas maneras que con [Parámetros de Query y Validaciones de String](query-params-str-validations.md){.internal-link target=_blank}.
|
||||
|
||||
@@ -139,7 +139,7 @@ Todas ellas comparten los mismos parámetros para validación adicional y metada
|
||||
|
||||
///
|
||||
|
||||
/// note | Nota técnica
|
||||
/// note | Detalles técnicos
|
||||
|
||||
Cuando importas `Query`, `Path` y otros de `fastapi`, en realidad son funciones.
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# Parámetros de Path
|
||||
# Parámetros de Path { #path-parameters }
|
||||
|
||||
Puedes declarar "parámetros" o "variables" de path con la misma sintaxis que se usa en los format strings de Python:
|
||||
|
||||
{* ../../docs_src/path_params/tutorial001.py hl[6:7] *}
|
||||
{* ../../docs_src/path_params/tutorial001_py39.py hl[6:7] *}
|
||||
|
||||
El valor del parámetro de path `item_id` se pasará a tu función como el argumento `item_id`.
|
||||
|
||||
@@ -12,11 +12,11 @@ Así que, si ejecutas este ejemplo y vas a <a href="http://127.0.0.1:8000/items/
|
||||
{"item_id":"foo"}
|
||||
```
|
||||
|
||||
## Parámetros de path con tipos
|
||||
## Parámetros de path con tipos { #path-parameters-with-types }
|
||||
|
||||
Puedes declarar el tipo de un parámetro de path en la función, usando anotaciones de tipos estándar de Python:
|
||||
|
||||
{* ../../docs_src/path_params/tutorial002.py hl[7] *}
|
||||
{* ../../docs_src/path_params/tutorial002_py39.py hl[7] *}
|
||||
|
||||
En este caso, `item_id` se declara como un `int`.
|
||||
|
||||
@@ -26,7 +26,7 @@ Esto te dará soporte del editor dentro de tu función, con chequeo de errores,
|
||||
|
||||
///
|
||||
|
||||
## Conversión de datos
|
||||
## <abbr title="también conocido como: serialización, parsing, marshalling">Conversión</abbr> de datos { #data-conversion }
|
||||
|
||||
Si ejecutas este ejemplo y abres tu navegador en <a href="http://127.0.0.1:8000/items/3" class="external-link" target="_blank">http://127.0.0.1:8000/items/3</a>, verás un response de:
|
||||
|
||||
@@ -38,11 +38,11 @@ Si ejecutas este ejemplo y abres tu navegador en <a href="http://127.0.0.1:8000/
|
||||
|
||||
Nota que el valor que tu función recibió (y devolvió) es `3`, como un `int` de Python, no un string `"3"`.
|
||||
|
||||
Entonces, con esa declaración de tipo, **FastAPI** te ofrece <abbr title="converting the string that comes from an HTTP request into Python data">"parsing"</abbr> automático de requests.
|
||||
Entonces, con esa declaración de tipo, **FastAPI** te ofrece <abbr title="convertir el string que viene de un request HTTP en datos de Python">"parsing"</abbr> automático de request.
|
||||
|
||||
///
|
||||
|
||||
## Validación de datos
|
||||
## Validación de datos { #data-validation }
|
||||
|
||||
Pero si vas al navegador en <a href="http://127.0.0.1:8000/items/foo" class="external-link" target="_blank">http://127.0.0.1:8000/items/foo</a>, verás un bonito error HTTP de:
|
||||
|
||||
@@ -76,7 +76,7 @@ Esto es increíblemente útil mientras desarrollas y depuras código que interac
|
||||
|
||||
///
|
||||
|
||||
## Documentación
|
||||
## Documentación { #documentation }
|
||||
|
||||
Y cuando abras tu navegador en <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>, verás una documentación de API automática e interactiva como:
|
||||
|
||||
@@ -90,7 +90,7 @@ Nota que el parámetro de path está declarado como un entero.
|
||||
|
||||
///
|
||||
|
||||
## Beneficios basados en estándares, documentación alternativa
|
||||
## Beneficios basados en estándares, documentación alternativa { #standards-based-benefits-alternative-documentation }
|
||||
|
||||
Y porque el esquema generado es del estándar <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md" class="external-link" target="_blank">OpenAPI</a>, hay muchas herramientas compatibles.
|
||||
|
||||
@@ -100,7 +100,7 @@ Debido a esto, el propio **FastAPI** proporciona una documentación de API alter
|
||||
|
||||
De la misma manera, hay muchas herramientas compatibles. Incluyendo herramientas de generación de código para muchos lenguajes.
|
||||
|
||||
## Pydantic
|
||||
## Pydantic { #pydantic }
|
||||
|
||||
Toda la validación de datos se realiza internamente con <a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic</a>, así que obtienes todos los beneficios de esta. Y sabes que estás en buenas manos.
|
||||
|
||||
@@ -108,7 +108,7 @@ Puedes usar las mismas declaraciones de tipo con `str`, `float`, `bool` y muchos
|
||||
|
||||
Varios de estos se exploran en los siguientes capítulos del tutorial.
|
||||
|
||||
## El orden importa
|
||||
## El orden importa { #order-matters }
|
||||
|
||||
Al crear *path operations*, puedes encontrarte en situaciones donde tienes un path fijo.
|
||||
|
||||
@@ -118,21 +118,21 @@ Y luego también puedes tener un path `/users/{user_id}` para obtener datos sobr
|
||||
|
||||
Debido a que las *path operations* se evalúan en orden, necesitas asegurarte de que el path para `/users/me` se declara antes que el de `/users/{user_id}`:
|
||||
|
||||
{* ../../docs_src/path_params/tutorial003.py hl[6,11] *}
|
||||
{* ../../docs_src/path_params/tutorial003_py39.py hl[6,11] *}
|
||||
|
||||
De lo contrario, el path para `/users/{user_id}` también coincidiría para `/users/me`, "pensando" que está recibiendo un parámetro `user_id` con un valor de `"me"`.
|
||||
|
||||
De manera similar, no puedes redefinir una path operation:
|
||||
|
||||
{* ../../docs_src/path_params/tutorial003b.py hl[6,11] *}
|
||||
{* ../../docs_src/path_params/tutorial003b_py39.py hl[6,11] *}
|
||||
|
||||
La primera siempre será utilizada ya que el path coincide primero.
|
||||
|
||||
## Valores predefinidos
|
||||
## Valores predefinidos { #predefined-values }
|
||||
|
||||
Si tienes una *path operation* que recibe un *path parameter*, pero quieres que los valores posibles válidos del *path parameter* estén predefinidos, puedes usar un <abbr title="Enumeration">`Enum`</abbr> estándar de Python.
|
||||
Si tienes una *path operation* que recibe un *path parameter*, pero quieres que los valores posibles válidos del *path parameter* estén predefinidos, puedes usar un <abbr title="Enumeration – Enumeración">`Enum`</abbr> estándar de Python.
|
||||
|
||||
### Crear una clase `Enum`
|
||||
### Crear una clase `Enum` { #create-an-enum-class }
|
||||
|
||||
Importa `Enum` y crea una subclase que herede de `str` y de `Enum`.
|
||||
|
||||
@@ -140,13 +140,7 @@ Al heredar de `str`, la documentación de la API podrá saber que los valores de
|
||||
|
||||
Luego crea atributos de clase con valores fijos, que serán los valores válidos disponibles:
|
||||
|
||||
{* ../../docs_src/path_params/tutorial005.py hl[1,6:9] *}
|
||||
|
||||
/// info | Información
|
||||
|
||||
<a href="https://docs.python.org/3/library/enum.html" class="external-link" target="_blank">Las enumeraciones (o enums) están disponibles en Python</a> desde la versión 3.4.
|
||||
|
||||
///
|
||||
{* ../../docs_src/path_params/tutorial005_py39.py hl[1,6:9] *}
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
@@ -154,33 +148,33 @@ Si te estás preguntando, "AlexNet", "ResNet" y "LeNet" son solo nombres de <abb
|
||||
|
||||
///
|
||||
|
||||
### Declarar un *path parameter*
|
||||
### Declarar un *path parameter* { #declare-a-path-parameter }
|
||||
|
||||
Luego crea un *path parameter* con una anotación de tipo usando la clase enum que creaste (`ModelName`):
|
||||
|
||||
{* ../../docs_src/path_params/tutorial005.py hl[16] *}
|
||||
{* ../../docs_src/path_params/tutorial005_py39.py hl[16] *}
|
||||
|
||||
### Revisa la documentación
|
||||
### Revisa la documentación { #check-the-docs }
|
||||
|
||||
Como los valores disponibles para el *path parameter* están predefinidos, la documentación interactiva puede mostrarlos de manera ordenada:
|
||||
|
||||
<img src="/img/tutorial/path-params/image03.png">
|
||||
|
||||
### Trabajando con *enumeraciones* de Python
|
||||
### Trabajando con *enumeraciones* de Python { #working-with-python-enumerations }
|
||||
|
||||
El valor del *path parameter* será un *miembro* de enumeración.
|
||||
|
||||
#### Comparar *miembros* de enumeraciones
|
||||
#### Comparar *miembros* de enumeraciones { #compare-enumeration-members }
|
||||
|
||||
Puedes compararlo con el *miembro* de enumeración en tu enum creada `ModelName`:
|
||||
|
||||
{* ../../docs_src/path_params/tutorial005.py hl[17] *}
|
||||
{* ../../docs_src/path_params/tutorial005_py39.py hl[17] *}
|
||||
|
||||
#### Obtener el valor de *enumeración*
|
||||
#### Obtener el valor de *enumeración* { #get-the-enumeration-value }
|
||||
|
||||
Puedes obtener el valor actual (un `str` en este caso) usando `model_name.value`, o en general, `your_enum_member.value`:
|
||||
|
||||
{* ../../docs_src/path_params/tutorial005.py hl[20] *}
|
||||
{* ../../docs_src/path_params/tutorial005_py39.py hl[20] *}
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
@@ -188,13 +182,13 @@ También podrías acceder al valor `"lenet"` con `ModelName.lenet.value`.
|
||||
|
||||
///
|
||||
|
||||
#### Devolver *miembros* de enumeración
|
||||
#### Devolver *miembros* de enumeración { #return-enumeration-members }
|
||||
|
||||
Puedes devolver *miembros de enum* desde tu *path operation*, incluso anidados en un cuerpo JSON (por ejemplo, un `dict`).
|
||||
|
||||
Serán convertidos a sus valores correspondientes (cadenas en este caso) antes de devolverlos al cliente:
|
||||
|
||||
{* ../../docs_src/path_params/tutorial005.py hl[18,21,23] *}
|
||||
{* ../../docs_src/path_params/tutorial005_py39.py hl[18,21,23] *}
|
||||
|
||||
En tu cliente recibirás un response JSON como:
|
||||
|
||||
@@ -205,7 +199,7 @@ En tu cliente recibirás un response JSON como:
|
||||
}
|
||||
```
|
||||
|
||||
## Parámetros de path conteniendo paths
|
||||
## Parámetros de path conteniendo paths { #path-parameters-containing-paths }
|
||||
|
||||
Imaginemos que tienes una *path operation* con un path `/files/{file_path}`.
|
||||
|
||||
@@ -213,7 +207,7 @@ Pero necesitas que `file_path` en sí mismo contenga un *path*, como `home/johnd
|
||||
|
||||
Entonces, la URL para ese archivo sería algo como: `/files/home/johndoe/myfile.txt`.
|
||||
|
||||
### Soporte de OpenAPI
|
||||
### Soporte de OpenAPI { #openapi-support }
|
||||
|
||||
OpenAPI no soporta una manera de declarar un *path parameter* para que contenga un *path* dentro, ya que eso podría llevar a escenarios que son difíciles de probar y definir.
|
||||
|
||||
@@ -221,7 +215,7 @@ Sin embargo, todavía puedes hacerlo en **FastAPI**, usando una de las herramien
|
||||
|
||||
Y la documentación seguiría funcionando, aunque no agregue ninguna documentación indicando que el parámetro debe contener un path.
|
||||
|
||||
### Convertidor de Path
|
||||
### Convertidor de Path { #path-convertor }
|
||||
|
||||
Usando una opción directamente de Starlette puedes declarar un *path parameter* conteniendo un *path* usando una URL como:
|
||||
|
||||
@@ -233,7 +227,7 @@ En este caso, el nombre del parámetro es `file_path`, y la última parte, `:pat
|
||||
|
||||
Así que, puedes usarlo con:
|
||||
|
||||
{* ../../docs_src/path_params/tutorial004.py hl[6] *}
|
||||
{* ../../docs_src/path_params/tutorial004_py39.py hl[6] *}
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
@@ -243,12 +237,12 @@ En ese caso, la URL sería: `/files//home/johndoe/myfile.txt`, con una doble bar
|
||||
|
||||
///
|
||||
|
||||
## Resumen
|
||||
## Resumen { #recap }
|
||||
|
||||
Con **FastAPI**, al usar declaraciones de tipo estándar de Python, cortas e intuitivas, obtienes:
|
||||
|
||||
* Soporte del editor: chequeo de errores, autocompletado, etc.
|
||||
* "<abbr title="converting the string that comes from an HTTP request into Python data">parsing</abbr>" de datos
|
||||
* "<abbr title="convertir el string que viene de un request HTTP en datos de Python">parsing</abbr>" de datos
|
||||
* Validación de datos
|
||||
* Anotación de API y documentación automática
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Modelos de Parámetros Query
|
||||
# Modelos de Parámetros Query { #query-parameter-models }
|
||||
|
||||
Si tienes un grupo de **parámetros query** que están relacionados, puedes crear un **modelo de Pydantic** para declararlos.
|
||||
|
||||
@@ -10,7 +10,7 @@ Esto es compatible desde la versión `0.115.0` de FastAPI. 🤓
|
||||
|
||||
///
|
||||
|
||||
## Parámetros Query con un Modelo Pydantic
|
||||
## Parámetros Query con un Modelo Pydantic { #query-parameters-with-a-pydantic-model }
|
||||
|
||||
Declara los **parámetros query** que necesitas en un **modelo de Pydantic**, y luego declara el parámetro como `Query`:
|
||||
|
||||
@@ -18,7 +18,7 @@ Declara los **parámetros query** que necesitas en un **modelo de Pydantic**, y
|
||||
|
||||
**FastAPI** **extraerá** los datos para **cada campo** de los **parámetros query** en el request y te proporcionará el modelo de Pydantic que definiste.
|
||||
|
||||
## Revisa la Documentación
|
||||
## Revisa la Documentación { #check-the-docs }
|
||||
|
||||
Puedes ver los parámetros query en la UI de documentación en `/docs`:
|
||||
|
||||
@@ -26,7 +26,7 @@ Puedes ver los parámetros query en la UI de documentación en `/docs`:
|
||||
<img src="/img/tutorial/query-param-models/image01.png">
|
||||
</div>
|
||||
|
||||
## Prohibir Parámetros Query Extras
|
||||
## Prohibir Parámetros Query Extras { #forbid-extra-query-parameters }
|
||||
|
||||
En algunos casos de uso especiales (probablemente no muy comunes), podrías querer **restringir** los parámetros query que deseas recibir.
|
||||
|
||||
@@ -57,7 +57,7 @@ Recibirán un response de **error** que les indica que el parámetro query `tool
|
||||
}
|
||||
```
|
||||
|
||||
## Resumen
|
||||
## Resumen { #summary }
|
||||
|
||||
Puedes usar **modelos de Pydantic** para declarar **parámetros query** en **FastAPI**. 😎
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Parámetros de Query y Validaciones de String
|
||||
# Parámetros de Query y Validaciones de String { #query-parameters-and-string-validations }
|
||||
|
||||
**FastAPI** te permite declarar información adicional y validación para tus parámetros.
|
||||
|
||||
@@ -6,48 +6,28 @@ Tomemos esta aplicación como ejemplo:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial001_py310.py hl[7] *}
|
||||
|
||||
El parámetro de query `q` es del tipo `Union[str, None]` (o `str | None` en Python 3.10), lo que significa que es de tipo `str` pero también podría ser `None`, y de hecho, el valor por defecto es `None`, así que FastAPI sabrá que no es requerido.
|
||||
El parámetro de query `q` es de tipo `str | None`, lo que significa que es de tipo `str` pero también podría ser `None`, y de hecho, el valor por defecto es `None`, así que FastAPI sabrá que no es requerido.
|
||||
|
||||
/// note | Nota
|
||||
|
||||
FastAPI sabrá que el valor de `q` no es requerido por el valor por defecto `= None`.
|
||||
|
||||
El `Union` en `Union[str, None]` permitirá a tu editor darte un mejor soporte y detectar errores.
|
||||
Tener `str | None` permitirá que tu editor te dé un mejor soporte y detecte errores.
|
||||
|
||||
///
|
||||
|
||||
## Validaciones adicionales
|
||||
## Validaciones adicionales { #additional-validation }
|
||||
|
||||
Vamos a hacer que, aunque `q` sea opcional, siempre que se proporcione, **su longitud no exceda los 50 caracteres**.
|
||||
|
||||
### Importar `Query` y `Annotated`
|
||||
### Importar `Query` y `Annotated` { #import-query-and-annotated }
|
||||
|
||||
Para lograr eso, primero importa:
|
||||
|
||||
* `Query` desde `fastapi`
|
||||
* `Annotated` desde `typing` (o desde `typing_extensions` en Python por debajo de 3.9)
|
||||
* `Annotated` desde `typing`
|
||||
|
||||
//// tab | Python 3.10+
|
||||
|
||||
En Python 3.9 o superior, `Annotated` es parte de la biblioteca estándar, así que puedes importarlo desde `typing`.
|
||||
|
||||
```Python hl_lines="1 3"
|
||||
{!> ../../docs_src/query_params_str_validations/tutorial002_an_py310.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+
|
||||
|
||||
En versiones de Python por debajo de 3.9 importas `Annotated` desde `typing_extensions`.
|
||||
|
||||
Ya estará instalado con FastAPI.
|
||||
|
||||
```Python hl_lines="3-4"
|
||||
{!> ../../docs_src/query_params_str_validations/tutorial002_an.py!}
|
||||
```
|
||||
|
||||
////
|
||||
{* ../../docs_src/query_params_str_validations/tutorial002_an_py310.py hl[1,3] *}
|
||||
|
||||
/// info | Información
|
||||
|
||||
@@ -59,9 +39,9 @@ Asegúrate de [Actualizar la versión de FastAPI](../deployment/versions.md#upgr
|
||||
|
||||
///
|
||||
|
||||
## Usar `Annotated` en el tipo del parámetro `q`
|
||||
## Usar `Annotated` en el tipo del parámetro `q` { #use-annotated-in-the-type-for-the-q-parameter }
|
||||
|
||||
¿Recuerdas que te dije antes que `Annotated` puede ser usado para agregar metadatos a tus parámetros en la [Introducción a Tipos de Python](../python-types.md#type-hints-with-metadata-annotations){.internal-link target=_blank}?
|
||||
¿Recuerdas que te dije antes que `Annotated` puede usarse para agregar metadatos a tus parámetros en la [Introducción a Tipos de Python](../python-types.md#type-hints-with-metadata-annotations){.internal-link target=_blank}?
|
||||
|
||||
Ahora es el momento de usarlo con FastAPI. 🚀
|
||||
|
||||
@@ -75,7 +55,7 @@ q: str | None = None
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python
|
||||
q: Union[str, None] = None
|
||||
@@ -93,7 +73,7 @@ q: Annotated[str | None] = None
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python
|
||||
q: Annotated[Union[str, None]] = None
|
||||
@@ -105,7 +85,7 @@ Ambas versiones significan lo mismo, `q` es un parámetro que puede ser un `str`
|
||||
|
||||
Ahora vamos a lo divertido. 🎉
|
||||
|
||||
## Agregar `Query` a `Annotated` en el parámetro `q`
|
||||
## Agregar `Query` a `Annotated` en el parámetro `q` { #add-query-to-annotated-in-the-q-parameter }
|
||||
|
||||
Ahora que tenemos este `Annotated` donde podemos poner más información (en este caso algunas validaciones adicionales), agrega `Query` dentro de `Annotated`, y establece el parámetro `max_length` a `50`:
|
||||
|
||||
@@ -127,9 +107,9 @@ FastAPI ahora:
|
||||
* 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
|
||||
## Alternativa (antigua): `Query` como valor por defecto { #alternative-old-query-as-the-default-value }
|
||||
|
||||
Versiones anteriores de FastAPI (antes de <abbr title="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 <abbr title="before 2023-03 - antes de 2023-03">0.95.0</abbr>) requerían que usaras `Query` como el valor por defecto de tu parámetro, en lugar de ponerlo en `Annotated`, hay una alta probabilidad de que veas código usándolo alrededor, así que te lo explicaré.
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
@@ -141,63 +121,32 @@ Así es como usarías `Query()` como el valor por defecto de tu parámetro de fu
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial002_py310.py hl[7] *}
|
||||
|
||||
Ya que en este caso (sin usar `Annotated`) debemos reemplazar el valor por defecto `None` en la función con `Query()`, ahora necesitamos establecer el valor por defecto con el parámetro `Query(default=None)`, esto sirve al mismo propósito de definir ese valor por defecto (al menos para FastAPI).
|
||||
Como en este caso (sin usar `Annotated`) debemos reemplazar el valor por defecto `None` en la función con `Query()`, ahora necesitamos establecer el valor por defecto con el parámetro `Query(default=None)`, esto sirve al mismo propósito de definir ese valor por defecto (al menos para FastAPI).
|
||||
|
||||
Entonces:
|
||||
|
||||
```Python
|
||||
q: Union[str, None] = Query(default=None)
|
||||
```
|
||||
|
||||
...hace que el parámetro sea opcional, con un valor por defecto de `None`, lo mismo que:
|
||||
|
||||
```Python
|
||||
q: Union[str, None] = None
|
||||
```
|
||||
|
||||
Y en Python 3.10 y superior:
|
||||
|
||||
```Python
|
||||
q: str | None = Query(default=None)
|
||||
```
|
||||
|
||||
...hace que el parámetro sea opcional, con un valor por defecto de `None`, lo mismo que:
|
||||
|
||||
|
||||
```Python
|
||||
q: str | None = None
|
||||
```
|
||||
|
||||
Pero las versiones de `Query` lo declaran explícitamente como un parámetro de query.
|
||||
|
||||
/// info | Información
|
||||
|
||||
Ten en cuenta que la parte más importante para hacer un parámetro opcional es la parte:
|
||||
|
||||
```Python
|
||||
= None
|
||||
```
|
||||
|
||||
o la parte:
|
||||
|
||||
```Python
|
||||
= Query(default=None)
|
||||
```
|
||||
|
||||
ya que usará ese `None` como el valor por defecto, y de esa manera hará el parámetro **no requerido**.
|
||||
|
||||
La parte `Union[str, None]` permite que tu editor brinde un mejor soporte, pero no es lo que le dice a FastAPI que este parámetro no es requerido.
|
||||
|
||||
///
|
||||
Pero la versión con `Query` lo declara explícitamente como un parámetro de query.
|
||||
|
||||
Luego, podemos pasar más parámetros a `Query`. En este caso, el parámetro `max_length` que se aplica a los strings:
|
||||
|
||||
```Python
|
||||
q: Union[str, None] = Query(default=None, max_length=50)
|
||||
q: str | None = Query(default=None, max_length=50)
|
||||
```
|
||||
|
||||
Esto validará los datos, mostrará un error claro cuando los datos no sean válidos, y documentará el parámetro en el esquema del *path operation* de OpenaPI.
|
||||
Esto validará los datos, mostrará un error claro cuando los datos no sean válidos, y documentará el parámetro en el esquema del *path operation* de OpenAPI.
|
||||
|
||||
### `Query` como valor por defecto o en `Annotated`
|
||||
### `Query` como valor por defecto o en `Annotated` { #query-as-the-default-value-or-in-annotated }
|
||||
|
||||
Ten en cuenta que cuando uses `Query` dentro de `Annotated` no puedes usar el parámetro `default` para `Query`.
|
||||
|
||||
@@ -217,13 +166,13 @@ Así que utilizarías (preferentemente):
|
||||
q: Annotated[str, Query()] = "rick"
|
||||
```
|
||||
|
||||
...o en code bases más antiguos encontrarás:
|
||||
...o en code bases más antiguas encontrarás:
|
||||
|
||||
```Python
|
||||
q: str = Query(default="rick")
|
||||
```
|
||||
|
||||
### Ventajas de `Annotated`
|
||||
### 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. 🤓
|
||||
|
||||
@@ -235,13 +184,13 @@ Cuando no usas `Annotated` y en su lugar usas el estilo de valor por defecto **(
|
||||
|
||||
Dado que `Annotated` puede tener más de una anotación de metadato, ahora podrías incluso usar la misma función con otras herramientas, como <a href="https://typer.tiangolo.com/" class="external-link" target="_blank">Typer</a>. 🚀
|
||||
|
||||
## Agregar más validaciones
|
||||
## Agregar más validaciones { #add-more-validations }
|
||||
|
||||
También puedes agregar un parámetro `min_length`:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial003_an_py310.py hl[10] *}
|
||||
|
||||
## Agregar expresiones regulares
|
||||
## Agregar expresiones regulares { #add-regular-expressions }
|
||||
|
||||
Puedes definir una <abbr title="Una expresión regular, regex o regexp es una secuencia de caracteres que define un patrón de búsqueda para strings.">expresión regular</abbr> `pattern` que el parámetro debe coincidir:
|
||||
|
||||
@@ -249,29 +198,15 @@ Puedes definir una <abbr title="Una expresión regular, regex o regexp es una se
|
||||
|
||||
Este patrón específico de expresión regular comprueba que el valor recibido del parámetro:
|
||||
|
||||
* `^`: comience con los siguientes caracteres, no tiene caracteres antes.
|
||||
* `^`: comienza con los siguientes caracteres, no tiene caracteres antes.
|
||||
* `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.
|
||||
|
||||
Pero cuando las necesites y vayas a aprenderlas, ya sabes que puedes usarlas directamente en **FastAPI**.
|
||||
Ahora sabes que cuando las necesites puedes usarlas en **FastAPI**.
|
||||
|
||||
### Pydantic v1 `regex` en lugar de `pattern`
|
||||
|
||||
Antes de la versión 2 de Pydantic y antes de FastAPI 0.100.0, el parámetro se llamaba `regex` en lugar de `pattern`, pero ahora está en desuso.
|
||||
|
||||
Todavía podrías ver algo de código que lo usa:
|
||||
|
||||
//// tab | Pydantic v1
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial004_regex_an_py310.py hl[11] *}
|
||||
|
||||
////
|
||||
|
||||
Pero que sepas que esto está deprecado y debería actualizarse para usar el nuevo parámetro `pattern`. 🤓
|
||||
|
||||
## Valores por defecto
|
||||
## Valores por defecto { #default-values }
|
||||
|
||||
Puedes, por supuesto, usar valores por defecto diferentes de `None`.
|
||||
|
||||
@@ -285,7 +220,7 @@ Tener un valor por defecto de cualquier tipo, incluyendo `None`, hace que el par
|
||||
|
||||
///
|
||||
|
||||
## Parámetros requeridos
|
||||
## Parámetros requeridos { #required-parameters }
|
||||
|
||||
Cuando no necesitamos declarar más validaciones o metadatos, podemos hacer que el parámetro de query `q` sea requerido simplemente no declarando un valor por defecto, como:
|
||||
|
||||
@@ -296,52 +231,28 @@ q: str
|
||||
en lugar de:
|
||||
|
||||
```Python
|
||||
q: Union[str, None] = None
|
||||
q: str | None = None
|
||||
```
|
||||
|
||||
Pero ahora lo estamos declarando con `Query`, por ejemplo, como:
|
||||
|
||||
//// tab | Annotated
|
||||
|
||||
```Python
|
||||
q: Annotated[Union[str, None], Query(min_length=3)] = None
|
||||
q: Annotated[str | None, Query(min_length=3)] = None
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | non-Annotated
|
||||
|
||||
```Python
|
||||
q: Union[str, None] = Query(default=None, min_length=3)
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
Así que, cuando necesites declarar un valor como requerido mientras usas `Query`, simplemente puedes no declarar un valor por defecto:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial006_an_py39.py hl[9] *}
|
||||
|
||||
### Requerido, puede ser `None`
|
||||
### Requerido, puede ser `None` { #required-can-be-none }
|
||||
|
||||
Puedes declarar que un parámetro puede aceptar `None`, pero que aún así es requerido. Esto obligaría a los clientes a enviar un valor, incluso si el valor es `None`.
|
||||
|
||||
Para hacer eso, puedes declarar que `None` es un tipo válido pero aún usar `...` como el valor por defecto:
|
||||
Para hacer eso, puedes declarar que `None` es un tipo válido pero simplemente no declarar un valor por defecto:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial006c_an_py310.py hl[9] *}
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
Pydantic, que es lo que impulsa toda la validación y serialización de datos en FastAPI, tiene un comportamiento especial cuando usas `Optional` o `Union[Something, None]` sin un valor por defecto, puedes leer más al respecto en la documentación de Pydantic sobre <a href="https://docs.pydantic.dev/2.3/usage/models/#required-optional-fields" class="external-link" target="_blank">Campos requeridos</a>.
|
||||
|
||||
///
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
Recuerda que en la mayoría de los casos, cuando algo es requerido, puedes simplemente omitir el default, así que normalmente no tienes que usar `...`.
|
||||
|
||||
///
|
||||
|
||||
## Lista de parámetros de Query / múltiples valores
|
||||
## Lista de parámetros de Query / múltiples valores { #query-parameter-list-multiple-values }
|
||||
|
||||
Cuando defines un parámetro de query explícitamente con `Query` también puedes declararlo para recibir una lista de valores, o dicho de otra manera, para recibir múltiples valores.
|
||||
|
||||
@@ -355,7 +266,7 @@ Entonces, con una URL como:
|
||||
http://localhost:8000/items/?q=foo&q=bar
|
||||
```
|
||||
|
||||
recibirías los múltiples valores del *query parameter* `q` (`foo` y `bar`) en una `list` de Python dentro de tu *path operation function*, en el *parámetro de función* `q`.
|
||||
recibirías los múltiples valores de los *query parameters* `q` (`foo` y `bar`) en una `list` de Python dentro de tu *path operation function*, en el *parámetro de función* `q`.
|
||||
|
||||
Entonces, el response a esa URL sería:
|
||||
|
||||
@@ -378,9 +289,9 @@ La documentación interactiva de API se actualizará en consecuencia, para permi
|
||||
|
||||
<img src="/img/tutorial/query-params-str-validations/image02.png">
|
||||
|
||||
### Lista de parámetros de Query / múltiples valores con valores por defecto
|
||||
### Lista de parámetros de Query / múltiples valores con valores por defecto { #query-parameter-list-multiple-values-with-defaults }
|
||||
|
||||
Y también puedes definir un valor por defecto `list` de valores si no se proporcionan ninguno:
|
||||
También puedes definir un valor por defecto `list` de valores si no se proporciona ninguno:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial012_an_py39.py hl[9] *}
|
||||
|
||||
@@ -401,9 +312,9 @@ el valor por defecto de `q` será: `["foo", "bar"]` y tu response será:
|
||||
}
|
||||
```
|
||||
|
||||
#### Usando solo `list`
|
||||
#### Usando solo `list` { #using-just-list }
|
||||
|
||||
También puedes usar `list` directamente en lugar de `List[str]` (o `list[str]` en Python 3.9+):
|
||||
También puedes usar `list` directamente en lugar de `list[str]`:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial013_an_py39.py hl[9] *}
|
||||
|
||||
@@ -411,11 +322,11 @@ También puedes usar `list` directamente en lugar de `List[str]` (o `list[str]`
|
||||
|
||||
Ten en cuenta que en este caso, FastAPI no comprobará el contenido de la lista.
|
||||
|
||||
Por ejemplo, `List[int]` comprobaría (y documentaría) que el contenido de la lista son enteros. Pero `list` sola no lo haría.
|
||||
Por ejemplo, `list[int]` comprobaría (y documentaría) que el contenido de la lista son enteros. Pero `list` sola no lo haría.
|
||||
|
||||
///
|
||||
|
||||
## Declarar más metadatos
|
||||
## Declarar más metadatos { #declare-more-metadata }
|
||||
|
||||
Puedes agregar más información sobre el parámetro.
|
||||
|
||||
@@ -437,7 +348,7 @@ Y una `description`:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial008_an_py310.py hl[14] *}
|
||||
|
||||
## Alias para parámetros
|
||||
## Alias para parámetros { #alias-parameters }
|
||||
|
||||
Imagina que quieres que el parámetro sea `item-query`.
|
||||
|
||||
@@ -457,11 +368,11 @@ Entonces puedes declarar un `alias`, y ese alias será usado para encontrar el v
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial009_an_py310.py hl[9] *}
|
||||
|
||||
## Declarar parámetros obsoletos
|
||||
## Declarar parámetros obsoletos { #deprecating-parameters }
|
||||
|
||||
Ahora digamos que ya no te gusta este parámetro.
|
||||
|
||||
Tienes que dejarlo allí por un tiempo porque hay clientes usándolo, pero quieres que la documentación lo muestre claramente como <abbr title="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 <abbr title="obsolete, recommended not to use it - obsoleto, se recomienda no usarlo">deprecated</abbr>.
|
||||
|
||||
Luego pasa el parámetro `deprecated=True` a `Query`:
|
||||
|
||||
@@ -471,13 +382,75 @@ La documentación lo mostrará así:
|
||||
|
||||
<img src="/img/tutorial/query-params-str-validations/image01.png">
|
||||
|
||||
## Excluir parámetros de OpenAPI
|
||||
## Excluir parámetros de OpenAPI { #exclude-parameters-from-openapi }
|
||||
|
||||
Para excluir un parámetro de query del esquema de OpenAPI generado (y por lo tanto, de los sistemas de documentación automática), establece el parámetro `include_in_schema` de `Query` a `False`:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial014_an_py310.py hl[10] *}
|
||||
|
||||
## Recapitulación
|
||||
## Validación personalizada { #custom-validation }
|
||||
|
||||
Podría haber casos donde necesites hacer alguna **validación personalizada** que no puede hacerse con los parámetros mostrados arriba.
|
||||
|
||||
En esos casos, puedes usar una **función validadora personalizada** que se aplique después de la validación normal (por ejemplo, después de validar que el valor es un `str`).
|
||||
|
||||
Puedes lograr eso usando <a href="https://docs.pydantic.dev/latest/concepts/validators/#field-after-validator" class="external-link" target="_blank">`AfterValidator` de Pydantic</a> dentro de `Annotated`.
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
Pydantic también tiene <a href="https://docs.pydantic.dev/latest/concepts/validators/#field-before-validator" class="external-link" target="_blank">`BeforeValidator`</a> y otros. 🤓
|
||||
|
||||
///
|
||||
|
||||
Por ejemplo, este validador personalizado comprueba que el ID del ítem empiece con `isbn-` para un número de libro <abbr title="ISBN means International Standard Book Number - ISBN significa International Standard Book Number">ISBN</abbr> o con `imdb-` para un ID de URL de película de <abbr title="IMDB (Internet Movie Database) is a website with information about movies - IMDB (Internet Movie Database) es un sitio web con información sobre películas">IMDB</abbr>:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial015_an_py310.py hl[5,16:19,24] *}
|
||||
|
||||
/// info | Información
|
||||
|
||||
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.
|
||||
|
||||
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. 🤸
|
||||
|
||||
---
|
||||
|
||||
Pero si te da curiosidad este ejemplo de código específico y sigues entretenido, aquí tienes algunos detalles extra.
|
||||
|
||||
#### String con `value.startswith()` { #string-with-value-startswith }
|
||||
|
||||
¿Lo notaste? un string usando `value.startswith()` puede recibir una tupla, y comprobará cada valor en la tupla:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial015_an_py310.py ln[16:19] hl[17] *}
|
||||
|
||||
#### Un ítem aleatorio { #a-random-item }
|
||||
|
||||
Con `data.items()` obtenemos un <abbr title="Algo que podemos iterar con un for loop, como una list, set, etc.">objeto iterable</abbr> con tuplas que contienen la clave y el valor para cada elemento del diccionario.
|
||||
|
||||
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 **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? 🐍
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial015_an_py310.py ln[22:30] hl[29] *}
|
||||
|
||||
## Recapitulación { #recap }
|
||||
|
||||
Puedes declarar validaciones y metadatos adicionales para tus parámetros.
|
||||
|
||||
@@ -494,6 +467,8 @@ Validaciones específicas para strings:
|
||||
* `max_length`
|
||||
* `pattern`
|
||||
|
||||
Validaciones personalizadas usando `AfterValidator`.
|
||||
|
||||
En estos ejemplos viste cómo declarar validaciones para valores de tipo `str`.
|
||||
|
||||
Mira los siguientes capítulos para aprender cómo declarar validaciones para otros tipos, como números.
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# Parámetros de Query
|
||||
# Parámetros de Query { #query-parameters }
|
||||
|
||||
Cuando declaras otros parámetros de función que no son parte de los parámetros de path, son automáticamente interpretados como parámetros de "query".
|
||||
|
||||
{* ../../docs_src/query_params/tutorial001.py hl[9] *}
|
||||
{* ../../docs_src/query_params/tutorial001_py39.py hl[9] *}
|
||||
|
||||
La query es el conjunto de pares clave-valor que van después del `?` en una URL, separados por caracteres `&`.
|
||||
|
||||
@@ -24,11 +24,11 @@ Pero cuando los declaras con tipos de Python (en el ejemplo anterior, como `int`
|
||||
Todo el mismo proceso que se aplica para los parámetros de path también se aplica para los parámetros de query:
|
||||
|
||||
* Soporte del editor (obviamente)
|
||||
* <abbr title="convirtiendo el string que viene de un request HTTP en datos de Python">"Parsing"</abbr> de datos
|
||||
* <abbr title="convirtiendo el string que viene de un request HTTP en datos de Python">"parsing"</abbr> de datos
|
||||
* Validación de datos
|
||||
* Documentación automática
|
||||
|
||||
## Valores por defecto
|
||||
## Valores por defecto { #defaults }
|
||||
|
||||
Como los parámetros de query no son una parte fija de un path, pueden ser opcionales y pueden tener valores por defecto.
|
||||
|
||||
@@ -57,19 +57,21 @@ Los valores de los parámetros en tu función serán:
|
||||
* `skip=20`: porque lo configuraste en la URL
|
||||
* `limit=10`: porque ese era el valor por defecto
|
||||
|
||||
## Parámetros opcionales
|
||||
## Parámetros opcionales { #optional-parameters }
|
||||
|
||||
De la misma manera, puedes declarar parámetros de query opcionales, estableciendo su valor por defecto en `None`:
|
||||
|
||||
{* ../../docs_src/query_params/tutorial002_py310.py hl[7] *}
|
||||
|
||||
En este caso, el parámetro de función `q` será opcional y será `None` por defecto.
|
||||
|
||||
/// check | Revisa
|
||||
|
||||
Además, nota que **FastAPI** es lo suficientemente inteligente para notar que el parámetro de path `item_id` es un parámetro de path y `q` no lo es, por lo tanto, es un parámetro de query.
|
||||
|
||||
///
|
||||
|
||||
## Conversión de tipos en parámetros de query
|
||||
## Conversión de tipos en parámetros de query { #query-parameter-type-conversion }
|
||||
|
||||
También puedes declarar tipos `bool`, y serán convertidos:
|
||||
|
||||
@@ -107,7 +109,7 @@ http://127.0.0.1:8000/items/foo?short=yes
|
||||
|
||||
o cualquier otra variación (mayúsculas, primera letra en mayúscula, etc.), tu función verá el parámetro `short` con un valor `bool` de `True`. De lo contrario, será `False`.
|
||||
|
||||
## Múltiples parámetros de path y de query
|
||||
## Múltiples parámetros de path y de query { #multiple-path-and-query-parameters }
|
||||
|
||||
Puedes declarar múltiples parámetros de path y de query al mismo tiempo, **FastAPI** sabe cuál es cuál.
|
||||
|
||||
@@ -117,7 +119,7 @@ Serán detectados por nombre:
|
||||
|
||||
{* ../../docs_src/query_params/tutorial004_py310.py hl[6,8] *}
|
||||
|
||||
## Parámetros de query requeridos
|
||||
## Parámetros de query requeridos { #required-query-parameters }
|
||||
|
||||
Cuando declaras un valor por defecto para parámetros que no son de path (por ahora, solo hemos visto parámetros de query), entonces no es requerido.
|
||||
|
||||
@@ -125,7 +127,7 @@ Si no quieres agregar un valor específico pero solo hacer que sea opcional, est
|
||||
|
||||
Pero cuando quieres hacer un parámetro de query requerido, simplemente no declares ningún valor por defecto:
|
||||
|
||||
{* ../../docs_src/query_params/tutorial005.py hl[6:7] *}
|
||||
{* ../../docs_src/query_params/tutorial005_py39.py hl[6:7] *}
|
||||
|
||||
Aquí el parámetro de query `needy` es un parámetro de query requerido de tipo `str`.
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Archivos de Request
|
||||
# Archivos de Request { #request-files }
|
||||
|
||||
Puedes definir archivos que serán subidos por el cliente utilizando `File`.
|
||||
|
||||
@@ -16,13 +16,13 @@ Esto es porque los archivos subidos se envían como "form data".
|
||||
|
||||
///
|
||||
|
||||
## Importar `File`
|
||||
## Importar `File` { #import-file }
|
||||
|
||||
Importa `File` y `UploadFile` desde `fastapi`:
|
||||
|
||||
{* ../../docs_src/request_files/tutorial001_an_py39.py hl[3] *}
|
||||
|
||||
## Definir Parámetros `File`
|
||||
## Definir Parámetros `File` { #define-file-parameters }
|
||||
|
||||
Crea parámetros de archivo de la misma manera que lo harías para `Body` o `Form`:
|
||||
|
||||
@@ -50,7 +50,7 @@ Ten en cuenta que esto significa que todo el contenido se almacenará en memoria
|
||||
|
||||
Pero hay varios casos en los que podrías beneficiarte de usar `UploadFile`.
|
||||
|
||||
## Parámetros de Archivo con `UploadFile`
|
||||
## Parámetros de Archivo con `UploadFile` { #file-parameters-with-uploadfile }
|
||||
|
||||
Define un parámetro de archivo con un tipo de `UploadFile`:
|
||||
|
||||
@@ -66,7 +66,7 @@ Usar `UploadFile` tiene varias ventajas sobre `bytes`:
|
||||
* Tiene una interfaz `async` <a href="https://docs.python.org/3/glossary.html#term-file-like-object" class="external-link" target="_blank">parecida a un archivo</a>.
|
||||
* Expone un objeto Python real <a href="https://docs.python.org/3/library/tempfile.html#tempfile.SpooledTemporaryFile" class="external-link" target="_blank">`SpooledTemporaryFile`</a> que puedes pasar directamente a otros paquetes que esperan un objeto parecido a un archivo.
|
||||
|
||||
### `UploadFile`
|
||||
### `UploadFile` { #uploadfile }
|
||||
|
||||
`UploadFile` tiene los siguientes atributos:
|
||||
|
||||
@@ -109,7 +109,7 @@ El `UploadFile` de **FastAPI** hereda directamente del `UploadFile` de **Starlet
|
||||
|
||||
///
|
||||
|
||||
## Qué es "Form Data"
|
||||
## Qué es "Form Data" { #what-is-form-data }
|
||||
|
||||
La manera en que los forms de HTML (`<form></form>`) envían los datos al servidor normalmente utiliza una codificación "especial" para esos datos, es diferente de JSON.
|
||||
|
||||
@@ -121,7 +121,7 @@ Los datos de los forms normalmente se codifican usando el "media type" `applicat
|
||||
|
||||
Pero cuando el formulario incluye archivos, se codifica como `multipart/form-data`. Si usas `File`, **FastAPI** sabrá que tiene que obtener los archivos de la parte correcta del cuerpo.
|
||||
|
||||
Si deseas leer más sobre estas codificaciones y campos de formularios, dirígete a la <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST" class="external-link" target="_blank"><abbr title="Mozilla Developer Network">MDN</abbr> web docs para <code>POST</code></a>.
|
||||
Si deseas leer más sobre estas codificaciones y campos de formularios, dirígete a la <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST" class="external-link" target="_blank"><abbr title="Mozilla Developer Network – Red de Desarrolladores de Mozilla">MDN</abbr> web docs para <code>POST</code></a>.
|
||||
|
||||
///
|
||||
|
||||
@@ -133,19 +133,19 @@ Esto no es una limitación de **FastAPI**, es parte del protocolo HTTP.
|
||||
|
||||
///
|
||||
|
||||
## Subida de Archivos Opcional
|
||||
## Subida de Archivos Opcional { #optional-file-upload }
|
||||
|
||||
Puedes hacer un archivo opcional utilizando anotaciones de tipos estándar y estableciendo un valor por defecto de `None`:
|
||||
|
||||
{* ../../docs_src/request_files/tutorial001_02_an_py310.py hl[9,17] *}
|
||||
|
||||
## `UploadFile` con Metadatos Adicionales
|
||||
## `UploadFile` con Metadatos Adicionales { #uploadfile-with-additional-metadata }
|
||||
|
||||
También puedes usar `File()` con `UploadFile`, por ejemplo, para establecer metadatos adicionales:
|
||||
|
||||
{* ../../docs_src/request_files/tutorial001_03_an_py39.py hl[9,15] *}
|
||||
|
||||
## Subidas de Múltiples Archivos
|
||||
## Subidas de Múltiples Archivos { #multiple-file-uploads }
|
||||
|
||||
Es posible subir varios archivos al mismo tiempo.
|
||||
|
||||
@@ -165,12 +165,12 @@ También podrías usar `from starlette.responses import HTMLResponse`.
|
||||
|
||||
///
|
||||
|
||||
### Subidas de Múltiples Archivos con Metadatos Adicionales
|
||||
### Subidas de Múltiples Archivos con Metadatos Adicionales { #multiple-file-uploads-with-additional-metadata }
|
||||
|
||||
Y de la misma manera que antes, puedes usar `File()` para establecer parámetros adicionales, incluso para `UploadFile`:
|
||||
|
||||
{* ../../docs_src/request_files/tutorial003_an_py39.py hl[11,18:20] *}
|
||||
|
||||
## Recapitulación
|
||||
## Recapitulación { #recap }
|
||||
|
||||
Usa `File`, `bytes` y `UploadFile` para declarar archivos que se subirán en el request, enviados como form data.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user