- web架构:前后端分离,前端使用vue,后端使用django 的rest framework
- django版本3.2
- django-excel 版本0.0.10
- djangorestframework版本3.12.4
- 界面导入excel并保存到数据库
- 导入model包含外键类型
- 导入excel表格列顺序任意,即不需要强制按照excel导出顺序
#models.py from django.db import models from django.utils import timezone class MyITtype(models.Model): name = models.CharField(verbose_name="名称", max_length=128, unique=True) ittype = models.SmallIntegerField(verbose_name="类型") comment = models.TextField(verbose_name="备注", blank=True, default="") def __str__(self): return self.name class meta: db_table = "MyITtype" verbose_name = "it资产类型" class MyAsset(models.Model): ittype = models.ForeignKey(MyITtype, on_delete=models.SET_NULL, verbose_name="产品类型", null=True) code = models.CharField(verbose_name="资产编码", max_length=128, unique=True) buytime = models.DateField(verbose_name="入仓时间", default=timezone.now) usetime = models.DateField(verbose_name="分配时间", default=timezone.now) comment = models.TextField(verbose_name="规格说明", blank=True, default="") user = models.CharField(verbose_name="使用人", max_length=128, blank=True, default="") status = models.IntegerField(verbose_name="状态") def __str__(self): return self.code class meta: db_table = "opGTITAsset" verbose_name = "it固产"三、功能实现
- 不使用序列化类:需要手动实现外键值转换为外键对应类对象
- 使用序化类:本文优选这种方法
- django配置文件中加入文件上传后处理方法
- 为了通用性,特意封装了一个excel导入类
- 为了兼容导入保存到数据库(save_excel_to_db)和不保存到数据库(deal_excel),特意拆分为多个步骤
- 为了防止导入时根据主键覆盖指定内容,特意删除了id列内容,id为model默认主键
#settings.py加入以下内容 #处理上传excel内容 FILE_UPLOAD_HANDLERS = [ "django_excel.ExcelMemoryFileUploadHandler", "django_excel.TemporaryExcelFileUploadHandler" ]
#asset_load.py from rest_framework.views import APIView from rest_framework.response import Response from rest_framework.serializers import ModelSerializer from .models import MyAsset class ITAssetSer(ModelSerializer): class meta: model = MyAsset fields = '__all__' class GTLoadExcel(): """ django 处理excel数据导入 将excel内容写入数据库时,需要注意以下两点 1、默认唯一值是name,如果你的model不是时,需要重写get_instance方法 2、默认删除id列内容,如果还有特殊处理,需要重写deal_special_field方法 deal_special_field重新注意点 a、需要同时删除id列时,加入父类id处理或者调用父类+自定义内容 b、不需要删除id列时,完全重写 """ def __init__(self, ser_class, class_name, sheet): """ 初始化 :param ser_class: model序化类 :param class_name: 待 *** 作model类名称 :param sheet: 导入excel内容 """ self.ser_class = ser_class self.class_name = class_name self.sheet = sheet def save_excel_to_db(self): self.deal_excel() self.save_data() def deal_excel(self): """ 处理输入的excel并返回其内容列表 :return: [{},{}] """ self.data_list = self.deal_row_data() return self.data_list def save_data(self): # 保存到数据库 for data in self.data_list: self.in_obj_data = data self.deal_special_field() obj = self.get_instance() if obj: # update ser = self.ser_class(obj, data=data) else: # create ser = self.ser_class(data=data) if ser.is_valid(): ser.save() else: logger.info(ser.errors) raise def deal_special_field(self): """ 处理特殊 字段内容,如删除excel中主键内容,如id,防止输入主键内容意外覆盖正常内容 :param data: :return: """ master_key = "id" # 删除主键列内容 del self.in_obj_data[master_key] def get_instance(self): """ 根据唯一字段获取对应对象 :return: """ obj = None try: obj = self.class_name.objects.get(name=self.in_obj_data["name"]) except: pass return obj def deal_row_data(self): """ 处理导入excel中每一个行内容 :return: """ sheet = self.sheet header = sheet.row[0] attr_list = self.get_attr_map(header) post_data_list = [] for idx in range(1, sheet.number_of_rows()): rdata = sheet.row[idx] tmp = dict(zip(attr_list, rdata)) post_data_list.append(tmp) return post_data_list def get_attr_map(self, header): """ 建立表头和model字段对应关系 :param header: :return: """ field_dict = {field.verbose_name: field.name for field in self.class_name._meta.fields} attr_list = [] for idx, head in enumerate(header): attr_list.append(field_dict[head]) return attr_list class ITAssetLoad(APIView): use_model = MyAsset queryset = MyAsset.objects.all() serializer_class = ITAssetSer def post(self, request, *args, **kwargs): fobj = request.FILES["file"] sheet = fobj.get_sheet() instance = ITAssetExcelSpecial(ser_class=self.serializer_class, class_name=self.use_model, sheet=sheet) instance.save_excel_to_db() return Response({}) def put(self, request, *args, **kwargs): self.http_method_not_allowed(request, *args, **kwargs) class ITAssetExcelSpecial(GTLoadExcel): def get_instance(self): obj = None try: obj = self.class_name.objects.get(code=self.in_obj_data["code"]) except: pass return obj def deal_special_field(self): """ 删除id所在列 特殊处理时间字段 :param data: :return: """ data = self.in_obj_data # 删除id列 master_key = "id" # 删除主键列内容,默认防止导入时意外覆盖主键列内容 del data[master_key] #excel中时间默认被转换为datetime类型,但是需要date类型,因此需要转换一下 data["buytime"] = data["buytime"].date() data["usetime"] = data["usetime"].date()
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)