Adding lazy='raise' to relationships with cascade='all, delete-orphan' broke db.delete() — SQLAlchemy tried to lazy-load related objects for Python-side cascade but lazy='raise' blocked it with MissingGreenlet. Fix: Add passive_deletes=True to subtasks, comments, assignments, tasks, and members relationships. This tells SQLAlchemy to defer cascade to PostgreSQL's ondelete=CASCADE FK constraint instead of loading objects in Python. Both the FK and ORM cascade are now aligned. Also added onError handler to deleteTaskMutation so failures are visible via toast instead of failing silently. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
54 lines
2.4 KiB
Python
54 lines
2.4 KiB
Python
import sqlalchemy as sa
|
|
from sqlalchemy import String, Text, Integer, Date, ForeignKey, func
|
|
from sqlalchemy.orm import Mapped, mapped_column, relationship as sa_relationship
|
|
from datetime import datetime, date
|
|
from typing import Optional, List
|
|
from app.database import Base
|
|
|
|
|
|
class ProjectTask(Base):
|
|
__tablename__ = "project_tasks"
|
|
|
|
id: Mapped[int] = mapped_column(primary_key=True, index=True)
|
|
project_id: Mapped[int] = mapped_column(Integer, ForeignKey("projects.id", ondelete="CASCADE"), nullable=False)
|
|
parent_task_id: Mapped[Optional[int]] = mapped_column(
|
|
Integer, ForeignKey("project_tasks.id", ondelete="CASCADE"), nullable=True
|
|
)
|
|
title: Mapped[str] = mapped_column(String(255), nullable=False)
|
|
description: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
|
|
status: Mapped[str] = mapped_column(String(20), default="pending")
|
|
priority: Mapped[str] = mapped_column(String(20), default="medium")
|
|
due_date: Mapped[Optional[date]] = mapped_column(Date, nullable=True)
|
|
person_id: Mapped[Optional[int]] = mapped_column(Integer, ForeignKey("people.id", ondelete="SET NULL"), nullable=True)
|
|
sort_order: Mapped[int] = mapped_column(Integer, default=0)
|
|
version: Mapped[int] = mapped_column(Integer, default=1, server_default=sa.text("1"))
|
|
created_at: Mapped[datetime] = mapped_column(default=func.now())
|
|
updated_at: Mapped[datetime] = mapped_column(default=func.now(), onupdate=func.now())
|
|
|
|
# Relationships — lazy="raise" to prevent N+1 (mirrors CalendarMember pattern)
|
|
project: Mapped["Project"] = sa_relationship(back_populates="tasks", lazy="raise")
|
|
person: Mapped[Optional["Person"]] = sa_relationship(back_populates="assigned_tasks", lazy="raise")
|
|
parent_task: Mapped[Optional["ProjectTask"]] = sa_relationship(
|
|
back_populates="subtasks",
|
|
remote_side=[id],
|
|
lazy="raise",
|
|
)
|
|
subtasks: Mapped[List["ProjectTask"]] = sa_relationship(
|
|
back_populates="parent_task",
|
|
cascade="all, delete-orphan",
|
|
passive_deletes=True,
|
|
lazy="raise",
|
|
)
|
|
comments: Mapped[List["TaskComment"]] = sa_relationship(
|
|
back_populates="task",
|
|
cascade="all, delete-orphan",
|
|
passive_deletes=True,
|
|
lazy="raise",
|
|
)
|
|
assignments: Mapped[List["ProjectTaskAssignment"]] = sa_relationship(
|
|
back_populates="task",
|
|
cascade="all, delete-orphan",
|
|
passive_deletes=True,
|
|
lazy="raise",
|
|
)
|