Django:在更新模型之前,我想“看看”模型它以前的属性

发布于 2024-09-08 17:15:07 字数 225 浏览 5 评论 0原文

当对 Django 模型(.save())执行更新/创建时,我希望能够“介入”并将某些特定属性与之前设置的内容进行比较(如果它们以前根本就存在)。

我正在考虑预保存信号,并查找执行.objects.get(instance.id)的原始模型,但这感觉很浪费。另外,验证是否已在 pre_save() 中进行?

When an update/create is performed on a Django model (.save()) I would like to be able to "step in" and compare some particular attributes to what they were set to previously (if they previously existed at all).

I'm thinking Pre-Save Signals, with a look-up to the original model doing a .objects.get(instance.id), but that feels wasteful. Also, has the validation already happened in pre_save()?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

梦巷 2024-09-15 17:15:07

关于 模型验证< /a>:

请注意,当您调用模型的 save() 方法时,不会自动调用 full_clean()

然后,关于预保存信号,请注意,您将获取正在保存的实例作为参数发送消息。由于模型的前一个版本仅存在于数据库中,我不知道您还能在哪里获得属性的先前值...

您没有说出为什么要这样做,所以很难说,但是我现在正在考虑的其他解决方案:

* defining a custom signal that is sent everytime the attributes you are interested in are modified... This signal would then send two arguments : new value, old value
* perform the check directly when setting the attributes

如果您提供更多详细信息,可能会更容易...

编辑:

没错...如果您发出自定义的“foo_has_updated”,您将无法确定修改是否已保存。

在这种情况下,我想您可以在初始化实例时缓存您感兴趣的变量,并捕获保存后或保存前信号。

* With pre-save, you would be able to pre-process the data, but the saving operation might fail
* With post-save, you would be sure that the data has been saved.

缓存你的变量可以这样完成:

class CachedModel(models.Model):
    cached_vars = [var1, var2, varN]
    def __init__(self, *args, **kwargs):
        super(CachedModel, self).__init__(*args, **kwargs)
        self.var_cache = {}
        for var in self.cached_vars:
            self.var_cache[var] = copy.copy(getattr(self, var))

或者类似的事情......然后,在你的信号处理程序中:

def post_save_handler(sender, **kwargs):
    instance = kwargs["instance"]
    [(instance.var_cache[var], getattr(instance, var)) for var in instance.cached_var]
    #[(<initial value>, <saved value>)

你得到了你需要的(我认为)!

about model validation :

Note that full_clean() will not be called automatically when you call your model’s save() method

Then, about the pre-save signal, note that you get the instance that is being saved sent as a parameter with the message. As the former version of your model exists only in the database, I don't see where else you could get the previous value of the attributes ...

You don't tell why you want to do this so it's hard to say, but other solutions I'm thinking of right now :

* defining a custom signal that is sent everytime the attributes you are interested in are modified... This signal would then send two arguments : new value, old value
* perform the check directly when setting the attributes

If you give more details, it might be easier...

EDIT :

That's right ... If you emit a custom 'foo_has_updated', you will not be sure that the modification is saved.

In this case, I guess you could cache the variables that interest you while initializing the instance, and catch the post-save OR pre-save signal.

* With pre-save, you would be able to pre-process the data, but the saving operation might fail
* With post-save, you would be sure that the data has been saved.

Caching your variables could be done like this :

class CachedModel(models.Model):
    cached_vars = [var1, var2, varN]
    def __init__(self, *args, **kwargs):
        super(CachedModel, self).__init__(*args, **kwargs)
        self.var_cache = {}
        for var in self.cached_vars:
            self.var_cache[var] = copy.copy(getattr(self, var))

Or something like this ... Then, in your signal handler :

def post_save_handler(sender, **kwargs):
    instance = kwargs["instance"]
    [(instance.var_cache[var], getattr(instance, var)) for var in instance.cached_var]
    #[(<initial value>, <saved value>)

And you got what you needed (I think)!!!

蓝礼 2024-09-15 17:15:07

这是我的想法:尝试使用属性

假设您有此类:

class Foo(models.Model):
    name = models.CharField()

相反,重命名您的字段(如果是第一次,则不需要迁移
你正在这样做)并且:

class Foo(models.Model):
    _name = models.CharField()

    @property
    def name(self):
        return self._name

    @name.setter
    def name(self, new_value):
        if not getattr(self, '_initial_name', False):
            self._initial_name = self._name

        if new_value != self._initial_name:
            self._name_changed = True
        else:
            self._name_changed = False

        self._name = new_value

