我知道这个问题有点老了,但我也遇到了同样的问题,想分享我的经验。
我对 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 上工作。