阳春市文章资讯

Python使用docxtpl库高效生成Word文档的方法

2026-03-27 11:52:01 浏览次数:3
详细信息
使用 docxtpl 高效生成 Word 文档

docxtpl 是一个基于 python-docx 和 Jinja2 的 Python 库,它允许你在 Word 模板中使用 Jinja2 语法,然后动态填充数据生成 Word 文档。

1. 安装

pip install docxtpl

2. 基础用法

2.1 简单示例

创建模板文档 (template.docx):

在 Word 中创建模板,使用 {{ 变量名 }} 作为占位符 保存为 template.docx

Python 代码:

from docxtpl import DocxTemplate

# 加载模板
doc = DocxTemplate("template.docx")

# 准备上下文数据
context = {
    'company_name': 'ABC 科技有限公司',
    'date': '2023-12-01',
    'client_name': '张先生',
    'amount': '15000',
    'project_name': '网站开发项目'
}

# 渲染文档
doc.render(context)

# 保存文档
doc.save("generated_document.docx")
print("文档已生成")

2.2 完整示例

from docxtpl import DocxTemplate
from datetime import datetime

def generate_contract():
    """
    生成合同文档示例
    """
    # 加载模板
    doc = DocxTemplate("contract_template.docx")

    # 准备数据
    context = {
        # 基本信息
        'contract_number': 'HT202312001',
        'sign_date': datetime.now().strftime('%Y年%m月%d日'),
        'company_name': '创新科技股份有限公司',
        'client_name': '卓越企业集团',

        # 项目信息
        'project_name': '智能管理系统开发',
        'project_content': [
            '需求分析与系统设计',
            '前后端开发与实现',
            '系统测试与优化',
            '部署与培训',
            '三个月维护期支持'
        ],
        'project_duration': '2023年12月1日至2024年3月1日',
        'total_amount': '¥150,000.00',
        'payment_schedule': [
            {'phase': '合同签订后', 'amount': '¥45,000.00', 'percentage': '30%'},
            {'phase': '开发完成时', 'amount': '¥75,000.00', 'percentage': '50%'},
            {'phase': '验收合格后', 'amount': '¥30,000.00', 'percentage': '20%'},
        ],

        # 联系信息
        'contact_person': '李经理',
        'phone': '13800138000',
        'email': 'contact@innovate-tech.com',
        'address': '北京市海淀区科技园A座1001室'
    }

    # 渲染文档
    doc.render(context)

    # 保存
    output_filename = f"合同_{context['contract_number']}.docx"
    doc.save(output_filename)

    return output_filename

if __name__ == "__main__":
    result = generate_contract()
    print(f"合同已生成: {result}")

3. 高级功能

3.1 处理复杂数据结构

from docxtpl import DocxTemplate

def generate_report_with_tables():
    """
    生成包含表格和列表的报告
    """
    doc = DocxTemplate("report_template.docx")

    context = {
        'report_title': '2023年第四季度销售报告',
        'report_date': '2023-12-31',

        # 表格数据
        'sales_data': [
            {
                'region': '华北',
                'q1': 1500000,
                'q2': 1800000,
                'q3': 2100000,
                'q4': 2500000,
                'total': 7900000
            },
            {
                'region': '华东',
                'q1': 2200000,
                'q2': 2400000,
                'q3': 2600000,
                'q4': 3000000,
                'total': 10200000
            },
            {
                'region': '华南',
                'q1': 1800000,
                'q2': 2000000,
                'q3': 2300000,
                'q4': 2700000,
                'total': 8800000
            }
        ],

        # 列表数据
        'achievements': [
            '完成年度销售目标的120%',
            '成功开拓5个新市场',
            '客户满意度提升至95%',
            '团队规模扩大至50人'
        ],

        # 带条件的变量
        'performance': '优秀',
        'next_year_target': '增长30%'
    }

    doc.render(context)
    doc.save("季度销售报告.docx")
    print("报告已生成")

# 在模板中使用表格循环
"""
模板示例:
季度销售报表

{% for item in sales_data %}
{{ item.region }}地区:
第一季度:{{ item.q1 }}
第二季度:{{ item.q2 }}
第三季度:{{ item.q3 }}
第四季度: {{ item.q4 }}
总计:{{ item.total }}
{% endfor %}
"""

3.2 使用条件语句

def generate_invoice_with_conditions():
    """
    生成带有条件逻辑的发票
    """
    doc = DocxTemplate("invoice_template.docx")

    order_items = [
        {'name': '笔记本电脑', 'quantity': 2, 'unit_price': 6500, 'discount': 500},
        {'name': '显示器', 'quantity': 3, 'unit_price': 1500, 'discount': 0},
        {'name': '键盘鼠标套装', 'quantity': 5, 'unit_price': 300, 'discount': 50},
    ]

    # 计算总金额
    total_amount = sum(item['quantity'] * item['unit_price'] for item in order_items)
    total_discount = sum(item['discount'] for item in order_items)
    amount_payable = total_amount - total_discount

    context = {
        'invoice_number': 'INV202312001',
        'order_items': order_items,
        'total_amount': total_amount,
        'total_discount': total_discount,
        'amount_payable': amount_payable,

        # 条件判断
        'has_discount': total_discount > 0,
        'discount_note': f'已享受优惠 ¥{total_discount}' if total_discount > 0 else '无优惠',

        # 金额大写转换
        'amount_in_words': num_to_cn(amount_payable)
    }

    doc.render(context)
    doc.save(f"发票_{context['invoice_number']}.docx")

