Coverage for postrfp/model/tags.py: 100%
17 statements
« prev ^ index » next coverage.py v7.11.0, created at 2025-10-22 21:34 +0000
« prev ^ index » next coverage.py v7.11.0, created at 2025-10-22 21:34 +0000
1from typing import Optional
3from sqlalchemy import (
4 Table,
5 UniqueConstraint,
6 Integer,
7 ForeignKey,
8 Column,
9 CheckConstraint,
10)
11from sqlalchemy.orm import Mapped, mapped_column, relationship
12from sqlalchemy.types import VARCHAR
14from .humans import Organisation
15from .questionnaire.nodes import QuestionInstance
16from .meta import Base
18tags_qinstances_table = Table(
19 "tags_qinstances",
20 Base.metadata,
21 Column("tag_id", Integer, ForeignKey("tags.id", ondelete="CASCADE"), index=True),
22 Column(
23 "question_instance_id",
24 Integer,
25 ForeignKey(QuestionInstance.id, ondelete="CASCADE"),
26 index=True,
27 ),
28 UniqueConstraint("tag_id", "question_instance_id"),
29)
32class Tag(Base):
33 __tablename__ = "tags"
34 __table_args__ = (
35 UniqueConstraint("org_id", "name"),
36 # MariaDB/MySQL regex
37 CheckConstraint("colour REGEXP '^#[0-9A-Fa-f]{6}$'", name="ck_tag_colour_hex6"),
38 )
40 org_id: Mapped[Optional[str]] = mapped_column(
41 VARCHAR(length=50),
42 ForeignKey("organisations.id", ondelete="CASCADE", onupdate="CASCADE"),
43 nullable=True,
44 )
45 name: Mapped[str] = mapped_column(VARCHAR(length=128), nullable=False)
46 description: Mapped[Optional[str]] = mapped_column(
47 VARCHAR(length=256), nullable=True
48 )
49 colour: Mapped[Optional[str]] = mapped_column(
50 VARCHAR(length=7),
51 nullable=True,
52 comment="Hex color code (#RRGGBB)",
53 )
55 organisation: Mapped[Organisation] = relationship(
56 Organisation, primaryjoin=org_id == Organisation.id, back_populates="tags"
57 )
59 question_instances = relationship(
60 QuestionInstance,
61 secondary=tags_qinstances_table,
62 passive_deletes=True,
63 lazy="dynamic",
64 )