首页 > 编程语言 >Python中配置Excel导出模板

Python中配置Excel导出模板

时间:2023-12-20 15:56:16浏览次数:27  
标签:return name get Python Excel mapping def 模板 cls

定义Excel列对象

class ExcelColumn:
    """
    定义Excel中的列

    参数:
        name (str): 列的名称。
        width (int | None, 可选): 列的宽度。默认为 None。
        required (bool, 可选): 指示列是否必需。默认为 False。
        mapping_factory (Callable | dict | None, 可选): 实例的映射工厂。
            可以是可调用对象、字典或 None。默认为 None。
        update (bool, 可选): 指示列是否应该更新,用于ExcelWriter写入的时候是否覆盖写入。默认为 True。
    """
    def __init__(self, name: str, *, width: int | None = None, required=False,
                 mapping_factory: Callable | dict | None = None, update=True):
        self.name = name if not required else f'*{name}'
        self.width = width
        self.required = required
        self.mapping_keys = []
        self.mapping_reverse = None
        if not mapping_factory:
            self.mapping = lambda x: x
        elif isinstance(mapping_factory, dict):
            mapping_factory_reverse = {v: k for k, v in mapping_factory.items()}
            self.mapping = lambda x: mapping_factory.get(x)
            self.mapping_reverse = lambda x: mapping_factory_reverse.get(x)
            self.mapping_keys = list(mapping_factory.keys())
        else:
            self.mapping = mapping_factory
        self.update = update  

定义Excel模板元类

class ExcelTemplateMetaclass(type):

    def __new__(cls, name, bases, attrs):
        if name == 'ExcelTemplate':
            return type.__new__(cls, name, bases, attrs)
        columns = dict()   # 保存Excel的列信息
        for k, v in attrs.items():
            if isinstance(v, ExcelColumn):
                columns[k] = v
        for k in columns.keys():
            attrs.pop(k)
        attrs['__columns__'] = columns
        return type.__new__(cls, name, bases, attrs)

定义Excel模板基类

class ExcelTemplate(metaclass=ExcelTemplateMetaclass):
    __sheet_name__ = 'Sheet1'
    __columns__: dict[str, ExcelColumn]
    headers = {
        'Content-Type': 'application/vnd.ms-excel',
        'Content-Disposition': f'attachment;filename=download.xlsx'
    }

    @staticmethod
    async def get_download_data(query: QuerySet, limit: int=None):
        """传入一个querySet,最后导对应的数据,limit为最多允许导出的数据"""
        if not limit:
            limit = settings.EXCEL_DOWNLOAD_LIMIT
        pagination = query.limit(limit).offset(0)  # 最多允许导出100W的数据
        return await pagination.all()

    @classmethod
    def _get_upload_title_mapping(cls):
        return {v.name: k for k, v in cls.__columns__.items()}

    @classmethod
    def _get_upload_value_mappings(cls):
        return {v.name: v.mapping for k, v in cls.__columns__.items()}

    @staticmethod
    def _get_data_validation(columnIndex, excelColumn: ExcelColumn):
        dv = DataValidation(
            type='list',
            formula1=f'"{",".join(excelColumn.mapping_keys)}"',
            allow_blank=not excelColumn.required
        )
        dv.error = f"{excelColumn.name}只能从str({excelColumn.mapping_keys})中选择"
        dv.errorTitle = "无效的输入"
        dv.prompt = '请从下拉框中选择数据'
        dv.promptTitle = f"{excelColumn.name}选择"
        columnName = get_column_letter(columnIndex)
        dv.add(f'{columnName}{2}:{columnName}{11}')  # 数据验证区域,2-11行
        return dv

    @classmethod
    def _set_data_validation(cls, sheet):
        for index, (k, v) in enumerate(cls.__columns__.items(), start=1):
            if v.mapping_keys:
                dv = cls._get_data_validation(index, v)
                sheet.data_validations.append(dv)

    @classmethod
    def get_title(cls):
        return [val for val, _ in cls._get_title()]

    @classmethod
    def get_update_title(cls):
        return [k for k, v in cls.__columns__.items() if v.update]

    @classmethod
    def _get_title(cls):
        return [
            (v.name, v.width) for k, v in cls.__columns__.items()
        ]

    @classmethod
    def _get_title_mapping(cls):
        return {v.name: k for k, v in cls.__columns__.items()}

    @classmethod
    def get_template(cls) -> BytesIO:
        file = BytesIO()
        excel = ExcelWriter(file)
        sheet = excel.write(cls.__sheet_name__, [], title=cls.__columns__.values())
        cls._set_data_validation(sheet)
        excel.save()
        file.seek(0)
        return file

    @classmethod
    def get_template_as_stream_response(cls) -> StreamingResponse:
        return StreamingResponse(cls.get_template(), media_type='xls/xlsx', headers=cls.headers)

    @classmethod
    def save_template_as_file(cls, filename: str):
        with open(filename, 'wb') as f:
            f.write(cls.get_template().getvalue())

    @classmethod
    def read(cls, file: bytes | str) -> "":
        def get_value(key, value):
            mapping = value_mappings.get(key)
            if not mapping:
                return value
            return mapping(value)

        def get_title(key):
            return title_mapping.get(key, key)

        title_mapping = cls._get_upload_title_mapping()
        value_mappings = cls._get_upload_value_mappings()
        if isinstance(file, bytes):
            data = BytesIO()
            data.write(file)
            data.seek(0)
            excel = ExcelReader(data)
        else:
            excel = ExcelReader(file)
        return (
            {get_title(k): get_value(k, v) for k, v in row.items()}
            for row in excel.read(sheet=cls.__sheet_name__) if any(row.values())
        )

    @classmethod
    def write(cls, data):
        def translate_row_values(row_values):
            """将行数据转换为excel可以显示的值,主要是为了将后台枚举的数字转换为用户可读的字符"""
            result = {}
            for k, v in dict(row_values).items():
                # 没有定义的列不会被导入到Excel中
                if k not in cls.__columns__:
                    continue
                out_key = cls.__columns__[k].name
                # 处理时区问题,Excel保存时不支持时区参数
                if isinstance(v, datetime):
                    result[out_key] = v.replace(tzinfo=None)
                    continue

                # 将枚举数据转换为具体的值
                mapping_reverse = cls.__columns__.get(k).mapping_reverse
                if mapping_reverse:
                    result[out_key] = mapping_reverse(v)
                    continue

                result[out_key] = v
            return result

        file = BytesIO()
        excel = ExcelWriter(file)
        sheet = excel.write(
            cls.__sheet_name__,
            (translate_row_values(row) for row in data),  # 处理待写入的数据
            title=cls.__columns__.values()
        )
        cls._set_data_validation(sheet)
        excel.save()
        file.seek(0)
        return file

    @classmethod
    def write_as_stream_response(cls, data):
        """保存问一个文件响应对象,用于返回给前端"""
        return StreamingResponse(cls.write(data), media_type='xls/xlsx', headers=cls.headers)

    @classmethod
    def write_as_file(cls, filename: str, data):
        """将数据保存为一个文件"""
        with open(filename, 'wb') as f:
            f.write(cls.write(data).getvalue())

