Handling race condition in model.save() (Django)
How should one handle a possible race condition in a models' save() method?

For example, the following example implements a model with an ordered list of related items. When creating a new Item the current list size is used as the its position. From what I can tell, this can go wrong if multiple Items are created concurrently (will it?)

    class OrderedList(models.Model):
        # ....
        @property
        def item_count(self):
            return self.item_set.count()
    
    class Item(models.Model):
        # ...
        parent = models.ForeignKey(OrderedList)
        position = models.IntegerField()
        class Meta:
            unique_together = (('parent','position'))
    
        def save(self, *args, **kwargs):
            if not self.id:
                # use item count as next position number
                self.position = parent.item_count
            super(Item, self).save(*args, **kwargs)

I've come across [@transactions].commit_on_success() but that seems to apply only to views. Even if it did apply to model methods, I still wouldn't know how to properly handle a failed transaction.

I am currenly handling it like so, but it feels more like a hack than a solution

    def save(self, *args, **kwargs):
        while not self.id:
            try:
                self.position = parent.item_count
                super(Item, self).save(*args, **kwargs)
            except IntegrityError:
                # chill out, then try again
                time.sleep(0.5)

Any suggestions?

 [@transactions]: http://docs.djangoproject.com/en/dev/topics/db/transactions/#controlling-transaction-management-in-views
Handling race condition in model.save() (Django)
===

How should one handle a possible race condition in a models' save() method?

For example, the following example implements a model with an ordered list of related items. When creating a new Item the current list size is used as the its position. From what I can tell, this can go wrong if multiple Items are created concurrently (will it?)

    class OrderedList(models.Model):
        # ....
        @property
        def item_count(self):
            return self.item_set.count()
    
    class Item(models.Model):
        # ...
        parent = models.ForeignKey(OrderedList)
        position = models.IntegerField()
        class Meta:
            unique_together = (('parent','position'))
    
        def save(self, *args, **kwargs):
            if not self.id:
                # use item count as next position number
                self.position = parent.item_count
            super(Item, self).save(*args, **kwargs)

I've come across [@transactions].commit_on_success() but that seems to apply only to views. Even if it did apply to model methods, I still wouldn't know how to properly handle a failed transaction.

I am currenly handling it like so, but it feels more like a hack than a solution

    def save(self, *args, **kwargs):
        while not self.id:
            try:
                self.position = parent.item_count
                super(Item, self).save(*args, **kwargs)
            except IntegrityError:
                # chill out, then try again
                time.sleep(0.5)

Any suggestions?

 [@transactions]: http://docs.djangoproject.com/en/dev/topics/db/transactions/#controlling-transaction-management-in-views