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
相关产品推荐
相关产品推荐

