You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
78 lines
2.1 KiB
Python
78 lines
2.1 KiB
Python
"""
|
|
Soft delete utilities.
|
|
|
|
Provides reusable functions to convert hard-delete operations into soft-deletes.
|
|
The `soft_delete_record` function works with any model that inherits `SoftDeleteMixin`
|
|
(which is included in `DefaultMixin`).
|
|
|
|
Usage in service files:
|
|
from src.soft_delete import soft_delete_record, apply_not_deleted_filter
|
|
|
|
async def delete(*, db_session, record_id: str):
|
|
await soft_delete_record(db_session=db_session, model=MyModel, record_id=record_id)
|
|
|
|
async def get_all(*, db_session, ...):
|
|
query = Select(MyModel)
|
|
query = apply_not_deleted_filter(query, MyModel)
|
|
...
|
|
"""
|
|
|
|
import logging
|
|
from datetime import datetime
|
|
from typing import TypeVar, Type
|
|
|
|
from sqlalchemy import Select, Update
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
import pytz
|
|
from src.config import TIMEZONE
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
T = TypeVar("T")
|
|
|
|
|
|
def apply_not_deleted_filter(query: Select, model) -> Select:
|
|
"""
|
|
Appends a WHERE clause to exclude soft-deleted records.
|
|
Use this on all "get" queries to hide deleted data.
|
|
|
|
Example:
|
|
query = Select(OverhaulScope)
|
|
query = apply_not_deleted_filter(query, OverhaulScope)
|
|
"""
|
|
if hasattr(model, "deleted_at"):
|
|
return query.filter(model.deleted_at.is_(None))
|
|
return query
|
|
|
|
|
|
async def soft_delete_record(
|
|
*,
|
|
db_session: AsyncSession,
|
|
model: Type[T],
|
|
record_id: str,
|
|
id_column: str = "id",
|
|
) -> None:
|
|
"""
|
|
Soft-delete a record by setting its `deleted_at` timestamp.
|
|
Instead of DELETE, this performs an UPDATE.
|
|
|
|
Args:
|
|
db_session: The async database session.
|
|
model: The SQLAlchemy model class.
|
|
record_id: The value of the ID to match.
|
|
id_column: The name of the ID column (default: "id").
|
|
"""
|
|
now = datetime.now(pytz.timezone(TIMEZONE))
|
|
id_col = getattr(model, id_column)
|
|
|
|
stmt = (
|
|
Update(model)
|
|
.where(id_col == record_id)
|
|
.values(deleted_at=now)
|
|
)
|
|
await db_session.execute(stmt)
|
|
await db_session.commit()
|
|
|
|
log.info(f"Soft-deleted {model.__tablename__} record: {record_id}")
|