mgcdanny
mgcdanny

Reputation: 1202

Python Pydantic - how to have an "optional" field but if present required to conform to not None value?

I am trying to validate an object that has "optional" fields in the sense that they may or may not be present. But when they are present, the fields should conform to a specific type definition (not None).

In the example below, the "size" field is optional but allows None. I want the "size" field to be optional, but if present it should be a float.

from pydantic import BaseModel

class Foo(BaseModel):
    count: int
    size: float = None  # how to make this an optional float? 

 >>> Foo(count=5)
 Foo(count=5, size=None)  # GOOD - "size" is not present, value of None is OK


 >>> Foo(count=5, size=None)
 Foo(count=5, size=None) # BAD - if field "size" is present, it should be a float

 # BONUS
 >>> Foo(count=5)
 Foo(count=5)  # BEST - "size" is not present, it is not required to be present, so we don't care about about validating it all.  We are using Foo.json(exclude_unset=True) handles this for us which is fine.

Upvotes: 24

Views: 48800

Answers (3)

elyte5star
elyte5star

Reputation: 321

@validator is deprecated in Pydantic V2. Use @field_validator instead.

from pydantic import BaseModel, field_validator
from typing import Optional

class Foo(BaseModel):
   count: int
   size: Optional[float] = None

   @field_validator("size")
   @classmethod
   def prevent_none(cls, v: float):
       assert v is not None, "size may not be None"
       return v

Upvotes: 1

Stig Johan B.
Stig Johan B.

Reputation: 421

It's possible to do with a validator.

from pydantic import BaseModel, ValidationError, validator

class Foo(BaseModel):
    count: int
    size: float = None

    @validator('size')
    def size_is_some(cls, v):
        if v is None:
            raise ValidationError('Cannot set size to None')
        return float(v)

This works as intended:

>>> Foo(count=5)
Foo(count=5, size=None)

>>> Foo(count=5, size=1.6)
Foo(count=5, size=1.6)

>>> Foo(count=5, size=None)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "[venv]/lib/python3.6/site-packages/pydantic/main.py", line 283, in __init__
    raise validation_error
pydantic.error_wrappers.ValidationError: 1 validation error for Foo
size
  Cannot set size to None (type=value_error)

Upvotes: 11

Yevhen Dmytrenko
Yevhen Dmytrenko

Reputation: 85

from pydantic import BaseModel
from typing import Optional

class Foo(BaseModel):
    count: int
    size: Optional[float]


obj = Foo(count=5)
obj_2 = Foo(count=5, size=1.6)

# count=5 size=None
print(obj)
# count=5 size=1.6
print(obj_2)

It can be confusing but Optional type from typing will give you possibility to have "optional" fields

Upvotes: 5

Related Questions