Beanie
Async Python ODM for MongoDB built on Pydantic. Current version: 2.0.1 (Nov 2025). v2.0 breaking change: dropped Motor in favor of pymongo AsyncMongoClient. Requires Pydantic v2. Inner class Collection removed — use Settings instead. Must call init_beanie() before any document operations. Sync version is Bunnet (separate package).
Warnings
- breaking Beanie v2.0 dropped Motor. Use pymongo AsyncMongoClient instead of AsyncIOMotorClient. Motor-based code breaks with ImportError or unexpected behavior.
- breaking Inner class Collection removed in v2. Use Settings instead. Collection still referenced in most tutorials and LLM-generated code.
- breaking Beanie v2.0 requires Pydantic v2. Pydantic v1 patterns (@validator, class Config) break. See pydantic registry entry for migration.
- breaking Sync usage removed in beanie. There is no sync version. Use Bunnet (pip install bunnet) for synchronous MongoDB access.
- gotcha init_beanie() must be called before any Document operation. Calling Document.find() or Document.insert() before init raises RuntimeError. Common mistake in FastAPI startup.
- gotcha All document models must be passed to init_beanie(document_models=[...]). Missing a model means that model's collection is not configured — operations silently fail or error.
- gotcha allow_index_dropping defaults to False. Indexes are not deleted automatically when removed from model. Old indexes accumulate silently.
Install
-
pip install beanie
Imports
- Document + init_beanie
from pymongo import AsyncMongoClient from beanie import Document, Indexed, init_beanie from pydantic import BaseModel from typing import Optional class Category(BaseModel): name: str class Product(Document): name: str price: Indexed(float) category: Optional[Category] = None class Settings: # v2 style — not Collection name = 'products' async def init(): client = AsyncMongoClient('mongodb://localhost:27017') await init_beanie( database=client.mydb, document_models=[Product] ) - CRUD operations
# All operations require init_beanie() to have been called first # Create product = await Product(name='Widget', price=9.99).insert() # or: product = await Product.insert_one(Product(name='Widget', price=9.99)) # Find products = await Product.find(Product.price < 10.0).to_list() product = await Product.find_one(Product.name == 'Widget') product = await Product.get(product_id) # by _id # Update await product.set({Product.price: 12.99}) # or: product.price = 12.99 await product.save() # Delete await product.delete()
Quickstart
# pip install beanie
from pymongo import AsyncMongoClient
from beanie import Document, Indexed, init_beanie
from typing import Optional
import asyncio
class Product(Document):
name: str
price: Indexed(float)
in_stock: bool = True
class Settings:
name = 'products' # MongoDB collection name
async def main():
client = AsyncMongoClient('mongodb://localhost:27017')
await init_beanie(database=client.shop, document_models=[Product])
# Insert
p = await Product(name='Widget', price=9.99).insert()
print(p.id) # ObjectId
# Query
cheap = await Product.find(Product.price < 20.0).to_list()
one = await Product.find_one(Product.name == 'Widget')
# Update
await one.set({Product.price: 11.99})
# Delete
await one.delete()
asyncio.run(main())