【办公神器】python-docx-template完全指南:30分钟掌握高级Word模板自动化技术
【办公神器】python-docx-template完全指南:30分钟掌握高级Word模板自动化技术
1. python-docx-template基础概念
1.1 核心优势与特点
python-docx-template
基于Jinja2模板引擎,为Word文档提供了强大的模板功能:
- 所见即所得的模板设计:在Word中直接设计模板,保留所有格式
- 强大的变量替换:支持文本、表格、图片等多种内容替换
- 逻辑控制能力:条件语句、循环和复杂表达式
- 与python-docx互补:结合两者优势实现更复杂功能
- 保留Word原生格式:完美保留样式、页眉页脚和其他格式元素
1.2 与其他库的比较
功能 | python-docx-template | python-docx | docxtpl |
---|---|---|---|
设计方式 | 模板驱动 | 代码驱动 | 模板驱动 |
学习曲线 | 简单 | 中等 | 中等 |
格式控制 | 直接在Word中完成 | 需要代码实现 | 直接在Word中完成 |
变量替换 | ✓ | 需自行实现 | ✓ |
条件逻辑 | ✓ | 需自行实现 | ✓ |
复杂结构 | 表格、列表、图片 | 完全可控 | 有限支持 |
适用场景 | 批量生成标准化文档 | 精确控制文档结构 | 简单文档生成 |
2. 环境配置与安装
2.1 安装步骤
# 安装核心库
pip install docxtpl
# 安装可选依赖(图像处理)
pip install pillow
# 安装可选依赖(Excel数据导入)
pip install pandas openpyxl
2.2 基本导入
from docxtpl import DocxTemplate, InlineImage
from docx.shared import Mm, Inches, Pt
import jinja2
3. 模板创建与基础应用
3.1 创建第一个Word模板
创建一个模板需要在Word文档中插入特定格式的变量:
- 打开Word创建新文档
- 在需要动态内容的位置插入变量,格式为
{ { variable_name }}
- 根据需要设置文本格式、字体、对齐方式等
- 保存为.docx文件
模板示例:
尊敬的{
{ name }}:
感谢您对{
{ company }}的关注与支持。我们已收到您于{
{ date }}提交的申请,
申请编号为{
{ application_id }}。我们将在{
{ response_days }}个工作日内给予回复。
如有疑问,请联系:{
{ contact_email }}
此致
敬礼
{
{ company }}
{
{ current_date }}
3.2 基本变量替换
from docxtpl import DocxTemplate
from datetime import datetime
# 加载模板
doc = DocxTemplate("template.docx")
# 准备上下文数据
context = {
"name": "张三",
"company": "ABC科技有限公司",
"date": "2023年5月15日",
"application_id": "APP-2023-0587",
"response_days": 3,
"contact_email": "support@abctech.com",
"current_date": datetime.now().strftime("%Y年%m月%d日")
}
# 渲染文档
doc.render(context)
# 保存生成的文档
doc.save("generated_document.docx")
3.3 自定义Jinja2环境
from docxtpl import DocxTemplate
import jinja2
# 创建自定义过滤器
def currency_format(value):
"""将数字格式化为货币形式"""
return "{:,.2f}".format(value)
def date_format(value, format="%Y年%m月%d日"):
"""格式化日期"""
if isinstance(value, str):
from datetime import datetime
try:
value = datetime.strptime(value, "%Y-%m-%d")
except:
return value
return value.strftime(format)
# 设置自定义Jinja2环境
doc = DocxTemplate("invoice_template.docx")
jinja_env = jinja2.Environment(autoescape=True)
jinja_env.filters["currency"] = currency_format
jinja_env.filters["date_format"] = date_format
# 使用自定义环境
doc.render(context, jinja_env)
doc.save("generated_invoice.docx")
4. 高级变量与格式处理
4.1 富文本内容
from docxtpl import DocxTemplate, RichText
doc = DocxTemplate("richtext_template.docx")
# 创建富文本对象
rt = RichText()
rt.add('这是', style="Normal")
rt.add('红色', color='FF0000')
rt.add('和', style="Normal")
rt.add('粗体', bold=True)
rt.add('文本', style="Normal")
rt.add(',还有', style="Normal")
rt.add('斜体', italic=True)
rt.add('效果', style="Normal")
# 添加到上下文
context = {
"rich_paragraph": rt
}
# 渲染文档
doc.render(context)
doc.save("richtext_output.docx")
4.2 图片插入
from docxtpl import DocxTemplate, InlineImage
from docx.shared import Mm
doc = DocxTemplate("image_template.docx")
# 准备图片
logo_image = InlineImage(doc, "company_logo.png", width=Mm(30))
signature = InlineImage(doc, "signature.png", width=Mm(50))
# 添加到上下文
context = {
"company_name": "XYZ集团",
"logo": logo_image,
"manager_name": "李经理",
"signature": signature
}
# 渲染文档
doc.render(context)
doc.save("with_images.docx")
4.3 表格处理
from docxtpl import DocxTemplate
doc = DocxTemplate("table_template.docx")
# 准备表格数据
products = [
{
"id": "P001", "name": "笔记本电脑", "price": 5999.00, "quantity": 2},
{
"id": "P002", "name": "无线鼠标", "price": 99.00, "quantity": 5},
{
"id": "P003", "name": "显示器", "price": 1299.00, "quantity": 1},
{
"id": "P004", "name": "机械键盘", "price": 399.00, "quantity": 3}
]
# 计算每个产品的小计和总计
for product in products:
product["subtotal"] = product["price"] * product["quantity"]
total = sum(product["subtotal"] for product in products)
# 添加到上下文
context = {
"customer": "王先生",
"order_id": "ORD-2023-0892",
"products": products,
"total": total
}
# 渲染文档
doc.render(context)
doc.save("invoice_with_table.docx")
5. 逻辑控制与条件渲染
5.1 条件语句
在Word模板中使用条件语句:
尊敬的客户:
{% if membership == "premium" %}
感谢您成为我们的高级会员,您将享受专属VIP服务。
{% elif membership == "standard" %}
感谢您成为我们的标准会员,您将享受优质服务。
{% else %}
感谢您的惠顾,希望您体验愉快。
{% endif %}
{% if balance > 1000 %}
您当前账户余额充足,可以享受额外优惠。
{% endif %}
Python代码:
from docxtpl import DocxTemplate
doc = DocxTemplate("conditional_template.docx")
# 准备不同上下文数据
premium_context = {
"customer_name": "张女士",
"membership": "premium",
"balance": 1500.00
}
standard_context = {
"customer_name": "李先生",
"membership": "standard",
"balance": 800.00
}
# 渲染不同文档
doc.render(premium_context)
doc.save("premium_letter.docx")
# 重新加载模板(避免重复渲染)
doc = DocxTemplate("conditional_template.docx")
doc.render(standard_context)
doc.save("standard_letter.docx")
5.2 循环语句
在Word模板中使用循环:
交易记录摘要:
{% for transaction in transactions %}
- {
{ transaction.date }} | {
{ transaction.type }} | {
{ transaction.amount }}元
{% endfor %}
{% if transactions|length > 5 %}
以上仅显示最近5笔交易,查看完整记录请登录网站。
{% endif %}
Python代码:
from docxtpl import DocxTemplate
from datetime import datetime, timedelta
doc = DocxTemplate("loop_template.docx")
# 生成交易记录数据
transactions = []
today = datetime.now()
for i in range(7):
tdate = today - timedelta(days=i)
ttype = "存入" if i % 3 == 0 else "支出"
tamount = 1000 + i * 100 if ttype == "存入" else 500 + i * 50
transactions.append({
"date": tdate.strftime("%Y-%m-%d"),
"type": ttype,
"amount": tamount
})
# 添加到上下文
context = {
"customer_name": "王女士",
"account_id": "6225 **** **** 3752",
"transactions": transactions
}
# 渲染文档
doc.render(context)
doc.save("transaction_report.docx")
5.3 高级表达式
在Word模板中使用计算和表达式:
账单摘要:
基础费用:{
{ base_price }}元
附加服务:{
{ additional_services }}元
优惠折扣:{
{ discount }}元
应付金额:{
{ base_price + additional_services - discount }}元
{% if (base_price + additional_services - discount) > 2000 %}
您的消费已达到高级会员标准,可享受后续服务9折优惠。
{% endif %}
Python代码:
from docxtpl import DocxTemplate
doc = DocxTemplate("expression_template.docx")
# 准备上下文数据
context = {
"customer_id": "CID-38291",
"base_price": 1299.00,
"additional_services": 899.00,
"discount": 199.00
}
# 渲染文档
doc.render(context)
doc.save("bill_with_calculations.docx")
6. 复杂表格与动态生成
6.1 动态行表格
from docxtpl import DocxTemplate
doc = DocxTemplate("dynamic_table_template.docx")
# 动态生成学生成绩数据
students = []
for i in range(1, 11):
math_score = 70 + (i % 3) * 10
english_score = 75 + (i % 4) * 8
science_score = 80 + (i % 5) * 5
total = math_score + english_score + science_score
average = total / 3
students.append({
"id": f"S{
2023100 + i}",
"name": f"学生{
i}",
"math": math_score,
"english": english_score,
"science": science_score,
"total": total,
"average": round(average, 1),
"pass": "通过" if average >= 60 else "未通过"
})
# 添加到上下文
context = {
"class_name": "高一(3)班",
"term": "2023年春季学期",
"exam_date": "2023年6月15日",
"students": students
}
# 渲染文档
doc.render(context)
doc.save("class_report_card.docx")
6.2 嵌套表格
from docxtpl import DocxTemplate
doc = DocxTemplate("nested_table_template.docx")
# 准备部门和员工数据
departments = [
{
"name": "研发部",
"manager": "张工",
"employees": [
{
"id": "E001", "name": "李明", "position": "高级工程师", "salary": 18000},
{
"id": "E002", "name": "王芳", "position": "工程师", "salary": 15000},
{
"id": "E003", "name": "赵强", "position": "工程师", "salary": 14000}
]
},
{
"name": "市场部",
"manager": "刘总",
"employees": [
{
"id": "E010", "name": "张丽", "position": "市场经理", "salary": 16000},
{
"id": "E011", "name": "周红", "position": "销售主管", "salary": 13000}
]
}
]
# 计算部门统计信息
for dept in departments:
dept["employee_count"] = len(dept["employees"])
dept["avg_salary"] = sum(e["salary"] for e in dept["employees"]) / dept["employee_count"]
# 添加到上下文
context = {
"company_name": "ABC科技有限公司",
"report_date": "2023年7月1日",
"departments": departments
}
# 渲染文档
doc.render(context)
doc.save("company_structure_report.docx")
6.3 表格样式控制
from docxtpl import DocxTemplate, RichText
doc = DocxTemplate("styled_table_template.docx")
# 准备数据
sales_data = [
{
"region": "东区", "q1": 1250000, "q2": 1380000, "q3": 1420000, "q4": 1600000},
{
"region": "西区", "q1": 980000, "q2": 1040000, "q3": 1100000, "q4": 1250000},
{
"region": "南区", "q1": 1100000, "q2": 1150000, "q3": 1300000, "q4": 1450000},
{
"region": "北区", "q1": 850000, "q2": 920000, "q3": 980000, "q4": 1050000}
]
# 计算同比增长并添加样式
for region in sales_data:
# 计算年度总销售额
region["total"] = region["q1"] + region["q2"] + region["q3"] + region["q4"]
# 创建富文本对象并添加颜色(根据销售额)
for quarter in ["q1", "q2", "q3", "q4", "total"]:
value = region[quarter]
rt = RichText()
if quarter == "total":
# 总销售额显示为粗体
rt.add(f"{
value:,}", bold=True)
else:
# 季度销售额根据数值显示不同颜色
if value >= 1300000:
rt.add(f"{
value:,}", color="007700") # 绿色(好)
elif value >= 1000000:
rt.add(f"{
value:,}", color="000000") # 黑色(一般)
else:
rt.add(f"{
value:,}", color="CC0000") # 红色(差)
region[f"{
quarter}_styled"] = rt
# 添加到上下文
context = {
"year": "2023",
"report_title": "区域销售业绩分析",
"sales_data": sales_data
}
# 渲染文档
doc.render(context)
doc.save("sales_performance_report.docx")
7. 实用场景案例
7.1 客户合同生成系统
from docxtpl import DocxTemplate, InlineImage
from docx.shared import Mm
from datetime import datetime
import os
import pandas as pd
def generate_contract(customer_data, template_path="contract_template.docx", output_dir="generated_contracts"):
"""根据客户数据生成合同"""
# 确保输出目录存在
os.makedirs(output_dir, exist_ok=True)
# 加载合同模板
doc = DocxTemplate(template_path)
# 准备上下文数据
context = customer_data.copy()
# 添加日期格式化
context["contract_date"] = datetime.now().strftime("%Y年%m月%d日")
context["start_date"] = datetime.strptime(context["start_date"], "%Y-%m-%d").strftime("%Y年%m月%d日")
context["end_date"] = datetime.strptime(context["end_date"], "%Y-%m-%d").strftime("%Y年%m月%d日")
# 计算合同金额中文大写
from num2chinese import num2chinese
context["amount_chinese"] = num2chinese(context["amount"])
# 添加签名图片
if "signature_path" in context and os.path.exists(context["signature_path"]):
context["signature"] = InlineImage(doc, context["signature_path"], width=Mm(30))
# 添加公司印章图片
if "stamp_path" in context and os.path.exists(context["stamp_path"]):
context["company_stamp"] = InlineImage(doc, context["stamp_path"], width=Mm(40))
# 渲染文档
doc.render(context)
# 构建输出文件名
output_filename = f"合同_{
context['customer_name']}_{
context['contract_id']}.docx"
output_path = os.path.join(output_dir, output_filename)
# 保存生成的合同
doc.save(output_path)
return output_path
# 从Excel导入客户数据
def generate_contracts_from_excel(excel_path, template_path, output_dir):
"""从Excel批量生成合同"""
# 读取Excel数据
df = pd.read_excel(excel_path)
# 转换为字典列表
customers = df.to_dict(orient='records')
# 生成的合同路径列表
generated_files = []
# 批量生成合同
for customer in customers:
output_path = generate_contract(customer, template_path, output_dir)
generated_files.append(output_path)
print(f"已生成合同: {
output_path}")
return generated_files
# 使用示例
generated_contracts = generate_contracts_from_excel(
"customer_data.xlsx",
"contract_template.docx",
"output_contracts"
)
print(f"成功生成 {
len(generated_contracts)} 份合同文档")
7.2 定制化证书生成
from docxtpl import DocxTemplate, InlineImage
from docx.shared import Mm
import os
import pandas as pd
from datetime import datetime
import qrcode
from PIL import Image
def generate_certificate(participant_data, template_path="certificate_template.docx",
output_dir="generated_certificates", add_qr=True):
"""生成参与者证书"""
# 确保输出目录存在
os.makedirs(output_dir, exist_ok=True)
# 加载证书模板
doc = DocxTemplate(template_path)
# 准备上下文数据
context = participant_data.copy()
# 格式化日期
issue_date = datetime.now().strftime("%Y年%m月%d日")
context["issue_date"] = issue_date
# 生成证书验证二维码
if add_qr:
qr_data = f"证书ID: {
context['certificate_id']}
"
qr_data += f"姓名: {
context['name']}
"
qr_data += f"课程: {
context['course_name']}
"
qr_data += f"日期: {
issue_date}
"
qr_data +=