0

0

在Django中高效导入Excel数据到数据库模型

霞舞

霞舞

发布时间:2025-11-27 14:25:02

|

975人浏览过

|

来源于php中文网

原创

在django中高效导入excel数据到数据库模型

1. 引言

在企业级应用中,批量导入数据是常见的需求,尤其当需要从外部系统(如Excel文件)迁移大量数据时。Django作为一个功能强大的Web框架,结合openpyxl这样的第三方库,可以轻松实现这一功能。本教程将以导入计算机产品信息为例,演示如何在Django中构建一个健壮的Excel数据导入系统。

2. 环境准备与依赖安装

首先,我们需要安装openpyxl库,它是Python中用于读写Excel .xlsx 文件的标准库。

pip install openpyxl

3. 定义Django模型

我们将使用一个Product模型来存储计算机产品的详细信息。

# models.py
from django.db import models
from django.utils import timezone

class Product(models.Model):
    model = models.CharField(max_length=50, null=True, verbose_name="型号")
    serial = models.CharField(max_length=50, null=True, unique=True, verbose_name="序列号") # 建议添加unique=True
    hd_size = models.CharField(max_length=50, null=True, verbose_name="硬盘大小")
    ram = models.CharField(max_length=50, null=True, verbose_name="内存")
    processor = models.CharField(max_length=50, null=True, verbose_name="处理器")
    date_created = models.DateTimeField(default=timezone.now, verbose_name="创建日期")
    date_updated = models.DateTimeField(auto_now=True, verbose_name="更新日期")

    class Meta:
        verbose_name = "产品"
        verbose_name_plural = "产品列表"
        ordering = ['-date_created']

    def __str__(self):
        return f"{self.serial} - {self.model}"

注意: 在serial字段中添加unique=True是一个好习惯,可以防止数据库层面出现重复序列号,尽管后续导入逻辑中可以手动检查。

4. 创建Excel文件上传表单

为了让用户能够上传Excel文件,我们需要一个HTML表单。这个表单必须使用POST方法,并且enctype属性必须设置为multipart/form-data以支持文件上传。

