詹戈。线程安全更新或创建。

2024-04-27 04:45:48 发布

您现在位置:Python中文网/ 问答频道 /正文

我们知道,更新是线程安全的操作。 意思是,当你这样做的时候:

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

而不是:

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

您的应用程序相对线程安全,并且操作SomeModel.objects.filter(id=1).update(some_field=100)不会重写其他模型字段中的数据。

我的问题是。。如果有办法的话

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

但是如果物体不存在的话,它会被创造出来吗?


Tags: 数据模型id应用程序fieldgetobjectssave
3条回答

您可以使用Django的内置get_或_create,但它在模型本身上操作,而不是在queryset上操作。

你可以这样使用:

me = SomeModel.objects.get_or_create(id=1)
me.some_field = 100
me.save()

如果有多个线程,则应用程序将需要确定模型的哪个实例是正确的。通常我要做的是从数据库中刷新模型,进行更改,然后保存它,这样就不会有很长时间处于断开连接状态。

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

我认为,问题中提供的代码并不是很具有示范性:谁想为模型设置id? 假设我们需要这个,我们同时进行操作:

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})

使用update_or_create函数,取决于哪个线程优先,对象将被创建和更新,没有例外。这将是线程安全的,但显然用处不大:取决于竞争条件值SomeModek.objects.get(some__unique_field=1).some_field可以是1或2。

Django提供F对象,因此我们可以升级代码:

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})

您需要django的select_for_update()方法(以及支持行级锁定的后端,如PostgreSQL)与手动事务管理相结合。

try:
    with transaction.commit_on_success():
        SomeModel.objects.create(pk=1, some_field=100)
except IntegrityError: #unique id already exists, so update instead
    with transaction.commit_on_success():
        object = SomeModel.objects.select_for_update().get(pk=1)
        object.some_field=100
        object.save()

请注意,如果其他进程删除了两个查询之间的对象,则会出现SomeModel.DoesNotExist异常。

Django 1.7及更高版本还支持原子操作和内置的update_or_create()方法。

相关问题 更多 >