Django 1.11测试模式下如何断开模型信号?或绕开ORM创建对象?
嘿,我来帮你搞定Django 1.11测试里的信号问题~先拆解下你遇到的错误,再给你两种可行的解决方案:
解决测试中
post_save信号触发问题&修复AttributeError 先解释你遇到的AttributeError
你直接用patch(post_save)肯定会报错!因为unittest.mock.patch要求传入的是信号/接收器的字符串模块路径,而不是信号对象本身。它内部会尝试对参数做rsplit来解析模块路径,但信号对象不是字符串,自然就抛出了'ModelSignal' object has no attribute 'rsplit'的错误。
方案一:正确禁用post_save信号
1. 全局禁用所有post_save信号
最简单的方式是直接patch信号的send方法,这样所有post_save信号的接收器都不会被触发:
from django.db.models.signals import post_save from unittest.mock import patch from django.test import TestCase class YourInstanceTest(TestCase): def setUp(self): with patch('django.db.models.signals.post_save.send'): # 这里创建对象时,post_save信号完全不会触发 self.instance = Instance.objects.create(field1="value1", field2="value2")
2. 只禁用特定模型的接收器
如果只想禁用某个特定的post_save回调(比如你给Instance模型注册的接收器),可以直接patch那个回调函数的模块路径。假设你的接收器定义在yourapp.signals.instance_post_save:
from unittest.mock import patch from django.test import TestCase class YourInstanceTest(TestCase): def setUp(self): with patch('yourapp.signals.instance_post_save') as mock_receiver: self.instance = Instance.objects.create(field1="value1", field2="value2") # 还可以验证接收器确实没被调用 mock_receiver.assert_not_called()
方案二:绕开ORM直接创建对象
如果你想彻底避开ORM的信号机制,可以直接用数据库连接执行SQL插入,这样完全不会触发任何ORM层面的信号:
from django.db import connection from django.test import TestCase class YourInstanceTest(TestCase): def setUp(self): with connection.cursor() as cursor: # 注意表名是「app名_模型名」,比如你的app叫yourapp,模型是Instance,表名就是yourapp_instance cursor.execute( "INSERT INTO yourapp_instance (field1, field2) VALUES (%s, %s)", ("value1", "value2") ) # 获取刚插入的自增ID(MySQL用763106,PostgreSQL用currval(pg_get_serial_sequence('yourapp_instance', 'id'))) cursor.execute("SELECT 763106") instance_id = cursor.fetchone()[0] # 如果需要模型实例,用get()获取——注意:get()不会触发信号,只有save/create操作才会 self.instance = Instance.objects.get(id=instance_id)
⚠️ 小提示:不同数据库获取自增ID的SQL语句不一样,上面是MySQL的写法,PostgreSQL需要调整成对应的语句哦。另外,Django的TestCase会自动管理事务,测试结束后会回滚所有操作,不用担心数据污染。
内容的提问来源于stack exchange,提问作者Beliaf
相关产品推荐
相关产品推荐

