同时更新模型及其相关父级的时间戳

时间:2018-06-07 13:39:26

标签: python django django-models timestamp

我们说我有以下型号:

class Blog(TimeStampedModel):
    summary = models.TextField()
    text = models.TextField()

class BlogComment(TimeStampedModel):
    author = models.CharField()
    text = models.CharField()
    blog = models.ForeignKey(Blog, models.CASCADE, related_name='comments')

class BlogTag(TimeStampedModel):
    name = models.CharField()
    blog = models.ForeignKey(Blog, models.CASCADE, related_name='tags')

这三个都继承自以下模型,该模型实现了一些时间戳处理:

class TimeStampedModel(models.Model):
    last_changed = models.DateTimeField()
    created_at = models.DateTimeField(default=timezone.now)

    def save(self, *args, **kwargs):
        try:
            self._meta.get_field('blog')
        except models.FieldDoesNotExist:
            self.last_changed = timezone.now()
            super(TimeStampedModel, self).save(*args, **kwargs)
        else:
            now = timezone.now()
            self.blog.last_changed = now
            self.last_changed = now
            with transaction.atomic():
                super(TimeStampedModel, self).save(*args, **kwargs)
                self.blog.save()

    class Meta:
        abstract = True

自定义save()背后的基本思想是,当例如注释(BlogComment的实例)更新时,它应该更新注释实例和注释实例的last_changed时间戳。相关博客文章。

不幸的是,时间戳设置并不完美,因为它会在调用自己的保存时覆盖博客的时间戳,并且时间最终会略有不同:

In [1]: b = Blog.objects.get(id=1)

In [2]: comment0 = t.comments.all()[0]

In [3]: b.last_changed, comment0.last_changed
Out[3]:
(datetime.datetime(2018, 6, 7, 12, 54, 12, 516855, tzinfo=<UTC>),
 datetime.datetime(2018, 6, 7, 10, 22, 09, 201690, tzinfo=<UTC>))

In [4]: comment0.text
Out[4]: 'Some text'

In [5]: comment0.text = 'test'

In [6]: comment0.save()

In [7]: t = blog.objects.get(id=1)

In [8]: comment0 = t.comments.all()[0]

In [9]: t.last_changed, comment0.last_changed
Out[9]:
(datetime.datetime(2018, 6, 7, 13, 25, 58, 64131, tzinfo=<UTC>),
 datetime.datetime(2018, 6, 7, 13, 25, 58, 61960, tzinfo=<UTC>))

另一个问题是save函数依赖于名为blog的外键,由于添加使用不同外键名称的模型需要更新save函数,因此这看起来很麻烦。

对于更新相关模型的时间戳这个问题,是否有更通用的解决方案?如何修改上面的save()函数以使用相同时间戳持续更新模型?

1 个答案:

答案 0 :(得分:1)

如果在每个模型中使用相同的外键引用,则找到解决方案:

def save(self, *args, **kwargs):
    self.last_changed = timezone.now()
    try:
        self._meta.get_field('blog')
    except models.FieldDoesNotExist:
        super(TimeStampedModel, self).save(*args, **kwargs)
    else:
        self.blog.last_changed = self.last_changed
        with transaction.atomic():
            super(TimeStampedModel, self).save(*args, **kwargs)
            super(TimeStampedModel, self.blog).save(*args, **kwargs)