innercontext/backend/innercontext/models/routine.py
Piotr Oleszczyk f72d5ba1b7 fix(models): add cascade delete-orphan to parent-child relationships
Without cascade, SQLAlchemy tried to NULL-out foreign keys on child rows
before deleting the parent, hitting NOT NULL constraints in PostgreSQL.

- Routine.steps: cascade="all, delete-orphan" (routine_steps.routine_id)
- MedicationEntry.usage_history: cascade="all, delete-orphan"
  (medication_usages.medication_record_id)

Product.inventory already had cascade set correctly.
No DB migration needed — ORM-level only.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-01 00:59:10 +01:00

73 lines
2.4 KiB
Python

from datetime import date, datetime
from typing import TYPE_CHECKING, ClassVar, List, Optional
from uuid import UUID, uuid4
from sqlalchemy import Column, DateTime, UniqueConstraint
from sqlmodel import Field, Relationship, SQLModel
from .base import utc_now
from .domain import Domain
from .enums import GroomingAction, PartOfDay
if TYPE_CHECKING:
from .product import Product
class Routine(SQLModel, table=True):
__tablename__ = "routines"
__domains__: ClassVar[frozenset[Domain]] = frozenset({Domain.SKINCARE})
__table_args__ = (
UniqueConstraint(
"routine_date", "part_of_day", name="uq_routine_date_part_of_day"
),
)
id: UUID = Field(default_factory=uuid4, primary_key=True)
routine_date: date = Field(index=True)
part_of_day: PartOfDay = Field(index=True)
notes: str | None = Field(default=None)
created_at: datetime = Field(default_factory=utc_now, nullable=False)
updated_at: datetime = Field(
default_factory=utc_now,
sa_column=Column(
DateTime(timezone=True),
default=utc_now,
onupdate=utc_now,
nullable=False,
),
)
steps: List["RoutineStep"] = Relationship(
back_populates="routine",
sa_relationship_kwargs={"cascade": "all, delete-orphan"},
)
class GroomingSchedule(SQLModel, table=True):
__tablename__ = "grooming_schedule"
__domains__: ClassVar[frozenset[Domain]] = frozenset({Domain.SKINCARE})
id: UUID = Field(default_factory=uuid4, primary_key=True)
day_of_week: int = Field(ge=0, le=6, index=True) # 0 = poniedziałek, 6 = niedziela
action: GroomingAction
notes: str | None = Field(default=None)
class RoutineStep(SQLModel, table=True):
__tablename__ = "routine_steps"
__domains__: ClassVar[frozenset[Domain]] = frozenset({Domain.SKINCARE})
id: UUID = Field(default_factory=uuid4, primary_key=True)
routine_id: UUID = Field(foreign_key="routines.id", index=True)
product_id: UUID | None = Field(default=None, foreign_key="products.id", index=True)
order_index: int = Field(ge=0)
action_type: GroomingAction | None = Field(default=None)
action_notes: str | None = Field(default=None)
dose: str | None = Field(default=None)
region: str | None = Field(default=None)
routine: Routine = Relationship(back_populates="steps")
product: Optional["Product"] = Relationship()