Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django. Thread safe update or create.

Tags:

python

django

We know, that update - is thread safe operation. It means, that when you do:

  SomeModel.objects.filter(id=1).update(some_field=100)

Instead of:

sm = SomeModel.objects.get(id=1)
sm.some_field=100
sm.save()

Your application is relativly thread safe and operation SomeModel.objects.filter(id=1).update(some_field=100) will not rewrite data in other model fields.

My question is.. If there any way to do

  SomeModel.objects.filter(id=1).update(some_field=100)

but with creation of object if it does not exists?

like image 979
Nikolay Fominyh Avatar asked Apr 03 '12 09:04

Nikolay Fominyh


People also ask

Is Django save thread-safe?

The Django ORM is thread-safe. But what you're talking about is a concurrency problem, where data in the database is changed by one process while it's being used in another process. Look at using F expressions to make sure your calculations use up-to-date data.

Is Django Get_or_create Atomic?

This method is atomic assuming correct usage, correct database configuration, and correct behavior of the underlying database.

What is Get_or_create?

get_or_create , is an awesome helper utility to have at your disposal when you need an object matching some specifications, but there should only be exactly one match — you want to retrieve it if it already exists, and create it if it doesn't.

Is Django threaded?

Node. js has a single threaded, non-blocking I/O mode of execution, while Django being a framework of Python, has a multi-threaded mode of execution.


1 Answers

from django.db import IntegrityError

def update_or_create(model, filter_kwargs, update_kwargs)
    if not model.objects.filter(**filter_kwargs).update(**update_kwargs):
        kwargs = filter_kwargs.copy()
        kwargs.update(update_kwargs)
        try:
            model.objects.create(**kwargs)
        except IntegrityError:
            if not model.objects.filter(**filter_kwargs).update(**update_kwargs):
                raise  # re-raise IntegrityError

I think, code provided in the question is not very demonstrative: who want to set id for model? Lets assume we need this, and we have simultaneous operations:

def thread1():
    update_or_create(SomeModel, {'some_unique_field':1}, {'some_field': 1})

def thread2():
    update_or_create(SomeModel, {'some_unique_field':1}, {'some_field': 2})

With update_or_create function, depends on which thread comes first, object will be created and updated with no exception. This will be thread-safe, but obviously has little use: depends on race condition value of SomeModek.objects.get(some__unique_field=1).some_field could be 1 or 2.

Django provides F objects, so we can upgrade our code:

from django.db.models import F

def thread1():
    update_or_create(SomeModel, 
                     {'some_unique_field':1}, 
                     {'some_field': F('some_field') + 1})

def thread2():
    update_or_create(SomeModel, 
                     {'some_unique_field':1},
                     {'some_field': F('some_field') + 2})
like image 94
Nik Avatar answered Oct 02 '22 13:10

Nik