You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.03 11:18:28