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 django-users+unsubscr...@googlegroups.com.
To post to this group, send email to django-users@googlegroups.com.
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