Django 1.11重写save()读取外键报错,如何自动创建关联Parent?
解决Django 1.11中保存未关联Parent的Child时自动创建Parent的问题
这个问题的核心在于:Django对于非空ForeignKey字段,当实例未保存且外键未设置时,直接访问该关联属性(比如self.parent)会抛出RelatedObjectDoesNotExist异常,而不是返回None,所以原来的判断方式行不通。
这里有两种可靠的解决方式:
方法1:直接检查外键的ID字段(推荐)
Django的ForeignKey字段会自动生成一个对应的{field_name}_id字段,直接访问这个字段不会触发关联对象的加载,也就不会抛出异常。结合实例的_state.adding属性判断是否是新建对象,代码如下:
class Parent(models.Model): pass class Child(models.Model): parent = models.ForeignKey(Parent, on_delete=models.PROTECT, related_name='children') def save(self, *args, **kwargs): # _state.adding 表示该对象是新建的(尚未保存到数据库) if self._state.adding and self.parent_id is None: self.parent = Parent.objects.create() super().save(*args, **kwargs)
这种方式高效且直观,避免了异常捕获的开销。
方法2:捕获RelatedObjectDoesNotExist异常
如果更倾向于通过访问关联属性来判断,也可以捕获对应的异常:
class Parent(models.Model): pass class Child(models.Model): parent = models.ForeignKey(Parent, on_delete=models.PROTECT, related_name='children') def save(self, *args, **kwargs): try: # 尝试访问parent,未设置时会抛出异常 _ = self.parent except Child.parent.RelatedObjectDoesNotExist: self.parent = Parent.objects.create() super().save(*args, **kwargs)
这种方式虽然可行,但不如方法1高效,因为异常捕获的成本相对更高。
为什么原来的代码会报错?
在Django 1.11中,当你访问未设置的非空ForeignKey属性时,会调用该字段的__get__方法,而该方法会在关联对象不存在时抛出RelatedObjectDoesNotExist异常,而不是返回None——这就是你遇到报错的原因。
内容的提问来源于stack exchange,提问作者epalm
相关产品推荐
相关产品推荐

