"""User — a person with login. Identity is internal so one user can link multiple auth providers later (the provider-link table arrives with the auth slice). ``hashed_password`` is nullable: external/OIDC users have none. """ import uuid from datetime import datetime from sqlalchemy import DateTime, ForeignKey, String from sqlalchemy.orm import Mapped, mapped_column from app.models.base import Base from app.models.mixins import SoftDelete, Timestamps, UUIDPrimaryKey class User(Base, UUIDPrimaryKey, Timestamps, SoftDelete): __tablename__ = "users" email: Mapped[str] = mapped_column(String(320), unique=True, index=True) email_verified_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True)) display_name: Mapped[str | None] = mapped_column(String(255)) hashed_password: Mapped[str | None] = mapped_column(String(255)) # The Person record that *is* this user ("home person"). Cleared if that # person is deleted, so the link can never dangle. self_person_id: Mapped[uuid.UUID | None] = mapped_column( # use_alter + explicit name: users<->persons<->trees form an FK cycle, # so this constraint must be created/dropped via ALTER, not inline. ForeignKey( "persons.id", ondelete="SET NULL", name="fk_users_self_person_id", use_alter=True, ) )