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.
Así, continuando con el user_dict de arriba, escribiendo:
Las funciones adicionales de soporte fake_password_hasher y fake_save_user son solo para demostrar un posible flujo de los datos, pero por supuesto no proporcionan ninguna seguridad real.
Reducir la duplicación de código es una de las ideas centrales en FastAPI.
Como la duplicación de código aumenta las posibilidades de bugs, problemas de seguridad, problemas de desincronización de código (cuando actualizas en un lugar pero no en los otros), etc.
Y estos modelos están todos compartiendo muchos de los datos y duplicando nombres y tipos de atributos.
Podemos hacerlo mejor.
Podemos declarar un modelo UserBase que sirva como base para nuestros otros modelos. Y luego podemos hacer subclases de ese modelo que hereden sus atributos (declaraciones de tipo, validación, etc).
Toda la conversión de datos, validación, documentación, etc. seguirá funcionando de manera normal.
De esa manera, podemos declarar solo las diferencias entre los modelos (con password en texto plano, con hashed_password y sin contraseña):
Puedes declarar que una response sea la Union de dos o más tipos, lo que significa que la response podría ser cualquiera de ellos.
Se definirá en OpenAPI con anyOf.
Para hacer eso, usa el type hint estándar de Python typing.Union:
Nota
Al definir una Union, incluye el tipo más específico primero, seguido del tipo menos específico. En el ejemplo de abajo, el más específico PlaneItem va antes que CarItem en Union[PlaneItem, CarItem].
fromfastapiimportFastAPIfrompydanticimportBaseModelapp=FastAPI()classBaseItem(BaseModel):description:strtype:strclassCarItem(BaseItem):type:str="car"classPlaneItem(BaseItem):type:str="plane"size:intitems={"item1":{"description":"All my friends drive a low rider","type":"car"},"item2":{"description":"Music is my aeroplane, it's my aeroplane","type":"plane","size":5,},}@app.get("/items/{item_id}",response_model=PlaneItem|CarItem)asyncdefread_item(item_id:str):returnitems[item_id]
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.
Si estuviera en una anotación de tipo podríamos haber usado la barra vertical, como:
some_variable:PlaneItem|CarItem
Pero si pusiéramos 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 interpretarlo como una anotación de tipo.
De la misma manera, puedes declarar responses de listas de objetos.
Para eso, usa la list estándar de Python:
fromfastapiimportFastAPIfrompydanticimportBaseModelapp=FastAPI()classItem(BaseModel):name:strdescription:stritems=[{"name":"Foo","description":"There comes my hero"},{"name":"Red","description":"It's my aeroplane"},]@app.get("/items/",response_model=list[Item])asyncdefread_items():returnitems
También puedes declarar una response usando un dict arbitrario simple, declarando solo el tipo de las claves y valores, sin usar un modelo de Pydantic.
Esto es útil si no conoces de antemano los nombres válidos de campos/atributos (que serían necesarios para un modelo de Pydantic).
Usa múltiples modelos de Pydantic y hereda libremente para cada caso.
No necesitas tener un único modelo de datos por entidad si esa entidad debe poder tener diferentes "estados". La "entidad" user es un ejemplo, con estados que incluyen password, password_hash, o sin contraseña.