"""Citations attached to a sentence (句子引用关联表). A sentence may quote **several** references, which makes this a real many-to-many relation rather than a column on ``paper_sentence`` — hence a table of its own. Why there is no foreign key to a reference table ------------------------------------------------ References are going to be maintained in their own table later. This one therefore stores a plain ``reference_id`` integer and declares **no** foreign key at all: a key to a table that does not exist yet cannot be validated, declared or migrated. The column is nullable so a citation can be written before it has been linked to a reference row. What makes a citation valid --------------------------- ``quote`` — the quoted content — is required and must not be blank. A citation that points at something without saying what it points at is not usable in a document, so the API rejects it (``app.schemas.paper.CitationInput``) rather than storing a dangling half-record. The reverse is fine: a citation may carry its quoted content with ``reference_id`` still empty, and be linked later. """ from typing import TYPE_CHECKING from sqlalchemy import ForeignKey, Index, Integer, Text from sqlalchemy.orm import Mapped, mapped_column, relationship from app.db.base import Base if TYPE_CHECKING: # pragma: no cover - typing only from app.models.paper_sentence import PaperSentence class PaperSentenceReference(Base): """One citation of one sentence: what is quoted, and optionally by which id.""" __tablename__ = "paper_sentence_reference" __table_args__ = ( # Reads are "the citations of this sentence, in order". Index("ix_paper_sentence_reference_sentence_sort", "sentence_id", "sort"), # The reference side is indexed for the lookup that arrives with the # reference table: "which sentences quote reference N". Index("ix_paper_sentence_reference_reference_id", "reference_id"), ) id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) sentence_id: Mapped[int] = mapped_column( ForeignKey("paper_sentence.id", ondelete="CASCADE"), nullable=False, ) #: The id this citation will resolve to once the reference library exists. #: Deliberately not a foreign key yet — see the module docstring. reference_id: Mapped[int | None] = mapped_column(Integer, nullable=True) #: 引用内容 — required, non-blank. The citation's whole payload today. quote: Mapped[str] = mapped_column(Text, nullable=False) #: Order among the citations of one sentence. Assigned from the order the #: client sent them, so ``[1]`` in the text is the first stored row. sort: Mapped[int] = mapped_column(Integer, nullable=False, default=0) sentence: Mapped["PaperSentence"] = relationship(back_populates="citations") def __repr__(self) -> str: # pragma: no cover - debugging aid return ( f"" )