Soft Deletes
Use Case 5: Soft Deletes Pattern
Scenario: Implement soft delete functionality for data recovery.
Python
from mongo_ops import BaseDocument, BaseRepository
from datetime import datetime
from typing import Optional
class SoftDeleteDocument(BaseDocument):
"""Base document with soft delete support"""
is_deleted: bool = False
deleted_at: Optional[datetime] = None
deleted_by: Optional[str] = None
class Task(SoftDeleteDocument):
title: str
description: str
assignee_id: str
status: str = "pending"
priority: str = "medium"
class SoftDeleteRepository(BaseRepository[T]):
"""Repository with soft delete operations"""
async def soft_delete(self, id: str, deleted_by: str = None) -> Optional[T]:
"""Soft delete a document"""
return await self.update(id, {
"is_deleted": True,
"deleted_at": datetime.utcnow(),
"deleted_by": deleted_by
})
async def restore(self, id: str) -> Optional[T]:
"""Restore a soft-deleted document"""
return await self.update(id, {
"is_deleted": False,
"deleted_at": None,
"deleted_by": None
})
async def get_active(self, skip: int = 0, limit: int = 100):
"""Get only non-deleted documents"""
return await self.get_many(
filter={"is_deleted": False},
skip=skip,
limit=limit
)
async def get_deleted(self, skip: int = 0, limit: int = 100):
"""Get deleted documents"""
return await self.get_many(
filter={"is_deleted": True},
skip=skip,
limit=limit
)
async def permanent_delete(self, id: str) -> bool:
"""Permanently delete a document"""
return await self.delete(id)
class TaskRepository(SoftDeleteRepository[Task]):
def __init__(self):
super().__init__("tasks", Task)
# Usage in FastAPI
@app.delete("/tasks/{task_id}")
async def soft_delete_task(task_id: str, user_id: str):
task = await task_repo.soft_delete(task_id, deleted_by=user_id)
if not task:
raise HTTPException(status_code=404, detail="Task not found")
return {"message": "Task deleted", "task": task}
@app.post("/tasks/{task_id}/restore")
async def restore_task(task_id: str):
task = await task_repo.restore(task_id)
if not task:
raise HTTPException(status_code=404, detail="Task not found")
return {"message": "Task restored", "task": task}
@app.get("/tasks/", response_model=List[Task])
async def list_active_tasks(skip: int = 0, limit: int = 10):
return await task_repo.get_active(skip, limit)
@app.get("/tasks/deleted", response_model=List[Task])
async def list_deleted_tasks(skip: int = 0, limit: int = 10):
return await task_repo.get_deleted(skip, limit)