迁移至Postgres后Django的on_delete=models.CASCADE失效,如何解决?
看起来问题出在数据库层面的外键约束没有带上ON DELETE CASCADE属性——虽然你在Django模型里明确设置了on_delete=models.CASCADE,但pgloader从SQLite迁移到Postgres时,并没有把这个级联删除规则同步到Postgres的外键约束中。SQLite对外键级联的处理比较宽松,而Postgres严格遵循自身的约束规则,这就导致删除Reply时,Postgres会因为关联的ReplyImage仍存在而抛出完整性错误。
另外补充一点:你在模型里重写的delete()方法和post_delete信号,在调用QuerySet.delete()(比如post.replies.all().delete())时不会触发模型的delete()方法——因为QuerySet的批量删除是直接生成SQL语句操作数据库,绕过了模型实例的delete()逻辑。虽然会触发post_delete信号,但此时数据库层面的约束已经阻止了删除操作,信号里的逻辑根本没机会执行。
我们可以通过Django的迁移系统来修复Postgres的外键约束,步骤如下:
生成空迁移文件
在项目根目录下运行命令(替换app为你的实际应用名):python manage.py makemigrations app --empty编辑迁移文件,添加级联约束修复逻辑
打开刚生成的迁移文件(比如app/migrations/00XX_auto_XXXXXX_XXXX.py),替换内容为:from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ ('app', '00XX_your_previous_migration_file'), # 替换为你项目中最近的迁移文件名 ] operations = [ migrations.RunSQL( """ ALTER TABLE app_replyimage DROP CONSTRAINT app_replyimage_reply_id_fkey, ADD CONSTRAINT app_replyimage_reply_id_fkey FOREIGN KEY (reply_id) REFERENCES app_reply(id) ON DELETE CASCADE; """ ), ]注意修改
dependencies中的迁移文件名,确保与你项目中的实际情况一致。执行迁移
运行命令完成约束修复:python manage.py migrate
修复完成后,Postgres的外键约束就会和Django模型定义的on_delete=models.CASCADE保持一致,删除Reply实例时,数据库会自动删除关联的ReplyImage,不会再出现完整性错误。
内容的提问来源于stack exchange,提问作者Hmong Bong

