Peewee字段更新后自动恢复默认值问题排查求助
Peewee模型字段意外重置问题分析与解决
问题场景
原始Peewee模型:
db = SqliteDatabase(Settings.stats_conf_database_location) class ActivityTracker(Model): date = DateField(unique=True) activity_time_curr = IntegerField(default=0) # active seconds today activity_time_max = IntegerField() # max active seconds today class Meta: database = db
添加新字段并完成迁移:
blocked = BooleanField(default=False)
执行以下更新语句后,10-30秒内blocked会自动恢复为0,activity_time_curr也会回滚到旧值:
ActivityTracker.update(blocked=True).where(ActivityTracker.date == datetime.today()).execute()
该问题在多台Windows设备的大量进程中均出现,期间无其他代码执行。
排查到的相关代码
activity_tracker_obj, created = ActivityTracker.get_or_create(date=_datetime.date.today(), defaults={'activity_time_curr': 0, 'activity_time_max': max_working_seconds_today, 'blocked': False}) InstaPyLimits.activity_tracker_obj = activity_tracker_obj # 全局存储当日记录对象 def update_daily_activity(time): activity_tracker = InstaPyLimits.activity_tracker_obj new_time = activity_tracker.activity_time_curr + time activity_tracker.activity_time_curr = new_time activity_tracker.save()
疑问解答
为什么activity_tracker.save()会重置blocked值为0?
全局变量InstaPyLimits.activity_tracker_obj是程序启动(或首次调用get_or_create)时加载到内存的对象,它的blocked值是当时从数据库读取的旧值(或创建时的默认False)。当你用SQL语句直接更新数据库后,内存里的这个对象并没有同步最新的数据库状态。后续调用save()时,Peewee会将内存对象的所有字段值全量写入数据库,这就把已经被更新为True的blocked又覆盖成了旧的False(SQLite中布尔值以0/1存储,所以显示为0)。
为什么使用旧值?
因为全局存储的对象和数据库状态已经脱节,没有重新从数据库拉取最新数据,一直持有初始化时的旧字段值。
是否需要重新从数据库获取对象?
是的,这是解决问题的核心思路之一,也可以采用其他更高效的原子更新方式。
解决方案
方案1:每次更新前重新获取最新对象
修改update_daily_activity函数,每次操作都基于数据库最新数据:
def update_daily_activity(time): # 从数据库重新拉取当日最新记录 activity_tracker = ActivityTracker.get(ActivityTracker.date == _datetime.date.today()) new_time = activity_tracker.activity_time_curr + time activity_tracker.activity_time_curr = new_time activity_tracker.save()
方案2:使用原子更新(推荐)
直接通过SQL语句完成累加操作,无需加载对象到内存,完全不会影响其他字段:
def update_daily_activity(time): ActivityTracker.update( activity_time_curr=ActivityTracker.activity_time_curr + time ).where( ActivityTracker.date == _datetime.date.today() ).execute()
方案3:刷新全局对象的数据库状态
如果必须保留全局对象,在修改前先同步最新数据:
def update_daily_activity(time): activity_tracker = InstaPyLimits.activity_tracker_obj # 从数据库重新加载所有字段的最新值 activity_tracker.refresh_from_db() new_time = activity_tracker.activity_time_curr + time activity_tracker.activity_time_curr = new_time activity_tracker.save()
内容的提问来源于stack exchange,提问作者Si si
相关产品推荐
相关产品推荐

