
在企业级应用中,批量导入数据是常见的需求,尤其当需要从外部系统(如Excel文件)迁移大量数据时。Django作为一个功能强大的Web框架,结合openpyxl这样的第三方库,可以轻松实现这一功能。本教程将以导入计算机产品信息为例,演示如何在Django中构建一个健壮的Excel数据导入系统。
首先,我们需要安装openpyxl库,它是Python中用于读写Excel .xlsx 文件的标准库。
pip install openpyxl
我们将使用一个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是一个好习惯,可以防止数据库层面出现重复序列号,尽管后续导入逻辑中可以手动检查。
为了让用户能够上传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" 属性可以帮助浏览器过滤文件类型,但服务器端仍需进行严格的文件类型校验。
这是实现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)
关键点说明:
创建一个模板来显示导入操作的结果,包括成功导入的数量和任何失败的行。
<!-- 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>
在你的应用urls.py中添加相应的URL模式,将URL路径映射到我们创建的视图函数。
PHP 独特的语法混合了 C、Java、Perl 以及 PHP 自创新的语法。它可以比 CGI或者Perl更快速的执行动态网页。用PHP做出的动态页面与其他的编程语言相比,PHP是将程序嵌入到HTML文档中去执行,执行效率比完全生成HTML标记的CGI要高许多。下面介绍了十个PHP高级应用技巧。 1, 使用 ip2long() 和 long2ip() 函数来把 IP 地址转化成整型存储到数据库里
440
# 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。
数据验证: 教程中的视图已经加入了基本的非空和唯一性验证。对于更复杂的数据类型(如日期、数字),需要进行类型转换和格式校验。可以考虑使用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()可能会导致所有错误行都无法保存。此时,可以考虑在循环外部捕获整体异常,或使用更细粒度的事务管理。上述示例中,为了方便报告每行错误,我们选择逐行处理,并在视图中收集错误。
用户体验: 提供友好的用户界面,例如上传进度条、导入成功/失败的详细报告,以及下载错误行列表的功能。
安全性: 除了文件类型校验,还应限制上传文件的大小,并确保文件存储在安全的位置。
通过本教程,你已经学会了如何在Django应用中利用openpyxl库实现Excel数据导入功能。从模型定义、文件上传表单、核心导入逻辑到结果展示,我们提供了一个完整的解决方案。记住,数据验证、错误处理和用户体验是构建高质量导入功能的关键要素。根据你的具体需求,可以进一步扩展和优化此基础架构。
以上就是在Django中高效导入Excel数据到数据库模型的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号