roshii
roshii

Reputation: 627

How to parse and read "_id" field from and to a pydantic model?

I am trying to parse MongoDB data to a pydantic schema but fail to read its _id field which seem to just disappear from the schema.
The issue is definitely related to the underscore in front of the object attribute. I can't change _id field name since that would imply not parsing the field at all.
Please find below the code I use (using int instead of ObjectId for the sake of simplification)

from pydantic import BaseModel

class User_1(BaseModel):
    _id: int

data_1 = {"_id": 1}

parsed_1 = User_1(**data_1)
print(parsed_1.schema())

class User_2(BaseModel):
    id: int

data_2 = {"id": 1}

parsed_2 = User_2(**data_2)
print(parsed_2.schema())

User_1 is parsed successfully since its _id field is required but can't be read afterwards. User_2 works in the above example by fails if attached to Mongo which doesn't provide any id field but _id.

Output of the code above reads as follows:

User_1  {'title': 'User_1', 'type': 'object', 'properties': {}}
User_2  {'title': 'User_2', 'type': 'object', 'properties': {'id': {'title': 'Id', 'type': 'integer'}}, 'required': ['id']}

Upvotes: 24

Views: 21431

Answers (3)

hem charan
hem charan

Reputation: 125

The other two answers don't work for me, I don't know why. Maybe the versions are deprecated, here is the updated answer:

from pydantic import Field
from bson import ObjectId 
from typing import Optional

id: Optional[ObjectId] = Field(alias='_id')
title: str
cover: str

class Config:
    json_encoders = {
        ObjectId: str
    }
    arbitrary_types_allowed = True

Upvotes: -1

Zeailer
Zeailer

Reputation: 66

I know that you had asked this ages ago, but for anyone else still struggling with it here's a suggested solution by MongoDb

When applying their solution we get the following code.

from typing import Optional, Annotated
from pydantic import BaseModel, Field, BeforeValidator

PyObjectId = Annotated[str, BeforeValidator(str)]

class User_1(BaseModel):
    id: Optional[PyObjectId] = Field(alias="_id", default=None)

All the validation and model conversions work just fine, without any class Config, or other workarounds. Hope this helps somebody.

Upvotes: 5

Samuel Colvin
Samuel Colvin

Reputation: 13349

you need to use an alias for that field name

from pydantic import BaseModel, Field

class User_1(BaseModel):
    id: int = Field(..., alias='_id')

See the docs here.

Upvotes: 36

Related Questions