Django批量导入用户创建Profile时报错Field 'id' expected a number but got nan
问题原因与修复方案
报错根因
- Pandas读取CSV时空单元格会默认转换为
NaN类型,你直接将row['company_id']、row['app_id']赋值给Profile外键字段时,外键需要接收数字或None值,NaN不属于合法值,触发Field 'id' expected a number but got nan报错。 - 你在循环阶段将尚未入库的User实例提前绑定给Profile,User执行
bulk_create后才会生成数据库id,但之前存入profiles列表的Profile关联的仍是无id的旧User实例,入库时user外键也会触发同类id不合法错误。
具体修复方法
修复思路:首先统一处理CSV读取的NaN为合法的None值,其次等User批量入库生成id后,再组装Profile实例入库。
修复后代码如下:
import pandas as pd import io from django.contrib.auth.hashers import make_password import logging LOGGER = logging.getLogger(__name__) def import_users(csv_file): # 读取CSV时关闭默认NaN转换,避免空值转NaN df = pd.read_csv(io.StringIO(csv_file.decode('utf-8')), keep_default_na=False) users = [] # 暂存每行对应的Profile参数,不提前创建实例 profile_params_list = [] for index, row in df.iterrows(): user = User( username=row['username'], password=make_password(str(row['password'])), first_name=row['firstname'], last_name=row['lastname'], is_active=True, email=row.get('email') or None ) users.append(user) # 处理Profile参数,空值转None profile_params = {} # company允许为null,空值直接转None if pd.notna(row['company_id']): profile_params['company_id'] = row['company_id'] else: profile_params['company_id'] = None # app字段非空有默认值,只有当app_id非空时才赋值,否则走模型默认值 if pd.notna(row['app_id']): profile_params['app_id'] = row['app_id'] profile_params_list.append(profile_params) # 批量创建User,生成数据库id try: users = User.objects.bulk_create(users, ignore_conflicts=False) except Exception as e: LOGGER.exception(f"用户批量创建失败: {str(e)}") return # 绑定已生成id的User实例,组装Profile列表 profiles = [] for user, profile_params in zip(users, profile_params_list): profiles.append(Profile(user=user, **profile_params)) # 批量创建Profile try: Profile.objects.bulk_create(profiles, ignore_conflicts=False) except Exception as e: LOGGER.exception(f"Profile批量创建失败: {str(e)}")
额外注意事项
- 若业务要求
company_id必须传值,可在参数处理阶段增加校验,空值直接跳过该行或者抛出错误提示,避免入库空的关联公司。 - 若CSV中
app_id字段保证所有行都有合法值,不需要走模型默认值,可直接按company_id的逻辑处理转换即可。
内容的提问来源于stack exchange,提问作者Stefan
相关产品推荐
相关产品推荐

