13

当在 Django 模型上执行更新/创建时.save()

我在想 Pre-Save Signals,查找原始模型做 a .objects.get(instance.id),但这感觉很浪费。另外,验证是否已经发生pre_save()

4

5 回答 5

8

关于模型验证

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

然后,关于pre-save signal,请注意您将保存的实例作为参数与消息一起发送。由于您的模型的前一个版本仅存在于数据库中,我看不出您还能在哪里获得属性的先前值......

你不告诉你为什么要这样做,所以很难说,但我现在正在考虑其他解决方案:

* 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>)

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

于 2010-07-09T20:48:08.970 回答
2

这是我的想法:玩转 properties

假设你有这个类:

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)
于 2012-07-09T07:45:54.557 回答
0

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

于 2010-07-11T13:51:44.857 回答
0

虽然我非常赞同Sébastien Piquemal 的回答,但我最终还是同时使用了pre_savepost_save信号。我没有覆盖__init__(),而是在 中做一些非常相似的事情pre_save,然后检查/比较中的值,post_save如果满足某些条件,则从那里发出自定义信号。

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

于 2010-08-24T20:32:46.937 回答
0

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

以这样的模型为例:

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)
于 2015-04-22T15:45:38.333 回答