- Published on
Working with Beanie ODM for MongoDB
- Authors
- Name
- Ajitesh Abhishek
- @ajiteshleo
Introduction
I was working on a Python project recently that was getting complex. We used Beanie to simplify the ODM aspect. The documentation required multiple iterations to figure out how to implement it correctly in my codebase. Here are a few things I learned that I'm sharing with you. As long as you follow these conventions, there are many benefits to using Beanie, which is what I hope this blog demonstrates.
Some critical things to note:
- The Document class, although inherited from the Pydantic BaseModel class, has some additional features:
- id is automatically added to the model (it's stored as _id in the database and converted to id in the response)
- Indexed fields are automatically added to the model
- When you inherit from MongoModel, you get the following features:
- id is automatically added to the model
- Indexed fields are automatically added to the model
So when defining a schema for FastAPI model, you should inherit from MongoModel instead of BaseModel (Pydantic).
For FastAPI responses to not return the alias (_id), we need to use response_model_by_alias=False in addition to the new type of schema (inherited from MongoModel)
One additional consideration is that when querying the database, you need to convert the string id to ObjectId and assign it to the _id field. This has to be done manually.
As long as you follow these conventions, you get the following features:
- JSON serialization/deserialization: Data returned from the database is automatically converted to the FastAPI model (including id vs _id)
- Automatic index management
- No complex handling of _id and id in the code
Usage Guide
1. Model Definition
Models should inherit from MongoModel
instead of Pydantic's BaseModel
:
from core.mongodb import MongoModel
class User(MongoModel):
name: str
email: str
class Settings:
name = "users" # MongoDB collection name
2. Schema Definition
Response schemas should also inherit from MongoModel
:
class UserResponse(MongoModel):
name: str
email: str
3. FastAPI Integration
When using with FastAPI endpoints, always set response_model_by_alias=False
:
@router.get(
"/user/{user_id}",
response_model=UserResponse,
response_model_by_alias=False # Important!
)
async def get_user(user_id: str) -> UserResponse:
user = await User.get(user_id)
return user
4. Working with IDs
When querying by ID, convert string IDs to MongoDB ObjectId:
from bson import ObjectId
# Querying by ID
query = {"_id": ObjectId(id)}
response = await User.get(query)
For more detailed information, check out the Beanie documentation, MongoDB documentation, and FastAPI documentation.