24

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']}

4 Answers 4

36

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.

Sign up to request clarification or add additional context in comments.

4 Comments

Thanks! For those who wish to convert an object back to dict containing the _id, just use User_1(_id=1234).dict(by_alias=True) so you end up with a dict having the _id key.
using Field didn't work, i used Config.fields instead
could you explain this more?
In old versions of pydantic you could configure more details by creating a class named "Config" inside your model (docs.pydantic.dev/1.10/usage/model_config). Until version 2.10 they would still mention the old way (docs.pydantic.dev/2.10/concepts/config). But you shouldn't use it nowadays
6

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.

Comments

0

Okay so I recently needed to create a data object reference with Pydantic. I was able to get

Object Definition

class User(BaseModel):
    '''
        User Model for Deserialization With MongoDB
    '''
    id: str
    username: str
    api_key: str
    expiration: datetime
    last_used: datetime 
    created_at: datetime
    updated_at: datetime

Attempted Unpacking Object

I gave up on trying to unpack the object with the following, clearly there is something stupid I am missing here.

user = User(id=str(found_user["_id"]), **found_user)

Error Output

_id
  Input should be a valid string [type=string_type, input_value=ObjectId('6942069420'), input_type=ObjectId]
    For further information visit https://errors.pydantic.dev/2.12/v/string_type

Explicit Object Fields

Adding the fields explicitly. This feels stupid but works.

 user = User(id=str(found_user['_id']),
                            username=found_user['username'],
                            api_key=found_user['api_key'],
                            expiration=found_user['expiration'],
                            last_used=found_user['last_used'],
                            created_at=found_user['created_at'],
                            updated_at=found_user['updated_at'])

Comments

-1

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.