How to use two variable types in a pydantic.BaseModel with typing.Union?
Question:
I need my model to accept either a bytes type variable or a string type variable and to raise an exception if any other type was passed.
from typing import Union
from pydantic import BaseModel
class MyModel(BaseModel):
a: Union[bytes, str]
m1 = MyModel(a='123')
m2 = MyModel(a=b'123')
print(type(m1.a))
print(type(m2.a))
In my case the model interprets both bytes and string as bytes.
Output:
<class 'bytes'>
<class 'bytes'>
Desired output:
<class 'str'>
<class 'bytes'>
The desired output above can be achieved if I re-assign member a:
m1 = MyModel(a='123')
m1.a = '123'
Is it possible to get it in one go?
Answers:
The problem you are facing is that the str
type does some automatic conversions (here in the docs):
strings are accepted as-is, int
float
and Decimal
are coerced using str(v)
, bytes
and bytearray
are converted using v.decode()
, enums inheriting from str
are converted using v.value
, and all other types cause an error
bytes
are accepted as-is, bytearray
is converted using bytes(v)
, str
are converted using v.encode()
, and int
, float
, and Decimal
are coerced using str(v).encode()
You can use StrictTypes
to avoid automatic conversion between compatible types (e.g.: str
and bytes
):
from typing import Union
from pydantic import BaseModel, StrictStr, StrictBytes
class MyModel(BaseModel):
a: Union[StrictStr, StrictBytes]
m1 = MyModel(a='123')
m2 = MyModel(a=b'123')
print(type(m1.a))
print(type(m2.a))
Output will be as expected:
<class 'str'>
<class 'bytes'>
I need my model to accept either a bytes type variable or a string type variable and to raise an exception if any other type was passed.
from typing import Union
from pydantic import BaseModel
class MyModel(BaseModel):
a: Union[bytes, str]
m1 = MyModel(a='123')
m2 = MyModel(a=b'123')
print(type(m1.a))
print(type(m2.a))
In my case the model interprets both bytes and string as bytes.
Output:
<class 'bytes'>
<class 'bytes'>
Desired output:
<class 'str'>
<class 'bytes'>
The desired output above can be achieved if I re-assign member a:
m1 = MyModel(a='123')
m1.a = '123'
Is it possible to get it in one go?
The problem you are facing is that the str
type does some automatic conversions (here in the docs):
strings are accepted as-is,
int
float
andDecimal
are coerced usingstr(v)
,bytes
andbytearray
are converted usingv.decode()
, enums inheriting fromstr
are converted usingv.value
, and all other types cause an error
bytes
are accepted as-is,bytearray
is converted usingbytes(v)
,str
are converted usingv.encode()
, andint
,float
, andDecimal
are coerced usingstr(v).encode()
You can use StrictTypes
to avoid automatic conversion between compatible types (e.g.: str
and bytes
):
from typing import Union
from pydantic import BaseModel, StrictStr, StrictBytes
class MyModel(BaseModel):
a: Union[StrictStr, StrictBytes]
m1 = MyModel(a='123')
m2 = MyModel(a=b'123')
print(type(m1.a))
print(type(m2.a))
Output will be as expected:
<class 'str'>
<class 'bytes'>