I am trying to parse MongoDB records to a pydantic model but failing to do so for ObjectId
From what I understood, I need to setup validator for ObjectId and did try to both extend ObjectId class and add the validator
decorator to my class using ObjectId. which I did as follows.
from pydantic import BaseModel, validator
from bson.objectid import ObjectId
class ObjectId(ObjectId):
pass
@classmethod
def __get_validators__(cls):
yield cls.validate
@classmethod
def validate(cls, v):
if not isinstance(v, ObjectId):
raise TypeError('ObjectId required')
return str(v)
class User(BaseModel):
who: ObjectId
class User1(BaseModel):
who: ObjectId
@validator('who')
def validate(cls, v):
if not isinstance(v, ObjectId):
raise TypeError('ObjectId required')
return str(v)
data = {"who":ObjectId('123456781234567812345678')}
Unfortunately, both "solution" are failing as follows:
>>> test = User(**data)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "pydantic/main.py", line 274, in pydantic.main.BaseModel.__init__
pydantic.error_wrappers.ValidationError: 1 validation error for User
id
field required (type=value_error.missing)
>>> test = User1(**data)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "pydantic/main.py", line 274, in pydantic.main.BaseModel.__init__
pydantic.error_wrappers.ValidationError: 1 validation error for User1
who
ObjectId required (type=type_error)
There is definitely something that I am missing here.
You first test case works fine. The problem is with how you overwrite ObjectId
.
from pydantic import BaseModel
from bson.objectid import ObjectId as BsonObjectId
class PydanticObjectId(BsonObjectId):
@classmethod
def __get_validators__(cls):
yield cls.validate
@classmethod
def validate(cls, v):
if not isinstance(v, BsonObjectId):
raise TypeError('ObjectId required')
return str(v)
class User(BaseModel):
who: PydanticObjectId
print(User(who=BsonObjectId('123456781234567812345678')))
prints
who='123456781234567812345678'
Only pydantic should use pydantic type. Mongo will provide you with bsons ObjectId. So instantiate your data with real ObjectId.
So data = {"who":ObjectId('123456781234567812345678')}
is wrong, as it uses your child ObjectId class.
Use AfterValidator https://docs.pydantic.dev/latest/usage/validators/
from typing_extensions import Annotated
from pydantic import BaseModel
from pydantic.functional_validators import AfterValidator
from bson import ObjectId as _ObjectId
def check_object_id(value: str) -> str:
if not _ObjectId.is_valid(value):
raise ValueError('Invalid ObjectId')
return value
ObjectId = Annotated[str, AfterValidator(check_object_id)]
class Example(BaseModel):
id: ObjectId
print(Example(id='5f9b3b3b9d9f3d0001a3b3b3'))
print(Example(id='1'))