Django 2.0+中ForeignKey必须指定on_delete,否则启动报TypeError;需显式设置related_name避免反向关系冲突;跨app引用须用'app.Model'格式;访问外键属性前应使用select_related优化查询。

ForeignKey字段定义时必须指定on_delete,否则Django 2.0+直接报错
不加 on_delete 会触发 TypeError: __init__() missing 1 required positional argument: 'on_delete'。这不是警告,是启动就崩——Django 强制你思考级联行为,不能偷懒留空。
常见错误是照抄旧教程或从 Python shell 里临时建模,忘了补这个参数。它不是可选的“高级配置”,而是模型定义的必需项。
-
on_delete=models.CASCADE:关联对象删了,本条也删(比如删用户,他发的所有评论一起消失) -
on_delete=models.PROTECT:想删父对象时直接抛ProtectedError,强制你先处理子数据 -
on_delete=models.SET_NULL:需配合null=True,父对象删了,外键字段变 NULL -
on_delete=models.SET_DEFAULT:要求字段有default=...,且 default 值在父表中真实存在(否则查不到)
ForeignKey反向查询名(related_name)不设会自动生成,但容易冲突
没写 related_name 时,Django 默认用 小写模型名_set,比如 author.book_set.all()。问题在于:如果一个模型被多个外键引用(比如 Book 既有 author 又有 editor 都指向 User),就会出现两个 user.book_set,迁移时报 RelatedObjectDoesNotExist 或命名冲突。
解决办法很简单:显式命名,且带业务语义:
author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='authored_books')editor = models.ForeignKey(User, on_delete=models.CASCADE, related_name='edited_books')- 值设为
'+'(如related_name='+')可禁用反向关系,适合纯单向使用场景
ForeignKey字段名和关联模型名写错,报错信息很绕但原因固定
典型错误现象:ValueError: Cannot assign "<user: alice>": "Book.author" must be a "Author" instance.</user:>——明明用了 User 模型,却提示要 Author 实例。这是因为 ForeignKey 指向的模型名写错了,比如:
author = models.ForeignKey('User', ...)
但实际模型在 accounts.models.User,而 Django 默认在当前 app 查找。正确写法是:
- 同一 app 内:直接写模型类名
models.ForeignKey(User, ...)(注意是类,不是字符串) - 跨 app:用字符串
'accounts.User'或'auth.User'(Django 自带的) - 自引用(比如树形结构):用
'self'或'myapp.MyModel'
字符串写错大小写、app 名拼错、漏掉点号,都会导致运行时报 LookupError: Model '<xxx>' not found</xxx> 或类型不匹配。
ForeignKey查数据库时默认不 join,.select_related() 才能避免 N+1
写 Book.objects.all() 然后循环 book.author.name,每本书都会单独查一次 User 表——100 本书就是 101 次查询。这不是 Django 的 bug,是它的懒加载设计:外键字段只存 ID,真正访问 .author 时才去查。
修复只有一行代码:
- 单层外键:用
Book.objects.select_related('author').all() - 多层(比如 author.profile.avatar):写成
select_related('author__profile__avatar') - 别用
prefetch_related()替代——它是为多对多/反向外键准备的,对正向 ForeignKey 无效,还拖慢性能
开发时开 django-debug-toolbar,一眼就能看到查询数爆增,但上线后没人盯这个,所以得养成习惯:只要模板里用了外键属性,视图里就得加 select_related。










