Skip to content

# 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)