上传 Excel 文件批量导入数据库
1. 安装依赖 openpyxl
pip install openpyxl
2. 前端上传表单
文件上传表单必须添加属性 enctype="multipart/form-data"
<form method="post" enctype="multipart/form-data" action="/depart/multi/">
{% csrf_token %}
<input type="file" name="exc">
<input type="submit" value="上传" class="btn btn-info btn-sm" >
</form>
3. 后端视图:读取 Excel 并写入数据库
def depart_multi(request):
"""批量上传excel文件,导入部门数据"""
from openpyxl import load_workbook
# 1.获取用户上传的excel文件对象
file_object = request.FILES.get("exc")
print(type(file_object))
# 2.将上传文件对象交给openpyxl读取
wb = load_workbook(file_object)
sheet = wb.worksheets[0] # 获取第一个工作表
# 3.循环读取每一行数据,min_row=2代表从第二行开始(跳过表头)
for row in sheet.iter_rows(min_row=2):
text = row[0].value
print(text)
# 判断数据库是否已经存在该部门,避免重复插入
exists = models.Department.objects.filter(title=text).exists()
if not exists:
models.Department.objects.create(title=text)
return HttpResponse("Excel批量导入完成")
说明:
min_row=2:跳过 Excel 表头行,从业务数据行读取;request.FILES获取上传文件对象;- openpyxl 可以直接读取 Django 上传的文件对象,不需要先保存本地。
二、案例:表单混合数据上传(普通 Form 组件)
场景:用户输入普通表单数据 + 上传文件;做表单校验,不能为空,校验失败返回错误提示。
- Form 组件自动生成 HTML 标签,支持
type=file文件控件form.cleaned_data同时获取普通表单数据 + 文件对象- 表单提交必须携带
enctype="multipart/form-data"
2.1 前端 HTML 模板
{% extends 'layout.html' %}
{% block content %}
<div>
<div class="container">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">{{ title }}</h3>
</div>
<div class="panel-body">
<form class="form-horizontal" enctype="multipart/form-data" method="post" novalidate>
{% csrf_token %}
{% for field in form %}
<div class="form-group">
<label >{{ field.label}}:</label>
{{ field }}
<span style="color: red">{{ field.errors.0 }}</span>
</div>
{% endfor %}
<button type="submit" class="btn btn-primary">提 交</button>
</form>
</div>
</div>
</div>
</div>
{% endblock %}
2.2 后端 Form 类 & 视图函数(旧 static 存储方案)
import os
from django import forms
from django.shortcuts import render, HttpResponse
from app01 import models
# 自定义Bootstrap样式Form基类(笔记中简写BooStrapFrom)
class BooStrapFrom(forms.Form):
pass
class UpForm(BooStrapFrom):
bootstrp_exclude_fields = ['img']
name = forms.CharField(label="姓名")
age = forms.IntegerField(label='年龄')
img = forms.FileField(label="头像")
def upload_form(request):
title = "Form上传"
if request.method == "GET":
form = UpForm()
return render(request,'upload_form.html',{"form":form,"title":title})
# POST请求:同时传入POST文本数据、FILES文件数据
form = UpForm(data=request.POST, files = request.FILES)
if form.is_valid():
# cleaned_data 获取校验完成的数据
"""
{'name': 'dsfsd', 'age': 123,
'img': <InMemoryUploadedFile: 4fe6c4ad166cc4392aaf9e2631acdc8.jpg (image/jpeg)>}
"""
image_object = form.cleaned_data.get("img")
# 路径拼接:数据库存储相对路径、磁盘真实写入路径
db_file_path = os.path.join("static","img",image_object.name)
file_path = os.path.join("app01", db_file_path)
# 分块写入文件到磁盘
f = open(file_path,mode="wb")
for chunk in image_object.chunks():
f.write(chunk)
f.close()
# 将文件相对路径存入数据库
models.Boss.objects.create(
name=form.cleaned_data["name"],
age=form.cleaned_data["age"],
img = db_file_path,
)
return HttpResponse("提交成功")
# 校验失败返回页面,携带错误信息
return render(request,'upload_form.html',{"form":form,"title":title})
三、Django 静态文件与用户上传文件区分
两个特殊文件夹
- static :项目静态资源目录;存放开发写死的 css、js、背景图片,开发人员预置资源。
- media :专门存放用户上传文件,用户通过网页上传产生的图片、附件、Excel,统一放在 media 目录。
3.1 Media 配置 settings.py
import os
from django.conf import settings
# media磁盘真实物理路径
MEDIA_ROOT = os.path.join(BASE_DIR,"media")
# media浏览器访问URL前缀
MEDIA_URL = "/media/"
3.2 urls.py 配置媒体文件访问路由
from django.urls import path,re_path
from django.views.static import serve
from django.conf import settings
urlpatterns = [
# media文件访问路由
re_path(r'media/(?P<path>.*)$', serve, {'document_root':settings.MEDIA_ROOT}, name='media'),
]
配置完成后浏览器可以直接通过
http://127.0.0.1:8000/media/文件名访问用户上传文件。
3.3 Form 组件使用 Media 目录存储文件(修改后完整视图)
import os
from django import forms
from django.shortcuts import render, HttpResponse
from django.conf import settings
from app01 import models
class BooStrapFrom(forms.Form):
pass
class UpForm(BooStrapFrom):
bootstrp_exclude_fields = ['img']
name = forms.CharField(label="姓名")
age = forms.IntegerField(label='年龄')
img = forms.FileField(label="头像")
def upload_form(request):
title = "Form上传"
if request.method == "GET":
form = UpForm()
return render(request,'upload_form.html',{"form":form,"title":title})
form = UpForm(data=request.POST, files = request.FILES)
if form.is_valid():
image_object = form.cleaned_data.get("img")
# media相对路径(存入数据库)
media_relative_path = os.path.join("media", image_object.name)
# media磁盘绝对路径(真实写入磁盘)
media_full_path = os.path.join(settings.MEDIA_ROOT, image_object.name)
# 文件分块保存
f = open(media_full_path,mode="wb")
for chunk in image_object.chunks():
f.write(chunk)
f.close()
# 将相对路径存入数据库
models.Boss.objects.create(
name=form.cleaned_data["name"],
age=form.cleaned_data["age"],
img = media_relative_path,
)
return HttpResponse("提交成功")
return render(request,'upload_form.html',{"form":form,"title":title})
四、ModelForm 实现文件上传(推荐,自动保存文件)
ModelForm 优势:
- 自动表单校验;
models.FileField指定upload_to,自动把文件保存到 media 对应子文件夹;form.save()一行完成数据入库,不需要手动 open、write 写文件。
4.1 models.py 模型类
from django.db import models
class City(models.Model):
"""城市"""
name = models.CharField(verbose_name="名称",max_length=32)
age = models.IntegerField(verbose_name="人口")
# FileField:upload_to 指定文件保存到media/city目录下
img = models.FileField(verbose_name="Logo",max_length=128,upload_to='city/')
4.2 定义 ModelForm
from django import forms
from app01 import models
class BooStrapModelFrom(forms.ModelForm):
pass
class UpModelForm(BooStrapModelFrom):
bootstrp_exclude_fields = ['img']
class Meta:
model = models.City
fields = "__all__"
4.3 视图函数
def upload_modal_form(request):
"""上传文件和数据 ModelForm方式"""
title="ModalFrom上传数据"
if request.method == "GET":
form = UpModelForm()
return render(request,'upload_form.html',{"form":form,"title":title})
# POST提交,传入POST和FILES
form = UpModelForm(data=request.POST, files = request.FILES)
if form.is_valid():
# ✨自动完成:文件保存到media/city目录 + 数据库写入文件路径
form.save()
return HttpResponse("上传成功")
return render(request, 'upload_form.html', {"form": form, "title": title})
⚠️前提条件:必须已经配置好 settings.py 的 MEDIA_ROOT、MEDIA_URL,urls 配置 media 访问路由。
五、三种文件上传方式对比总结
-
原生手动方式
file_object = request.FILES.get("exc")
自己手动 open、chunk 读写文件;手动写数据库路径;全部逻辑自己编码,自由度最高。
- 普通 Form 组件
- 由 Form 完成表单校验;
- 通过
form.cleaned_data拿到普通字段与文件对象; - 文件读写、保存磁盘、写入数据库仍需要手动编码实现。
- ModelForm(项目开发首选)
- 模型使用
models.FileField(upload_to='xxx/'); - 完成表单校验;
- 自动保存文件到 media 目录,自动将路径存入数据库,直接调用
form.save()即可。
注意要点:
- 文件上传表单必须添加
enctype="multipart/form-data";- 文件数据存放在
request.FILES,普通文本数据在request.POST;- 用户上传文件统一交给 media 目录管理,不要存放在 static;
- 大文件使用
.chunks()分块读写,避免内存溢出。