Hi Carsten,

Did you try using select_for_update() with get_or_create()[1] in an
atomic()[2] context?

@transation.atomic
def demonstrate_the_problem():
    d = date.today() 
    t = TestModel.objects.select_for_update().get_or_create(
        jahr=d.year, monat=d.month
    )
    # ... long `some_value` computation
    t.some_value = 123
    t.save(update_fields={'some_value'})
    return t

Note that in this case if another thread tries to select_for_update() it is
going to block at the get_of_create() until the first thread's transaction
commits.

If you'd like to prevent other threads from blocking you might want to use 
the
`nowait` option of select_for_update() and catch the `OperationalError` that
might be raised in order to return something else.

Cheers,
Simon

[1] 
https://docs.djangoproject.com/en/1.9/ref/models/querysets/#get-or-create
[2] 
https://docs.djangoproject.com/en/1.9/topics/db/transactions/#django.db.transaction.atomic

Le mercredi 11 mai 2016 09:44:12 UTC-4, Carsten Fuchs a écrit :
>
> Dear Django group, 
>
> please consider this code: 
>
>
>      from datetime import date 
>      from django.db import models 
>
>
>      class TestModel(models.Model): 
>          jahr = models.SmallIntegerField() 
>          monat = models.SmallIntegerField() 
>          some_value = models.SmallIntegerField() 
>
>          class Meta: 
>              unique_together = ('jahr', 'monat') 
>
>
>      def demonstrate_the_problem(): 
>          d = date.today() 
>
>          try: 
>              t = TestModel.objects.get(jahr=d.year, monat=d.month) 
>              # t exists, no need to create or modify it. 
>              return t.some_value 
>          except TestModel.DoesNotExist: 
>              # t did not yet exist, so we have to create it anew. 
>              # Note that there is a "unique together" constraint in place 
>              # that makes sure that the tuple (jahr, monat) only exists 
> once. 
>              # Thus we create a new instance, then lock it with 
>              # select_for_update()  --  but this is still not atomic! 
>              TestModel(jahr=d.year, monat=d.month).save() 
>              t = TestModel.objects.get( 
>                      jahr=d.year, monat=d.month).select_for_update() 
>
>          # A long computation, eventually setting fields in the new t, 
>          # then saving it for the next call to this function. 
>          t.some_value = 123 
>          t.save() 
>          return t.some_value 
>
>
> The problem is that another thread too may have created a TestModel with 
> the 
> same (jahr, monat) in the timespan between our "does not exist" and 
> "lock", 
> triggering a violation of the "unique" constraint. 
>
> Thus, the question is how we can make the sequence "does not exist – 
> create anew 
> – lock" atomic? 
>
> Any feedback would very much be appreciated! 
>
> Many thanks and best regards, 
> Carsten 
>
>

-- 
You received this message because you are subscribed to the Google Groups 
"Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
Visit this group at https://groups.google.com/group/django-users.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-users/433d0f20-513c-4e3d-9e41-777e3bdc8407%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to