pytest-django如何在测试数据库创建完成后注册自定义数据库类型
问题根因
你自定义的django_db_setup没有生效的核心原因是:pytest内置fixture的覆盖规则要求重写的fixture必须放在项目根目录的conftest.py中才会生效,且直接覆盖pytest-django的核心fixture容易出现版本兼容问题,不推荐这么做。
另外你原代码在元类初始化阶段(即模块导入时)就执行数据库操作的设计本身不符合Django最佳实践,模块导入阶段测试数据库还未创建,必然会触发注册失败的问题。
解决方案
第一步:修正字段元类的自动注册逻辑
移除元类中模块导入时的自动注册调用,避免导入阶段就访问数据库:
class FieldMeta(type): def __init__(cls, name, bases, clsdict): super().__init__(name, bases, clsdict) # 删除这行自动调用逻辑 # cls.register_type() # register_type方法其余逻辑保持不变
生产环境可以在Django应用的apps.py的ready钩子中执行注册,保证项目启动、数据库连接初始化完成后再操作:
# your_app/apps.py from django.apps import AppConfig class YourAppConfig(AppConfig): default_auto_field = 'django.db.models.BigAutoField' name = 'your_app' def ready(self): from .fields import FooField FooField.register_type()
第二步:配置测试环境全局注册逻辑
不需要重写django_db_setup,直接在项目根目录的conftest.py中新增一个自动执行的会话级fixture,依赖django_db_setup保证测试数据库创建完成后再执行注册:
# 项目根目录conftest.py import pytest from your_app.fields import FooField @pytest.fixture(scope="session", autouse=True) def register_custom_db_types(django_db_setup, django_db_blocker): with django_db_blocker.unblock(): FooField.register_type()
这个fixture会在整个测试会话启动、测试数据库创建迁移完成后自动执行,所有测试用例都可以直接使用自定义的数据库类型。
可选方案:通过迁移创建自定义类型
如果需要在迁移中固化自定义类型的创建逻辑,可以新增一个空迁移写入类型创建代码,这样不管是生产还是测试环境,执行迁移时都会自动创建foo类型:
- 生成空迁移文件
python manage.py makemigrations your_app --empty
- 编辑迁移文件内容
from django.db import migrations def create_foo_type(apps, schema_editor): schema_editor.execute("CREATE TYPE foo AS (first integer, second integer);") def drop_foo_type(apps, schema_editor): schema_editor.execute("DROP TYPE foo;") class Migration(migrations.Migration): dependencies = [ ('your_app', '000x_previous_migration'), # 替换为你的上一个迁移编号 ] operations = [ migrations.RunPython(create_foo_type, drop_foo_type), ]
内容的提问来源于stack exchange,提问作者Alex
相关产品推荐
相关产品推荐