使用ExcelTemplate管理模板

class OrderExcelTemplate(ExcelTemplate):
    id = ExcelColumn('id', required=True)
    orderNo = ExcelColumn('订单号', required=True)
    price = ExcelColumn('价格', required=False)


OrderExcelTemplate.save_template_as_file('order_template.xlsx')

标签:return,name,get,Python,Excel,mapping,def,模板,cls
From: https://www.cnblogs.com/liulangjuanzhou/p/17916669.html

相关文章

  • Python语言实现两台计算机用TCP协议跨局域网通信
    成果展示:(这张图是在我本地电脑上用pycharm运行两个程序测试,实际可以在两台电脑上分别运行。)设备要求和实现的功能:实现的功能:跨局域网通信(仅支持两台计算机)跨局域网收发小文件,支持缓存在服务器,再一键接收(仅支持两台计算机)使用方法:在服务器上运行server.py程序,在两台客户......
  • python中的 时间、日期写法。
    python打印当前日期时间 一、打印 带日期与时间点 方法一:使用datetime模块:importdatetimenow=datetime.datetime.now()print(now)效果如下: 方法二:使用time模块:importtimenow=time.strftime("%Y-%m-%d%H:%M:%S",time.localtime())print(now) ......
  • 提高Python开发效率的实用方法
    Python作为一种简洁而强大的编程语言,广泛应用于各种领域的软件开发。提高Python开发效率是开发者们关注的重要课题。本文将分享一些实用的方法,帮助您在Python开发中更高效地完成任务,提高代码质量和开发速度。1.使用虚拟环境:在项目开发中,使用虚拟环境是一种良好的实践。虚拟环境可以......
  • 使用Python进行Firefox证书上传和删除证书的步骤
    在Web开发和测试过程中,有时需要在Firefox浏览器中管理证书,包括上传证书和删除证书。本文将介绍如何使用Python和Selenium库进行这些操作,以便更方便地处理证书管理。1.安装Selenium库和WebDriver:首先,确保已安装Selenium库和相应浏览器的WebDriver。可以使用以下命令安装Selenium:```b......
  • Python爬虫框架推荐及其特点
    在网络爬虫开发中,选择适合的爬虫框架可以大大提高开发效率和爬取数据的质量。Python作为一种广泛应用于爬虫开发的编程语言,有许多优秀的爬虫框架可供选择。本文将介绍几个好用的Python爬虫框架,并列举它们的特点,帮助您选择适合自己的框架。1.Scrapy:Scrapy是一个强大的开源爬虫框架,被......
  • CentOS安装Python3
    前置准备检查是否已经安装Python3:命令行直接输入Python3下载Python3的安装包https://www.python.org/ftp/python/安装安装依赖yuminstallzlib-develbzip2-devellibffi-developenssl-develncurses-develsqlite-develreadline-develtk-develgccmake下载Pyth......
  • 接口自动化之excel读写封装
    本次封装基于openpyxl进行的二次封装安装openpyxlpipinstallopenpyxl封装ExcelReader采用yield的方式返回数据,减少内存的占用classExcelReader:def__init__(self,filename):self._excel:Workbook=load_workbook(filename,read_only=True)d......
  • 利用Python进行数据分析_Pandas_数据规整
    数据规整1.时间序列以及截面对齐importpandasaspdimportnumpyasnpfrompandasimportSeries,DataFrameimportwarningswarnings.filterwarnings("ignore")#设置一个日期范围date_range=pd.date_range(start="2023-01-01",end="2023-01-10",freq=......
  • 分类模型评估(混淆矩阵, precision, recall, f1-score)的原理和Python实现
    混淆矩阵当我们已经获取到一个分类模型的预测值,可以通过不同指标来进行评估。往往衡量二分类模型是基于以下的混淆矩阵概念:TruePositive:真实值为正、预测值为正(真阳性)FalsePositive:真实值为负、预测值为正(假阳性)FalseNegative:真实值为正、预测值为负(假阴性)TrueNegative......
  • python-docx删除文档部分内容
    1fromdocx.documentimportDocumentas_Document2fromdocx.oxml.text.paragraphimportCT_P3fromdocx.oxml.tableimportCT_Tbl4fromdocx.tableimport_Cell,Table5fromdocx.text.paragraphimportParagraph678defword_cut(document):9......