|
|
""" |
|
|
SQLAlchemy Models for Todo Application |
|
|
|
|
|
This module defines the database schema for: |
|
|
- User accounts with password hashing |
|
|
- Todo items with soft delete support |
|
|
- Session management for JWT refresh tokens |
|
|
|
|
|
All models use UUID primary keys and timestamp tracking. |
|
|
""" |
|
|
|
|
|
import uuid |
|
|
from datetime import datetime |
|
|
from typing import Optional |
|
|
|
|
|
from sqlalchemy import Boolean, DateTime, String, Text, Enum as SQLEnum, ForeignKey |
|
|
from sqlalchemy.dialects.postgresql import UUID |
|
|
from sqlalchemy.orm import Mapped, mapped_column, relationship |
|
|
from enum import Enum |
|
|
|
|
|
from src.models.database import Base |
|
|
|
|
|
|
|
|
|
|
|
class TodoStatus(str, Enum): |
|
|
"""Todo status enumeration""" |
|
|
PENDING = "pending" |
|
|
IN_PROGRESS = "in_progress" |
|
|
COMPLETED = "completed" |
|
|
|
|
|
|
|
|
class TodoPriority(str, Enum): |
|
|
"""Todo priority enumeration""" |
|
|
LOW = "low" |
|
|
MEDIUM = "medium" |
|
|
HIGH = "high" |
|
|
|
|
|
|
|
|
class UserRole(str, Enum): |
|
|
"""User role enumeration""" |
|
|
USER = "user" |
|
|
ADMIN = "admin" |
|
|
|
|
|
|
|
|
|
|
|
class TimestampMixin: |
|
|
"""Mixin for adding created_at and updated_at timestamps""" |
|
|
created_at: Mapped[datetime] = mapped_column( |
|
|
DateTime(timezone=True), |
|
|
default=lambda: datetime.utcnow(), |
|
|
nullable=False |
|
|
) |
|
|
updated_at: Mapped[datetime] = mapped_column( |
|
|
DateTime(timezone=True), |
|
|
default=lambda: datetime.utcnow(), |
|
|
onupdate=lambda: datetime.utcnow(), |
|
|
nullable=False |
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
class User(Base, TimestampMixin): |
|
|
""" |
|
|
User account model |
|
|
|
|
|
Fields: |
|
|
id: UUID primary key |
|
|
email: Unique email address (used for login) |
|
|
password_hash: Bcrypt hashed password |
|
|
role: User role (user or admin) |
|
|
is_verified: Email verification status |
|
|
created_at: Account creation timestamp |
|
|
updated_at: Last update timestamp |
|
|
|
|
|
Relationships: |
|
|
todos: User's todo items |
|
|
sessions: User's active sessions |
|
|
""" |
|
|
__tablename__ = "users" |
|
|
|
|
|
id: Mapped[uuid.UUID] = mapped_column( |
|
|
UUID(as_uuid=True), |
|
|
primary_key=True, |
|
|
default=uuid.uuid4 |
|
|
) |
|
|
email: Mapped[str] = mapped_column( |
|
|
String(255), |
|
|
unique=True, |
|
|
index=True, |
|
|
nullable=False |
|
|
) |
|
|
password_hash: Mapped[str] = mapped_column( |
|
|
String(255), |
|
|
nullable=False |
|
|
) |
|
|
role: Mapped[UserRole] = mapped_column( |
|
|
SQLEnum(UserRole), |
|
|
default=UserRole.USER, |
|
|
nullable=False |
|
|
) |
|
|
is_verified: Mapped[bool] = mapped_column( |
|
|
Boolean, |
|
|
default=False, |
|
|
nullable=False |
|
|
) |
|
|
|
|
|
|
|
|
todos = relationship("Todo", back_populates="user", cascade="all, delete-orphan") |
|
|
sessions = relationship("Session", back_populates="user", cascade="all, delete-orphan") |
|
|
|
|
|
|
|
|
|
|
|
class Todo(Base, TimestampMixin): |
|
|
""" |
|
|
Todo item model with soft delete support |
|
|
|
|
|
Fields: |
|
|
id: UUID primary key |
|
|
user_id: Foreign key to users table |
|
|
title: Todo title |
|
|
description: Detailed description (optional) |
|
|
status: Current status (pending, in_progress, completed) |
|
|
priority: Priority level (low, medium, high) |
|
|
due_date: Due date for completion (optional) |
|
|
category: Category label (optional) |
|
|
created_at: Creation timestamp |
|
|
updated_at: Last update timestamp |
|
|
deleted_at: Soft delete timestamp (null if not deleted) |
|
|
|
|
|
Relationships: |
|
|
user: Owner of the todo |
|
|
""" |
|
|
__tablename__ = "todos" |
|
|
|
|
|
id: Mapped[uuid.UUID] = mapped_column( |
|
|
UUID(as_uuid=True), |
|
|
primary_key=True, |
|
|
default=uuid.uuid4 |
|
|
) |
|
|
user_id: Mapped[uuid.UUID] = mapped_column( |
|
|
UUID(as_uuid=True), |
|
|
ForeignKey("users.id", ondelete="CASCADE"), |
|
|
nullable=False, |
|
|
index=True |
|
|
) |
|
|
title: Mapped[str] = mapped_column( |
|
|
String(255), |
|
|
nullable=False |
|
|
) |
|
|
description: Mapped[Optional[str]] = mapped_column( |
|
|
Text, |
|
|
nullable=True |
|
|
) |
|
|
status: Mapped[TodoStatus] = mapped_column( |
|
|
SQLEnum(TodoStatus), |
|
|
default=TodoStatus.PENDING, |
|
|
nullable=False |
|
|
) |
|
|
priority: Mapped[TodoPriority] = mapped_column( |
|
|
SQLEnum(TodoPriority), |
|
|
default=TodoPriority.MEDIUM, |
|
|
nullable=False |
|
|
) |
|
|
due_date: Mapped[Optional[datetime]] = mapped_column( |
|
|
DateTime(timezone=True), |
|
|
nullable=True |
|
|
) |
|
|
category: Mapped[Optional[str]] = mapped_column( |
|
|
String(100), |
|
|
nullable=True |
|
|
) |
|
|
deleted_at: Mapped[Optional[datetime]] = mapped_column( |
|
|
DateTime(timezone=True), |
|
|
nullable=True |
|
|
) |
|
|
|
|
|
|
|
|
user = relationship("User", back_populates="todos") |
|
|
|
|
|
|
|
|
|
|
|
class Session(Base, TimestampMixin): |
|
|
""" |
|
|
Session model for JWT refresh token management |
|
|
|
|
|
Fields: |
|
|
id: UUID primary key |
|
|
user_id: Foreign key to users table |
|
|
refresh_token: Hashed refresh token |
|
|
expires_at: Token expiration timestamp |
|
|
created_at: Session creation timestamp |
|
|
revoked_at: Token revocation timestamp (null if active) |
|
|
|
|
|
Relationships: |
|
|
user: Owner of the session |
|
|
""" |
|
|
__tablename__ = "sessions" |
|
|
|
|
|
id: Mapped[uuid.UUID] = mapped_column( |
|
|
UUID(as_uuid=True), |
|
|
primary_key=True, |
|
|
default=uuid.uuid4 |
|
|
) |
|
|
user_id: Mapped[uuid.UUID] = mapped_column( |
|
|
UUID(as_uuid=True), |
|
|
ForeignKey("users.id", ondelete="CASCADE"), |
|
|
nullable=False, |
|
|
index=True |
|
|
) |
|
|
refresh_token: Mapped[str] = mapped_column( |
|
|
String(500), |
|
|
unique=True, |
|
|
index=True, |
|
|
nullable=False |
|
|
) |
|
|
expires_at: Mapped[datetime] = mapped_column( |
|
|
DateTime(timezone=True), |
|
|
nullable=False |
|
|
) |
|
|
revoked_at: Mapped[Optional[datetime]] = mapped_column( |
|
|
DateTime(timezone=True), |
|
|
nullable=True |
|
|
) |
|
|
|
|
|
|
|
|
user = relationship("User", back_populates="sessions") |
|
|
|