关于 Django 文档get_or_create的解释

最近在看 Django的 get_or_create 的文档的时候,发现了这样一句话, 文档地址

If you are using MySQL, be sure to use the READ COMMITTED isolation level rather than REPEATABLE READ (the default), otherwise you may see cases where get_or_create will raise an IntegrityError but the object won’t appear in a subsequent get() call.

我对于上面这句话的理解就是,如果使用 MySQL的话,需要将数据库的隔离级别从默认的可重复读改成提交读。否则的话,你可能会看到 get_or_create 方法抛出了一个 IntegrityError 异常,但是随后在数据库中查询的时候却找不到你要插入的记录。

我查了一下 get_or_create 的源码:它创建数据库记录的代码是这样的:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
def _create_object_from_params(self, lookup, params):
    """
    Tries to create an object using passed params.
    Used by get_or_create and update_or_create
    """
    try:
        with transaction.atomic(using=self.db):
            params = {k: v() if callable(v) else v for k, v in params.items()}
            obj = self.create(**params)
        return obj, True
    except IntegrityError:
        exc_info = sys.exc_info()
        try:
            return self.get(**lookup), False
        except self.model.DoesNotExist:
            pass
        six.reraise(*exc_info)

_create_object_from_params 做的事情就是开启一个事务,然后通过 insert 语句插入数据

文档中这句话针对的应该是这种情况:

1
2
3
from django.db import transaction
with transaction.atomic():
     User.objects.get_or_create(nickname='xxx')

如果将 get_or_create语句包裹在一个事务中,且MySQL的隔离级别设置成可重复读的话,上述语句确实会抛出IntergrityError的异常。

我看了一下get_or_create的实现,它的执行过程应该是这样的:

由于get_or_create实际上执行了三个事务,如果我们没有手动地把它放在一个事务中的话,它应该是不会抛出IntegrityError异常的。如果我们手动地把它放在一个事务中的话,而数据库的隔离级别又是可重复读的话,那么就很容易出现 @LeoJay 所说的情况,数据取又取不出来,存又存不进去。

以上就是我们对于get_or_create的理解,由于我水平有限,不对的地方还请大家指正。

参考链接