Become a sponsor

统一使用软删除机制,通过 is_delete 字段标记记录状态,不物理删除数据。删除操作支持逗号分隔的多 ID 批量删除。
软删除约定
is_delete = False(0):正常记录(默认值)is_delete = True(1):已删除记录is_delete=False,业务层无需关心| 维度 | 软删除 | 硬删除 |
|---|---|---|
| 实现方式 | 更新 is_delete=True | DELETE FROM table |
| 数据可恢复 | ✅ 可恢复 | ❌ 不可恢复 |
| 查询影响 | 需加 is_delete=False | 数据已不存在 |
| 存储空间 | 占用(数据仍在表中) | 释放 |
| 适用场景 | 业务数据(用户、订单等) | 日志、临时数据 |
| 项目约定 | ✅ 统一使用 | ❌ 禁止使用 |
所有业务模型继承 BaseModel,自动包含 is_delete 字段:
# application/models.py
class BaseModel(models.Model):
"""基础模型类"""
class Meta:
abstract = True
id = models.AutoField(primary_key=True)
create_user = models.CharField(max_length=50, null=True, blank=True)
create_time = models.DateTimeField(auto_now_add=True)
update_user = models.CharField(max_length=50, null=True, blank=True)
update_time = models.DateTimeField(auto_now=True)
is_delete = models.IntegerField(default=0, db_comment='是否删除:0-正常 1-已删除')DELETE /{module}/delete/{id}支持逗号分隔的多 ID 删除:
DELETE /example/delete/1,2,3@method_decorator(check_login, name="delete")
class ExampleDeleteView(PermissionRequired, View):
"""删除案例视图"""
permission_required = ('sys:example:delete',)
@operation_log(title="案例管理-删除记录", log_type=LogType.DELETE)
def delete(self, request, id):
# 演示环境禁止操作
if DJANGO_DEMO:
return R.failed("演示环境,暂无操作权限")
result = services.delete_examples(id)
return resultdef delete_examples(instance_ids):
"""删除案例(批量删除)"""
if not instance_ids:
return R.failed("记录ID不存在")
try:
# 解析ID列表
id_list = [int(id_str.strip()) for id_str in instance_ids.split(',') if id_str.strip()]
if not id_list:
return R.failed("无效的记录ID")
# 批量更新删除标识(逻辑删除)
updated_count = Example.objects.filter(
id__in=id_list,
is_delete=False
).update(is_delete=True)
return R.ok(msg=f"本次共删除{updated_count}条数据")
except ValueError:
return R.failed("记录ID格式错误")
except Exception as e:
logging.error(f"删除案例异常,IDs: {instance_ids}, 错误: {str(e)}")
return R.failed(msg="删除失败,请稍后重试")返回值说明
delete 方法返回实际软删除的条数。如果传入的 ID 中包含已删除或不存在的记录,返回值会小于入参数量。
1. View 接收请求(id 参数,逗号分隔)
↓
2. 演示模式检查(DJANGO_DEMO)
↓(拦截则返回 R.failed)
3. Service.delete_examples(id)
↓
4. 解析 ID 列表(逗号分隔转 list)
↓
5. ORM 批量更新 is_delete=True
↓
6. 返回 R.ok(msg="本次共删除N条数据")所有查询方法必须过滤 is_delete=False:
# 分页查询
query = Example.objects.filter(is_delete=False)
# 详情查询
instance = Example.objects.filter(is_delete=False, id=id).first()
# 列表查询
queryset = Example.objects.filter(is_delete=False, status=1)
# 带筛选条件的查询
query = Example.objects.filter(is_delete=False, name__contains=name)常见错误
忘记加 is_delete=False 是最常见的 bug,会导致已删除的数据重新出现在列表中。建议在代码审查时重点检查。
当模块之间存在关联关系时,删除父记录需要同步处理子记录:
def delete_role(role_ids):
"""删除角色(同步清理关联数据)"""
id_list = [int(id_str.strip()) for id_str in role_ids.split(',')]
# 1. 软删除角色
updated_count = Role.objects.filter(
id__in=id_list, is_delete=False
).update(is_delete=True)
# 2. 同步软删除角色菜单关联
RoleMenu.objects.filter(
role_id__in=id_list, is_delete=False
).update(is_delete=True)
# 3. 同步软删除用户角色关联
UserRole.objects.filter(
role_id__in=id_list, is_delete=False
).update(is_delete=True)
return R.ok(msg=f"本次共删除{updated_count}条数据")软删除的数据可以通过恢复接口重新启用:
def restore_examples(instance_ids):
"""恢复已删除的案例"""
id_list = [int(id_str.strip()) for id_str in instance_ids.split(',')]
updated_count = Example.objects.filter(
id__in=id_list, is_delete=True
).update(is_delete=False)
return R.ok(msg=f"本次共恢复{updated_count}条数据")恢复场景
数据恢复通常用于:
| 操作 | 权限标识 | 说明 |
|---|---|---|
| 删除 | sys:{module}:delete | 删除记录(支持批量) |
| 提示 | 原因 |
|---|---|
| "记录ID不存在" | 入参为空 |
| "无效的记录ID" | ID 格式错误或全部无效 |
| "记录ID格式错误" | ID 包含非数字字符 |
1. 统一软删除:所有业务数据使用 is_delete 字段,禁止物理删除
2. 查询过滤:所有查询加 is_delete=False,避免遗漏
3. 级联处理:删除父记录时同步处理关联的子记录
4. 批量删除:支持逗号分隔的多 ID,提高操作效率
5. 错误提示:返回明确的错误信息,便于排查
6. 操作日志:删除操作记录操作日志,便于审计删除规范统一使用软删除(is_delete 标记),支持逗号分隔的多 ID 批量删除。Service 层解析 ID 列表后通过 ORM 批量更新 is_delete=True。所有查询方法自动过滤 is_delete=False,业务层无需关心软删除逻辑。级联删除时需同步处理关联数据,保证数据一致性。