Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion docs/usage/crud.md
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ count = await user_crud.delete_model_by_column(
)
```

逻辑删除会把标记字段设为 `True`。如果模型包含删除时间字段,也会写入当前时间。
逻辑删除默认会把标记字段设为 `True`。如果模型包含删除时间字段,也会写入当前时间。

```python
count = await user_crud.delete_model_by_column(
Expand All @@ -214,6 +214,20 @@ count = await user_crud.delete_model_by_column(
)
```

如果逻辑删除标记需要写入自定义值,可以使用 `deleted_flag_value`。该参数也支持 SQLAlchemy 表达式,例如把 `deleted` 字段写成当前行的 `id`。

```python
count = await user_crud.delete_model_by_column(
session,
logical_deletion=True,
deleted_flag_column='deleted',
deleted_flag_value=User.id,
deleted_at_column='deleted_time',
id=1,
deleted=0
)
```

## 统计和存在性

```python
Expand Down
4 changes: 3 additions & 1 deletion sqlalchemy_crud_plus/crud.py
Original file line number Diff line number Diff line change
Expand Up @@ -751,6 +751,7 @@ async def delete_model_by_column(
allow_multiple: bool = False,
logical_deletion: bool = False,
deleted_flag_column: str = 'is_deleted',
deleted_flag_value: Any = True,
deleted_at_column: str = 'deleted_at',
deleted_at_factory: datetime | Callable[[], datetime] | None = None,
flush: bool = False,
Expand All @@ -764,6 +765,7 @@ async def delete_model_by_column(
:param allow_multiple: If `True`, allows deleting multiple records that match the filters
:param logical_deletion: If `True`, enable logical deletion instead of physical deletion
:param deleted_flag_column: Column name for logical deletion flag
:param deleted_flag_value: Value written to logical deletion flag column
:param deleted_at_column: Column name for delete time,automatic judgment
:param deleted_at_factory: The delete time value or factory function
:param flush: If `True`, flush all object changes to the database
Expand All @@ -785,7 +787,7 @@ async def delete_model_by_column(
if total_count > 1:
raise MultipleResultsError(f'Only one record is expected to be deleted, found {total_count} records.')

data: dict[str, Any] = {deleted_flag_column: True}
data: dict[str, Any] = {deleted_flag_column: deleted_flag_value}

if deleted_at_column in self.model_column_names:
if deleted_at_factory is None:
Expand Down
2 changes: 2 additions & 0 deletions tests/models/basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@ class Ins(Base):
id: Mapped[int] = mapped_column(init=False, primary_key=True, index=True, autoincrement=True)
name: Mapped[str] = mapped_column(String(64))
is_deleted: Mapped[bool] = mapped_column(default=False)
deleted: Mapped[int] = mapped_column(default=0)
created_time: Mapped[datetime] = mapped_column(init=False, default_factory=datetime.now)
updated_time: Mapped[datetime | None] = mapped_column(init=False, onupdate=datetime.now)
deleted_time: Mapped[datetime | None] = mapped_column(init=False, default=None)


class InsPks(Base):
Expand Down
30 changes: 30 additions & 0 deletions tests/test_delete.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from datetime import datetime

import pytest

from sqlalchemy.ext.asyncio import AsyncSession
Expand Down Expand Up @@ -148,6 +150,34 @@ async def test_logical_delete_single_record(db: AsyncSession, sample_ins: list[I
assert updated_item.is_deleted is True


@pytest.mark.asyncio
async def test_logical_delete_with_deleted_flag_value_expression(
db: AsyncSession, sample_ins: list[Ins], crud_ins: CRUDPlus[Ins]
):
item = sample_ins[0]

deleted_time = datetime(2024, 1, 1, 12, 0, 0)

async with db.begin():
count = await crud_ins.delete_model_by_column(
db,
logical_deletion=True,
deleted_flag_column='deleted',
deleted_flag_value=crud_ins.model.id,
deleted_at_column='deleted_time',
deleted_at_factory=deleted_time,
id=item.id,
deleted=0,
)

assert count == 1

updated_item = await crud_ins.select_model(db, item.id)
assert updated_item is not None
assert updated_item.deleted == item.id
assert updated_item.deleted_time == deleted_time


@pytest.mark.asyncio
async def test_logical_delete_multiple_records(db: AsyncSession, sample_ins: list[Ins], crud_ins: CRUDPlus[Ins]):
before_count = await crud_ins.count(db, is_deleted=False)
Expand Down
Loading