Django/DRF上传Excel后如何覆盖数据库所有Leaderboard对象
实现DRF视图完成Excel上传更新排行榜
现有基础信息
你已在models.py中定义排行榜模型:
class Leaderboard(models.Model): rank = models.IntegerField() team_name = models.CharField(max_length=100) score = models.IntegerField()
已有的视图初始代码框架:
def update_leaderboard(request): new_excel = request.body data = pd.read_excel(new_excel) # Overwrite the database here ...
核心需求:前端上传新Excel时,清空原有排行榜旧数据,写入解析后的新数据;项目基于Django Rest Framework开发,前端为React。
方案合理性说明
如果你的排行榜场景是定期全量更新成绩、无实时增量计分逻辑,这种全量覆盖的方案完全合理:逻辑简单、没有增量更新时的数据合并冲突问题,维护成本极低,完全可以用。
完整实现代码
首先注意几个容易踩的坑:
- DRF中上传的文件不要从
request.body取,要从request.FILES获取,前端上传必须使用multipart/form-data格式 - 删旧数据+写新数据必须放在数据库事务中,避免解析/写入中途报错导致旧数据被清空、新数据没写入,出现空排行榜的问题
- 批量写入用
bulk_create,不要循环单条save,数据量大时性能差距非常明显 - 提前校验Excel列名,避免列名不匹配导致写库报错
首先安装依赖(如果没装的话):
pip install pandas openpyxl
然后写视图函数,用DRF的FBV写法即可:
注:本地调试暂时不需要权限校验,可以把
@permission_classes([IsAdminUser])替换为@permission_classes([AllowAny]),但生产环境必须加权限校验,避免公开接口被恶意调用篡改数据。
import pandas as pd from django.db import transaction from rest_framework.decorators import api_view, permission_classes from rest_framework.permissions import IsAdminUser, AllowAny from rest_framework.response import Response from rest_framework import status from .models import Leaderboard @api_view(['POST']) @permission_classes([IsAdminUser]) def update_leaderboard(request): # 校验是否上传了文件 if 'excel_file' not in request.FILES: return Response( {"error": "未上传Excel文件,请检查传参字段名为excel_file"}, status=status.HTTP_400_BAD_REQUEST ) excel_file = request.FILES['excel_file'] try: # 解析Excel df = pd.read_excel(excel_file) # 校验必填列是否存在 required_cols = {'rank', 'team_name', 'score'} if not required_cols.issubset(set(df.columns)): missing = required_cols - set(df.columns) return Response( {"error": f"Excel缺少必填列:{','.join(missing)}"}, status=status.HTTP_400_BAD_REQUEST ) # 数据类型转换,避免类型不对写库报错 df['rank'] = df['rank'].astype(int) df['score'] = df['score'].astype(int) df['team_name'] = df['team_name'].astype(str).str.strip() # 事务执行:删旧数据+批量写新数据,要么全成功要么全回滚 with transaction.atomic(): # 清空旧数据 Leaderboard.objects.all().delete() # 组装要插入的对象列表 leaderboard_objs = [ Leaderboard( rank=row['rank'], team_name=row['team_name'], score=row['score'] ) for _, row in df.iterrows() ] # 批量写入 Leaderboard.objects.bulk_create(leaderboard_objs, batch_size=500) except Exception as e: return Response( {"error": f"处理文件失败:{str(e)}"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR ) return Response( {"msg": "排行榜更新成功", "count": len(leaderboard_objs)}, status=status.HTTP_200_OK )
写完记得在urls.py里配置路由即可。
React前端上传注意点
上传时用FormData包装文件,不要手动设置Content-Type请求头,让浏览器自动生成带boundary的multipart/form-data头,否则后端会解析失败,示例代码:
const uploadExcel = async (file) => { const formData = new FormData(); formData.append('excel_file', file); // 字段名和后端保持一致为excel_file const res = await fetch('/api/update_leaderboard/', { method: 'POST', headers: { 'Authorization': `Bearer ${yourToken}` // 用了鉴权的话加在这里即可,不要手动加Content-Type }, body: formData }) // 后续处理响应逻辑 }
内容的提问来源于stack exchange,提问作者Owenn
相关产品推荐
相关产品推荐

