Docker化Django:迁移中执行自定义SQL脚本遇报错的解决方法
解决Django迁移中执行自定义SQL脚本的问题
我来帮你梳理清楚问题根源,以及如何正确实现让自定义SQL脚本在初始迁移完成后执行:
问题核心原因
你当前的代码有两个关键问题,直接导致了报错:
- RunPython的调用方式错误:你在
migrations.RunPython()里直接执行了load_data_from_sql('my_app_base.sql'),这会让Django在生成迁移文件(makemigrations阶段)就触发SQL脚本执行,而不是等到实际迁移(migrate阶段)才运行。此时数据库里连auth_user表都还没创建,自然会抛出relation does not exist的错误。 - 迁移依赖的完整性不足:你的数据迁移只依赖了自身应用的初始迁移,但没有明确依赖Django auth模块的初始迁移,无法保证
auth_user表先于你的SQL脚本创建完成。
正确实现步骤
1. 修复迁移文件的RunPython调用
修改my_app/migrations/0001_initial_data.py,调整函数传递方式,确保SQL脚本只在migrate阶段执行:
import os from functools import partial from django.db import migrations, connection def load_data_from_sql(apps, schema_editor, filename): # 必须保留apps和schema_editor参数,这是RunPython要求的回调签名 file_path = os.path.join(os.path.dirname(__file__), '../sql/', filename) with open(file_path, 'r') as f: sql_statement = f.read() with connection.cursor() as cursor: cursor.execute(sql_statement) class Migration(migrations.Migration): dependencies = [ ('my_app', '0001_initial'), # 明确依赖auth模块的初始迁移,确保auth_user表已创建 ('auth', '0001_initial'), ] operations = [ # 使用partial传递参数,避免提前执行函数 migrations.RunPython(partial(load_data_from_sql, filename='my_app_base.sql')) ]
如果不想用functools.partial,也可以用lambda包装:
operations = [ migrations.RunPython(lambda apps, schema_editor: load_data_from_sql(apps, schema_editor, 'my_app_base.sql')) ]
2. 优化entrypoint.sh的命令逻辑
你的entrypoint.sh里的makemigrations不需要每次容器启动都执行(迁移文件应该提前生成并提交到代码库),建议调整为仅在开发环境可选执行,避免不必要的数据库交互:
#!/bin/sh if [ "$DATABASE" = "postgres" ] then echo "Waiting for postgres..." while ! nc -z $SQL_HOST $SQL_PORT; do sleep 0.1 done echo "PostgreSQL started" fi # 仅在开发环境自动生成迁移(可选,根据你的需求调整) # if [ "$DEBUG" = "True" ]; then # python manage.py makemigrations my_app # fi python manage.py migrate exec "$@"
3. 正确的执行流程
现在按以下步骤操作即可正常运行:
- 先清理之前可能生成的错误迁移文件,然后重新生成正确的迁移:
docker-compose exec web python manage.py makemigrations my_app - 重新构建并启动容器,entrypoint.sh会自动执行迁移流程:
docker-compose up --build
为什么之前的临时操作能成功?
你提到删除0001_initial_data.py后能正常建表,之后再添加就能成功,是因为此时数据库已经完成了auth_user和my_app表的创建,再执行迁移时,SQL脚本是在migrate阶段调用的,数据库状态已经符合要求,所以不会报错。
内容的提问来源于stack exchange,提问作者zar3bski
相关产品推荐
相关产品推荐

