如何在Django中集成JavaScript实现一次文件上传运行3个视图函数
解决方案
错误根因
- 第一个报错:
request.FILES['docfile']是Django封装的内存/临时文件对象,不是你定义的Document模型实例,无法直接调用save()方法,需要先实例化Document模型再执行保存操作。 - 第二个报错:HTTP协议单次请求仅支持返回一个响应,无法同时返回文件流和页面渲染数据,因此采用AJAX异步提交表单的方案,一次上传后同时完成数据存储、页面渲染、文件下载三个需求。
后端配置修改
1. settings.py 新增媒体文件配置
# 配置媒体文件存储路径,用于存放生成的Excel文件 MEDIA_URL = '/media/' MEDIA_ROOT = BASE_DIR / 'media'
配置完成后在项目根目录手动创建media文件夹,并开启读写权限。
2. urls.py 路由配置
from django.conf import settings from django.conf.urls.static import static from .views import upload_file urlpatterns = [ # 原有其他路由 path('upload/', upload_file, name='upload_file'), ] # 开发环境下暴露媒体文件访问路径,生产环境建议用Nginx托管 if settings.DEBUG: urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
3. views.py 合并逻辑优化
import os import io import pandas as pd from django.conf import settings from django.shortcuts import render from django.http import JsonResponse from .forms import DocumentForm from .models import Document def upload_file(request): if request.method == "POST": form = DocumentForm(request.POST, request.FILES) if form.is_valid(): # 对应原save_exls逻辑:保存原始上传文件 raw_doc = Document(docfile=request.FILES['docfile']) raw_doc.save() # 对应原pandas_exls和html_exls逻辑:统一处理Excel output = io.BytesIO() uploaded_file = request.FILES['docfile'] dfs = pd.read_excel(uploaded_file, sheet_name=None, index_col=[0]) writer = pd.ExcelWriter(output) render_html = "" for name, df in dfs.items(): # 原有pandas计算逻辑,生成done对象 df.columns = df.columns.str.split('_', expand=True) new_data = df.stack(0) # --- 此处保留你原有计算逻辑 --- # done = 你的计算结果 done.to_excel(writer, sheet_name=name) # 按需取对应sheet的HTML内容,示例取最后一个sheet的结果 render_html = done.to_html() writer.save() output.seek(0) # 保存计算后的Excel文件到媒体目录 calculated_filename = f"calculated_{uploaded_file.name}" save_path = os.path.join(settings.MEDIA_ROOT, calculated_filename) with open(save_path, 'wb') as f: f.write(output.getvalue()) # 构造下载链接返回给前端 download_url = f"{settings.MEDIA_URL}{calculated_filename}" return JsonResponse({ "status": "success", "render_html": render_html, "download_url": download_url }) return JsonResponse({"status": "error", "msg": "表单校验失败,请检查上传文件"}) else: form = DocumentForm() return render(request, 'list.html', {'form': form})
前端模板修改
替换原list.html全部内容:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Django site</title> </head> <body> <form id="uploadForm" method="post" enctype="multipart/form-data"> {% csrf_token %} <p>{{ form.non_field_errors }}</p> <p>{{ form.docfile.label_tag }} {{ form.docfile.help_text }}</p> <p> {{ form.docfile.errors }} {{ form.docfile }} </p> <p><input type="submit" value="Upload"/></p> </form> <br/> <div id="htmlRenderArea"> {{html|safe}} </div> <script> // 拦截表单默认提交行为,用AJAX异步上传 document.getElementById('uploadForm').addEventListener('submit', function(e) { e.preventDefault(); const formData = new FormData(this); fetch("{% url 'upload_file' %}", { method: 'POST', body: formData, headers: { 'X-CSRFToken': document.querySelector('input[name="csrfmiddlewaretoken"]').value } }) .then(res => res.json()) .then(data => { if(data.status === 'success') { // 渲染处理后的HTML表格 document.getElementById('htmlRenderArea').innerHTML = data.render_html; // 自动触发计算后文件下载 const downloadLink = document.createElement('a'); downloadLink.href = data.download_url; downloadLink.download = ''; document.body.appendChild(downloadLink); downloadLink.click(); document.body.removeChild(downloadLink); } else { alert(data.msg); } }) .catch(err => { console.error('上传失败:', err); alert('上传请求失败,请重试'); }); }) </script> </body> </html>
内容的提问来源于stack exchange,提问作者project.py
相关产品推荐
相关产品推荐

