Pydantic provides the following arguments for exporting models using the model.dict(...)
method. Update: the model.dict(...)
was deprecated (but still supported) and replaced by model.model_dump(...)
. Those parameters are as follows:
exclude_unset
: whether fields which were not explicitly set when
creating the model should be excluded from the returned dictionary;
default False
exclude_none
: whether fields which are equal to None should be
excluded from the returned dictionary; default False
Since you are refering to excluding optional unset parameters, you can use the first method (i.e., exclude_unset
). This is useful when one would like to exclude a parameter only if it has not been set to either some value or None
.
The exclude_none
argument, however, ignores that fact that an attribute may have been intentionally set to None
, and hence, excludes it from the returned dictionary.
Example:
from pydantic import BaseModel
from typing import List, Union
class Text(BaseModel):
id: str
text: str = None
class TextsRequest(BaseModel):
data: List[Text] # in Python 3.9+ you can use: data: list[Text]
n_processes: Union[int, None] = None
t = TextsRequest(**{'data': [{'id': '1', 'text': 'The text 1'}], 'n_processes': None})
print(t.model_dump(exclude_none=True))
#> {'data': [{'id': '1', 'text': 'The text 1'}]}
print(t.model_dump(exclude_unset=True))
#> {'data': [{'id': '1', 'text': 'The text 1'}], 'n_processes': None}
Excluding Unset
or None
parameters from the response
If what you needed is excluding Unset
or None
parameters from the endpoint's response, without necessarily calling model.dict(...)
(or, in Pydantic V2 model.model_dump(...)
) inside the endpoint on your own, you could instead use the endpoint's decorator parameter response_model_exclude_unset
or response_model_exclude_none
(see the relevant documentation and related answers here and here). To achieve that, FastAPI, behind the scenes, uses the aforementioned model.dict(...)
(or, in Pydantic V2 model.model_dump(...)
) method with its exclude_unset
or exclude_none
parameter.
Examples:
@app.post("/create", response_model_exclude_unset=True)
async def create_graph(t: TextsRequest):
return t
or
@app.post("/create", response_model_exclude_none=True)
async def create_graph(t: TextsRequest):
return t
About Optional Parameters
Using Union[int, None]
is the same as using Optional[int]
(both are equivalent). The most important part, however, to make a parameter optional is the part = None
.
As per FastAPI documentation (see admonition Note and Info in the link provided):
Note
FastAPI will know that the value of q
is not required because of the
default value = None
.
The Union
in Union[str, None]
will allow your editor to give you
better support and detect errors.
Info
Have in mind that the most important part to make a parameter optional
is the part: = None
, as it will use that None
as the default value, and that way make the
parameter not required.
The Union[str, None]
part allows your editor to provide better
support, but it is not what tells FastAPI that this parameter is
not required.
Hence, regardless of the option you may choose to use, if it is not followed by the = None
part, FastAPI won't know that the value of the parameter is optional, and hence, the user will have to provide some value for it. One can also check that through the auto-generated API docs at http://127.0.0.1:8000/docs, where the parameter
or request body
will appear as a Required
field.
For example, any of the below would require the user to pass some body
content in their request for the TextsRequest
model:
@app.post("/upload")
def upload(t: Union[TextsRequest, None]):
pass
@app.post("/upload")
def upload(t: Optional[TextsRequest]):
pass
If, however, the above TextsRequest
definitions were succeeded by = None
, for example:
@app.post("/upload")
def upload(t: Union[TextsRequest, None] = None):
pass
@app.post("/upload")
def upload(t: Optional[TextsRequest] = None):
pass
@app.post("/upload")
def upload(t: TextsRequest = None): # this should work as well
pass
the parameter (or body) would be optional, as = None
would tell FastAPI that this parameter is not required.
In Python 3.10+
The good news is that in Python 3.10 and above, you don't have to worry about names like Optional
and Union
, as you can simply use the vertical bar |
(also called bitwise or operator, but that meaning is not relevant here) to define an optional parameter (or simply, unions of types). However, the same rule applies to this option as well, i.e., you would still need to add the = None
part, if you would like to make the parameter optional (as demonstrated in the example given below).
Example:
@app.post("/upload")
def upload(t: TextsRequest | None = None):
pass