Django中PurchaseOrder与DeliveryOrder互设外键报NameError的解决咨询
循环外键引用报NameError的解决方法
这个报错的本质是Python按顺序解析类定义时,读到PurchaseOrder中引用了尚未完成定义的DeliveryOrder类;由于两个模型是互相引用的循环依赖关系,单纯调整两个类的书写顺序根本无法规避问题。
Django为这类双向关联、循环引用的场景内置了延迟字符串引用机制——外键定义时不需要提前拿到关联的模型类对象,只要以字符串形式标注关联模型的位置,Django就会在所有模型全量加载完成后再解析关联关系,完美适配这类场景。
具体修正操作
- 将所有直接书写类名的外键第一个参数,替换为符合格式的字符串引用:
'<INSTALLED_APPS中配置的应用名>.<关联模型类名>' - 补全所有外键缺失的必填
on_delete参数,避免触发新的字段校验报错 - (推荐)为两个双向关联的外键补充
related_name参数,避免Django默认生成的反向查询名称出现冲突
修正后参考代码
先确认两个模型所属的应用名称,比如模型放在purchase应用下,对应引用字符串就写'purchase.PurchaseOrder'、'purchase.DeliveryOrder'即可。
PurchaseOrder 模型修正后代码
class PurchaseOrder(models.Model): part = models.ForeignKey(Part, on_delete=models.CASCADE) # 替换直接引用的类名为字符串形式,补全on_delete参数 deliveryorder = models.ForeignKey( "purchase.DeliveryOrder", on_delete=models.CASCADE, related_name="po_delivery_records" ) po_quantity = models.PositiveIntegerField(default=0) supplier = models.ForeignKey(Supplier, on_delete=models.CASCADE) product = models.ForeignKey(Product, on_delete=models.CASCADE) created_date = models.DateField(auto_now_add=True) def __str__(self): return self.part.partname
DeliveryOrder 模型修正后代码
class DeliveryOrder(models.Model): # 替换直接引用的类名为字符串形式 purchaseorder = models.ForeignKey( "purchase.PurchaseOrder", on_delete=models.CASCADE, related_name="do_purchase_records" ) do_quantity = models.PositiveIntegerField(default=0) created_date = models.DateField(auto_now_add=True) def __str__(self): # 原代码返回整数类型,转成字符串避免类型错误 return str(self.do_quantity)
落地注意事项
- 字符串引用中的应用名、模型名必须和实际配置、定义的内容完全大小写匹配,否则会出现模型找不到的报错
- 不需要调整两个模型在.py文件中的书写顺序,哪怕PurchaseOrder完全写在DeliveryOrder之前,这种字符串引用的方式也不会触发NameError
- 代码修改完成后,重新执行
makemigrations命令即可正常生成迁移文件
内容的提问来源于stack exchange,提问作者Toufiqur Rahman
相关产品推荐
相关产品推荐

