Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pydantic: Detect if a field value is missing or given as null

I want to allow users to selectively update fields using PUT calls. On the pydantic model, I have made the fields Optional. In the FastAPI handler if the model attribute is None, then the field was not given and I do not update it.

The problem with this approach is that there is no way for the client to "blank out" a field that isn't required for certain types.

In particular, I have date fields that I want clients to be able to clear by sending in a null in the JSON. How can I detect the difference between the client sending null or the client not sending the field/value at all? The model attribute is just None in either case.

like image 206
Jeremy Avatar asked Feb 16 '21 17:02

Jeremy


People also ask

What is __ root __ In Pydantic?

Pydantic models can be defined with a custom root type by declaring the __root__ field. The root type can be any type supported by pydantic, and is specified by the type hint on the __root__ field.

What is Pydantic Constr?

constr is a specific type that give validation rules regarding this specific type. You have equivalent for all classic python types.

What is Pydantic field?

Pydantic is a useful library for data parsing and validation. It coerces input types to the declared type (using type hints), accumulates all the errors using ValidationError & it's also well documented making it easily discoverable.

What is the use of optional in pydantic?

The type Optional [x] is a shorthand for Union [x, None]. Optional [x] can also be used to specify a required field that can take None as a value. See more details in Required Fields. pydantic uses python's standard enum classes to define choices. Pydantic supports the following datetime types: datetime fields can be:

What is the difference between callable and pydantic fields?

Callable fields only perform a simple check that the argument is callable; no validation of arguments, their types, or the return type is performed. pydantic supports the use of Type [T] to specify that a field may only accept classes (not instances) that are subclasses of T.

How to dynamically modify a pydantic field according to another?

Newer versions of pydantic (starting with 0.20) handle your use case much better. If you want to be able to dynamically modify a field according to another one, you can use the values argument. It holds all the previous fields, and careful: the order matters. You could do this either using a validator or a root_validator.

What is the best way to validate a pydantic model?

In most of the cases you will probably be fine with standard pydantic models. You can use Generic Classes as field types and perform custom validation based on the "type parameters" (or sub-types) with __get_validators__.


Video Answer


2 Answers

The pydantic documentation desccribes two options that can be used with the .dict() method of models.

  • exclude_unset: whether fields which were not explicitly set when creating the model should be excluded from the returned dictionary; default False. Prior to v1.0, exclude_unset was known as skip_defaults; use of skip_defaults is now deprecated

  • exclude_defaults: whether fields which are equal to their default values (whether set or otherwise) should be excluded from the returned dictionary; default False

So you can create a model class with optional fields:

from typing import Optional
from pydantic import BaseModel


class MyModel(BaseModel):
    foo: Optional[int] = None
    bar: Optional[int] = None

And still generate a dict with fields explicitely set to None, but without default values:

baz = MyModel(foo=None)
assert baz.dict(exclude_unset=True) == {"foo": None}

baz = MyModel(bar=None)
assert baz.dict(exclude_unset=True) == {"bar": None}

like image 56
gcharbon Avatar answered Oct 20 '22 12:10

gcharbon


You can check obj.__fields_set__ to see whether the value was missing or not.

from typing import Optional
from pydantic import BaseModel

class Foo(BaseModel):
    first: Optional[int] = None
    second: Optional[int] = None

foo = Foo.parse_raw('{"first": null}')

assert foo.first is None and foo.second is None
assert foo.__fields_set__ == {"first"}
like image 33
alex_noname Avatar answered Oct 20 '22 12:10

alex_noname