From 394a9109416c1a4d2322026ffd1d06cd0017b179 Mon Sep 17 00:00:00 2001 From: Leopoldadmin Date: Thu, 4 Jun 2026 00:06:19 +0000 Subject: [PATCH] Upload app/models/tag_link.py --- app/models/tag_link.py | 50 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 app/models/tag_link.py diff --git a/app/models/tag_link.py b/app/models/tag_link.py new file mode 100644 index 0000000..1332f36 --- /dev/null +++ b/app/models/tag_link.py @@ -0,0 +1,50 @@ +"""TagLink model: polymorphic many-to-many between tags and accounts/contacts/deals.""" + +from __future__ import annotations + +from enum import Enum +from typing import TYPE_CHECKING + +from sqlalchemy import ForeignKey, Integer, String, UniqueConstraint +from sqlalchemy.orm import Mapped, mapped_column, relationship + +from app.models.base import Base, OrgScopedMixin, SoftDeleteMixin, TimestampMixin + +if TYPE_CHECKING: + from app.models.tag import Tag + + +class TagLinkParentType(str, Enum): + """Polymorphic parent type for tag links (no DB FK, validated in service layer).""" + + account = "account" + contact = "contact" + deal = "deal" + + +class TagLink(Base, TimestampMixin, SoftDeleteMixin, OrgScopedMixin): + __tablename__ = "tag_links" + __table_args__ = ( + UniqueConstraint( + "tag_id", "parent_type", "parent_id", name="uq_tag_link" + ), + ) + + id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True) + tag_id: Mapped[int] = mapped_column( + ForeignKey("tags.id"), nullable=False, index=True + ) + parent_type: Mapped[TagLinkParentType] = mapped_column( + String(32), nullable=False, index=True + ) + # parent_id is intentionally NOT a DB FK because of polymorphic parent. + # Service layer (tag_service.link_tag) validates existence. + parent_id: Mapped[int] = mapped_column(Integer, nullable=False, index=True) + + tag: Mapped["Tag"] = relationship("Tag", back_populates="links", lazy="joined") + + def __repr__(self) -> str: + return f"" + + +__all__ = ["TagLink", "TagLinkParentType"]