简体   繁体   中英

Avoid race condition during creating object with incremented field

I'm trying to create MyModel object but I want to set bar field to incremented value of already existed largest bar value in db for specified foo . The problem here are race conditions. I wanted to perform all logic on db side in one step without sucess. I have found solution but it's not the most elegant way. Infinite loops are always bad idea.

from django.db import models, IntegrityError
from django.db.models import Max


class MyModel(models.Model):
    foo = models.UUIDField(default=uuid4)
    bar = models.PositiveIntegerField()

    class Meta:
        constraints = [
            models.UniqueConstraint(
                fields=['id', 'other_id'],
                name='unique_id_other_id'
            )
        ]

    @classmethod
    def create_my_model(cls, data):
        while True:
            bar = (cls.objects.filter(foo=data['foo']).aggregate(Max('bar')).get('bar_max')
                   or 0) + 1
            try:
                cls.objects.create(bar=bar, **data)
            except IntegrityError:
                continue

I will be glad if anyone can point me any direction how to handle this. BR

Solution

Use django.db.models.AutoField

class MyModel(models.Model):
    foo = models.UUIDField(primary_key=True, default=uuid4)
    bar = models.AutoField()

An IntegerField that automatically increments according to available IDs. You usually won't need to use this directly; a primary key field will automatically be added to your model if you don't specify otherwise. See Automatic primary key fields.

https://docs.djangoproject.com/en/3.0/ref/models/fields/#autofield

Commentary

Though personally I'll just do the following and leave the primary key alone (which is the de facto way of doing things in Django)

class MyModel(models.Model):
    foo = models.UUIDField(unique=True, default=uuid4)

which will give MyModel an automatic primary key field

id = models.AutoField(primary_key=True)

https://docs.djangoproject.com/en/3.0/topics/db/models/#automatic-primary-key-fields

or if you want to rename the automatic primary key field:

class MyModel(models.Model):
    bar = models.AutoField(primary_key=True)
    foo = models.UUIDField(unique=True, default=uuid4)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM