Simple solution
I think the simplest solution is to configure your model with the extra = "allow"
setting (it is set to extra = "ignore"
by default). With that setting, passing any extra name-value-pairs to the model constructor will dynamically create fields on that model instance with the values and types as provided.
Here is an example:
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Model(BaseModel):
user_name: str
age: int
class Config:
extra = "allow"
@app.post("/home")
def write_home(model: Model) -> Model:
print(model)
return model
Now you can POST arbitrary additional data like this for example:
{
"user_name": "string",
"age": 0,
"height": 3.14
}
The output of the print
statement is user_name='string' age=0 height=3.14
and the response body is exactly the same as that of the request.
Potential risk
There is one big caveat here in my opinion, which is not specific to FastAPI, but to Pydantic models in general:
With the extra = "allow"
setting, any field name will be available. This can have potentially serious unintended consequences because the provided names can override existing names in the model namespace, including those of internal attributes (e.g. __fields__
) and pre-defined methods (e.g. dict
).
In the context of a FastAPI endpoint, imagine a situation, where someone POST
s a payload like this:
{
"user_name": "string",
"age": 0,
"dict": 1
}
This will work just fine up until the point, where the dict
method of that instance needs to be called, which happens to be the case during the formation of the response.
In other words, our print(model)
will work seemingly fine, yielding user_name='string' age=0 dict=1
, but the attempt to return this from our route handler will crash the server with a TypeError: 'int' object is not callable
.
This is just an example, but this should illustrate, why this may be dangerous or at least problematic, if you do not handle it properly.
Other caveats
A few minor caveats you also need to be aware of:
- This may be obvious, but no validation will be done on any of those extra field values. After being parsed via the configured (or default) JSON decoder, they will be assigned to the model instance as is.
- The OpenAPI documentation can of course not display those fields as being either part of the accepted request body schema or included in the response model schema.