Django 1.11中使用only()/defer()后如何获取保存字段?
解决Django 1.11中only/defer获取对象后无法获知更新字段的问题
嘿,我在Django 1.11开发时也遇到过一模一样的问题——用only()或defer()拉取对象后,调用save()时update_fields默认是空的,没法判断哪些字段被修改了,也就没办法控制特定动作的执行时机。下面给你几个可行的解决方案:
方法一:手动追踪字段初始值(最稳妥,无依赖)
自己在模型里维护已加载字段的初始值,保存时对比当前值找出变化的字段。代码示例:
class YourModel(models.Model): name = models.CharField(max_length=100) age = models.IntegerField() # 其他字段... def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # 仅记录当前已加载的字段(因为only/defer可能没拉取全部字段) self._original_values = { field.name: getattr(self, field.name) for field in self._meta.fields if field.name in self.__dict__ } def save(self, *args, **kwargs): # 如果没有手动指定update_fields,自动计算变化的字段 if not kwargs.get('update_fields'): changed_fields = [] for field_name, original_val in self._original_values.items(): current_val = getattr(self, field_name) if current_val != original_val: changed_fields.append(field_name) kwargs['update_fields'] = changed_fields # 这里判断是否需要执行你的特定动作 target_fields = ['name', 'age'] # 替换成你关心的字段 if any(field in kwargs['update_fields'] for field in target_fields): # 执行你的特定动作,比如发送通知、更新关联数据等 print(f"执行特定动作,更新的字段:{kwargs['update_fields']}") # 调用父类保存方法 super().save(*args, **kwargs) # 更新初始值,为下次保存做准备 self._original_values = { field.name: getattr(self, field.name) for field in self._meta.fields if field.name in self.__dict__ }
这个方案的好处是完全自定义,不依赖Django内部属性或第三方库,兼容性和稳定性都很好。
方法二:使用第三方库django-model-utils的FieldTracker
如果不想自己写追踪逻辑,可以用django-model-utils提供的FieldTracker,它能自动帮你追踪字段变化。
首先安装库:
pip install django-model-utils
然后在模型中使用:
from model_utils import FieldTracker class YourModel(models.Model): name = models.CharField(max_length=100) age = models.IntegerField() tracker = FieldTracker() def save(self, *args, **kwargs): if not kwargs.get('update_fields'): # 获取所有变化的字段 changed_fields = list(self.tracker.changed().keys()) kwargs['update_fields'] = changed_fields # 判断是否执行特定动作 if 'name' in kwargs['update_fields']: # 你的特定动作代码 pass super().save(*args, **kwargs)
这个方案更简洁,但需要引入第三方依赖,如果你项目允许的话可以用。
方法三:利用Django内部的_db_cache和_deferred_fields
Django 1.11中,从数据库加载的对象会把原始值存在_db_cache属性里,defer()的字段会存在_deferred_fields里。可以利用这两个属性来判断变化的字段:
class YourModel(models.Model): name = models.CharField(max_length=100) age = models.IntegerField() def save(self, *args, **kwargs): if not kwargs.get('update_fields'): # 获取当前已加载的字段(排除延迟加载的) loaded_fields = [f.name for f in self._meta.fields if f.name not in self._deferred_fields] changed_fields = [] for field_name in loaded_fields: original_val = self._db_cache.get(field_name) current_val = getattr(self, field_name) if current_val != original_val: changed_fields.append(field_name) kwargs['update_fields'] = changed_fields # 执行特定动作的逻辑 if any(f in kwargs['update_fields'] for f in ['age']): # 你的动作 pass super().save(*args, **kwargs)
注意:_db_cache是Django内部属性,虽然1.11里可用,但后续版本可能会变动,所以如果追求长期稳定性,还是推荐方法一。
内容的提问来源于stack exchange,提问作者poolet
相关产品推荐
相关产品推荐