def num_to_cn(num):
    """数字转中文大写"""
    # 简化实现,实际使用时可以使用完整实现
    return "人民币" + str(num) + "元整"

3.3 插入图片

from docxtpl import DocxTemplate, InlineImage
from docx.shared import Mm

def generate_certificate_with_image():
    """
    生成带有图片的证书
    """
    doc = DocxTemplate("certificate_template.docx")

    context = {
        'student_name': '张三',
        'course_name': 'Python高级编程',
        'completion_date': '2023年12月15日',
        'score': '98',

        # 插入图片
        'logo': InlineImage(doc, 'company_logo.png', width=Mm(50)),
        'signature': InlineImage(doc, 'signature.png', width=Mm(80)),
        'stamp': InlineImage(doc, 'stamp.png', width=Mm(40))
    }

    doc.render(context)
    doc.save("毕业证书.docx")

3.4 使用 RichText

from docxtpl import DocxTemplate, RichText

def generate_document_with_richtext():
    """
    生成带有富文本格式的文档
    """
    doc = DocxTemplate("richtext_template.docx")

    # 创建富文本
    rt = RichText()
    rt.add('正常文本 ')

    rt.add('加粗文本', bold=True)
    rt.add(' ')

    rt.add('红色文本', color='FF0000')
    rt.add(' ')

    rt.add('带下划线的文本', underline=True)
    rt.add('\n')

    rt.add('不同大小的文本', size=24)

    context = {
        'title': '富文本示例文档',
        'richtext_content': rt,
        'footer_note': '使用docxtpl生成'
    }

    doc.render(context)
    doc.save("富文本示例.docx")

4. 模板设计技巧

4.1 Word 模板制作指南

变量格式: {{ variable_name }} 循环语句:
{% for item in items %}
{{ item.name }}
{% endfor %}
条件语句:
{% if condition %}
显示的内容
{% endif %}
表格循环: 在表格行中使用循环 嵌套结构: 支持嵌套的循环和条件

4.2 常用模板标签

