infrastructure.persistence.base¶
src.infrastructure.persistence.base
¶
Base model and mixins for all database entities.
This module provides: - BaseModel: Base class for ALL models (provides id, created_at) - TimestampMixin: Internal mixin that adds updated_at - BaseMutableModel: Recommended base for mutable models (combines above)
Following hexagonal architecture: - This is an infrastructure concern (database implementation detail) - Domain entities should NOT inherit from this - Domain entities are mapped to/from database models
Usage
For mutable models (can be updated) - RECOMMENDED¶
class User(BaseMutableModel): tablename = "users" email: Mapped[str] # Has: id, created_at, updated_at
For immutable models (cannot be updated)¶
class AuditLog(BaseModel): tablename = "audit_logs" action: Mapped[str] # Has: id, created_at (no updated_at)
Architecture
BaseModel (id, created_at) ↑ ├── BaseMutableModel (+ updated_at via TimestampMixin) │ ├── User │ └── Provider │ └── AuditLog (immutable, no updated_at)
Note: While we use PostgreSQL for Dashtam, we keep the base model reasonably database-agnostic by using SQLAlchemy's Uuid type which works across different databases.
Classes¶
BaseModel
¶
Bases: DeclarativeBase
Base class for all database models (mutable and immutable).
Provides common fields that ALL database models need: - id: UUID primary key (auto-generated) - created_at: Timestamp when record was created (UTC)
For mutable models, also inherit from TimestampMixin to get updated_at.
Example
Mutable model (can be updated)¶
class User(TimestampMixin, BaseModel): tablename = "users" email: Mapped[str] # Has: id, created_at, updated_at
Immutable model (cannot be updated)¶
class AuditLog(BaseModel): tablename = "audit_logs" action: Mapped[str] # Has: id, created_at (no updated_at)
This is an infrastructure concern - domain entities should not inherit from or depend on this class.
Source code in src/infrastructure/persistence/base.py
Functions¶
__repr__
¶
String representation for debugging.
Returns:
| Name | Type | Description |
|---|---|---|
str |
str
|
String showing class name and ID. |
to_dict
¶
Convert model to dictionary (for debugging/logging).
Returns:
| Name | Type | Description |
|---|---|---|
dict |
dict[str, Any]
|
Dictionary representation of the model. |
Note
Mutable models that use TimestampMixin will have updated_at added to the dictionary via the mixin's to_dict() extension.
Source code in src/infrastructure/persistence/base.py
TimestampMixin
¶
Mixin for mutable models that track updates.
Adds updated_at field that automatically updates on record modification.
Note
This is typically used via BaseMutableModel, not directly. Use BaseMutableModel instead of mixing TimestampMixin + BaseModel manually.
Direct usage (advanced): class CustomModel(TimestampMixin, SomeOtherMixin, BaseModel): tablename = "custom" # Order matters for MRO!
Recommended usage
Use BaseMutableModel instead (see below).
Source code in src/infrastructure/persistence/base.py
Functions¶
to_dict
¶
Extend BaseModel.to_dict() to include updated_at.
Returns:
| Name | Type | Description |
|---|---|---|
dict |
dict[str, Any]
|
Dictionary representation including updated_at. |
Note
This method extends the base to_dict() method using Python's MRO (Method Resolution Order). It calls super().to_dict() to get id and created_at, then adds updated_at.
Source code in src/infrastructure/persistence/base.py
BaseMutableModel
¶
Bases: TimestampMixin, BaseModel
Base class for mutable database models.
Combines TimestampMixin + BaseModel with proper MRO (Method Resolution Order). Use this for any model that can be modified after creation.
Provides
- id: UUID primary key (from BaseModel)
- created_at: Timestamp when created (from BaseModel)
- updated_at: Timestamp when last updated (from TimestampMixin)
Usage
class User(BaseMutableModel): tablename = "users" email: Mapped[str] password_hash: Mapped[str] # Has: id, created_at, updated_at
class Provider(BaseMutableModel): tablename = "providers" name: Mapped[str] # Has: id, created_at, updated_at
When NOT to use
For immutable models (like audit logs), use BaseModel directly:
class AuditLog(BaseModel): # No updated_at tablename = "audit_logs" action: Mapped[str] # Has: id, created_at (no updated_at)
Benefits
- No need to remember mixin order (handled here)
- Single source of truth for MRO
- Explicit intent ("this model is mutable")
- Easy to extend with more mixins in the future
Future Extension Example
Add more mixins here in correct order¶
class BaseMutableModel(SoftDeleteMixin, TimestampMixin, BaseModel): # All mutable models get soft-delete + timestamps pass