Django|Excel 批量上传、Form/ModelForm 文件上传、Media 媒体文件配置

上传 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 静态文件与用户上传文件区分

两个特殊文件夹

  1. static :项目静态资源目录;存放开发写死的 css、js、背景图片,开发人员预置资源
  2. 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 优势:

  1. 自动表单校验;
  2. models.FileField 指定upload_to自动把文件保存到 media 对应子文件夹
  3. 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 访问路由。


五、三种文件上传方式对比总结

  1. 原生手动方式

    file_object = request.FILES.get("exc")

自己手动 open、chunk 读写文件;手动写数据库路径;全部逻辑自己编码,自由度最高。

  1. 普通 Form 组件
  • 由 Form 完成表单校验;
  • 通过form.cleaned_data拿到普通字段与文件对象;
  • 文件读写、保存磁盘、写入数据库仍需要手动编码实现
  1. ModelForm(项目开发首选)
  • 模型使用 models.FileField(upload_to='xxx/')
  • 完成表单校验;
  • 自动保存文件到 media 目录,自动将路径存入数据库,直接调用form.save()即可

注意要点:

  1. 文件上传表单必须添加 enctype="multipart/form-data"
  2. 文件数据存放在request.FILES,普通文本数据在request.POST
  3. 用户上传文件统一交给 media 目录管理,不要存放在 static;
  4. 大文件使用 .chunks()分块读写,避免内存溢出。
相关推荐
pjj198541 小时前
Hadoop-python中使用大数据
大数据·hadoop·python
古少侠1 小时前
用 DS随心转 把 AI 给的表格导出成能筛选、能统计的 Excel
人工智能·excel
威联通安全存储1 小时前
TS-h1887XU-RP 在模具制造行业试模与改模数据场景的部署
python·制造
步行cgn1 小时前
Spring Environment 详解:Spring Boot 配置管理的核心接口
spring boot·python·spring
Csvn1 小时前
🐍 Day 12: 编码与字符集 — 告别乱码噩梦
后端·python
小刘在重生~1 小时前
Django|Ajax 入门、JSON 返回、ECharts 图表、文件上传完整笔记
ajax·django·json
2601_962283881 小时前
Python 开发框架:Django、Flask和FastAPI
python·django·flask·fastapi·web开发
lzfshub1 小时前
Open-DIS Python发送DIS实体状态PDU:实现坦克炮塔与主炮部件参数
java·网络·python·dis