models.py 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. """
  2. SQL Alchemy models declaration.
  3. https://docs.sqlalchemy.org/en/14/orm/declarative_styles.html#example-two-dataclasses-with-declarative-table
  4. Dataclass style for powerful autocompletion support.
  5. https://alembic.sqlalchemy.org/en/latest/tutorial.html
  6. Note, it is used by alembic migrations logic, see `alembic/env.py`
  7. Alembic shortcuts:
  8. # create migration
  9. alembic revision --autogenerate -m "migration_name"
  10. # apply all migrations
  11. alembic upgrade head
  12. """
  13. import uuid
  14. from datetime import datetime
  15. from sqlalchemy import Boolean, Column, DateTime, ForeignKey, String, Table
  16. from sqlalchemy.dialects.postgresql import UUID
  17. from sqlalchemy.ext.hybrid import hybrid_property
  18. from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship
  19. from sqlalchemy.sql import func
  20. class Base(DeclarativeBase):
  21. pass
  22. def uid_column() -> Mapped[str]:
  23. """Returns a posrtgreSQL UUID column for SQL ORM"""
  24. return mapped_column(
  25. UUID(as_uuid=False), primary_key=True, default=lambda _: str(uuid.uuid4())
  26. )
  27. class User(Base):
  28. __tablename__ = "user_model"
  29. id: Mapped[str] = uid_column()
  30. email: Mapped[str] = mapped_column(
  31. String(254), nullable=False, unique=True, index=True
  32. )
  33. hashed_password: Mapped[str] = mapped_column(String(128), nullable=False)
  34. class Project(Base):
  35. __tablename__ = "projects"
  36. id: Mapped[str] = uid_column()
  37. created_at: Mapped[datetime] = mapped_column(
  38. DateTime(timezone=True), server_default=func.now()
  39. )
  40. updated_at: Mapped[datetime] = mapped_column(
  41. DateTime(timezone=True), default=datetime.now, onupdate=func.now()
  42. )
  43. name: Mapped[str] = mapped_column(
  44. String(128), nullable=False, unique=True, index=True
  45. )
  46. is_public: Mapped[bool] = mapped_column(Boolean())
  47. volunteers: Mapped[list["Volunteer"]] = relationship(
  48. back_populates="project", cascade="delete-orphan"
  49. )
  50. slots: Mapped[list["Slot"]] = relationship(
  51. back_populates="project", cascade="delete-orphan"
  52. )
  53. sms: Mapped[list["Sms"]] = relationship(
  54. back_populates="project", cascade="delete-orphan"
  55. )
  56. association_table_volunteer_slot = Table(
  57. "association_volunteer_slot",
  58. Base.metadata,
  59. Column(
  60. "volunteer_id",
  61. ForeignKey("volunteers.id", ondelete="CASCADE"),
  62. primary_key=True,
  63. ),
  64. Column("slot_id", ForeignKey("slots.id", ondelete="CASCADE"), primary_key=True),
  65. )
  66. class Volunteer(Base):
  67. __tablename__ = "volunteers"
  68. id: Mapped[str] = uid_column()
  69. project_id: Mapped[str] = mapped_column(
  70. ForeignKey("projects.id", ondelete="CASCADE")
  71. )
  72. project: Mapped["Project"] = relationship(back_populates="volunteers")
  73. created_at: Mapped[datetime] = mapped_column(
  74. DateTime(timezone=True), server_default=func.now()
  75. )
  76. updated_at: Mapped[datetime] = mapped_column(
  77. DateTime(timezone=True), default=datetime.now, onupdate=func.now()
  78. )
  79. name: Mapped[str] = mapped_column(String(128))
  80. surname: Mapped[str] = mapped_column(String(128))
  81. email: Mapped[str] = mapped_column(String(128))
  82. phone_number: Mapped[str] = mapped_column(String(128))
  83. automatic_sms: Mapped[bool] = mapped_column(Boolean(), default=False)
  84. slots: Mapped[list["Slot"]] = relationship(
  85. secondary=association_table_volunteer_slot, back_populates="volunteers"
  86. )
  87. @hybrid_property
  88. def slots_id(self) -> list[str]:
  89. return [s.id for s in self.slots]
  90. class Slot(Base):
  91. __tablename__ = "slots"
  92. id: Mapped[str] = uid_column()
  93. project_id: Mapped[str] = mapped_column(
  94. ForeignKey("projects.id", ondelete="CASCADE")
  95. )
  96. project: Mapped["Project"] = relationship(back_populates="slots")
  97. created_at: Mapped[datetime] = mapped_column(
  98. DateTime(timezone=True), server_default=func.now()
  99. )
  100. updated_at: Mapped[datetime] = mapped_column(
  101. DateTime(timezone=True), default=datetime.now, onupdate=func.now()
  102. )
  103. title: Mapped[str] = mapped_column(String(128), nullable=False)
  104. description: Mapped[str] = mapped_column(String(), default="")
  105. starting_time: Mapped[datetime] = mapped_column(DateTime(timezone=True))
  106. ending_time: Mapped[datetime] = mapped_column(DateTime(timezone=True))
  107. volunteers: Mapped[list[Volunteer]] = relationship(
  108. secondary=association_table_volunteer_slot, back_populates="slots"
  109. )
  110. @hybrid_property
  111. def volunteers_id(self) -> list[str]:
  112. return [v.id for v in self.volunteers]
  113. class Sms(Base):
  114. __tablename__ = "sms"
  115. id: Mapped[str] = mapped_column(
  116. UUID(as_uuid=False), primary_key=True, default=lambda _: str(uuid.uuid4())
  117. )
  118. project_id: Mapped[str] = mapped_column(
  119. ForeignKey("projects.id", ondelete="CASCADE")
  120. )
  121. project: Mapped["Project"] = relationship(back_populates="sms")
  122. volunteer_id: Mapped[str] = mapped_column(ForeignKey("volunteers.id"))
  123. created_at: Mapped[datetime] = mapped_column(
  124. DateTime(timezone=True), server_default=func.now()
  125. )
  126. updated_at: Mapped[datetime] = mapped_column(
  127. DateTime(timezone=True), default=datetime.now, onupdate=func.now()
  128. )
  129. content: Mapped[str] = mapped_column(
  130. String(), nullable=False, unique=True, index=True
  131. )
  132. phone_number: Mapped[str] = mapped_column(String(24))
  133. sending_time: Mapped[datetime] = mapped_column(
  134. DateTime(timezone=True), default=datetime.now
  135. )
  136. send_time: Mapped[datetime] = mapped_column(DateTime(timezone=True))