I have this model:
class Text(BaseModel):
id: str
text: str = None
class TextsRequest(BaseModel):
data: list[Text]
n_processes: Union[int, None]
So, I want to be able to take requests like:
{"data": ["id": "1", "text": "The text 1"], "n_processes": 8}
and
{"data": ["id": "1", "text": "The text 1"]}.
Right now, in the second case I get:
{'data': [{'id': '1', 'text': 'The text 1'}], 'n_processes': None}
using this code:
app = FastAPI()
@app.post("/make_post/", response_model_exclude_none=True)
async def create_graph(request: TextsRequest):
input_data = jsonable_encoder(request)
So how can I exclude n_processes here?
Pydantic provides the following arguments for exporting models using the model.dict(...) method (Update: model.dict(...) has been deprecated 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; defaultFalse
exclude_none: whether fields which are equal to None should be excluded from the returned dictionary; defaultFalse
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}
Unset or None parameters from the responseIf 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
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
qis not required because of the default value= None.The
UnioninUnion[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 thatNoneas 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.
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
model.model_dump(...) instead.You can use exclude_none param of Pydantic's model.dict(...):
class Text(BaseModel):
id: str
text: str = None
class TextsRequest(BaseModel):
data: list[Text]
n_processes: Optional[int]
request = TextsRequest(**{"data": [{"id": "1", "text": "The text 1"}]})
print(request.dict(exclude_none=True))
Output:
{'data': [{'id': '1', 'text': 'The text 1'}]}
Also, it's more idiomatic to write Optional[int] instead of Union[int, None].
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With