数据迁移触发查询致测试报错:profile_user.is_deleted列不存在
解决Django迁移中Celery任务触发导致的
column does not exist测试错误 问题核心分析
你遇到的这个报错,本质是异步任务的执行时机和数据库schema变更不同步导致的:
- 你的
trigger_thumbnails_update迁移调用了Celery异步任务make_image_thumbnail.delay(),但在测试环境中,这个任务可能在添加is_deleted字段的迁移执行前就开始运行了。 - 即使迁移顺序看起来是对的,Celery任务使用的是当前代码中的
User模型(已经包含is_deleted字段),但此时数据库还没完成字段添加的迁移,查询时自然会找不到这个字段。
可行的解决方案
方案1:迁移中同步处理缩略图生成(最稳妥)
数据迁移场景下,尽量避免使用异步任务,直接同步执行缩略图生成逻辑,完全在数据库schema的上下文里操作:
def trigger_thumbnails_update(apps, schema_editor): """ Trigger thumbnails update for profile app """ User = apps.get_model('profile', 'User') # 注意:必须使用apps.get_model获取迁移版本的模型,不能直接导入当前的User for user in User.objects.all(): if user.photo: # 调用同步版本的生成函数,把原来Celery任务里的逻辑抽出来 generate_thumbnail_sync(user.photo, user.pk) # 抽离同步逻辑,避免异步依赖 def generate_thumbnail_sync(photo, user_pk): # 这里写原来make_image_thumbnail任务中的核心逻辑 # 注意:如果需要操作模型,也要用apps.get_model获取,避免导入当前模型 User = apps.get_model('profile', 'User') try: user = User.objects.get(pk=user_pk) except User.DoesNotExist: return # 生成缩略图并保存的逻辑...
这种方式完全不会有异步时序问题,迁移执行时数据库schema是确定的,查询不会出错。
方案2:调整迁移顺序,确保异步任务在schema变更后执行
如果一定要保留异步任务,你可以调整迁移的先后顺序:
- 把触发缩略图更新的迁移文件重命名(比如从
0009_trigger_thumbnails_update改成0011_trigger_thumbnails_update) - 修改它的
dependencies,指向添加is_deleted字段的迁移:dependencies = [ ('profile', '0010_auto_xxxxxx_xxxx'), # 替换成你添加is_deleted字段的迁移文件名 ] - 同时修改添加
is_deleted字段的迁移的dependencies,指向之前的基础迁移(比如0008_auto_20190611_2120)
这样迁移执行顺序就变成:基础迁移 → 添加字段 → 触发异步任务,此时数据库已经有了is_deleted字段,任务查询时就不会报错了。
方案3:修改Celery任务,避免查询不存在的字段
如果无法调整迁移顺序,你可以修改Celery任务,只查询需要的字段,避免触碰还未创建的is_deleted:
from django.apps import apps def make_image_thumbnail(pk): # 使用apps.get_model动态获取模型,适配当前数据库schema User = apps.get_model('profile', 'User') try: # 只查询生成缩略图需要的字段,不要查询is_deleted user = User.objects.only('photo', 'pk').get(pk=pk) except User.DoesNotExist: return # 生成缩略图的逻辑...
通过only()指定查询字段,即使数据库里还没有is_deleted,也不会在查询语句中出现这个字段,自然不会触发报错。
测试环境额外配置
在pytest中,建议开启Celery的同步执行模式,确保任务在迁移完成后再执行:
在测试配置文件(比如pytest.ini或conftest.py)中添加:
import django from django.conf import settings def pytest_configure(): settings.CELERY_TASK_ALWAYS_EAGER = True settings.CELERY_TASK_EAGER_PROPAGATES = True
这样所有Celery任务都会同步执行,完全避免异步时序问题。
内容的提问来源于stack exchange,提问作者Aymane Max
相关产品推荐
相关产品推荐

