Firestore Pydantic ODM
A modern async Object-Document Mapper (ODM) for Google Cloud Firestore built with Pydantic.
Firestore Pydantic ODM provides a fully typed, asynchronous, and Pythonic interface for Google Cloud Firestore. It combines Pydantic's validation with Firestore's scalability, allowing you to build applications with clean models, async CRUD operations, transactions, batch writes, subcollections, and efficient field projections.
📚 New to Firestore Pydantic ODM?
Start with the documentation:
Quick Links
| 🚀 Installation | https://fpo-python.santosdev.com/installation |
| ⚡ Quick Start | https://fpo-python.santosdev.com/quickstart |
| 📚 Concepts | https://fpo-python.santosdev.com/concepts/models |
| 🔍 Querying | https://fpo-python.santosdev.com/guides/querying |
| 📖 API Reference | https://fpo-python.santosdev.com/api/base-firestore-model |
Why Firestore Pydantic ODM?
- ✅ Fully asynchronous API (
async/await) - ✅ Pydantic v1 & v2 support
- ✅ Fully typed queries and models
- ✅ CRUD operations
- ✅ Batch writes & transactions
- ✅ Subcollections
- ✅ Field projections (fetch only the fields you need)
- ✅ Firestore Emulator support
- ✅ Built for production
Fully Tested
Every release runs integration tests against a real Firestore instance across Python 3.9–3.12 and both Pydantic v1 and v2.
View CI results → https://github.com/santosdevco/firestore-pydantic-odm/actions/workflows/release.yml
Installation
pip install firestore-pydantic-odm
Quick Start
1 · Define a model
from firestore_pydantic_odm import BaseFirestoreModel
class User(BaseFirestoreModel):
class Settings:
name = "users" # Firestore collection name
name: str
email: str
2 · Initialise Firestore
from firestore_pydantic_odm import FirestoreDB, BaseFirestoreModel
db = FirestoreDB(project_id="my-project", emulator_host="localhost:8080") # optional emulator
BaseFirestoreModel.initialize_db(db,[User]) # IMPORTANT the second parameter is a list with all models to initialize
3 · Async CRUD
user = User(name="Alice", email="alice@example.com")
await user.save() # CREATE
user.email = "alice@new.com"
await user.update() # UPDATE
await user.delete() # DELETE
3.1 · Subcollections
Declare parent relationships on the child model using Settings.parent.
class Post(BaseFirestoreModel):
class Settings:
name = "posts"
parent = User # Post lives under a User
title: str
body: str
Create and query subcollection documents by passing parent=:
user = User(name="Alice", email="alice@example.com")
await user.save()
post = Post(title="Hello", body="World")
await post.save(parent=user) # users/{user.id}/posts/{post.id}
async for p in Post.find(parent=user):
print(p.title)
You can also use the convenience accessor:
async for p in user.subcollection(Post).find():
print(p.title)
4 · Querying & Projections
# Simple filter
async for u in User.find(filters=[User.name == "Alice"]):
print(u)
# Single document
u = await User.find_one(filters=[User.email == "alice@new.com"])
Projections — selecting only the fields you need
from pydantic import BaseModel
class UserProjection(BaseModel):
name: str # only grab the `name` field
async for u in User.find(
filters=[User.age >= 18],
projection=UserProjection):
print(u.name) # `u` is an instance of UserProjection
# Fetch a single document with a projection
u = await User.find_one(
filters=[User.id == "abc123"],
projection=UserProjection)
How it works: the ODM converts
UserProjectioninto a Firestore field mask, so the RPC fetches only the columns defined in that class. Each item yielded byfind()(or returned byfind_one()) is therefore of typeUserProjection, giving you a cleanList[UserProjection]with exactly the data requested.
5 · Batch writes
from firestore_pydantic_odm import BatchOperation
ops = [
(BatchOperation.CREATE, User(name="Bob", email="bob@example.com")),
(BatchOperation.UPDATE, user), # previously fetched instance
(BatchOperation.DELETE, another_user) # instance with `id` set
]
await User.batch_write(ops)
Testing
The project ships with pytest and pytest-asyncio fixtures. To run the suite:
pytest
Set FIRESTORE_EMULATOR_HOST=localhost:8080 to run tests against the local emulator instead of production Firestore.
Contributing
- Fork the repository
git checkout -b feature/awesome- Write code & tests; ensure all tests pass
- Open a Pull Request describing your improvements
License
Distributed under the BSD 3-Clause License.
See the LICENSE file for full text.
Metadata
Release files for firestore-pydantic-odm 1.0.22
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| firestore_pydantic_odm-1.0.22.tar.gz | 24.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| firestore_pydantic_odm-1.0.22-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 39.9 kB
Release files / firestore_pydantic_odm-1.0.22.tar.gz
| Download URL | firestore_pydantic_odm-1.0.22.tar.gz |
|---|---|
| Size | 24.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
936e77b48744e54eb0ffbf83007f9c44fa1fbb317278039d2735e06485b343ce
|
|
BLAKE2b-256 checksum How to use checksums |
256731a6f3345ec39a13624dc08debbefccf9f619d82724be0d8e63de8e74769
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.12.8
|
Release files / firestore_pydantic_odm-1.0.22-py3-none-any.whl
| Download URL | firestore_pydantic_odm-1.0.22-py3-none-any.whl |
|---|---|
| Size | 15.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
0957084f1084e489ec6becbf95b5545be0f923dd1796a923deb02910d404c23a
|
|
BLAKE2b-256 checksum How to use checksums |
495da32105968272af574367489ad1e881291ba53347631c7ab4f187abe8b1e0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.12.8
|