模型中所有“子模型”更改的更新字段

时间:2020-01-08 11:21:58

标签: python django django-models

我知道如何更新ForeignKey的某些字段。例如,当我想每次更改Configuration或SomeOtherImportantClass时都更改last_modified字段时:

class Configuration(models.Model):
    title = models.TextField()
    last_modified = models.DateTimeField(auto_now=True)

class SomeOtherImportantClass(models.Model):
    conf = models.ForeignKey(Configuration)
    important_number = models.IntegerField()

    def save(self, *args, **kwargs):
        conf.last_modified = timezone.now() # I'm not sure if it is necessary
        conf.save()
        return super().save(*args, **kwargs)

但是在我的实际情况下,Cofiguration模型是30多个其他模型的ForeignKey。在它们中的每一个上,或者在更改另一个模型(具有ForeignKey的模型到具有ForeignKey做配置的某些模型)时,我都希望在每个模型中都更新configuration.last_modified字段。看起来像这样:

class Configuration(models.Model):
    title = models.TextField()
    last_modified = models.DateTimeField(auto_now=True)

class A(models.Model):
    conf = models.ForeignKey(Configuration)  # conf.last_modified must be updated on every change on A model object.

class B(models.Model):
    conf = models.ForeignKey(Configuration)  # same

...

class Z(models.Model):
    conf = models.ForeignKey(Configuration)  # same

class AA(models.Model):
    some_field = models.TextField()
    a = models.ForeignKey(A)
...

class ZZ(models.Model)
    some_field = models.TextField()
    z = models.ForeignKey(Z)

因此,即使AA对象字段“ some_field”已更改,我也要更新last_modified配置字段。是否有任何递归方法可以在Configuration或其他地方声明一次?

更新:像AAA和AAAA类这样的大家族也可以存在。

1 个答案:

答案 0 :(得分:1)

按照文档中的说明使用abstract base classes。对于A-Z,这非常简单:

class ConfigurationChild(Model):

    conf = ForeignKey(Configuration)

    class Meta:
        abstract = True

    def save(self):
        self.conf.last_modified = ...
        self.conf.save()
        super().save()

class A(ConfigurationChild):
    # other fields, without conf

对于孙子孙来说,这有点复杂,因为那样的话就没有直接引用conf了。在您填充到每个子类上的基类上设置一个属性:

class ConfigurationDescendant(Model):

    conf_handle = None

    class Meta:
        abstract = True

    def get_conf(self):
        if not self.conf_handle:
            return None  # or raise an error
        parent = getattr(self, self.conf_handle)
        if isinstance(parent, ConfigurationDescendant):
            return parent.get_conf()  # recursion
        else:
            # reached `ConfigurationChild` class, might want to check this
            return parent.conf if parent else None  

    def save(self):
        conf = self.get_conf()  
        # you might want to handle the case that the attribute is None
        if conf:
            conf.last_modified = ...
            conf.save()
        super().save()

class AA(ConfigurationDescendant):
    conf_handle = 'a'

    a = ForeignKey(A)

class AAA(ConfigurationDescendant):
    conf_handle = 'aa'
    aa = ForeignKey(AA)

上面的代码将处理由于父母之一中缺少conf_handle而导致链断裂的情况。在这种情况下,将返回None而不会发生任何事情。我不是在检查手柄的设置是否错误(即未正确指向父Configuration的方向),这将引发您可能想要的异常,以便您可以捕获错误。