Reputation: 4129
I need to keep track of the most recent time a user was mentioned in a post, and update this field each time a new post is created based on it's post time.
My current code looks like this:
from django.db.models.signals import post_save
from django.dispatch import receiver
from messageboard.models import Post
@receiver(post_save, sender=Post)
def user_last_mentioned_updater(sender, instance, **kwargs):
for users in instance.mentions:
user.last_mentioned = max(user.last_mentioned, instance.timestamp)
user.save()
However, if two posts are processed concurrently, this could potentially leave the last_mentioned
field at the timestamp of the earlier post.
Unfortunately, F
doesn't support the max
operation, when I try it I get a TypeError: unorderable types: datetime.datetime() > F()
:
user.last_mentioned = max(F('last_mentioned'), instance.timestamp)
How can I avoid this race condition?
If it matters, for the moment I'm using Postgresql for the ORM, although this may be subject to change.
Upvotes: 2
Views: 749
Reputation: 48952
Here's a version that should be free of race conditions and more efficient:
@receiver(post_save, sender=Post)
def user_last_mentioned_updater(sender, instance, **kwargs)
User.objects.filter(
id__in=[u.id for u in instance.mentions],
last_mentioned__lt=instance.timestamp,
).update(last_mentioned=instance.timestamp)
That is, we select the mentioned users who's timestamps need updating, and update them, all in a single SQL statement.
Upvotes: 2