为什么Django自定义validators可在shell导入,makemigrations却报错?
问题原因
这个问题的核心是Django迁移系统的可调用对象序列化逻辑,和项目的包导入结构发生了命名冲突:
- Django在序列化模型字段的校验器这类可调用对象时,会通过读取可调用对象的
__module__(所属模块路径)和__name__(自身名称)属性,拼接出完整的导入路径写进迁移文件里。 - 你的场景里,校验函数
local_file_validator存放在路径为my_app/validators/local_file_validator.py的模块中,所以函数的__module__属性值为my_app.validators.local_file_validator,__name__为local_file_validator,最终Django拼接出的导入路径是my_app.validators.local_file_validator.local_file_validator,可以从报错的迁移文件代码里看到这个路径。 - 你大概率在
my_app/validators/__init__.py中写了from .local_file_validator import local_file_validator,把这个函数直接导入到了my_app.validators包的顶层命名空间。当迁移文件被加载时,Python解析my_app.validators.local_file_validator时,拿到的是你提前导入的函数对象,而不是同名的子模块,后续尝试访问这个函数对象的local_file_validator属性自然就抛出了AttributeError。
是不是Django的Bug?
这不算Django的Bug,属于序列化机制的设计取舍。Django的序列化逻辑默认假设项目不会出现「父包命名空间下的对象和子模块重名」的情况,这种冲突是由项目的导入结构和命名习惯导致的。
解决方案
有几种可行的修复方式:
- 调整函数命名,让校验函数名和所在模块名不重复,也就是你已经测试可行的方案
- 调整包导入逻辑,删除
my_app/validators/__init__.py中对该函数的导入,在models中直接从子模块导入校验器:from .validators.local_file_validator import local_file_validator - 给校验器自定义序列化规则,给函数添加
deconstruct()方法,手动指定序列化后的导入路径:def local_file_validator(value): # 原有校验逻辑 pass def deconstruct(): # 返回值格式为 (导入路径, 位置参数, 关键字参数) return ('my_app.validators.local_file_validator', [], {}) local_file_validator.deconstruct = deconstruct
内容的提问来源于stack exchange,提问作者John
相关产品推荐
相关产品推荐

