我知道这个问题有点老了,但我也遇到了同样的问题,想分享我的经验。
我对 st0nes 的回答不太满意,因为(至少对于 postgres 而言)LOCK TABLE
只能在事务中发布声明。尽管在 Django 中,通常几乎所有事情都发生在一个事务中,但这LockingManager
并不能确定你实际上是在一个事务中,至少在我的理解中是这样。此外,我不想完全更改模型Manager
只是为了能够将其锁定在一个位置,因此我更多地寻找有点像的东西with transaction.atomic():
,但也锁定给定的模型。
所以我想出了这个:
from django.conf import settings
from django.db import DEFAULT_DB_ALIAS
from django.db.transaction import Atomic, get_connection
class LockedAtomicTransaction(Atomic):
"""
Does a atomic transaction, but also locks the entire table for any transactions, for the duration of this
transaction. Although this is the only way to avoid concurrency issues in certain situations, it should be used with
caution, since it has impacts on performance, for obvious reasons...
"""
def __init__(self, model, using=None, savepoint=None):
if using is None:
using = DEFAULT_DB_ALIAS
super().__init__(using, savepoint)
self.model = model
def __enter__(self):
super(LockedAtomicTransaction, self).__enter__()
# Make sure not to lock, when sqlite is used, or you'll run into problems while running tests!!!
if settings.DATABASES[self.using]['ENGINE'] != 'django.db.backends.sqlite3':
cursor = None
try:
cursor = get_connection(self.using).cursor()
cursor.execute(
'LOCK TABLE {db_table_name}'.format(db_table_name=self.model._meta.db_table)
)
finally:
if cursor and not cursor.closed:
cursor.close()
所以如果我现在想锁定模型ModelToLock
,可以这样使用:
with LockedAtomicTransaction(ModelToLock):
# do whatever you want to do
ModelToLock.objects.create()
编辑:请注意,我只使用 postgres 对此进行了测试。但据我了解,它也应该像这样在 mysql 上工作。