django导入excel

django导入excel,第1张

django导入excel django restframework 导出excel内容,可以查看另外一篇文章 一、基础环境
  • web架构:前后端分离,前端使用vue,后端使用django 的rest framework
  • django版本3.2
  • django-excel 版本0.0.10
  • djangorestframework版本3.12.4
二、需求
  1. 界面导入excel并保存到数据库
  2. 导入model包含外键类型
  3. 导入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固产"
三、功能实现
  1. 不使用序列化类:需要手动实现外键值转换为外键对应类对象
  2. 使用序化类:本文优选这种方法
实现细节
  • 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()

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/4827666.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-11-10
下一篇 2022-11-10

发表评论

登录后才能评论

评论列表(0条)

保存