Django 3:修改models.py后因已应用迁移文件报错的解决咨询
我之前在models.py里添加了两个用于上传路径的函数,后来发现它们逻辑十分相似:
def upload_path(instance, fl_file_name): supplier = str(instance.fl_supplier).replace(' ', '-') return ('sources/{0}/{1}'.format(supplier.lower(), fl_file_name)) def upload_path_stock(instance, fl_file_name): supplier = str(instance.fl_st_supplier).replace(' ', '-') return ('sources/{0}/{1}'.format(supplier.lower(), fl_file_name))
最初的模型写法如下:
class Flow(models.Model): [...] fl_file_name = models.FileField( max_length=50, upload_to=upload_path_stock, verbose_name='Nom fichier')
我尝试注释掉def upload_path_stock函数,并修改模型属性:
class Flow(models.Model): [...] fl_file_name = models.FileField( max_length=50, upload_to=upload_path, verbose_name='Nom fichier') # <<<< HERE
但保存时触发了错误:
File "/Users/[...]/migrations/0063_auto_20220822_1834.py", line 27, in Migration
field=models.FileField(max_length=50, upload_to=flows.models.upload_path_stock, verbose_name='Nom fichier'),
AttributeError: module 'flows.models' has no attribute 'upload_path_stock'
错误原因是迁移文件0063中仍引用了upload_path_stock:
migrations.AlterField( model_name='flowstock', name='fl_st_file_name', field=models.FileField(max_length=50, upload_to=flows.models.upload_path_stock, verbose_name='Nom fichier'), ),
于是我取消了对该函数的注释,创建并应用了新的迁移(编号64)。但即使迁移64已应用,当我再次注释该函数并尝试保存模型时,仍会触发迁移63中的错误。
请问我能否直接修改迁移文件63中的路径,或者有没有更安全的处理方式?
不建议直接修改旧迁移文件
直接修改已应用过的迁移文件(比如0063)风险极高,后续团队协作或部署到其他环境时,极易出现迁移执行不一致的问题,导致数据库状态混乱。
安全处理步骤
暂时保留旧函数并标记废弃
先不要删除或注释upload_path_stock,可以添加注释标记它已废弃,避免后续误使用:# 已废弃,统一使用upload_path def upload_path_stock(instance, fl_file_name): supplier = str(instance.fl_st_supplier).replace(' ', '-') return ('sources/{0}/{1}'.format(supplier.lower(), fl_file_name))这样旧迁移文件引用它时不会报错,同时不影响新逻辑的运行。
合并重复逻辑,优化为通用函数
既然两个函数核心逻辑一致,仅获取供应商的字段名不同,可以将其重构为通用函数,通过参数指定字段名:def upload_path(instance, filename, supplier_field='fl_supplier'): supplier = str(getattr(instance, supplier_field)).replace(' ', '-') return f'sources/{supplier.lower()}/{filename}'然后在模型中使用
functools.partial绑定字段参数:from functools import partial class Flow(models.Model): [...] fl_file_name = models.FileField( max_length=50, upload_to=partial(upload_path, supplier_field='fl_st_supplier'), verbose_name='Nom fichier' )这样既保留原有功能,又消除了重复代码,后续无需再维护两个独立函数。
开发环境下的迁移重置(可选)
如果是个人独立开发环境,没有团队协作的顾虑,可以彻底重置迁移:- 先备份数据库
- 删除
migrations目录下除__init__.py外的所有文件 - 重新生成初始迁移:
python manage.py makemigrations - 伪应用初始迁移:
python manage.py migrate --fake-initial
这种方式能彻底清除旧迁移的引用,但绝对不能在团队协作环境中使用。
内容的提问来源于stack exchange,提问作者Abpostman1

