BaseFirestoreModel. Because BaseFirestoreModel is itself a Pydantic BaseModel, you get automatic data validation, type coercion, and IDE auto-complete for free — the ODM simply adds the Firestore-specific plumbing on top.
BaseFirestoreModel and Pydantic BaseModel
BaseFirestoreModel is a thin subclass of Pydantic’s BaseModel. All standard Pydantic features — field types, validators, Field(default=...), aliases, and nested models — work exactly as you would expect. The ODM extends the class with:
- Class-level CRUD methods (
save,update,delete,find,find_one,get). - A class variable
_dbthat holds the injectedFirestoreDBconnection. - A private instance attribute
_parent_pathused to track subcollection document paths. - A class-level registry
_registered_modelsused for cascade deletes.
The Settings Inner Class
Every model must declare an inner Settings class that describes how the model maps to Firestore.
| Attribute | Type | Purpose |
|---|---|---|
name | str | The Firestore collection name for top-level documents. |
parent | Type[BaseFirestoreModel] | The parent model type. Set this for subcollection models only. |
The Automatic id Field
Every BaseFirestoreModel subclass automatically gains an id field defined as Optional[str] = Field(default=None). It maps directly to the Firestore document ID.
- When you call
save()andidisNone, Firestore auto-generates one and assigns it back toself.id. - When you supply an explicit
idbefore saving, that value is used as the document ID — if a document with that ID already exists,save()raises aRuntimeError. - After any
find,find_one, orgetcall, the returned instance always hasidpopulated.
The
id field is excluded from the data written to Firestore. It is never stored inside the document body — Firestore manages it as the document reference key. The ODM always passes exclude={"id"} when serialising a model for writes.Field Definitions
Fields are declared the same way as in any Pydantic model. Standard Python type annotations,Optional fields with defaults, and Pydantic Field() with aliases are all fully supported.
The initialize_fields() Class Method
initialize_fields() is called automatically by init_firestore_odm() during application start-up. You rarely need to call it manually.
Internally, it iterates over every Pydantic field on the class and attaches a FirestoreField descriptor as a class-level attribute. This gives you expressive, composable query syntax at the class level:
user.age still gives you the integer.
Initialising the Database Connection
There are two ways to wire up theFirestoreDB client before using any model.
- init_firestore_odm() — recommended
- initialize_db() — per-model
init_firestore_odm() is the preferred approach. It accepts the database instance and a list of all model classes, registers them in _registered_models for cascade-delete discovery, injects _db into each class, and calls initialize_fields() on all of them in one shot.Complete Model Example
The following example defines two models — a top-levelUser and a Post subcollection — and shows how to initialise the ODM.
Pydantic v1 / v2 Compatibility
Firestore Pydantic ODM is tested against both Pydantic v1 and v2. Internally it uses a compatibility shim (pydantic_compat) that normalises differences between the two versions — model_dump() vs dict(), model_fields vs __fields__, ConfigDict vs the inner Config class, and so on. Your model definitions do not need any version-specific code; write standard Pydantic models and the ODM handles the rest.
Internally, the ODM sets the Pydantic model configuration as follows:
- Pydantic v2 ≥ 2.11 —
model_config = ConfigDict(validate_by_name=True, validate_by_alias=True)(the config key names changed in v2.11). - Pydantic v2 < 2.11 —
model_config = ConfigDict(populate_by_name=True). - Pydantic v1 — an inner
Configclass withallow_population_by_field_name = Trueandallow_population_by_alias = True.
Database Client
Configure production credentials, the emulator, and test mocks.
Subcollections
Model parent-child relationships and query nested collections.
