MyModel.objects.update_or_create() - >布尔数据是否已更新?

时间:2017-07-17 14:34:37

标签: django django-models

AFAIK Django没有提供通用方法来查看update_or_create()

是否更改了数据

布尔值created告诉我已经创建了一行。但是我怎么知道数据是否被更改(SQL UPDATE哪个更改了数据)

示例:

obj, created = MyModel.update_or_create(pk=12345,
                       defaults=dict(name='Doctor Zhivago'))

有三种情况:

  1. obj已创建。没问题,我有布尔变量created
  2. obj未创建,已更新。例如,之前的名字是" Machuca"。
  3. obj未创建且未更新。例如,之前的名字已经是" Dr. Zhivago"。
  4. 目前我无法区分case2和case3。

3 个答案:

答案 0 :(得分:6)

感觉有点受@bakkal的启发,我编写了一个可以应用于自定义Manager类的mixin类,然后将该自定义Manager分配给MyModel类。这会将返回的元组的格式更改为(obj, created, updated)。在defaults.items()中的值循环中,我正在检查是否有任何新值与旧值不同。

class UpdateOrCreateMixin(object):
    def update_or_create_v2(self, defaults=None, **kwargs):
        """
        Look up an object with the given kwargs, updating one with defaults
        if it exists, otherwise create a new one.
        Return a tuple (object, created, updated), where created and updated are 
        booleans specifying whether an object was created.
        """
        defaults = defaults or {}
        lookup, params = self._extract_model_params(defaults, **kwargs)
        self._for_write = True
        with transaction.atomic(using=self.db):
            try:
                obj = self.select_for_update().get(**lookup)
            except self.model.DoesNotExist:
                obj, created = self._create_object_from_params(lookup, params)
                if created:
                    return obj, created, False
            updated = False
            for k, v in defaults.items():
                oldattr = getattr(obj, k)
                if oldattr != (v() if callable(v) else v):
                    updated = True
                setattr(obj, k, v() if callable(v) else v)
            obj.save(using=self.db)
        return obj, False, updated

class CustomManager(UpdateOrCreateMixin, models.Manager):
    pass

class MyModel(models.Model)
    field = models.CharField(max_length=32)
    objects = CustomManager()

obj, created, updated = MyModel.objects.update_or_create_v2(pk=12345,
                   defaults=dict(name='Doctor Zhivago'))

答案 1 :(得分:2)

考虑到如何实现该接口,如果对象已被创建,则无法判断该对象是否已更新。因为在这种情况下它始终会调用save()

以下是update_or_create()目前的实施方式:

    with transaction.atomic(using=self.db):
        try:
            obj = self.select_for_update().get(**lookup)
        except self.model.DoesNotExist:
            obj, created = self._create_object_from_params(lookup, params)
            if created:
                return obj, created

        # [below part always runs the same if obj was not created]
        for k, v in defaults.items():
            setattr(obj, k, v() if callable(v) else v)
        obj.save(using=self.db)
    return obj, False

参考。 https://github.com/django/django/blob/master/django/db/models/query.py#L459

正如您所看到的,当没有创建对象时,它的实现方式无法判断它是否已更新。

但是,在调用save()之前,您可以手动验证查找/更新值是否与数据库中现有对象的值不同,然后设置updated标记。

答案 2 :(得分:0)

我目前正在使用混合方法来处理此类任务:

我没有last_edited的模型上有last_modifiedauto_now=True字段。

我使用django-dirtyfields而不是auto_now方法而不是save我做了类似的事情:

def save(*args, **kwargs):
    if self.is_dirty():
        self.last_modified = now()
    super().save(*args, **kwargs)

如果您不喜欢save方法中的混乱,可以使用pre_save信号并根据脏字段更新last_modified

然后在调用update_or_create之后,您可以非常安全地依赖last_modified字段来检查模型是否确实已被修改。

dirtyfield的api非常简单,你可以在没有太多设置痛苦的情况下玩它。