使用Django Excel导入数据时将文本转换为外键的实现方案
问题描述
我有如下Excel表格场景:表格中的author和brand_id字段对应Product模型的外键字段。用户通过Excel添加产品时,输入的是作者用户名和品牌名称(如表1),但模型要求存储对应的外键ID(如表2)。如何将接收的文本数据动态转换为数据库外键并保存?
表1:用户输入的Excel数据
| id | author | brand_id | name | barcode | unit |
|---|---|---|---|---|---|
| user1 | TestBrand1 | Product1 | TST1234 | 700 | |
| user2 | TestBrand2 | Product2 | TST5678 | 380 |
表2:数据库需要的格式
| id | author | brand_id | name | barcode | unit |
|---|---|---|---|---|---|
| 1 | 1 | Product1 | TST1234 | 700 | |
| 2 | 2 | Product2 | TST5678 | 380 |
我的Product模型
class Product(models.Model): id = models.AutoField(primary_key=True) author = models.ForeignKey(User,on_delete= models.CASCADE, verbose_name='Product Author', null=True) brand_id = models.ForeignKey(Brand,on_delete=models.CASCADE, verbose_name="Brand Names") name = models.CharField(max_length=255, verbose_name="Product Name",unique=True) barcode = models.CharField(max_length=255, verbose_name="Barcode") unit = models.CharField(max_length=255,verbose_name="Product Unit") def __str__(self): return self.name
当前添加产品的视图
@login_required(login_url="login") def addProduct(request): # 其他逻辑省略 elif 'save_file' in request.POST: dataset = Dataset() new_products = request.FILES['product_file'] if not new_products.name.endswith('xlsx'): messages.info(request,'Wrong file format!') return redirect(".") imported_data = dataset.load(new_products.read(), format='xlsx') for data in imported_data: values = Product( data[0], data[1], data[2], data[3], data[4], data[5], ) values.save() messages.success(request,"Product added from file succesfully!") return redirect("products") # 上下文省略 return render(request,"product/addproduct.html",context)
解决方案
核心思路是通过文本值查询对应的User和Brand实例,获取其主键后赋值给Product的外键字段。以下是具体实现:
1. 修改视图中的导入逻辑
在循环处理每条Excel数据时,单独处理author和brand_id字段,添加异常处理避免单个数据错误导致整个导入失败:
@login_required(login_url="login") def addProduct(request): # 其他逻辑省略 elif 'save_file' in request.POST: dataset = Dataset() new_products = request.FILES['product_file'] if not new_products.name.endswith('xlsx'): messages.info(request,'错误的文件格式!') return redirect(".") imported_data = dataset.load(new_products.read(), format='xlsx') for data in imported_data: # 对应Excel列顺序:data[1]是用户名,data[2]是品牌名称,需根据实际调整 try: # 根据用户名查询User实例 author_obj = User.objects.get(username=data[1]) # 根据品牌名称查询Brand实例(假设Brand模型有name字段,需匹配实际字段名) brand_obj = Brand.objects.get(name=data[2]) except User.DoesNotExist: messages.error(request, f"用户 {data[1]} 不存在,跳过该产品") continue except Brand.DoesNotExist: messages.error(request, f"品牌 {data[2]} 不存在,跳过该产品") continue # 创建Product对象,外键字段直接传入对应模型实例 product = Product( author=author_obj, brand_id=brand_obj, name=data[3], barcode=data[4], unit=data[5] ) try: product.save() except Exception as e: messages.error(request, f"产品 {data[3]} 保存失败:{str(e)}") continue messages.success(request,"产品已从文件成功添加!") return redirect("products") # 上下文省略 return render(request,"product/addproduct.html",context)
2. 关键说明
- 字段对应校验:确保Excel列的索引与代码中
data[x]的取值完全匹配,避免因列顺序错误导致数据映射混乱。 - 异常处理:捕获用户/品牌不存在的异常,跳过错误数据并给出明确提示,保证批量导入的稳定性。
- 外键赋值方式:Django外键字段支持直接传入模型实例,无需手动提取主键,框架会自动存储实例的
id值。若习惯手动指定主键,也可写成author_id=author_obj.id、brand_id_id=brand_obj.id(注意:外键字段名为brand_id时,对应的主键存储字段是brand_id_id)。 - 可选扩展:若业务需要自动创建不存在的用户或品牌,可将
get方法替换为get_or_create,示例:# 自动创建不存在的用户(注意:User创建可能需要补充密码等必填字段) author_obj, created = User.objects.get_or_create(username=data[1]) # 自动创建不存在的品牌 brand_obj, created = Brand.objects.get_or_create(name=data[2])
内容的提问来源于stack exchange,提问作者Eric Maniel
相关产品推荐
相关产品推荐

