On save I want to receive names like name, name(1), name(2)
. I implemented the following code:
with transaction.atomic():
same_name_count = Folder.objects.filter(
owner=validated_data["owner"],
name__iregex=r"%s(\s\(\d+\))?" % validated_data["name"],
).count()
if same_name_count:
validated_data["name"] = f"{validated_data['name']} ({same_name_count+1})"
folder = Folder.objects.create(**validated_data)
But I still receive race condition and receive same names when I run this code in celery task. I also tried select_to_update
to lock all rows somehow and get correct count
My model looks like this:
class Folder(models.Model):
owner = models.ForeignKey('User')
name = models.CharField(max_length=255)
select_for_update
will lock all selected rows in the table, but will not prevent adding new rows.
What you need to do is to lock row in other table. This way the transaction will wait until this locked row is unlocked.
In your case the most suitable is User
table.
Here is example code:
with transaction.atomic():
# Assuming validated_data["owner"] is an id of the user.
# If its the User objects then add .id at the end
folder_owner = User.objects.select_for_update().get(id=validated_data["owner"])
same_name_count = Folder.objects.filter(
owner=folder_owner,
name__iregex=r"%s(\s\(\d+\))?" % validated_data["name"],
).count()
if same_name_count:
validated_data["name"] = f"{validated_data['name']} ({same_name_count+1})"
folder = Folder.objects.create(**validated_data)