<!-- templates/import_product.html -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>导入产品数据</title>
    <style>
        body { font-family: sans-serif; background-color: #333; color: whitesmoke; }
        .container { max-width: 600px; margin: 50px auto; padding: 20px; background-color: #444; border-radius: 8px; }
        form { display: flex; flex-direction: column; gap: 15px; }
        input[type="file"] { padding: 10px; border: 1px solid #555; background-color: #666; color: whitesmoke; border-radius: 4px; }
        button { padding: 10px 15px; background-color: #007bff; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 16px; }
        button:hover { background-color: #0056b3; }
        .go-back-btn { margin-top: 20px; padding: 10px 15px; background-color: #6c757d; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 16px; }
        .go-back-btn:hover { background-color: #5a6268; }
    </style>
</head>
<body>
    <div class="container">
        <p style="font-size:20px;">
            选择包含要导入的计算机数据的文件
        </p>
        <form method="post" enctype="multipart/form-data">
            {% csrf_token %}
            <input type="file" name="excel_file" accept=".xlsx, .xls">
            <button type="submit">导入</button>
        </form>

        <button class="go-back-btn" onclick="history.back()">返回</button>
    </div>
</body>
</html>

提示: accept=".xlsx, .xls" 属性可以帮助浏览器过滤文件类型,但服务器端仍需进行严格的文件类型校验。

5. 编写Django导入视图

这是实现Excel数据导入的核心逻辑。视图将处理文件上传、解析Excel数据,并将其保存到数据库中。

# views.py
import openpyxl
from django.shortcuts import render, redirect
from django.contrib.auth.decorators import login_required
from .models import Product # 假设Product模型在当前应用的models.py中
from django.forms import ValidationError # 用于自定义验证错误

# 假设 context 字典在其他地方定义或在视图内部构建
# context = {} # 示例:如果需要全局context,可以这样初始化

@login_required
def import_product(request):
    """
    处理Excel文件导入产品的视图。
    """
    if request.method == 'POST':
        if 'excel_file' not in request.FILES:
            # 处理没有文件上传的情况
            return render(request, 'import_product.html', {'error_message': '请选择一个Excel文件。'})

        excel_file = request.FILES['excel_file']

        # 校验文件类型,防止上传恶意文件
        if not excel_file.name.endswith(('.xlsx', '.xls')):
            return render(request, 'import_product.html', {'error_message': '文件类型不正确,请上传Excel文件 (.xlsx 或 .xls)。'})

        try:
            # 加载工作簿
            wb = openpyxl.load_workbook(excel_file)
            ws = wb.active # 获取活动工作表

            # 存储导入成功和失败的数据
            imported_count = 0
            failed_rows = []

            # 遍历工作表中的每一行,从第二行开始(跳过标题行)
            # values_only=True 返回单元格的值,而不是单元格对象
            for row_idx, row_data in enumerate(ws.iter_rows(min_row=2, values_only=True), start=2):
                # 检查行数据是否足够,防止索引越界
                if len(row_data) < 5: # 对应 model, serial, hd_size, ram, processor
                    failed_rows.append(f"第 {row_idx} 行:数据不完整。")
                    continue

                model, serial, hd_size, ram, processor = row_data[:5] # 只取前5列

                # 数据清洗和验证(重要步骤)
                # 示例:去除字符串两端空白
                model = str(model).strip() if model else ''
                serial = str(serial).strip() if serial else ''
                hd_size = str(hd_size).strip() if hd_size else ''
                ram = str(ram).strip() if ram else ''
                processor = str(processor).strip() if processor else ''

                # 进一步的业务逻辑验证,例如序列号唯一性
                if not serial:
                    failed_rows.append(f"第 {row_idx} 行:序列号不能为空。")
                    continue

                # 检查序列号是否已存在
                if Product.objects.filter(serial=serial).exists():
                    failed_rows.append(f"第 {row_idx} 行:序列号 '{serial}' 已存在。")
                    continue

                try:
                    # 创建Product对象并保存到数据库
                    Product.objects.create(
                        model=model,
                        serial=serial,
                        hd_size=hd_size,
                        ram=ram,
                        processor=processor
                    )
                    imported_count += 1
                except Exception as e:
                    failed_rows.append(f"第 {row_idx} 行:保存失败 - {e}")

            # 导入完成后,重定向到成功页面或显示结果
            if failed_rows:
                return render(request, 'import_result.html', {
                    'imported_count': imported_count,
                    'total_rows': ws.max_row - 1, # 总行数减去标题行
                    'failed_rows': failed_rows,
                    'status': '部分成功'
                })
            else:
                return render(request, 'import_result.html', {
                    'imported_count': imported_count,
                    'total_rows': ws.max_row - 1,
                    'status': '全部成功'
                })

        except Exception as e:
            # 捕获文件解析或其他未知错误
            return render(request, 'import_product.html', {'error_message': f'文件处理失败:{e}'})

    # GET请求时,显示上传表单
    return render(request, 'import_product.html')

# 导入结果展示页面
def import_result(request):
    """
    显示导入结果的视图。
    """
    return render(request, 'import_result.html')

# 其他视图,例如产品管理列表
@login_required
def product_mgt(request):
    # context['page_title'] = "Computer List" # 假设context是全局或在函数内定义
    context = {'page_title': "Computer List"} # 示例
    products = Product.objects.all()
    context['products'] = products
    return render(request, 'product_mgt.html', context)

关键点说明:

  • @login_required: 确保只有登录用户才能访问导入功能。
  • 文件类型校验: excel_file.name.endswith(('.xlsx', '.xls')) 是一个基本的安全措施,防止上传非Excel文件。
  • openpyxl.load_workbook(excel_file): 加载上传的Excel文件。
  • ws.iter_rows(min_row=2, values_only=True): 遍历工作表。min_row=2 跳过第一行(通常是标题行)。values_only=True 直接返回单元格的值,而不是单元格对象。
  • 数据清洗与验证: 在创建模型实例之前,对从Excel读取的数据进行必要的清洗(如strip()去除空白)和业务逻辑验证(如序列号非空、唯一性检查)。这一步至关重要,可以防止脏数据进入数据库。
  • 错误处理: 使用try-except块捕获文件解析和数据保存过程中可能出现的异常,并向用户提供友好的错误信息。
  • 批量操作: 对于大量数据,可以考虑使用Product.objects.bulk_create()来提高性能,但这需要你在内存中构建所有Product对象列表。如果需要处理每行的独立验证和错误报告,逐行创建可能更合适。
  • 结果反馈: 导入完成后,提供一个详细的结果页面,告知用户导入了多少条数据,哪些行导入失败以及失败原因。

6. 导入结果展示模板

创建一个模板来显示导入操作的结果,包括成功导入的数量和任何失败的行。

<!-- templates/import_result.html -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>导入结果</title>
    <style>
        body { font-family: sans-serif; background-color: #333; color: whitesmoke; }
        .container { max-width: 800px; margin: 50px auto; padding: 20px; background-color: #444; border-radius: 8px; }
        h1 { color: #007bff; }
        p { margin-bottom: 10px; }
        ul { list-style-type: none; padding: 0; }
        li { background-color: #555; margin-bottom: 5px; padding: 8px; border-radius: 4px; }
        .success { color: #28a745; font-weight: bold; }
        .error { color: #dc3545; font-weight: bold; }
        .info { color: #ffc107; font-weight: bold; }
        .go-back-btn { display: inline-block; margin-top: 20px; padding: 10px 15px; background-color: #6c757d; color: white; border: none; border-radius: 4px; cursor: pointer; text-decoration: none; }
        .go-back-btn:hover { background-color: #5a6268; }
    </style>
</head>
<body>
    <div class="container">
        <h1>导入结果</h1>
        <p>状态: <span class="{% if status == '全部成功' %}success{% elif status == '部分成功' %}info{% else %}error{% endif %}">{{ status }}</span></p>
        <p>总行数 (不含标题): {{ total_rows }}</p>
        <p>成功导入: <span class="success">{{ imported_count }}</span> 条</p>

        {% if failed_rows %}
            <h2>失败详情:</h2>
            <ul>
                {% for error in failed_rows %}
                    <li class="error">{{ error }}</li>
                {% endfor %}
            </ul>
        {% endif %}

        <a href="{% url 'import_product' %}" class="go-back-btn">继续导入</a>
        <a href="{% url 'product_mgt' %}" class="go-back-btn" style="margin-left: 10px;">查看产品列表</a>
    </div>
</body>
</html>

7. 配置URL路由

在你的应用urls.py中添加相应的URL模式,将URL路径映射到我们创建的视图函数。

PathFinder
PathFinder

AI驱动的销售漏斗分析工具

下载
# your_app/urls.py
from django.urls import path
from . import views

urlpatterns = [
    path('products/', views.product_mgt, name='product_mgt'),
    path('products/import/', views.import_product, name='import_product'),
    path('products/import/result/', views.import_result, name='import_result'), # 如果需要独立结果页
]

确保在项目的urls.py中包含了应用的urls.py。

8. 注意事项与最佳实践

  • 数据验证: 教程中的视图已经加入了基本的非空和唯一性验证。对于更复杂的数据类型(如日期、数字),需要进行类型转换和格式校验。可以考虑使用Django Forms的特性来辅助验证,即使不直接保存表单,也可以利用其clean()方法进行数据清洗和验证。

  • 错误处理与日志: 详细记录导入过程中的错误,并提供清晰的用户反馈。对于生产环境,应将错误信息记录到日志文件中,便于排查问题。

  • 大文件处理: 如果Excel文件非常大(数万或数十万行),直接在请求-响应周期内处理可能会导致超时。此时,应考虑使用异步任务队列(如Celery)在后台处理导入,并通过WebSocket或轮询机制向用户提供进度更新。

  • 事务管理: 批量导入操作最好放在数据库事务中。如果导入过程中出现任何错误,可以回滚整个事务,确保数据一致性。

    from django.db import transaction
    
    # ... 在 import_product 视图中 ...
    with transaction.atomic():
        for row_idx, row_data in enumerate(ws.iter_rows(min_row=2, values_only=True), start=2):
            # ... 数据处理和验证 ...
            Product.objects.create(...)

    然而,对于需要逐行报告错误的场景,直接使用transaction.atomic()可能会导致所有错误行都无法保存。此时,可以考虑在循环外部捕获整体异常,或使用更细粒度的事务管理。上述示例中,为了方便报告每行错误,我们选择逐行处理,并在视图中收集错误。

  • 用户体验: 提供友好的用户界面,例如上传进度条、导入成功/失败的详细报告,以及下载错误行列表的功能。

  • 安全性: 除了文件类型校验,还应限制上传文件的大小,并确保文件存储在安全的位置。

9. 总结

通过本教程,你已经学会了如何在Django应用中利用openpyxl库实现Excel数据导入功能。从模型定义、文件上传表单、核心导入逻辑到结果展示,我们提供了一个完整的解决方案。记住,数据验证、错误处理和用户体验是构建高质量导入功能的关键要素。根据你的具体需求,可以进一步扩展和优化此基础架构。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
Python Web 框架 Django 深度开发
Python Web 框架 Django 深度开发

本专题系统讲解 Python Django 框架的核心功能与进阶开发技巧,包括 Django 项目结构、数据库模型与迁移、视图与模板渲染、表单与认证管理、RESTful API 开发、Django 中间件与缓存优化、部署与性能调优。通过实战案例,帮助学习者掌握 使用 Django 快速构建功能全面的 Web 应用与全栈开发能力。

166

2026.02.04

数据类型有哪几种
数据类型有哪几种

数据类型有整型、浮点型、字符型、字符串型、布尔型、数组、结构体和枚举等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

338

2023.10.31

php数据类型
php数据类型

本专题整合了php数据类型相关内容,阅读专题下面的文章了解更多详细内容。

225

2025.10.31

c语言 数据类型
c语言 数据类型

本专题整合了c语言数据类型相关内容,阅读专题下面的文章了解更多详细内容。

138

2026.02.12

C++类型转换方式
C++类型转换方式

本专题整合了C++类型转换相关内容,想了解更多相关内容,请阅读专题下面的文章。

320

2025.07.15

数据库三范式
数据库三范式

数据库三范式是一种设计规范,用于规范化关系型数据库中的数据结构,它通过消除冗余数据、提高数据库性能和数据一致性,提供了一种有效的数据库设计方法。本专题提供数据库三范式相关的文章、下载和课程。

386

2023.06.29

如何删除数据库
如何删除数据库

删除数据库是指在MySQL中完全移除一个数据库及其所包含的所有数据和结构,作用包括:1、释放存储空间;2、确保数据的安全性;3、提高数据库的整体性能,加速查询和操作的执行速度。尽管删除数据库具有一些好处,但在执行任何删除操作之前,务必谨慎操作,并备份重要的数据。删除数据库将永久性地删除所有相关数据和结构,无法回滚。

2111

2023.08.14

vb怎么连接数据库
vb怎么连接数据库

在VB中,连接数据库通常使用ADO(ActiveX 数据对象)或 DAO(Data Access Objects)这两个技术来实现:1、引入ADO库;2、创建ADO连接对象;3、配置连接字符串;4、打开连接;5、执行SQL语句;6、处理查询结果;7、关闭连接即可。

357

2023.08.31

Python异步编程与Asyncio高并发应用实践
Python异步编程与Asyncio高并发应用实践

本专题围绕 Python 异步编程模型展开,深入讲解 Asyncio 框架的核心原理与应用实践。内容包括事件循环机制、协程任务调度、异步 IO 处理以及并发任务管理策略。通过构建高并发网络请求与异步数据处理案例,帮助开发者掌握 Python 在高并发场景中的高效开发方法,并提升系统资源利用率与整体运行性能。

37

2026.03.12

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Excel 教程
Excel 教程

共162课时 | 21.2万人学习

成为PHP架构师-自制PHP框架
成为PHP架构师-自制PHP框架

共28课时 | 2.6万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号