You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django迁移时ForeignKey默认值返回实例报错:期望数字却得模型实例

Django外键默认值迁移时的实例/主键问题

场景与初始代码

现有两个关联模型,User通过外键关联Department:

class Department(models.Model):
    name = models.CharField(max_length=250)
    # 其他字段...

class User(models.Model):
    # 其他字段...
    department = models.ForeignKey(Department, on_delete=models.CASCADE, null=True, blank=True)

需要将User的department字段改为必填项,同时给未设置部门的用户分配默认部门。为此编写了获取或创建默认部门的函数:

def get_default_department():
    try:
        default_department = Department.objects.get(name="Default")
    except Department.DoesNotExist:
        default_department = Department.objects.create(name="Default")
        # 其他字段初始化...
    return default_department

随后修改User模型的department字段:

class User(models.Model):
    # 其他字段...
    department = models.ForeignKey(Department, on_delete=models.CASCADE, null=False, blank=False, default=get_default_department)

迁移错误

执行makemigrations命令无异常,但执行migrate时抛出错误:

"TypeError: Field 'id' expected a number but got <Department: Default>"

解决方法

修改默认函数,返回部门实例的主键而非实例本身:

def get_default_department():
    try:
        default_department = Department.objects.get(name="Default").pk
    except Department.DoesNotExist:
        default_department = Department.objects.create(name="Default").pk
        # 其他字段初始化...
    return default_department

修改后再次执行migrate,问题解决。

疑问解答

日常开发中,Django ORM确实支持直接给外键字段赋值模型实例,ORM会自动将实例转换为对应的主键ID存入数据库。但迁移过程是特殊的:

  • 迁移操作直接对接数据库层,不会经过ORM完整的实例处理逻辑。当需要为现有数据填充默认值时,Django会直接调用default函数拿到返回值,然后尝试将其写入数据库的外键字段(该字段存储的是数值类型的主键)。如果返回的是模型实例,数据库无法识别,就会抛出类型错误。
  • 迁移完成后,日常的模型操作是通过ORM进行的,ORM会自动处理实例到主键的转换,所以此时即使把default函数改回返回实例,也能正常工作。

内容的提问来源于stack exchange,提问作者Ives Furtado

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 08:15:35