重写Django models.Manager的get_queryset方法时触发表不存在错误
解决Django makemigrations时自定义管理器触发的ProgrammingError
问题场景
你在Django中创建了存储音乐的Music模型,通过自定义管理器CustomManager在获取查询集时自动更新所有对象的last_queried_at字段,但执行makemigrations命令时出现以下错误:
django.db.utils.ProgrammingError: relation "app_music" does not exist LINE 1: UPDATE "app_music" SET "last_queried_at" = '2...
问题原因
- 迁移阶段表未创建:你的
CustomManager设置了use_in_migrations = True,导致Django在迁移过程中使用该管理器。但此时app_music表还未被创建,执行queryset.update()必然触发表不存在的错误。 - 逻辑不合理:在
get_queryset()中对全表执行update操作,每次获取查询集都会修改所有数据,既不符合业务逻辑,也会引发性能问题。
解决方案
1. 移除迁移中使用自定义管理器的配置
修改CustomManager,删除use_in_migrations = True这一行,让迁移过程使用默认管理器:
class CustomManager(models.Manager): # 移除该行,避免迁移时调用自定义管理器 # use_in_migrations = True def get_queryset(self): return super().get_queryset()
2. 正确实现last_queried_at的更新逻辑
不要在获取整个查询集时全表更新,而是针对单个查询对象进行更新,推荐两种实现方式:
方式一:重写get()方法
当通过get()获取单个对象时自动更新字段:
from django.utils import timezone class CustomManager(models.Manager): def get(self, *args, **kwargs): obj = super().get(*args, **kwargs) obj.last_queried_at = timezone.now() # 仅更新指定字段提升性能 obj.save(update_fields=['last_queried_at']) return obj
方式二:添加自定义查询方法
创建专门的方法用于获取并更新对象,业务代码中按需调用:
from django.utils import timezone class CustomManager(models.Manager): def fetch_and_update(self, *args, **kwargs): obj = self.get(*args, **kwargs) obj.last_queried_at = timezone.now() obj.save(update_fields=['last_queried_at']) return obj
使用示例:music_obj = Music.objects.fetch_and_update(id=1)
额外优化建议
如果你的需求是定期清理超过一个月未查询的音乐文件,建议使用Django的任务调度工具(如django-celery-beat)编写定时任务,定期执行清理逻辑,而非在查询管理器中做全表更新操作。
内容的提问来源于stack exchange,提问作者Mohirbek Mustaqimov
相关产品推荐
相关产品推荐

