【问题标题】:Django: Before a model is updated, I'd like to "look at" its previous attributesDjango:在更新模型之前,我想“查看”它以前的属性
【发布时间】:2011-03-14 01:53:45
【问题描述】:

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

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

【问题讨论】:

    标签: django django-models django-signals


    【解决方案1】:

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

    以这样的模型为例:

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

    在 pre_save 函数中,您可以比较实例版本 使用 db 版本。

    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)
    

    【讨论】:

      【解决方案2】:

      这是我的想法:玩转属性

      假设你有这个课程:

      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)
      

      【讨论】:

        【解决方案3】:

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

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

        【讨论】:

          【解决方案4】:

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

          【讨论】:

            【解决方案5】:

            关于model validation

            请注意,当您调用模型的 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”,您将无法确定修改是否已保存。

            在这种情况下,我想您可以在初始化实例时缓存您感兴趣的变量,并捕获 post-save OR pre-save 信号。

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

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

            【讨论】:

            • 感谢您的回答。具体来说,但非常笼统地,我想知道特定字段(例如,选择字段)何时更改。更进一步,我想按照你说的做 - 发生这种情况时发出一个自定义信号。因此回到我的问题 - 当模型更新时,我如何比较它以前的值来发送这个信号? (如果不能保证在 post_save 之前保存对象,那么在不验证的情况下发出 foo_has_updated 信号似乎是不对的……但这完全是另一个问题。
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2023-03-28
            • 2012-09-21
            • 2016-08-12
            相关资源
            最近更新 更多