Django测试中外键关联失败,疑与related_name='+'有关
问题场景
定义了如下模型:
class Model1(BaseModel): hidden = models.ForeignKey( Model2, default=10, related_name='+', ) class Model2(models.Model): # 模型内容
在Django测试过程中,保存Model1实例时触发外键约束错误,报错信息如下:
File "/app/src/save.py", line 317, in save_app model1.object.save() File "/app/venv/lib/python3.7/site-packages/django/db/models/base.py", line 740, in save force_update=force_update, update_fields=update_fields) File "/app/venv/lib/python3.7/site-packages/django/db/models/base.py", line 778, in save_base force_update, using, update_fields, File "/app/venv/lib/python3.7/site-packages/django/db/models/base.py", line 881, in _save_table results = self._do_insert(cls._base_manager, using, fields, returning_fields, raw) File "/app/venv/lib/python3.7/site-packages/django/db/models/base.py", line 921, in _do_insert using=using, raw=raw, File "/app/venv/lib/python3.7/site-packages/django/db/models/manager.py", line 85, in manager_method return getattr(self.get_queryset(), name)(*args, **kwargs) File "/app/venv/lib/python3.7/site-packages/django/db/models/query.py", line 1270, in _insert return query.get_compiler(using=using).execute_sql(returning_fields) File "/app/venv/lib/python3.7/site-packages/django/db/models/sql/compiler.py", line 1416, in execute_sql cursor.execute(sql, params) File "/app/venv/lib/python3.7/site-packages/django/db/backends/utils.py", line 66, in execute return self._execute_with_wrappers(sql, params, many=False, executor=self._execute) File "/app/venv/lib/python3.7/site-packages/django/db/backends/utils.py", line 75, in _execute_with_wrappers return executor(sql, params, many, context) File "/app/venv/lib/python3.7/site-packages/django/db/backends/utils.py", line 84, in _execute return self.cursor.execute(sql, params) File "/app/venv/lib/python3.7/site-packages/django/db/utils.py", line 90, in __exit__ raise dj_exc_value.with_traceback(traceback) from exc_value File "/app/venv/lib/python3.7/site-packages/django/db/backends/utils.py", line 84, in _execute return self.cursor.execute(sql, params) django.db.utils.IntegrityError: 对表"Model1"的插入或更新违反外键约束"Model1_hidden_1ba57f04_fk_Model2" DETAIL: 键(Model1_hidden_id)=(10)不存在于表"Model2"中。
问题根源
和related_name='+'无关,这个参数仅用于禁用Model2到Model1的反向关联管理器,不会影响外键约束的有效性。真正的问题是:测试数据库是全新初始化的,没有提前创建id=10的Model2实例,而Model1的外键默认值硬编码为10,插入时找不到对应记录,触发了数据库的完整性约束检查。
无需修改related_name的解决办法
1. 测试用例初始化时创建对应Model2实例
在测试类的setUp方法中,提前创建id为10的Model2记录,确保测试环境中有对应数据:
from django.test import TestCase from .models import Model1, Model2 class YourModelTestCase(TestCase): def setUp(self): # 提前创建外键默认值对应的Model2实例 Model2.objects.create(id=10) def test_model1_save(self): # 此时创建并保存Model1实例就不会触发约束错误 model1 = Model1() model1.save()
2. 用可调用对象作为外键默认值
将默认值从固定的10改为一个函数,动态获取或创建对应的Model2实例,确保任何环境下都有可用的关联记录:
def get_default_model2(): # get_or_create会自动创建不存在的记录 obj, _ = Model2.objects.get_or_create(id=10) return obj.id class Model1(BaseModel): hidden = models.ForeignKey( Model2, default=get_default_model2, related_name='+', )
3. 使用Fixture预加载测试数据
创建包含id=10的Model2记录的fixture文件,测试时自动加载到数据库:
- 首先在app目录下创建
fixtures/model2_default.json文件:
[ { "model": "your_app_name.model2", "pk": 10, "fields": {} } ]
- 然后在测试用例中指定加载该fixture:
from django.test import TestCase class YourModelTestCase(TestCase): fixtures = ['model2_default.json'] def test_model1_save(self): model1 = Model1() model1.save() # 正常执行
内容的提问来源于stack exchange,提问作者Andy Li
相关产品推荐
相关产品推荐

