为什么 Django 部署后出现 ImportError 找不到模块
最近有朋友在部署 Django 项目时遇到 ImportError,本地开发一切正常,一上生产环境就报模块找不到。这类问题我排查过不少,通常先从模块搜索路径和环境配置入手。下面把常见的几个方向和检查步骤整理出来,供遇到类似情况的朋友参考。
部署后出现 ImportError,最常见的原因是 Python 的模块搜索路径不包含项目根目录或虚拟环境未正确激活。可以通过在 Deploy 服务器的 Python 交互环境中执行 `import sys; print(sys.path)` 来确认路径列表。如果项目路径不在列表中,需要手动添加或确保 WSGI 配置文件(如 uwsgi、gunicorn 的配置文件)中设置了 `chdir` 或 `working_directory` 指向项目根目录。另外,检查虚拟环境是否在运行应用的用户下被正确 source 或通过 supervisor 的 `environment=PYTHONPATH` 参数指定。
如果 ImportError 提示找不到某个第三方模块,比如 django、psycopg2 等,应优先检查 `requirements.txt` 是否与实际部署环境匹配。常见的坑是本地开发环境与生产环境的操作系统、架构不同导致二进制包安装失败,或者使用了 `--user` 安装导致模块被安装到用户目录而非虚拟环境。解决方法是在部署机全新虚拟环境中执行 `pip install -r requirements.txt`,观察是否有错误输出。如果模块版本冲突,可使用 `pip freeze` 对比本地与远程的包列表。
1. 模块搜索路径与虚拟环境检查
部署后出现 ImportError,最常见的原因是 Python 的模块搜索路径不包含项目根目录或虚拟环境未正确激活。可以通过在 Deploy 服务器的 Python 交互环境中执行 import sys; print(sys.path) 来确认路径列表。如果项目路径不在列表中,需要手动添加或确保 WSGI 配置文件(如 uwsgi、gunicorn 的配置文件)中设置了 chdir 或 working_directory 指向项目根目录。另外,检查虚拟环境是否在运行应用的用户下被正确 source 或通过 supervisor 的 environment=PYTHONPATH 参数指定。
这里有个容易忽略的细节:虚拟环境的激活并不是简单的 source,有时 supervisor 或 systemd 服务文件里没有显式指定虚拟环境的 Python 解释器路径,导致使用了系统全局的 Python,自然找不到项目依赖。建议在启动命令中直接使用虚拟环境的 python 或 uwsgi 可执行文件,例如 /path/to/venv/bin/uwsgi,而不是依赖 PATH 环境变量。
2. 第三方依赖的版本与兼容性
如果 ImportError 提示找不到某个第三方模块,比如 django、psycopg2 等,应优先检查 requirements.txt 是否与实际部署环境匹配。常见的坑是本地开发环境与生产环境的操作系统、架构不同导致二进制包安装失败,或者使用了 --user 安装导致模块被安装到用户目录而非虚拟环境。解决方法是在部署机全新虚拟环境中执行 pip install -r requirements.txt,观察是否有错误输出。如果模块版本冲突,可使用 pip freeze 对比本地与远程的包列表。
我习惯在部署前先跑一下 pip check,它可以快速报告依赖间的冲突。另外,如果生产环境与开发环境的 Python 版本不一致(比如本地 Python 3.9,服务器 Python 3.11),某些 C 扩展模块(如 psycopg2-binary)可能不兼容。可以考虑使用纯 Python 版或重新编译。
3. 环境变量配置:DJANGO_SETTINGS_MODULE 与 PYTHONPATH
Django 部署时常因 DJANGO_SETTINGS_MODULE 环境变量未设置或设置错误而引发 ImportError。检查 uwsgi 或 gunicorn 的启动命令或配置文件,确保显式指定了该变量,例如 --env DJANGO_SETTINGS_MODULE=myproject.settings.production。另外,PYTHONPATH 变量缺少项目路径也会导致模块无法导入。若使用系统服务,可在 unit file 的 [Service] 段添加 Environment="PYTHONPATH=/path/to/project",注意路径不要多余空格。
一个经常出错的点是设置文件名称:比如你本地的 settings 是 settings.py,但生产环境用的是 settings_prod.py,环境变量却忘记改。另一个情况是,项目里多个 app 互相引用时,如果 PYTHONPATH 没有包含项目根目录,Django 的 INSTALLED_APPS 里注册的 app 就无法被正确发现。验证方法是登录服务器,在项目根目录下运行 python -c "import django; print(django.__version__)" 不报错,再逐步导入项目模块。
4. 相对导入导致的部署故障
开发过程中为了方便,经常在模块内使用相对导入(如 from .models import XXX),这种导入在 Django 部署时可能导致 ImportError。因为 WSGI 服务器可能以不同的方式计算包路径,相对导入要求模块必须作为包的一部分被导入。如果项目中的某些脚本被直接运行(如 python manage.py runserver)可以工作,但通过 WSGI 服务器时失败,应检查是否在非包目录下使用了相对导入。推荐将项目结构改为绝对导入,或将视图、模型等模块放在 apps 内。
举个例子,如果你的 utils.py 文件位于项目根目录(没有 __init__.py),而里面写了 from .helpers import func,这就会出问题。解决办法要么给根目录加个 __init__.py 转成包,要么改用 from helpers import func 绝对导入。
5. 缓存干扰与残留 .pyc 文件
旧版 .pyc 文件或 Python 缓存可能导致导入混乱。当代码文件被删除或移动时,残留的 .pyc 文件仍被 python 解释器加载,从而引发 ImportError。部署前务必清除所有 .pyc 文件和 __pycache__ 目录:在项目根目录执行 find . -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null; find . -name "*.pyc" -delete。此外,如果修改了模块路径,也需要重启 WSGI 进程,因为某些 Python 环境(如 mod_wsgi)会缓存已导入的模块。
有些时候,你明明已经删除了某个模块文件,但错误依然存在,很可能就是旧的 .pyc 在作祟。清除缓存后再重启应用,通常能解决这类幽灵导入问题。如果使用 uwsgi,建议加上 --py-cleanup 或 --reload-mercy 等参数,但最稳妥的还是手动清除一遍。
6. 文件权限问题
部署环境下如果 Web 服务器运行用户(如 www-data)对项目文件或模块没有读权限,会导致 ImportError。检查项目目录和文件的权限:确保 manage.py、wsgi.py 等关键文件至少是 644,目录是 755,并且所属组包含运行用户。尤其要注意虚拟环境中的 site-packages 目录,如果 pip install 时使用了 root 权限,可能导致普通用户无法读取。使用 ls -l 查看权限,并可以通过 chmod -R o+r /path/to/venv/lib/python3.x/site-packages 修复。
另外,如果项目代码放在类似 /root 或 /home/deployer 这样的目录下,而 Web 服务器用户是 www-data,它可能根本没有访问父目录的权限。建议把项目放到 /srv 或 /var/www 这类公共路径下,并确保从根目录到项目文件的每一级目录都有正确的执行权限(755)。可以用 namei -l /path/to/project 检查每个目录的权限。
小结
以上几个方向基本覆盖了部署时出现 ImportError 的主要原因。实际操作中,可以先看错误栈里的模块名称,再结合上述方法逐一排查。如果问题依旧,可以贴出具体的错误信息和 sys.path 输出,在社区里更容易定位。