简体   繁体   中英

How to exclude Optional unset values from a Pydantic model using FastAPI?

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?

You can use exclude_none param of 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]

Pydantic provides the following arguments for exporting models using the model.dict(...) method:

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 (ie, 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.dict(exclude_none=True))
#> {'data': [{'id': '1', 'text': 'The text 1'}]}
print(t.dict(exclude_unset=True))
#> {'data': [{'id': '1', 'text': 'The text 1'}], 'n_processes': None}

About Optional Parameters

Using Union[int, None] is the same as using Optional[int] = None or int | None int | None (in Python 3.10+). However, 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 (ie, Union , Optional , etc.) one may decide to use, if it is not followed by the = None part, FastAPI won't know that the value of the parameter is not required , and hence, the user will have to provide some value for it. One can also check this 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:

@app.post("/upload")
def upload(t: Union[TextsRequest, None]):
def upload(t: Optional[TextsRequest]):
def upload(t: TextsRequest | None):

any of the above would require the user to pass some body for the TextsRequest model in their request. If, however, the above TextsRequest definitions were succeeded by = None (eg, t: Union[TextsRequest, None] = None , or even simply t: TextsRequest = None ), the parameter or body would be optional (as = None would tell FastAPI that this parameter is not required ).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM