Django admin上传CSV入库成功但抛出IndexError索引越界问题
问题根因
报错来自手写CSV解析逻辑的两个缺陷,完全匹配你观察到的所有现象:
- 换行处理逻辑不兼容通用CSV格式:代码仅按
\n拆分文件行,但Windows系统、Excel导出的CSV默认使用\r\n作为行尾标记,且绝大多数CSV文件末尾会自带空换行,拆分后会在列表最后生成空字符串元素。循环遍历到这个空元素时,按逗号分割得到的fields列表长度不足2,访问fields[1]直接触发IndexError: list index out of range。 - 无行内容合法性校验:代码对拆分出的每一行都直接按下标取前两个字段,没有判断行是否为空、字段数是否足够,遇到空行、残缺行直接崩溃。
数据成功写入但页面报错的原因也很明确:循环遍历是按顺序逐行执行update_or_create,所有合法数据行的写入操作都在触发报错的空行之前执行完成,事务提交后数据已经存入Postgres,只是循环走到最后空行时抛出异常,才返回了错误页面。
不同来源CSV的报错规律对应原因:
- 记事本首次保存的CSV如果末尾没有追加空换行,拆分后无空行,首次上传不报错;第二次编辑保存时记事本会自动在文件末尾补换行,拆分出空行就会触发报错
- Excel导出的CSV默认使用
\r\n换行且自带末尾空行,首次上传拆分就会出现空行,直接触发报错
另外原代码还有一个隐藏逻辑bug:update_or_create把balance也作为查询匹配条件,会导致同名客户余额变动时无法更新原有记录,反而重复创建新条目。
修复方案
- 放弃手动字符串拆分解析CSV的写法,改用Python标准库
csv模块处理文件,自动适配不同换行符、带引号字段、字段内含逗号等CSV标准格式,从根源解决格式兼容问题。 - 增加行校验逻辑,遍历过程中自动跳过空行、字段数不足的无效行。
- 修正
update_or_create的参数逻辑,将唯一识别字段name作为查询条件,需要更新的balance字段放入defaults参数。 - 导入完成后增加成功提示,重定向回客户列表页,避免表单重复提交。
具体修改代码
首先在admin.py顶部补充导入:
import csv
将原upload_csv方法替换为以下内容:
def upload_csv(self, request): if request.method == "POST": csv_file = request.FILES["csv_upload"] if not csv_file.name.endswith('.csv'): messages.warning(request, '上传文件类型错误,请上传CSV格式文件') return HttpResponseRedirect(request.path_info) # 自动适配不同换行符拆分CSV内容,兼容编码异常 content = csv_file.read().decode("utf-8", errors="ignore") csv_reader = csv.reader(content.splitlines()) success_count = 0 for row in csv_reader: # 跳过空行、字段数不足的无效行 if len(row) < 2: continue # 跳过CSV表头行(如果你的文件没有表头可以删除这部分判断) if row[0].strip().lower() == "name" and row[1].strip().lower() == "balance": continue name = row[0].strip() balance = row[1].strip() if not name: continue # 按name匹配更新/创建记录,避免重复数据 customer.objects.update_or_create( name=name, defaults={"balance": balance} ) success_count += 1 messages.success(request, f"CSV导入完成,共成功处理{success_count}条记录") # 导入完成后跳转回客户列表页 return HttpResponseRedirect(reverse("admin:search_customer_changelist")) form = CsvImportForm() context = {"form": form} return render(request, "admin/search/csv_upload.html", context)
如果遇到Excel导出CSV解码报错,可以把decode部分改成编码自动兼容逻辑,先尝试utf-8解码,失败则用gbk解码即可。
内容的提问来源于stack exchange,提问作者tinnies
相关产品推荐
相关产品推荐

