MODELO DE RESPUESTA - TIPO DE RETORNO
Puede declarar el tipo utilizado para la respuesta anotando el tipo de
retorno de la función de operación de ruta.
Puede utilizar anotaciones de tipo de la misma manera que lo haría
para los datos de entrada en los parámetros de función, puede utilizar
modelos de Pydantic, listas, diccionarios, valores escalares como
números enteros, booleanos, etc.
FastAPI utilizará este tipo de retorno para:
Validar los datos devueltos.
Si los datos no son válidos (por ejemplo, falta un campo),
significa que el código de la aplicación no funciona
correctamente y no devuelve lo que debería, por lo que
devolverá un error de servidor en lugar de datos incorrectos.
De esta forma, usted y sus clientes pueden estar seguros de
recibir los datos y la forma de datos esperados.
Agregue un esquema JSON para la respuesta, en la operación
de ruta OpenAPI.
Esto será utilizado por los documentos automáticos.
También será utilizado por herramientas de generación
automática de código de cliente.
Pero lo más importante:
Limitará y filtrará los datos de salida a lo definido en el tipo de
retorno.
Esto es particularmente importante para la seguridad;
veremos más sobre ello a continuación.
RESPONSE_MODEL PARÁMETRO
Hay algunos casos en los que necesitas o deseas devolver algunos datos
que no son exactamente los que declara el tipo.
Por ejemplo, podría querer devolver un diccionario o un objeto de base
de datos, pero declararlo como un modelo de Pydantic. De esta
manera, el modelo de Pydantic se encargaría de toda la
documentación, validación, etc. de los datos del objeto devuelto (por
ejemplo, un diccionario o un objeto de base de datos).
Si agregó la anotación de tipo de retorno, las herramientas y los editores
se quejarían con un error (correcto) que le indicaría que su función está
devolviendo un tipo (por ejemplo, un dict) que es diferente de lo que
declaró (por ejemplo, un modelo de Pydantic).
En esos casos, puede utilizar el parámetro decorador de operación de
ruta response_model en lugar del tipo de retorno.
Puede utilizar el response_model parámetro en cualquiera de las
operaciones de ruta:
@[Link]()
@[Link]()
@[Link]()
@[Link]()
etc.
response_model recibe el mismo tipo que declararías para un campo
de modelo de Pydantic, por lo tanto, puede ser un modelo de
Pydantic, pero también puede ser, por ejemplo, un list modelo de
Pydantic, como list[Item].
FastAPI usará esto response_model para hacer toda la documentación
de datos, validación, etc. y también para convertir y filtrar los datos
de salida a su declaración de tipo.
RESPONSE_MODEL PRIORIDAD
Si declara tanto un tipo de retorno como
un response_model, response_model tendrán prioridad y serán
utilizados por FastAPI.
De esta forma, puedes agregar anotaciones de tipo correctas a tus
funciones, incluso cuando devuelves un tipo diferente al del modelo de
respuesta, para que las usen el editor y herramientas como mypy.
Además, puedes usar FastAPI para la validación de datos, la
documentación, etc., mediante el archivo response_model.
También puede usar response_model=None para deshabilitar la
creación de un modelo de respuesta para esa operación de ruta; es
posible que deba hacerlo si está agregando anotaciones de tipo para
cosas que no son campos de Pydantic válidos; verá un ejemplo de eso
en una de las secciones a continuación.
DEVOLVER LOS MISMOS DATOS DE ENTRADA
Aquí declaramos un UserIn modelo que contendrá una contraseña en
texto simple:
Y estamos usando este modelo para declarar nuestra entrada y el
mismo modelo para declarar nuestra salida:
Ahora, cada vez que un navegador crea un usuario con una contraseña,
la API devolverá la misma contraseña en la respuesta.
En este caso, podría no ser un problema, porque es el mismo usuario el
que envía la contraseña.
Pero si usamos el mismo modelo para otra operación de ruta, podríamos
estar enviando las contraseñas de nuestros usuarios a todos los clientes.
AGREGAR UN MODELO DE SALIDA
En lugar de eso, podemos crear un modelo de entrada con la contraseña
de texto simple y un modelo de salida sin ella:
Aquí, aunque nuestra función de operación de ruta devuelve la misma
entrada de usuario que contiene la contraseña:
...declaramos que response_model es nuestro modelo UserOut, eso
no incluye la contraseña:
Entonces, FastAPI se encargará de filtrar todos los datos que no estén
declarados en el modelo de salida (usando Pydantic).
RESPONSE_MODELO TIPO DE RETORNO
En este caso, debido a que los dos modelos son diferentes, si anotamos
el tipo de retorno de la función como UserOut, el editor y las
herramientas se quejarían de que estamos devolviendo un tipo no
válido, ya que son clases diferentes.
Por eso en este ejemplo tenemos que declararlo en el response_model
parámetro.
...pero continúa leyendo a continuación para ver cómo superarlo.
TIPO DE RETORNO Y FILTRADO DE DATOS
Continuemos con el ejemplo anterior. Queríamos anotar la función con
un tipo, pero queríamos poder devolver algo que realmente
incluyera más datos.
Queremos que FastAPI siga filtrando los datos usando el modelo de
respuesta. De esta manera, aunque la función devuelva más datos, la
respuesta solo incluirá los campos declarados en el modelo de
respuesta.
En el ejemplo anterior, debido a que las clases eran diferentes, tuvimos
que usar el response_model parámetro. Sin embargo, esto también
implica que no contamos con la compatibilidad del editor y las
herramientas para verificar el tipo de retorno de la función.
Pero en la mayoría de los casos donde necesitamos hacer algo como
esto, queremos que el modelo simplemente filtre/elimine algunos de
los datos como en este ejemplo.
Y en esos casos, podemos usar clases y herencia para aprovechar
las anotaciones de tipo de función para obtener un mejor soporte en
el editor y las herramientas, y aun así obtener el filtrado de
datos FastAPI.
Con esto, obtenemos soporte de herramientas, de editores y mypy ya
que este código es correcto en términos de tipos, pero también
obtenemos el filtrado de datos de FastAPI.
Recibe un objeto UserIn con los datos enviados en la petición.
Devuelve un BaseUser (sin la contraseña).
ANOTACIONES DE TIPO Y HERRAMIENTAS
Primero veamos cómo los editores, mypy y otras herramientas verían
esto.
BaseUser Tiene los campos base. Luego UserIn hereda BaseUser y
agrega el password campo, por lo que incluirá todos los campos de
ambos modelos.
Anotamos el tipo de retorno de la función como BaseUser, pero en
realidad estamos devolviendo una UserIn instancia.
El editor, mypy y otras herramientas no se quejarán de esto porque, en
términos de tipificación, UserIn es una subclase de BaseUser, lo que
significa que es un tipo válido cuando lo que se espera es cualquier cosa
que sea un BaseUser.
FILTRADO DE DATOS DE FASTAPI
Ahora, para FastAPI, verá el tipo de retorno y se asegurará de que lo
que devuelva incluya solo los campos que están declarados en el tipo.
FastAPI hace varias cosas internamente con Pydantic para asegurarse
de que esas mismas reglas de herencia de clase no se utilicen para el
filtrado de datos devueltos; de lo contrario, podría terminar devolviendo
muchos más datos de los que esperaba.
De esta manera, puede obtener lo mejor de ambos mundos: anotaciones
de tipo con soporte de herramientas y filtrado de datos.
VERLO EN LOS DOCUMENTOS
Cuando vea los documentos automáticos, podrá comprobar que el
modelo de entrada y el modelo de salida tendrán su propio esquema
JSON:
Y ambos modelos se utilizarán para la documentación de la API
interactiva:
OTRAS ANOTACIONES DE TIPO DE RETORNO
Puede haber casos en los que devuelvas algo que no es un campo
Pydantic válido y lo anotes en la función, solo para obtener el soporte
proporcionado por las herramientas (el editor, mypy, etc.).
DEVOLVER UNA RESPUESTA DIRECTAMENTE
El caso más común sería devolver una Respuesta directamente
como se explica más adelante en la documentación avanzada
FastAPI maneja automáticamente este caso simple porque la anotación
del tipo de retorno es la clase Response (o una subclase de ella).
Y las herramientas también estarán felices porque
tanto RedirectResponse como JSONResponse son subclases
de Response, por lo que la anotación de tipo es correcta.
ANOTAR UNA SUBCLASE DE RESPUESTA
También puedes utilizar una subclase de Response en la anotación de
tipo:
Esto también funcionará porque RedirectResponse es una subclase
de Response, y FastAPI manejará automáticamente este caso simple.
ANOTACIONES DE TIPO DE RETORNO NO VÁLIDO
Pero cuando devuelve algún otro objeto arbitrario que no es un tipo
válido de Pydantic (por ejemplo, un objeto de base de datos) y lo anota
de esa manera en la función, FastAPI intentará crear un modelo de
respuesta de Pydantic a partir de ese tipo de anotación y fallará.
Lo mismo sucedería si tuvieras algo así como una unión entre diferentes
tipos donde uno o más de ellos no son tipos Pydantic válidos, por
ejemplo, esto fallaría
...esto falla porque la anotación de tipo no es un tipo Pydantic y no es
solo una Responseclase o subclase única, es una unión (cualquiera
de las dos) entre a Response y a dict.
DESHABILITAR EL MODELO DE RESPUESTA
Continuando con el ejemplo anterior, es posible que no desees tener la
validación de datos, la documentación, el filtrado, etc. predeterminados
que realiza FastAPI.
Pero es posible que desees mantener la anotación del tipo de retorno en
la función para obtener el soporte de herramientas como editores y
verificadores de tipo (por ejemplo, mypy).
En este caso, puede deshabilitar la generación del modelo de respuesta
configurando response_model = None:
Esto hará que FastAPI omita la generación del modelo de respuesta y
de esa manera podrá tener cualquier anotación de tipo de retorno que
necesite sin que afecte a su aplicación FastAPI.
PARÁMETROS DE CODIFICACIÓN DEL MODELO DE RESPUESTA
Su modelo de respuesta podría tener valores predeterminados, como:
description: Union[str, None] = None(o str | None = None en
Python 3.10) tiene un valor predeterminado de None.
tax: float = 10.5 tiene un valor predeterminado de 10.5.
tags: List[str] = [] tiene un valor predeterminado de una lista
vacía: [].
pero es posible que desees omitirlos del resultado si en realidad no se
almacenaron.
Por ejemplo, si tiene modelos con muchos atributos opcionales en una
base de datos NoSQL, pero no desea enviar respuestas JSON muy
largas llenas de valores predeterminados.
UTILICE EL RESPONSE_MODEL_EXCLUDE_UNSET PARÁMETRO
Puede configurar el parámetro decorador de operación de
rutaresponse_model_exclude_unset=True:
y esos valores predeterminados no se incluirán en la respuesta, solo los
valores realmente establecidos.
Entonces, si envía una solicitud a esa operación de ruta para el
elemento con ID foo, la respuesta (sin incluir los valores
predeterminados) será:
DATOS CON VALORES PARA CAMPOS CON VALORES
PREDETERMINADOS
Pero si sus datos tienen valores para los campos del modelo con valores
predeterminados, como el elemento con ID bar:
Se incluirán en la respuesta.
DATOS CON LOS MISMOS VALORES QUE LOS PREDETERMINADOS
Si los datos tienen los mismos valores que los predeterminados, como el
elemento con ID baz:
FastAPI es lo suficientemente inteligente (en realidad, Pydantic es lo
suficientemente inteligente) como para darse cuenta de que,
aunque description, tax, y tags tienen los mismos valores que los
predeterminados, se configuraron explícitamente (en lugar de tomarse
de los predeterminados).
Por lo tanto, se incluirán en la respuesta JSON.
RESPONSE_MODEL_INCLUDE Y RESPONSE_MODEL_EXCLUDE
También puedes utilizar los parámetros del decorador de operaciones de
ruta response_model_include y response_model_exclude.
Toman un set of str con el nombre de los atributos a incluir (omitiendo el
resto) o a excluir (incluyendo el resto).
Esto se puede utilizar como un atajo rápido si solo tiene un modelo de
Pydantic y desea eliminar algunos datos de la salida.
USANDO LISTS EN LUGAR DE SETS
Si olvidas usar a set y usas a list o tuple en su lugar, FastAPI lo
convertirá a set y funcionará correctamente:
RESUMEN
Utilice el parámetro del decorador de operaciones de ruta
response_model para definir modelos de respuesta y, especialmente,
para garantizar que se filtren los datos privados.
Úselo response_model_exclude_unset para devolver solo los valores
establecidos explícitamente.