Enhance database initialization and error handling for CRUD operations

This commit is contained in:
Automated Action 2025-05-16 06:25:14 +00:00
parent f8685abae0
commit 7aca5a2ff6
4 changed files with 253 additions and 64 deletions

View File

@ -37,8 +37,32 @@ def create_task(
"""
Create new task.
"""
task = crud.task.create(db, obj_in=task_in)
return task
try:
print(f"Attempting to create task with data: {task_in.model_dump() if hasattr(task_in, 'model_dump') else task_in.dict()}")
# Handle datetime conversion for due_date
if task_in.due_date:
print(f"Due date before processing: {task_in.due_date}")
# Ensure due_date is properly formatted
if isinstance(task_in.due_date, str):
from datetime import datetime
try:
task_in.due_date = datetime.fromisoformat(task_in.due_date.replace('Z', '+00:00'))
except Exception as e:
print(f"Error parsing due_date: {e}")
# Create the task
task = crud.task.create(db, obj_in=task_in)
print(f"Task created successfully with ID: {task.id}")
return task
except Exception as e:
print(f"Error in create_task endpoint: {str(e)}")
import traceback
print(traceback.format_exc())
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Error creating task: {str(e)}",
)
@router.get("/{task_id}", response_model=Task)

View File

@ -27,12 +27,24 @@ class CRUDBase(Generic[ModelType, CreateSchemaType, UpdateSchemaType]):
return db.query(self.model).offset(skip).limit(limit).all()
def create(self, db: Session, *, obj_in: CreateSchemaType) -> ModelType:
obj_in_data = jsonable_encoder(obj_in)
db_obj = self.model(**obj_in_data)
db.add(db_obj)
db.commit()
db.refresh(db_obj)
return db_obj
try:
obj_in_data = jsonable_encoder(obj_in)
print(f"Creating {self.model.__name__} with data: {obj_in_data}")
db_obj = self.model(**obj_in_data)
db.add(db_obj)
db.commit()
db.refresh(db_obj)
print(f"Successfully created {self.model.__name__} with id: {db_obj.id}")
return db_obj
except Exception as e:
db.rollback()
error_msg = f"Error creating {self.model.__name__}: {str(e)}"
print(error_msg)
import traceback
print(traceback.format_exc())
raise Exception(error_msg) from e
def update(
self,

View File

@ -1,44 +1,119 @@
import os
import subprocess
import time
from pathlib import Path
from sqlalchemy import text
from sqlalchemy.orm import Session
from sqlalchemy import inspect, text
from sqlalchemy.exc import OperationalError
from app.db.base import Base # Import all models
from app.db.session import engine
from app.core.config import settings
def init_db() -> None:
"""Initialize database with required tables and data."""
"""Initialize database with required tables and data directly."""
# Ensure database directory exists
Path(settings.DB_DIR).mkdir(parents=True, exist_ok=True)
# Try to connect to check if the database is accessible
with engine.connect() as conn:
try:
conn.execute(text("SELECT 1"))
print("Database connection successful")
except Exception as e:
print(f"Database connection error: {e}")
raise
db_file = f"{settings.DB_DIR}/db.sqlite"
db_exists = os.path.exists(db_file)
# Run alembic upgrade to create tables
# If database doesn't exist or is empty, create tables
try:
# Get the project root directory (where alembic.ini is located)
project_root = Path(__file__).parent.parent.parent.absolute()
# Try to connect to check if the database is accessible
max_retries = 3
retry_count = 0
connected = False
# Change to project root directory and run alembic
os.chdir(project_root)
subprocess.run(["alembic", "upgrade", "head"], check=True)
print("Database migration successful")
except subprocess.CalledProcessError as e:
print(f"Database migration error: {e}")
raise
print("Database initialized successfully")
while retry_count < max_retries and not connected:
try:
with engine.connect() as conn:
conn.execute(text("SELECT 1"))
print(f"Database connection successful to {db_file}")
connected = True
except Exception as e:
retry_count += 1
print(f"Database connection error (attempt {retry_count}/{max_retries}): {e}")
time.sleep(1) # Wait a second before retrying
if not connected:
raise Exception(f"Failed to connect to database after {max_retries} attempts")
# Check if tables exist
inspector = inspect(engine)
existing_tables = inspector.get_table_names()
if not existing_tables:
print("No tables found in database. Creating tables...")
Base.metadata.create_all(bind=engine)
print(f"Created tables: {', '.join(inspector.get_table_names())}")
else:
print(f"Found existing tables: {', '.join(existing_tables)}")
# Print database file information
if db_exists:
file_size = os.path.getsize(db_file)
print(f"Database file exists at {db_file}, size: {file_size} bytes")
else:
print(f"Warning: Database file does not exist at {db_file} after initialization!")
print("Database initialization completed successfully")
except Exception as e:
print(f"Database initialization error: {str(e)}")
# Print detailed error but don't raise to allow app to start
import traceback
print(traceback.format_exc())
# Try to create a test file to check write permissions
try:
test_file = f"{settings.DB_DIR}/test_write.txt"
with open(test_file, 'w') as f:
f.write('test')
print(f"Successfully wrote test file: {test_file}")
os.remove(test_file)
except Exception as e:
print(f"Failed to write test file: {str(e)}")
def create_test_task():
"""Create a test task in the database to verify everything is working."""
try:
from app.crud.task import task as task_crud
from app.schemas.task import TaskCreate
from app.db.session import SessionLocal
from datetime import datetime, timedelta
db = SessionLocal()
try:
# Check if there are any tasks
existing_tasks = db.execute("SELECT COUNT(*) FROM task").scalar()
if existing_tasks > 0:
print(f"Test task not needed, found {existing_tasks} existing tasks")
return
# Create a test task
test_task = TaskCreate(
title="Test Task",
description="This is a test task created at database initialization",
priority="medium",
status="todo",
due_date=datetime.utcnow() + timedelta(days=7),
completed=False
)
created_task = task_crud.create(db, obj_in=test_task)
print(f"Created test task with ID: {created_task.id}")
finally:
db.close()
except Exception as e:
print(f"Error creating test task: {e}")
import traceback
print(traceback.format_exc())
if __name__ == "__main__":
init_db()
init_db()
create_test_task()

140
main.py
View File

@ -15,10 +15,31 @@ from app.core.config import settings
from app.db import init_db
# Initialize the database on startup
print("Starting database initialization...")
try:
# Get absolute path of the database file
from pathlib import Path
db_path = Path("/app/storage/db/db.sqlite").absolute()
print(f"Database path: {db_path}")
# Check directory permissions
db_dir = Path("/app/storage/db")
print(f"Database directory exists: {db_dir.exists()}")
print(f"Database directory is writable: {os.access(db_dir, os.W_OK)}")
# Initialize the database and create test task
init_db.init_db()
# Try to create a test task
try:
init_db.create_test_task()
except Exception as e:
print(f"Error creating test task: {e}")
# Continue anyway
except Exception as e:
print(f"Error initializing database: {e}")
import traceback
print(f"Detailed error: {traceback.format_exc()}")
# Continue with app startup even if DB init fails, to allow debugging
app = FastAPI(title=settings.PROJECT_NAME)
@ -110,44 +131,101 @@ def test_db_connection():
"""
from sqlalchemy import text, inspect
from app.db.session import engine
import traceback
import traceback, os, subprocess
from app.core.config import DB_DIR
try:
inspector = inspect(engine)
tables = inspector.get_table_names()
# Check directory structure and permissions
storage_info = {
"app_dir_exists": os.path.exists("/app"),
"app_dir_writable": os.access("/app", os.W_OK),
"storage_dir_exists": os.path.exists("/app/storage"),
"storage_dir_writable": os.access("/app/storage", os.W_OK) if os.path.exists("/app/storage") else False,
"db_dir_exists": os.path.exists(str(DB_DIR)),
"db_dir_writable": os.access(str(DB_DIR), os.W_OK) if os.path.exists(str(DB_DIR)) else False,
}
# Get disk usage information
disk_usage = {}
try:
df_output = subprocess.check_output(["df", "-h", "/app/storage"]).decode()
disk_usage["df_output"] = df_output.strip().split('\n')
except Exception as e:
disk_usage["error"] = str(e)
# Try database connection
connection_info = {}
inspector = None
try:
with engine.connect() as conn:
connection_info["connection"] = "successful"
connection_info["test_query"] = conn.execute(text("SELECT 1")).scalar()
inspector = inspect(engine)
except Exception as e:
connection_info["connection"] = "failed"
connection_info["error"] = str(e)
# Get table information if connection successful
table_info = {}
for table in tables:
columns = inspector.get_columns(table)
table_info[table] = [col['name'] for col in columns]
with engine.connect() as conn:
# Try to select from the task table to verify it exists
if inspector:
tables = inspector.get_table_names()
connection_info["tables"] = tables
for table in tables:
columns = inspector.get_columns(table)
table_info[table] = [col['name'] for col in columns]
# Try to query task table if it exists
if 'task' in tables:
result = conn.execute(text("SELECT COUNT(*) FROM task")).scalar()
task_count = result
else:
task_count = "Table 'task' not found"
# Check DB directory
import os
from app.core.config import DB_DIR
db_path = f"{DB_DIR}/db.sqlite"
db_exists = os.path.exists(db_path)
return {
"status": "ok",
"connection": "successful",
"tables": tables,
"table_details": table_info,
"task_count": task_count,
"db_path": db_path,
"db_exists": db_exists
}
with engine.connect() as conn:
try:
task_count = conn.execute(text("SELECT COUNT(*) FROM task")).scalar()
connection_info["task_count"] = task_count
except Exception as e:
connection_info["task_query_error"] = str(e)
# Database file information
db_file = f"{DB_DIR}/db.sqlite"
db_file_info = {
"path": db_file,
"exists": os.path.exists(db_file),
"size_bytes": os.path.getsize(db_file) if os.path.exists(db_file) else 0,
"writable": os.access(db_file, os.W_OK) if os.path.exists(db_file) else False,
}
# SQLAlchemy configuration
from app.core.config import settings
db_config = {
"sqlalchemy_url": settings.SQLALCHEMY_DATABASE_URL,
"connect_args": {"check_same_thread": False}
}
# Test file creation
write_test = {}
try:
test_file = f"{DB_DIR}/test_db.txt"
with open(test_file, 'w') as f:
f.write("Test write access")
write_test["success"] = True
write_test["path"] = test_file
os.remove(test_file) # Clean up
except Exception as e:
write_test["success"] = False
write_test["error"] = str(e)
return {
"status": "ok",
"storage_info": storage_info,
"disk_usage": disk_usage,
"connection_info": connection_info,
"table_info": table_info,
"db_file_info": db_file_info,
"db_config": db_config,
"write_test": write_test
}
except Exception as e:
return {
"status": "error",
"connection": "failed",
"error": str(e),
"global_error": str(e),
"traceback": traceback.format_exc()
}