{# 注释 #}

{# 变量 #}
{{ title }}

{# 循环 #}
{% for item in list %}
- {{ item.name }}
{% endfor %}

{# 条件 #}
{% if score >= 90 %}
优秀
{% elif score >= 60 %}
合格
{% else %}
不合格
{% endif %}

{# 过滤器 #}
{{ date|date_format("%Y-%m-%d") }}
{{ text|truncate(100) }}

5. 性能优化技巧

from docxtpl import DocxTemplate
import time
from functools import lru_cache

class DocumentGenerator:
    """文档生成器类"""

    def __init__(self, template_path):
        self.template_path = template_path
        self._template = None

    @property
    def template(self):
        """延迟加载模板"""
        if self._template is None:
            self._template = DocxTemplate(self.template_path)
        return self._template

    def generate_batch_documents(self, data_list):
        """
        批量生成文档
        """
        results = []
        for i, data in enumerate(data_list):
            try:
                # 每次使用新的模板实例
                doc = DocxTemplate(self.template_path)
                doc.render(data)

                filename = f"document_{i+1}.docx"
                doc.save(filename)
                results.append(filename)

                # 内存清理
                del doc

            except Exception as e:
                print(f"生成文档 {i+1} 时出错: {str(e)}")

        return results

    def generate_with_optimization(self, context, output_path):
        """
        优化版的文档生成
        """
        start_time = time.time()

        # 预处理数据
        processed_context = self._preprocess_context(context)

        # 生成文档
        doc = self.template
        doc.render(processed_context)
        doc.save(output_path)

        end_time = time.time()
        print(f"文档生成耗时: {end_time - start_time:.2f}秒")

        return output_path

    def _preprocess_context(self, context):
        """预处理上下文数据"""
        # 这里可以添加数据预处理逻辑
        return context

# 使用示例
if __name__ == "__main__":
    generator = DocumentGenerator("template.docx")

    # 准备批量数据
    batch_data = [
        {'name': f'用户{i}', 'id': i, 'date': '2023-12-01'} 
        for i in range(1, 101)
    ]

    # 批量生成
    results = generator.generate_batch_documents(batch_data)
    print(f"成功生成 {len(results)} 个文档")

6. 错误处理

import logging
from docxtpl import DocxTemplate, DocxTemplateError

# 配置日志
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s'
)

logger = logging.getLogger(__name__)

def safe_document_generation(template_path, context, output_path):
    """
    安全的文档生成函数
    """
    try:
        # 检查模板文件
        if not os.path.exists(template_path):
            raise FileNotFoundError(f"模板文件不存在: {template_path}")

        # 加载模板
        doc = DocxTemplate(template_path)

        # 验证上下文数据
        if not context or not isinstance(context, dict):
            raise ValueError("上下文数据必须为非空字典")

        # 渲染文档
        doc.render(context)

        # 保存文档
        doc.save(output_path)

        logger.info(f"文档已成功生成: {output_path}")
        return True

    except DocxTemplateError as e:
        logger.error(f"模板处理错误: {str(e)}")
        return False

    except Exception as e:
        logger.error(f"生成文档时发生错误: {str(e)}", exc_info=True)
        return False

# 使用示例
context = {
    'title': '测试文档',
    'content': '这是一个测试'
}

success = safe_document_generation(
    template_path="template.docx",
    context=context,
    output_path="output.docx"
)

if success:
    print("文档生成成功")
else:
    print("文档生成失败")

7. 实际应用示例

from docxtpl import DocxTemplate
import pandas as pd
from datetime import datetime
import os

class ReportGenerator:
    """报告生成器"""

    def __init__(self):
        self.templates_dir = "templates"
        self.output_dir = "output"
        self._ensure_directories()

    def _ensure_directories(self):
        """确保目录存在"""
        for directory in [self.templates_dir, self.output_dir]:
            os.makedirs(directory, exist_ok=True)

    def generate_from_excel(self, excel_path, template_name):
        """
        从 Excel 数据生成报告
        """
        # 读取 Excel 数据
        df = pd.read_excel(excel_path)

        # 加载模板
        template_path = os.path.join(self.templates_dir, f"{template_name}.docx")
        doc = DocxTemplate(template_path)

        # 准备数据
        reports = []
        for _, row in df.iterrows():
            context = {
                'report_id': row['ID'],
                'department': row['部门'],
                'manager': row['负责人'],
                'quarter': row['季度'],
                'target': row['目标'],
                'achievement': row['完成'],
                'completion_rate': f"{(row['完成']/row['目标'])*100:.1f}%" if row['目标'] > 0 else "0%",
                'analysis': row['分析说明'],
                'next_plan': row['下季度计划'],
                'generate_date': datetime.now().strftime('%Y-%m-%d %H:%M:%S')
            }

            # 生成单个报告
            doc = DocxTemplate(template_path)  # 重新加载模板
            doc.render(context)

            output_filename = f"报告_{row['部门']}_Q{row['季度']}.docx"
            output_path = os.path.join(self.output_dir, output_filename)
            doc.save(output_path)

            reports.append(output_path)

        return reports

    def generate_summary_report(self, data_list, template_name="summary_template"):
        """
        生成汇总报告
        """
        template_path = os.path.join(self.templates_dir, f"{template_name}.docx")
        doc = DocxTemplate(template_path)

        # 计算汇总数据
        total_target = sum(item['target'] for item in data_list)
        total_achievement = sum(item['achievement'] for item in data_list)
        avg_rate = (total_achievement / total_target * 100) if total_target > 0 else 0

        context = {
            'report_title': '各部门季度业绩汇总报告',
            'generate_date': datetime.now().strftime('%Y年%m月%d日'),
            'departments': data_list,
            'total_target': f"¥{total_target:,.2f}",
            'total_achievement': f"¥{total_achievement:,.2f}",
            'avg_completion_rate': f"{avg_rate:.1f}%",
            'best_department': max(data_list, key=lambda x: x['achievement'])['name'] if data_list else "无",
            'analysis': self._generate_analysis(data_list)
        }

        doc.render(context)

        output_path = os.path.join(self.output_dir, "汇总报告.docx")
        doc.save(output_path)

        return output_path

    def _generate_analysis(self, data):
        """生成分析报告"""
        # 简化的分析逻辑
        if not data:
            return "暂无数据"

        best = max(data, key=lambda x: x['achievement'])
        worst = min(data, key=lambda x: x['achievement'])

        return f"""
        本季度共 {len(data)} 个部门参与考核。
        表现最佳的部门是 {best['name']},完成业绩 {best['achievement']}。
        需要重点关注 {worst['name']} 部门的业绩提升。
        """

# 使用示例
if __name__ == "__main__":
    generator = ReportGenerator()

    # 模拟数据
    department_data = [
        {'name': '销售部', 'target': 1000000, 'achievement': 1200000},
        {'name': '技术部', 'target': 800000, 'achievement': 850000},
        {'name': '市场部', 'target': 600000, 'achievement': 750000},
        {'name': '行政部', 'target': 200000, 'achievement': 180000},
    ]

    # 生成汇总报告
    summary_report = generator.generate_summary_report(department_data)
    print(f"汇总报告已生成: {summary_report}")

总结

docxtpl 的优势

最佳实践

适用场景

这个方案特别适合需要大量生成格式统一的 Word 文档的业务场景,如合同、报告、证书等。

相关推荐