Python-使用xlsxwriter创建单元格下拉列表(数据校验)

在一个B/S系统中,用户经常需要从以Excel的方式填写后导入,此时网页上需要提供Excel填写的模板,将所需填写的字段以及所处的列的位置固定下来。其中,避免用户随意填写导致系统后台校验出错,对于某些字段,系统需要预设好固定的选项值,用户只能在给定的范围内选择,不允许人工输入填写。

如果字段值的数据范围长期不变,则可直接固化在Excel中;如果字段值偶尔会更新,需要在下载Excel模板时,实时从数据库获取,则需要动态生成该Excel模板。

静态Excel模板

制作Excel模板

新建Excel文件,设置好表头名称,从第二行开始设置数据校验。具体步骤为:

数据》数据验证》设置,“允许”选择“序列”,“来源”字段可手动输入文本(以英文逗号分隔)或选中任意sheet的任意表格内容。

提供下载

from flask import current_app, request, send_from_directory, send_file

def download_excel_template():
    """
        下载Excel模板
    """
    file_dir = os.path.join(current_app.config['LIB_DIR'], 'files', 'templates')  # 模板存放目录
    current_app.logger.info("下载%s下的文件:%s" % (file_dir, 'Template.xlsx'))
    return send_from_directory(file_dir, 'Template.xlsx', as_attachment=True)

动态Excel模板

更特殊的场景下,Excel单元格下拉列表的内容需要从系统中动态获取。例如,用户希望通过填写并上传Excel到某系统,由系统在openstack平台上创建一台虚拟机。虚机的hostname、ip等内容需要由用户自行指定,但虚机所使用的Flavor(即CPU/内存)、镜像(image)、卷的类型(volume type)、网络(network)、子网(subnet)等,名称较为复杂不便记忆,最好是能提供Excel的下拉选择功能。image等信息需要从openstack平台拉取,且管理员随时都有可能会在openstack平台上进行增删改的操作,因此为保持Excel模板的准确性、避免经常性的更新维护,可以动态Excel模板的方式提供下载。

这里,推荐使用xlsxwriter模块生成Excel文件。[Creating Excel files with Python and XlsxWriter — XlsxWriter Documentation]

确定Excel的列名

header_row = ['hostname', 'flavor', 'image']

获取下拉选项数据

flavors = get_openstack_flavor_names()
images = get_openstack_image_names()

列表直接写在文本中

通用函数
# chr(i)  65-90   # A,B,C ... Z

def get_column(idx):
    """
        根据数字序号,获取Excel的列名,从0开始
        例如,0->A
    """
    prefix = int(idx/26)
    suffix = idx%26
    if not prefix:
        return chr(suffix+65)
    else:
        return get_column(prefix)+chr(suffix+65)

def get_excel_cell_name(row_idx, col_idx):
    """
        根据行列的序号,获取单元格的名称,从(0,0)开始
        例如,(0,0)->A1
    """
    if row_idx < 0:
        raise ValueError("Excel的行号必须大于等于0")
    if col_idx < 0:
        raise ValueError("Excel的列号必须大于等于0")
    return "%s%s"%(get_column(col_idx), row_idx+1)
生成Excel

对于单元格的数据校验,可以使用其data_validation功能。Working with Data Validation — XlsxWriter Documentation

import xlsxwriter
from flask import current_app

def generate_excel_template():
    newfile = os.path.join(current_app.config['TMP_DIR'], 'download', 'Template-%s.xlsx' % (time.strftime("%Y%m%d%H%M")))
    workbook = xlsxwriter.Workbook(newfile)
    sheet = workbook.add_worksheet("Sheet1")
    row = 0
    # 表头
    for idx, header in enumerate(header_row):
        sheet.write(row, idx, header)
    row += 1
    # flavor
    flavors = get_openstack_flavor_names()  # todo,从数据库获取数据列表
    cellname = get_excel_cell_name(row, header_row.index("flavor"))
    sheet.data_validation(cellname, {"validate": "list", "source": flavors})
    # image
    images = get_openstack_image_names()    #todo,从数据库获取数据列表
    cellname = get_excel_cell_name(row, header_row.index("image"))
    sheet.data_validation(cellname, {"validate": "list", "source": images})
    workbook.close()
    return newfile

