已捕获DoesNotExist仍报错?Django外键查询异常求助
我在Django项目里明明用try-except捕获了TestModel_2.DoesNotExist异常,但运行时还是抛出了renoauth.models.DoesNotExist: TestModel_2 matching query does not exist错误,相关代码和报错堆栈如下:
相关代码片段
views.py 中的代码
try: testmodel2 = TestModel_2.objects.get(description='a') except TestModel_2.DoesNotExist: testmodel2 = None
models.py 中的模型定义
@python_2_unicode_compatible class TestModel_2(models.Model): description = models.CharField(max_length=34) created = models.DateTimeField(auto_now_add=True) def __str__(self): return "TestModel %s" % self.description @python_2_unicode_compatible class TestModelLog_2(models.Model): description = models.CharField(max_length=34) test_foreignkey = models.ForeignKey(TestModel_2, related_name='test_foreignkey_1', null=True, blank=True, on_delete=models.DO_NOTHING) test_foreignkey_2 = models.ForeignKey(TestModel_2, related_name='test_foreignkey_2', null=True, blank=True, on_delete=models.DO_NOTHING)
报错堆栈信息
Internal Server Error: /accounts/test2/ Traceback (most recent call last): File "D:\pythonDev\interpreters\interpreter1\lib\site-packages\django\db\models\fields\related_descriptors.py", line 170, in __get__ rel_obj = getattr(instance, self.cache_name) AttributeError: 'TestModelLog_2' object has no attribute '_test_foreignkey_2_cache' During handling of the above exception, another exception occurred: Traceback (most recent call last): File "D:\pythonDev\interpreters\interpreter1\lib\site-packages\django\core\handlers\exception.py", line 39, in inner response = get_response(request) File "D:\pythonDev\interpreters\interpreter1\lib\site-packages\django\core\handlers\base.py", line 187, in _get_response response = self.process_exception_by_middleware(e, request) File "D:\pythonDev\interpreters\interpreter1\lib\site-packages\django\core\handlers\base.py", line 185, in _get_response response = wrapped_callback(request, *callback_args, **callback_kwargs) File "D:\pythonDev\project\logintest\renoauth\views.py", line 56, in test2 if testmodel_2_log.test_foreignkey_2 is None: File "D:\pythonDev\interpreters\interpreter1\lib\site-packages\django\db\models\fields\related_descriptors.py", line 179, in __get__ rel_obj = qs.get() File "D:\pythonDev\interpreters\interpreter1\lib\site-packages\django\db\models\query.py", line 385, in get self.model._meta.object_name renoauth.models.DoesNotExist: TestModel_2 matching query does not exist. [06/Jan/2018 16:20:24] "POST /accounts/test2/ HTTP/1.1" 500 15678
问题出在哪?
你仔细看报错堆栈的关键行:if testmodel_2_log.test_foreignkey_2 is None:——这个异常根本不是来自你写的那个try-except块,而是在你访问TestModelLog_2实例的test_foreignkey_2属性时触发的。
为什么会这样?因为你给外键设置了null=True, blank=True,但用了on_delete=models.DO_NOTHING。这就意味着,如果数据库里某个TestModel_2记录被删了,关联的TestModelLog_2的test_foreignkey_2_id字段还保留着那个已删除的ID,变成了无效关联。
当你访问testmodel_2_log.test_foreignkey_2时,Django会自动去数据库查找对应的TestModel_2实例,找不到就抛出DoesNotExist异常,而这个操作完全在你之前的try-except范围之外,所以错误就冒出来了。
怎么解决?
这里给你几个实用的方案:
方案一:在访问外键时捕获异常
直接在你检查test_foreignkey_2的地方加try-except:
try: related_model = testmodel_2_log.test_foreignkey_2 except TestModel_2.DoesNotExist: related_model = None if related_model is None: # 你的业务逻辑
方案二:直接检查外键ID字段(更高效)
Django的外键字段会自动生成对应的xxx_id字段,你可以直接检查这个字段是否为None,这样不会触发数据库查询,避免异常:
if testmodel_2_log.test_foreignkey_2_id is None: # 你的业务逻辑
方案三:修复数据+修改on_delete策略
先清理数据库里的无效关联:找到所有test_foreignkey_2_id对应的TestModel_2不存在的TestModelLog_2记录,把test_foreignkey_2_id设为NULL。
然后修改外键的on_delete为SET_NULL,这样以后删除TestModel_2时,关联的TestModelLog_2外键会自动设为NULL,从根源避免无效关联:
test_foreignkey_2 = models.ForeignKey( TestModel_2, related_name='test_foreignkey_2', null=True, blank=True, on_delete=models.SET_NULL # 把DO_NOTHING改成SET_NULL )
内容的提问来源于stack exchange,提问作者David Foie Gras

