full-stack-todo / backend /models.py
Sohail Nawaz
deploy backend
f0827c6
Raw
History Blame Contribute Delete
5.11 kB
"""
SQLModel database models.
This module defines the database entities for the task management system.
The Task model includes user ownership for strict data isolation.
Phase III: Adds Conversation and Message models for AI chatbot functionality.
"""
from sqlmodel import SQLModel, Field, Relationship
from datetime import datetime
from typing import Optional, List
from passlib.context import CryptContext
import uuid
import hashlib
from sqlalchemy import Column, Text
# Password hashing context - using sha256_crypt as fallback for Python 3.13 compatibility
pwd_context = CryptContext(schemes=["sha256_crypt"], deprecated="auto")
class TaskBase(SQLModel):
"""
Base class for Task with common fields.
Provides shared attributes for Task, TaskCreate, and TaskUpdate models.
"""
title: str = Field(
min_length=1,
max_length=200,
description="Task title (1-200 characters)"
)
description: Optional[str] = Field(
default=None,
max_length=1000,
description="Optional task description (0-1000 characters)"
)
completed: bool = Field(
default=False,
description="Task completion status"
)
class Task(TaskBase, table=True):
"""
Task database model.
Represents a task entity stored in the tasks table.
Each task is owned by exactly one user (user_id references users table).
Table Name: tasks
"""
__tablename__ = "tasks"
id: Optional[int] = Field(
default=None,
primary_key=True,
description="Auto-incrementing primary key"
)
user_id: str = Field(
default="",
index=True,
nullable=False,
description="Owner's user ID (foreign key to users.id)"
)
created_at: datetime = Field(
default_factory=datetime.utcnow,
description="Task creation timestamp"
)
updated_at: datetime = Field(
default_factory=datetime.utcnow,
description="Last update timestamp"
)
class User(SQLModel, table=True):
"""
User database model.
Represents a user account in the users table.
Stores hashed passwords for secure authentication.
Table Name: users
"""
__tablename__ = "users"
id: str = Field(
default_factory=lambda: str(uuid.uuid4()),
primary_key=True,
description="Unique user identifier (UUID)"
)
email: str = Field(
unique=True,
index=True,
nullable=False,
description="User's email address (unique)"
)
name: str = Field(
default="",
description="User's display name"
)
hashed_password: str = Field(
description="Hashed password"
)
created_at: datetime = Field(
default_factory=datetime.utcnow,
description="Account creation timestamp"
)
def set_password(self, password: str) -> None:
"""Hash and set the user's password."""
self.hashed_password = pwd_context.hash(password)
def verify_password(self, password: str) -> bool:
"""Verify a password against the stored hash."""
return pwd_context.verify(password, self.hashed_password)
class UserRead(SQLModel):
"""
User response schema (excludes password).
"""
id: str
email: str
name: str
class Conversation(SQLModel, table=True):
"""
Conversation database model.
Represents a chat session between a user and the AI assistant.
Stores conversation metadata and relates to messages.
Table Name: conversation
"""
__tablename__ = "conversation"
id: Optional[int] = Field(default=None, primary_key=True)
user_id: str = Field(
foreign_key="users.id",
index=True,
nullable=False,
description="Owner's user ID from Better Auth"
)
created_at: datetime = Field(default_factory=datetime.utcnow)
updated_at: datetime = Field(default_factory=datetime.utcnow)
# Relationship to messages
messages: List["Message"] = Relationship(back_populates="conversation")
class Message(SQLModel, table=True):
"""
Message database model.
Represents a single message in a conversation (either user or assistant).
Messages are linked to both conversation and user for efficient querying.
Table Name: message
"""
__tablename__ = "message"
id: Optional[int] = Field(default=None, primary_key=True)
conversation_id: int = Field(
foreign_key="conversation.id",
index=True,
nullable=False,
description="Parent conversation"
)
user_id: str = Field(
nullable=False,
description="Redundant copy for fast filtering"
)
role: str = Field(
nullable=False,
description="Message sender: 'user' or 'assistant'"
)
content: str = Field(
sa_column=Column(Text, nullable=False),
description="Message content"
)
created_at: datetime = Field(default_factory=datetime.utcnow)
# Relationship to conversation
conversation: Optional[Conversation] = Relationship(back_populates="messages")