You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
102 lines
3.7 KiB
102 lines
3.7 KiB
from __future__ import annotations
|
|
|
|
from datetime import datetime
|
|
from typing import Any
|
|
|
|
from sqlalchemy import DateTime, ForeignKey, Index, String, UniqueConstraint
|
|
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
|
from werkzeug.security import check_password_hash, generate_password_hash
|
|
|
|
from app.models.base import BaseModel, ID_TYPE, TimestampMixin
|
|
|
|
|
|
class Account(BaseModel, TimestampMixin):
|
|
__tablename__ = "accounts"
|
|
__table_args__ = (
|
|
UniqueConstraint("username", name="uk_accounts_username"),
|
|
Index("idx_accounts_role_status", "role", "status"),
|
|
Index("idx_accounts_last_login_at", "last_login_at"),
|
|
)
|
|
|
|
id: Mapped[int] = mapped_column(ID_TYPE, primary_key=True)
|
|
username: Mapped[str] = mapped_column(String(64), nullable=False)
|
|
password_hash: Mapped[str] = mapped_column(String(255), nullable=False)
|
|
display_name: Mapped[str | None] = mapped_column(String(64), nullable=True)
|
|
role: Mapped[str] = mapped_column(String(32), nullable=False, default="editor")
|
|
status: Mapped[str] = mapped_column(String(16), nullable=False, default="active")
|
|
last_login_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True)
|
|
created_by: Mapped[int | None] = mapped_column(
|
|
ID_TYPE,
|
|
ForeignKey("accounts.id", ondelete="SET NULL"),
|
|
nullable=True,
|
|
)
|
|
updated_by: Mapped[int | None] = mapped_column(
|
|
ID_TYPE,
|
|
ForeignKey("accounts.id", ondelete="SET NULL"),
|
|
nullable=True,
|
|
)
|
|
|
|
creator: Mapped[Account | None] = relationship(
|
|
"Account",
|
|
remote_side="Account.id",
|
|
foreign_keys=[created_by],
|
|
post_update=True,
|
|
)
|
|
updater: Mapped[Account | None] = relationship(
|
|
"Account",
|
|
remote_side="Account.id",
|
|
foreign_keys=[updated_by],
|
|
post_update=True,
|
|
)
|
|
audit_logs: Mapped[list[Any]] = relationship("AuditLog", back_populates="actor")
|
|
created_households: Mapped[list[Any]] = relationship(
|
|
"Household",
|
|
foreign_keys="Household.created_by",
|
|
back_populates="created_by_account",
|
|
)
|
|
updated_households: Mapped[list[Any]] = relationship(
|
|
"Household",
|
|
foreign_keys="Household.updated_by",
|
|
back_populates="updated_by_account",
|
|
)
|
|
created_members: Mapped[list[Any]] = relationship(
|
|
"HouseholdMember",
|
|
foreign_keys="HouseholdMember.created_by",
|
|
back_populates="created_by_account",
|
|
)
|
|
updated_members: Mapped[list[Any]] = relationship(
|
|
"HouseholdMember",
|
|
foreign_keys="HouseholdMember.updated_by",
|
|
back_populates="updated_by_account",
|
|
)
|
|
created_gift_records: Mapped[list[Any]] = relationship(
|
|
"GiftRecord",
|
|
foreign_keys="GiftRecord.created_by",
|
|
back_populates="created_by_account",
|
|
)
|
|
updated_gift_records: Mapped[list[Any]] = relationship(
|
|
"GiftRecord",
|
|
foreign_keys="GiftRecord.updated_by",
|
|
back_populates="updated_by_account",
|
|
)
|
|
created_option_items: Mapped[list[Any]] = relationship(
|
|
"OptionItem",
|
|
foreign_keys="OptionItem.created_by",
|
|
back_populates="created_by_account",
|
|
)
|
|
updated_option_items: Mapped[list[Any]] = relationship(
|
|
"OptionItem",
|
|
foreign_keys="OptionItem.updated_by",
|
|
back_populates="updated_by_account",
|
|
)
|
|
created_share_links: Mapped[list[Any]] = relationship(
|
|
"ShareLink",
|
|
foreign_keys="ShareLink.created_by",
|
|
back_populates="creator",
|
|
)
|
|
|
|
def set_password(self, password: str) -> None:
|
|
self.password_hash = generate_password_hash(password)
|
|
|
|
def check_password(self, password: str) -> bool:
|
|
return check_password_hash(self.password_hash, password)
|
|
|