@app.route()    # 略
def download():
    file_path = generate_excel_template()
    return send_file(file_path, as_attachment=True)
    

注意:在该方法中,是将列表的内容直接放在了source里,xlsxwriter对此有所限制,内容长度不超过255,否则无法生效,该单元格最终会变成普通的单元格。

列表维护在单独sheet中

对于列表内容较长的场景,建议单独创建一个sheet,用于作为下拉列表的选项。上述代码可改造为:

def save_data_validate_options_sheet(workbook, object_options):
    """
        保存数据校验的待选项到单独的sheet,object_options格式为{'flavor':[], 'image':[]}
    """
    key2cellrange = {}
    opt_sheet = workbook.add_worksheet("options")
    col = 0
    for k in object_options:
        key2cellrange[k] = ""
        opt_sheet.write(0, col, k)
        row = 1
        for opt in object_options[k]:
            opt_sheet.write(row, col, opt)
            row += 1
        col_char = get_column(col)
        cellrange = "='{sheet}'!${col}${row1}:${col}${row2}".format(sheet=sheetname,col=col_char, row1=2, row2=row)
        key2cellrange[k] = cellrange
        col += 1
    return key2cellrange
    
def generate_excel_template():
    newfile = os.path.join(current_app.config['TMP_DIR'], 'download', 'Template-%s.xlsx' % (time.strftime("%Y%m%d%H%M")))
    workbook = xlsxwriter.Workbook(newfile)
    sheet = workbook.add_worksheet("Sheet1")
    row = 0
    # 表头
    for idx, header in enumerate(header_row):
        sheet.write(row, idx, header)
    row += 1
    # 统一获取待选项
    flavors = get_openstack_flavor_names()  # todo,从数据库获取数据列表
    images = get_openstack_image_names()    #todo,从数据库获取数据列表
    object_options = {"flavor": flavors, "image": images}
    key2cellrange = save_data_validate_options_sheet(workbook, object_options)
    # flavor
    cellname = get_excel_cell_name(row, header_row.index("flavor"))
    sheet.data_validation(cellname, {"validate": "list", "source": key2cellrange['flavor']})
    # image
    cellname = get_excel_cell_name(row, header_row.index("image"))
    sheet.data_validation(cellname, {"validate": "list", "source": key2cellrange['image']})
    workbook.close()
    return newfile
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
python-docx中,要合并单元格可以使用merge()方法。该方法接受一个参数,即要合并的目标单元格。合并后会返回一个新的合并后的单元格对象。下面是一个示例代码: ```python from docx import Document doc = Document() table = doc.add_table(5, 3, style="Table Grid") # 获取要合并的单元格 cell1 = table.cell(0, 0) cell2 = table.cell(0, 1) # 合并单元格 merged_cell = cell1.merge(cell2) # 修改合并后的单元格文本 merged_cell.text = "合并后的单元格" # 保存文档 doc.save("merged_table.docx") ``` 在这个示例中,我们首先创建了一个包含5行3列的表格。然后,我们获取了要合并的两个单元格cell1和cell2。接下来,我们使用merge()方法将这两个单元格合并成一个新的单元格merged_cell。最后,我们修改了合并后的单元格的文本内容为"合并后的单元格"。最后,我们保存了文档。 请注意,合并单元格的操作会改变表格的结构,因此在合并单元格之前,请确保你已经理解了表格的结构和要合并的单元格的位置。 #### 引用[.reference_title] - *1* [【python-docx】添加表格、合并单元格设置样式](https://blog.csdn.net/qq_39147299/article/details/125414499)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [python docx处理word文档中表格合并问题](https://blog.csdn.net/u013546508/article/details/88687661)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [python-docx 合并单元格](https://blog.csdn.net/qq1261275789/article/details/126380020)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值