如何保护Django中单个Customer模型实例(数据库行)不被删除?
解决方案:通过PostgreSQL触发器保护指定Customer记录
核心思路
直接在数据库层面添加触发器,拦截对account='INTERNAL'的Customer记录的删除操作——不管是通过Django ORM、直接SQL还是其他工具操作,都没法删掉这条数据,同时完全不影响其他客户的正常删除逻辑,也不用把所有查询都改成get_or_create。
实现步骤(Django迁移方式)
1. 创建空迁移文件
在你的Customer模型所在的Django app目录下执行:
python manage.py makemigrations --empty 你的app名称
把你的app名称替换成实际的app名(比如customers)。
2. 编写迁移代码
打开生成的空迁移文件,替换成以下内容:
from django.db import migrations def create_protect_trigger(apps, schema_editor): # 定义触发器函数:拦截INTERNAL客户的删除请求 trigger_func_sql = """ CREATE OR REPLACE FUNCTION protect_internal_customer() RETURNS TRIGGER AS $$ BEGIN IF OLD.account = 'INTERNAL' THEN RAISE EXCEPTION '禁止删除内部客户占位记录'; END IF; RETURN OLD; END; $$ LANGUAGE plpgsql; """ # 绑定触发器到Customer表的DELETE操作 trigger_sql = """ CREATE TRIGGER prevent_internal_customer_delete BEFORE DELETE ON 你的app名称_customer FOR EACH ROW EXECUTE FUNCTION protect_internal_customer(); """ # 执行SQL语句 schema_editor.execute(trigger_func_sql) schema_editor.execute(trigger_sql) def drop_protect_trigger(apps, schema_editor): # 回滚迁移时清理触发器和函数 schema_editor.execute("DROP TRIGGER IF EXISTS prevent_internal_customer_delete ON 你的app名称_customer;") schema_editor.execute("DROP FUNCTION IF EXISTS protect_internal_customer();") class Migration(migrations.Migration): dependencies = [ # 替换成你当前app的上一个迁移编号,比如('customers', '0003_add_phone_field') ('你的app名称', '0001_initial'), ] operations = [ migrations.RunPython(create_protect_trigger, drop_protect_trigger), ]
注意:
- 把代码中所有
你的app名称替换为实际的app名称 - 确保
dependencies里的迁移编号和你当前app的最新迁移一致
3. 执行迁移
运行以下命令生效:
python manage.py migrate
验证效果
- 尝试删除内部客户:不管用
Customer.objects.get(account='INTERNAL').delete()还是直接执行SQL,都会抛出异常,阻止删除 - 其他普通客户可以正常删除,完全不受影响
补充说明
这种方式比get_or_create更可靠——从数据库底层彻底锁死了删除操作,不会出现误删后再恢复的情况,也不用修改任何现有业务代码的查询逻辑,完美兼容外键的原有删除规则(比如其他客户的外键关联可以正常CASCADE或SET_NULL)
内容的提问来源于stack exchange,提问作者nigel222
相关产品推荐
相关产品推荐