我们向你的 Foo 实例添加了两个属性:'_initial_name' 和
'_name_changed' 和属性:'name'。这些不是模型字段,
永远不会保存到数据库中
。另外,你不必搞乱“_name”
只要“name”属性处理所有事情,字段就可以不再存在。

现在,您的“pre_save”或“post_save”信号处理程序可以检查已保存的内容
改变:

def handle_pre_save(sender, **kwargs):
    foo = kwargs['instance']
    if getattr(foo, '_name_changed', False):
        log.debug("foo changed its name from '%s' to '%s'",
                  foo._initial_name, foo.name)

Here's my idea: play around with properties.

Say you have this class:

class Foo(models.Model):
    name = models.CharField()

Instead, rename your field (you won't need a migration if it's the first time
you're doing this) and:

class Foo(models.Model):
    _name = models.CharField()

    @property
    def name(self):
        return self._name

    @name.setter
    def name(self, new_value):
        if not getattr(self, '_initial_name', False):
            self._initial_name = self._name

        if new_value != self._initial_name:
            self._name_changed = True
        else:
            self._name_changed = False

        self._name = new_value

We added two attributes to your Foo instances: '_initial_name' and
'_name_changed' and a property: 'name'. These are not model fields and will
never be saved to the database
. Also, you won't have to mess with the '_name'
field any longer as long as the 'name' property takes care of everything.

Now, your 'pre_save' or 'post_save' signal handler can make checks on what has
changed:

def handle_pre_save(sender, **kwargs):
    foo = kwargs['instance']
    if getattr(foo, '_name_changed', False):
        log.debug("foo changed its name from '%s' to '%s'",
                  foo._initial_name, foo.name)
森林散布 2024-09-15 17:15:07

您可以在保存之前询问数据库中当前存储的值。我这样做是为了仅记录更改的值,但每次保存时都会导致另一个数据库查询。

You can ask for the currently stored values in the database before you save. I have done this to log only changed values, but it causes another database query each time you save.

情栀口红 2024-09-15 17:15:07

虽然我非常赞成 Sébastien Piquemal 的回答 我最终最终使用了 pre_savepost_save 信号。我没有重写 __init__(),而是在 pre_save 中执行非常类似的操作,然后检查/比较 post_save 中的值并发出自定义信号如果满足某些条件,则从那里开始。

我想我仍然会接受他的回答,因为我花了很多时间。我看不出我们在哪里做的有什么不同,除了我在信号中做我的工作而他在初始化期间做。

While I very much approve of Sébastien Piquemal's answer I ultimately ended up using both the pre_save and post_save signals. Instead of overriding __init__(), I do something very similar in pre_save, and then check/compare the values in post_save and emit a custom signal from there if certain conditions are met.

I think I'll still accept his answer, for the time spent on it. I don't see where we're doing much different, except I'm doing my work in a signal and he's doing it during initialization.

懒猫 2024-09-15 17:15:07

您可以使用 pre_save 信号并将数据库记录(旧版本)与实例记录(已更新,但未保存在数据库版本中)进行比较。

以这样的模型为例:

class Person(models.Model):
    Name = models.CharField(max_length=200)

在 pre_save 函数中,您可以比较实例版本
与数据库版本。

def check_person_before_saving(sender, **kwargs):
    person_instance = kwargs['instance']
    if person_instance.id:
        # you are saving a Person that is already on the db
        # now you can get the db old version of Person before the updating

        # you should wrap this code on a try/except (just in case)
        person_db = Person.objects.get(id=person_instance.id)

        # do your compares between person_db and person_instance
        ...

# connect the signal to the function. You can use a decorator if you prefer
pre_save.connect(check_person_before_saving, sender=Person)

You can use the pre_save signal and compare the db record (the old version) with the instance record (the updated, but not saved in the db version).

Take a model like this one as example:

class Person(models.Model):
    Name = models.CharField(max_length=200)

In a pre_save function you can compare the instance version
with the db version.

def check_person_before_saving(sender, **kwargs):
    person_instance = kwargs['instance']
    if person_instance.id:
        # you are saving a Person that is already on the db
        # now you can get the db old version of Person before the updating

        # you should wrap this code on a try/except (just in case)
        person_db = Person.objects.get(id=person_instance.id)

        # do your compares between person_db and person_instance
        ...

# connect the signal to the function. You can use a decorator if you prefer
pre_save.connect(check_person_before_saving, sender=Person)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文