如何实现Django用户上传Excel/CSV自动插入PostgreSQL的fournisseurs模型?
当然可行!这在Django项目里是非常常见的批量数据导入需求,我给你梳理下具体的实现步骤,跟着来就能搞定:
第一步:先完善你的模型定义
首先要注意,你当前的fournisseur模型没有继承Django的models.Model,这是必须的前提,不然没法用Django的ORM和数据库交互。另外可以根据需求优化字段,比如给id_facture加唯一性约束,避免重复导入相同数据:
from django.db import models class Fournisseur(models.Model): # 首字母大写符合Python命名规范,更易读 id_facture = models.IntegerField(unique=True) # 唯一性约束防止重复数据 numpiece = models.IntegerField(null=True) class Meta: db_table = 'fournisseurs' # 指定数据库表名和你现有表一致
第二步:创建文件上传表单
写一个表单类来处理文件上传,同时限制允许的文件类型为CSV和Excel:
# forms.py from django import forms class FournisseurImportForm(forms.Form): import_file = forms.FileField( label="选择Excel/CSV文件", widget=forms.FileInput(attrs={'accept': '.csv,.xlsx'}) )
第三步:编写视图处理上传与数据导入
这里需要分别处理CSV和Excel文件,记得先安装处理Excel的依赖:pip install openpyxl。视图里要完成文件读取、数据验证、批量插入,还要做好异常处理,给用户清晰的提示:
# views.py import csv from openpyxl import load_workbook from django.shortcuts import render, redirect from django.contrib import messages from .models import Fournisseur from .forms import FournisseurImportForm def import_fournisseur(request): if request.method == 'POST': form = FournisseurImportForm(request.POST, request.FILES) if form.is_valid(): import_file = request.FILES['import_file'] file_ext = import_file.name.split('.')[-1].lower() data_list = [] try: if file_ext == 'csv': # 处理CSV文件 reader = csv.DictReader(import_file.read().decode('utf-8').splitlines()) for row in reader: # 验证必填字段 if not row.get('id_facture'): messages.error(request, "CSV文件中存在缺失id_facture的行") return redirect('import_fournisseur') # 转换数据类型,避免格式错误 try: id_facture = int(row['id_facture']) numpiece = int(row['numpiece']) if row['numpiece'] else None except ValueError: messages.error(request, f"行数据格式错误:{row}") return redirect('import_fournisseur') # 跳过已存在的数据 if not Fournisseur.objects.filter(id_facture=id_facture).exists(): data_list.append(Fournisseur(id_facture=id_facture, numpiece=numpiece)) elif file_ext == 'xlsx': # 处理Excel文件 wb = load_workbook(import_file) sheet = wb.active # 读取表头(假设第一行是列名:id_facture、numpiece) headers = [cell.value.lower() for cell in sheet[1]] if 'id_facture' not in headers: messages.error(request, "Excel文件缺少id_facture列") return redirect('import_fournisseur') id_col_idx = headers.index('id_facture') num_col_idx = headers.index('numpiece') if 'numpiece' in headers else None for row in sheet.iter_rows(min_row=2, values_only=True): id_facture = row[id_col_idx] if not id_facture: messages.error(request, "Excel文件中存在缺失id_facture的行") return redirect('import_fournisseur') try: id_facture = int(id_facture) numpiece = int(row[num_col_idx]) if num_col_idx is not None and row[num_col_idx] else None except ValueError: messages.error(request, f"行数据格式错误:{row}") return redirect('import_fournisseur') if not Fournisseur.objects.filter(id_facture=id_facture).exists(): data_list.append(Fournisseur(id_facture=id_facture, numpiece=numpiece)) else: messages.error(request, "仅支持CSV和XLSX格式的文件") return redirect('import_fournisseur') # 批量插入数据,比循环save()效率高很多 if data_list: Fournisseur.objects.bulk_create(data_list) messages.success(request, f"成功导入{len(data_list)}条供应商数据") else: messages.info(request, "没有新的可导入数据") return redirect('import_fournisseur') except Exception as e: messages.error(request, f"导入失败:{str(e)}") return redirect('import_fournisseur') else: form = FournisseurImportForm() return render(request, 'import_fournisseur.html', {'form': form})
第四步:创建上传页面模板
在templates目录下创建import_fournisseur.html,做一个简单的上传界面:
<!-- templates/import_fournisseur.html --> <!DOCTYPE html> <html> <head> <title>导入供应商数据</title> </head> <body> <h1>导入供应商数据</h1> {% if messages %} {% for message in messages %} <div style="color: {% if message.tags == 'success' %}green{% elif message.tags == 'error' %}red{% else %}blue{% endif %}"> {{ message }} </div> {% endfor %} {% endif %} <form method="post" enctype="multipart/form-data"> {% csrf_token %} {{ form.as_p }} <button type="submit">上传并导入</button> </form> </body> </html>
第五步:配置URL路由
在项目的urls.py里添加路由,让用户能访问到上传页面:
# urls.py from django.urls import path from .views import import_fournisseur urlpatterns = [ # 其他路由... path('import-fournisseur/', import_fournisseur, name='import_fournisseur'), ]
几个关键注意点
- 数据验证:一定要做字段格式检查和必填项验证,避免脏数据进入数据库
- 批量插入:用
bulk_create比循环调用save()效率高很多,尤其是数据量大的时候 - 异常处理:捕获文件读取、数据转换、数据库操作的异常,给用户友好的提示
- 权限控制:如果需要限制只有特定用户能导入数据,可以给视图添加
@login_required装饰器,或者结合Django的权限系统
内容的提问来源于stack exchange,提问作者Elroum
相关产品推荐
相关产品推荐

