You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用SQLite3测试Django自定义字段时无法禁用约束创建表的问题

问题原因分析

  1. 第一个错误(NotSupportedError):
    Django的TestCase默认会在事务上下文中运行测试方法,而SQLite不支持在事务执行过程中禁用外键约束。你调用constraint_checks_disabled()的时机是在事务内部,因此触发了该错误。
  2. 第二个错误(DatabaseOperationForbidden):
    SimpleTestCase是为无需数据库的测试场景设计的,默认禁止所有数据库操作。即便显式开启数据库访问权限,它也不会处理表的创建与清理,完全不适合你的测试需求。

另外你的测试类存在语法错误:SetUpClass应改为setUpClass,且必须添加@classmethod装饰器,否则该方法不会被Django测试框架正确执行。


正确解决方案

推荐使用TransactionTestCase作为父类(它通过创建/销毁表实现测试隔离,而非依赖事务回滚),并在setUpClass中正确处理动态模型与表的创建:

from django.test import TransactionTestCase
from django.db import connections
from django.db.models import Model
# 替换为你的字段和枚举类路径
from your_app.fields import HEnumField
from your_app.enums import MyHEnum

class TestMyField(TransactionTestCase):
    @classmethod
    def setUpClass(cls):
        super().setUpClass()
        
        # 动态定义测试模型
        class MyModel(Model):
            class Meta:
                app_label = 'test_app'
                managed = False  # 禁止Django自动管理该模型的表
            
            hfield = HEnumField(enum=MyHEnum)
        
        cls.MyModel = MyModel
        
        db_connection = connections['default']
        # 针对SQLite单独处理外键约束
        if db_connection.vendor == 'sqlite':
            with db_connection.cursor() as cursor:
                cursor.execute('PRAGMA foreign_keys = OFF;')
        
        # 创建模型对应的数据库表
        with db_connection.schema_editor() as schema_editor:
            schema_editor.create_model(cls.MyModel)
        
        # 可选:创建表后重新开启SQLite外键约束
        if db_connection.vendor == 'sqlite':
            with db_connection.cursor() as cursor:
                cursor.execute('PRAGMA foreign_keys = ON;')

    @classmethod
    def tearDownClass(cls):
        # 测试结束后清理动态创建的表
        db_connection = connections['default']
        with db_connection.schema_editor() as schema_editor:
            schema_editor.delete_model(cls.MyModel)
        super().tearDownClass()

    def test_field_functionality(self):
        # 编写你的测试逻辑
        instance = self.MyModel.objects.create(hfield=MyHEnum.SOME_OPTION)
        self.assertEqual(instance.hfield, MyHEnum.SOME_OPTION)

关键说明

  • 使用TransactionTestCase:它不会在测试启动前开启事务,因此能安全处理SQLite的外键约束禁用操作。
  • 设置managed=False:避免Django的自动迁移逻辑干扰手动创建的测试表。
  • 直接操作SQLite外键:通过PRAGMA语句禁用/开启外键,比constraint_checks_disabled()更可靠,后者无法在事务内生效。
  • 手动清理表:在tearDownClass中删除动态表,保证测试环境的干净隔离。

内容的提问来源于stack exchange,提问作者piscvau

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 21:25:07