You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 07:54:07