diff --git a/app/models/base.py b/app/models/base.py new file mode 100644 index 0000000..dd6656b --- /dev/null +++ b/app/models/base.py @@ -0,0 +1,52 @@ +"""Base model and reusable mixins for the CRM system.""" + +from __future__ import annotations + +from datetime import datetime +from typing import Optional + +from sqlalchemy import DateTime, ForeignKey, func +from sqlalchemy.orm import Mapped, mapped_column + +from app.core.db import Base + + +class TimestampMixin: + """Adds created_at and updated_at columns to a model.""" + + created_at: Mapped[datetime] = mapped_column( + DateTime(timezone=True), + server_default=func.now(), + nullable=False, + index=True, + ) + updated_at: Mapped[datetime] = mapped_column( + DateTime(timezone=True), + server_default=func.now(), + onupdate=func.now(), + nullable=False, + ) + + +class SoftDeleteMixin: + """Adds deleted_at column for soft-delete pattern.""" + + deleted_at: Mapped[Optional[datetime]] = mapped_column( + DateTime(timezone=True), + nullable=True, + default=None, + index=True, + ) + + +class OrgScopedMixin: + """Adds org_id foreign key. All queries must filter by org_id (OrgScopedQuery).""" + + org_id: Mapped[int] = mapped_column( + ForeignKey("orgs.id", ondelete="CASCADE"), + nullable=False, + index=True, + ) + + +__all__ = ["Base", "OrgScopedMixin", "SoftDeleteMixin", "TimestampMixin"]