此代码应该获取或创建一个对象,并在必要时更新它.该代码在一家网站上投入生产使用.

在某些情况下,当数据库繁忙时,它会抛出异常"DoesNotExist:MyObj匹配查询不存在".

# Model:
class MyObj(models.Model):
    thing = models.ForeignKey(Thing)
    owner = models.ForeignKey(User)
    state = models.BooleanField()
    class Meta:
        unique_together = (('thing', 'owner'),)

# Update or create myobj
@transaction.commit_on_success
def create_or_update_myobj(owner, thing, state)
    try:
        myobj, created = MyObj.objects.get_or_create(owner=user,thing=thing)

    except IntegrityError:
        myobj = MyObj.objects.get(owner=user,thing=thing)
        # Will sometimes throw "DoesNotExist: MyObj matching query does not exist"

    myobj.state = state
    myobj.save()

我在ubuntu上使用的是Innodb MySQL数据库.

我如何安全地处理这个问题?

推荐答案

这可能是与这里相同的问题的副产品:

Why doesn't this loop display an updated object count every five seconds?

基本上是get_or_create,can fail,如果你看一下它的来源,你会发现它是:get,if问题:save+一些诡计,if仍然有问题:再次get,if仍然有问题:投降并提高.

这意味着如果有两个同时运行create_or_update_myobj的线程(或进程)都试图获取或创建相同的对象,则:

  • 第一个线程试图获取它,但它还不存在,
  • 因此,线程试图创建它,但在创建对象之前.
  • .第二个线程试图获取它-这显然失败了
  • 现在,由于MySQLdb数据库连接的默认AUTOCOMMIT=OFF和可重复读取序列化级别,两个线程都冻结了MyObj表的视图.
  • 随后,第一线程会创建它的对象并优雅地返回它,但是.
  • ...第二个线程无法创建任何内容,因为它将违反unique约束
  • 有趣的是,由于MyObj表的冻结视图,第二个线程上的后续get看不到在第一线程中创建的对象

所以,如果你想安全地完成任何事情,可以try 以下方法:

 @transaction.commit_on_success
 def my_get_or_create(...):
     try:
         obj = MyObj.objects.create(...)
     except IntegrityError:
         transaction.commit()
         obj = MyObj.objects.get(...)
     return obj

编辑于2010年05月27日

这个问题还有第二种解决方案——使用读取提交隔离级别,而不是可重复读取.但是它测试得更少(至少在MySQL中),所以它可能会有更多的bug /问题——但是至少它允许将视图绑定到事务上,而不在中间.

编辑于2012年01月22日

以下是一些关于MySQL和Django的好博客(不是我的),与这个问题有关:

http://www.no-ack.org/2010/07/mysql-transactions-and-django.html

http://www.no-ack.org/2011/05/broken-transaction-management-in-mysql.html

Django相关问答推荐

Django显示交叉表格

Django Admin中显示的DateField下面的小提示是什么?

通过在其他查询中使用来过滤对象

Django allauth 社交帐户注册中邮箱的模板变量

Django中用于外键的嵌套循环

无法创建超级用户,因为 Django 中的一列(外键)不能为空

Django 关系嵌套related_name

未为部署的 django rest 框架加载静态文件

Django - 将 HTML 输出转换为变量

django-rest-framework 如何使模型序列化器字段成为必需

如何查看 Django 调试工具栏?

Django:在模板中显示当前语言环境

Django删除查询集的最后五个以外的所有内容

virtualenv(python3.4), pip install mysqlclient 错误

django 有条件地过滤对象

如何在 Django Rest Framework SimpleRouter 上使斜杠可选

PyCharm 无法正确识别需求 - Python、Django

Django - 了解 X-Sendfile

使用 get_object_or_404 获取数据库值

django过滤器超过几天?