依赖Postgres的容器化Django如何正确导入SQL转储文件
PostgreSQL转储导入报错解决方案
现有导入方式的问题
直接通过管道向运行中的psql导入SQL的方式存在两个核心缺陷,是所有报错的直接诱因:
- 导入目标库已经被Django的migrate命令初始化过,自带完整表结构、主键/外键约束,dump文件内的建表、建约束语句重复执行,就会触发
constraint already exists类报错 - 导入时没有临时关闭约束校验,只要dump内的表写入顺序和外键依赖顺序不一致,或者库内残留Django生成的初始数据,就会触发非空约束、外键约束类报错
无法删库场景下的正确导入步骤
全程不需要删除数据库,仅需操作public schema内的对象即可,操作前先停掉Django容器,避免进程占用写入:
- 停Django服务,替换命令中的
django为docker-compose配置里Django对应的服务名
docker-compose stop django
- 提前备份当前数据库,防止操作失误丢失数据
docker exec <postgres_container_id> pg_dump -U <db_user> <db_name> > /tmp/pre_import_backup.sql
- 清空目标库内所有现存表、序列、约束,不需要执行删库操作
cat <<EOF | docker exec -i <postgres_container_id> psql -U <db_user> -d <db_name> DO $$ DECLARE r RECORD; BEGIN FOR r IN (SELECT tablename FROM pg_tables WHERE schemaname = 'public') LOOP EXECUTE 'DROP TABLE IF EXISTS ' || quote_ident(r.tablename) || ' CASCADE'; END LOOP; FOR r IN (SELECT sequencename FROM pg_sequences WHERE schemaname = 'public') LOOP EXECUTE 'DROP SEQUENCE IF EXISTS ' || quote_ident(r.sequencename) || ' CASCADE'; END LOOP; FOR r IN (SELECT viewname FROM pg_views WHERE schemaname = 'public') LOOP EXECUTE 'DROP VIEW IF EXISTS ' || quote_ident(r.viewname) || ' CASCADE'; END LOOP; END $$; EOF
- 临时关闭会话级约束检查后导入转储文件,规避导入顺序导致的外键报错
(echo "SET session_replication_role = 'replica';"; cat /tmp/dump.sql) | docker exec -i <postgres_container_id> psql -U <db_user> -d <db_name>
解决Django启动后数据库重置问题
启动后数据被重置的本质,是Django migrate检测到库内结构和迁移记录不匹配时的自动建表行为,不需要修改迁移配置,导入完成后执行以下命令标记所有迁移为已应用即可:
- 伪造迁移执行记录,跳过实际建表逻辑
docker-compose run --rm django python manage.py migrate --fake
- 正常启动Django服务
docker-compose up -d django
注意事项
- 如果dump文件自带
CREATE DATABASE语句,提前删除对应段落,否则会触发库已存在报错 - 导入全程不需要停止PostgreSQL容器,仅需保证导入过程中没有其他业务连接写入数据库即可
- 导入完成后可以执行简单的select语句校验数据量,确认导入成功后再启动业务服务
内容的提问来源于stack exchange,提问作者Moiz Sohail
相关产品推荐
相关产品推荐

