Django通过call_command调用dumpdata失败 报ASCII编码错误
问题描述
需要通过编程方式调用Django内置的dumpdata命令,初始调用代码如下:
from django.core.management import call_command call_command('dumpdata', 'asdf', indent=2, output=filePath)
代码执行失败,抛出错误:Unable to serialize database: 'ascii' codec can't encode character '\xdf' in position 83: ordinal not in range(128)
但直接在终端执行如下命令可以正常运行:python3 manage.py dumpdata --indent=2 --output=dump.json.gz asdf
需要定位call_command()方式调用失败的具体原因。
运行环境
- Python版本:
3.6.9 - Django版本:
3.2.13 - 环境变量
$LANG配置:en_US.UTF-8 - 环境变量
$LC_ALL配置:en_US.UTF-8 - 环境变量
$LC_CTYPE配置:UTF-8
故障原因
报错本质是文件写入时的编码不匹配:
- Django 3.2版本的
dumpdata命令接收到output参数传入的文件路径时,会直接调用内置open()方法打开文件,且没有显式指定文件编码,此时Python会使用当前进程的默认首选编码(即locale.getpreferredencoding()的返回值)作为文件写入编码。 - 终端直接执行命令时,当前会话正确加载了UTF-8的locale配置,
locale.getpreferredencoding()返回UTF-8,可以正常序列化包含非ASCII字符(报错中的\xdf是德语小写字母ß)的数据。 - 通过代码调用
call_command()时,进程通常是web服务、后台任务、定时任务这类没有绑定交互终端的场景,不会正确继承shell的locale环境变量,导致locale.getpreferredencoding()回退到ASCII编码,遇到非ASCII字符时就会抛出编码错误。
修复方案
不要将文件路径传入output参数,改为自行以UTF-8编码打开文件对象,传入call_command的stdout参数即可,和终端执行逻辑完全对齐:
import gzip from django.core.management import call_command # 输出为普通json文件 with open("dump.json", "w", encoding="utf-8") as f: call_command("dumpdata", "asdf", indent=2, stdout=f) # 输出为gzip压缩的json文件,和终端执行.gz输出的逻辑一致 with gzip.open("dump.json.gz", "wt", encoding="utf-8") as f: call_command("dumpdata", "asdf", indent=2, stdout=f)
该写法绕开了Django自动打开文件时依赖进程默认编码的逻辑,全场景强制使用UTF-8编码写入,不会再触发ASCII编码报错。
内容的提问来源于stack exchange,提问作者kindoflike
相关产品推荐
相关产品推荐

