Django 1.11.1迁移至2.0.1后SQLite内存库测试表锁定排查求助
我之前迁移 Django 版本时也碰到过几乎一模一样的坑!先给你戳破核心问题:SQLite 的内存数据库(:memory:)是每个数据库连接私有的。LiveServerTestCase 会启动一个独立的 HTTP 服务线程,这个线程会新建一个数据库连接——它访问的内存库和你测试主线程里的完全不是同一个!这就导致两种混乱情况:要么服务线程找不到主线程建好的表,尝试重复创建时触发冲突;要么双方各自操作“私有库”时,因 SQLite 的单写特性触发锁错误。
下面给你一步步排查和解决的实用思路:
1. 先验证是不是内存库的共享问题
先把测试用的数据库临时改成磁盘文件试试,在测试环境的 settings.py 里修改:
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': 'test_temp_db.sqlite3', # 换成磁盘文件 } }
如果测试能正常跑,那百分之百是内存库的多线程共享问题——磁盘文件是所有连接共享的,不会出现“各玩各的库”的情况。
2. 让内存库支持多线程共享(不想用磁盘文件的话)
SQLite 其实提供了共享内存库的方案,用 file::memory:?cache=shared 作为数据库名称,它会把内存库作为共享缓存,让所有连接都能访问同一个实例。修改测试配置:
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': 'file::memory:?cache=shared', } }
注意:这个特性需要 SQLite 3.7.13 及以上版本支持,先跑 sqlite3 --version 确认你的版本达标。
3. 检查 LiveServerTestCase 的线程与迁移逻辑
Django 2.0 对比 1.11,LiveServerTestCase 的默认行为有调整,比如自动迁移的时机可能和主线程的数据库初始化冲突。你可以手动控制迁移时机:
class MyTestClass(LiveServerTestCase): @classmethod def setUpClass(cls): # 禁用自动迁移,让主线程先建好表再启动服务器 cls.migrate = False super().setUpClass() # 手动执行迁移 from django.core.management import call_command call_command('migrate')
这样能确保服务器线程启动时,数据库表已经完全就绪,避免冲突。
4. 排查未关闭的数据库连接
Django 2.0 对事务和连接的处理逻辑有变化,如果测试代码里手动打开了连接没关闭,或者事务没提交/回滚,很容易触发锁。可以在测试的清理步骤里强制关闭所有连接:
def tearDown(self): from django.db import connections for conn in connections.all(): conn.close() super().tearDown()
5. 换用更轻量的测试类(如果场景允许)
如果你的测试不需要真的启动 HTTP 服务器,只是验证视图逻辑,换成 TransactionTestCase 甚至普通的 TestCase 会稳定很多——毕竟 LiveServerTestCase 因为多线程特性,本身就引入了额外的复杂度。
内容的提问来源于stack exchange,提问作者Euphorbium

