Reputation: 6233
Let's say I have the following models:
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')
All three inherit from the following model, which implements some timestamp handling:
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
The basic idea behind the custom save()
is that when for example a comment (instance of BlogComment
) is updated, it should update the last_changed
timestamp of both the comment instance and the related blog entry.
Unfortunately, the timestamp setting is not perfect since it overrides the blog's timestamp when its own save is called and the times end up being just slightly different:
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>))
Another problem is that the save function relies on the foreign key being named blog
, which seems hacky since adding models that uses different foreign key names then requires an update of the save function.
Is there a more general solution to this problem of updating timestamps of related models? How can I amend the save()
function above to consistently update models with the same timestamp?
Upvotes: 1
Views: 171
Reputation: 6233
Found a solution if using the same foreign key reference in every model:
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)
Upvotes: 1