Template-based Word document generation using Jinja2 syntax. Create reports, contracts, and documents with loops, conditionals, tables, and mail merge capabilities.
Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.
Quelldateien prüfen
Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.
Mit Codex oder Claude installieren Kopieren Sie diesen Prompt, fügen Sie ihn in Codex, Claude oder einen anderen Assistant ein und lassen Sie die Skill-Seite prüfen und installieren.
Ein direkter Befehl überspringt den Prüf-Prompt. Prüfen Sie die Quelle, bevor Sie ihn ausführen.
Template-based Word document generation using Jinja2 syntax. Create reports, contracts, and documents with loops, conditionals, tables, and mail merge capabilities.
Generate Word documents from templates using Jinja2 syntax. Create professional reports, contracts, and documents with dynamic content, loops, conditionals, and tables.
Quick Start
# Install docxtpl
pip install docxtpl
# Install with image support
pip install docxtpl Pillow
# For Excel data sources
pip install docxtpl openpyxl pandas
# Verify installation
python -c "from docxtpl import DocxTemplate; print('docxtpl ready!')"
When to Use This Skill
USE when:
Generating documents from templates with dynamic data
Creating mail merge documents from data sources
Building reports with loops and conditional sections
Need to maintain consistent formatting across generated documents
Generating contracts, invoices, letters from templates
Processing batch document generation from databases or spreadsheets
Templates need professional formatting preserved
Non-technical users maintain template design
DON'T USE when:
Building documents programmatically from scratch (use python-docx)
Need complex document manipulation beyond template filling
PDF output is the final format (generate docx then convert)
Templates require complex macros or VBA
Real-time collaborative editing needed
Prerequisites
# Core installation
pip install docxtpl>=0.16.0
# For image handling
pip install docxtpl Pillow>=9.0.0
# For data processing
pip install docxtpl pandas>=2.0.0 openpyxl>=3.1.0
# For database connections
pip install docxtpl sqlalchemy psycopg2-binary
# All dependencies
pip install docxtpl Pillow pandas openpyxl sqlalchemy
Verify Installation
from docxtpl import DocxTemplate, InlineImage
from docx.shared import Mm, Inches
print("docxtpl installed successfully!")
# Quick test
# template = DocxTemplate("template.docx")
# context = {"name": "World"}
# template.render(context)
# template.save("output.docx")
Core Capabilities
1. Basic Template Rendering
Simple Variable Substitution:
"""
Basic template rendering with variable substitution.
"""from docxtpl import DocxTemplate
from typing importDict, Anyfrom pathlib import Path
defrender_simple_template(
template_path: str,
output_path: str,
context: Dict[str, Any]
) -> None:
"""
Render a template with simple variable substitution.
Args:
template_path: Path to .docx template
output_path: Path for output document
context: Dictionary of values to substitute
Template syntax:
{{ variable_name }} - Simple variable
{{ object.property }} - Nested property
"""# Load template
template = DocxTemplate(template_path)
# Render with context
template.render(context)
# Save output
template.save(output_path)
print(f"Document saved to {output_path}")
defcreate_sample_letter(output_path: str) -> None:
"""Create a sample letter using template rendering."""# First, create a simple template (normally you'd have this prepared)# Template content would have: {{ recipient_name }}, {{ date }}, etc.
context = {
"recipient_name": "John Smith",
"recipient_title": "Director of Operations",
"company_name": "Acme Corporation",
"street_address": "123 Business Ave",
"city_state_zip": "New York, NY 10001",
"date": "January 17, 2026",
"subject": "Project Proposal",
"salutation": "Dear Mr. Smith",
"body_paragraph_1": """
We are pleased to submit our proposal for the infrastructure
upgrade project. Our team has extensive experience in similar
projects and we are confident we can deliver exceptional results.
""".strip(),
"body_paragraph_2": """
The attached documents outline our approach, timeline, and
budget estimates. We would welcome the opportunity to discuss
this proposal at your convenience.
""".strip(),
"closing": "Sincerely",
"sender_name": "Jane Doe",
"sender_title": "Project Manager"
}
# Render template
render_simple_template("letter_template.docx", output_path, context)
# Example context for a business report
report_context = {
"report_title": "Q4 2025 Performance Report",
"prepared_by": "Analytics Team",
"date": "January 15, 2026",
"executive_summary": "Strong performance across all metrics...",
"total_revenue": "$2,450,000",
"growth_rate": "15.3%",
"customer_count": "1,250",
"key_achievements": [
"Launched new product line",
"Expanded to 3 new markets",
"Achieved ISO certification"
]
}
"""
Generate tables dynamically from data.
"""from docxtpl import DocxTemplate
from typing importList, Dict, Anyimport pandas as pd
defrender_data_table(
template_path: str,
output_path: str,
headers: List[str],
rows: List[List[Any]],
table_title: str = "") -> None:
"""
Render a simple data table.
Template structure:
{{ table_title }}
| Header 1 | Header 2 | Header 3 |
|----------|----------|----------|
{%tr for row in rows %}
| {{ row[0] }} | {{ row[1] }} | {{ row[2] }} |
{%tr endfor %}
"""
template = DocxTemplate(template_path)
# Convert rows to list of dicts for easier template access
row_dicts = []
for row in rows:
row_dict = {f"col{i}": val for i, val inenumerate(row)}
row_dicts.append(row_dict)
context = {
"table_title": table_title,
"headers": headers,
"rows": row_dicts,
"column_count": len(headers)
}
template.render(context)
template.save(output_path)
defrender_pandas_table(
template_path: str,
output_path: str,
df: pd.DataFrame,
title: str = "") -> None:
"""
Render a pandas DataFrame as a table.
Template:
{%tr for row in data %}
{%tc for cell in row %}{{ cell }}{%tc endfor %}
{%tr endfor %}
"""
template = DocxTemplate(template_path)
# Convert DataFrame to list of dicts
headers = df.columns.tolist()
data = df.values.tolist()
context = {
"title": title,
"headers": headers,
"data": data,
"row_count": len(data),
"col_count": len(headers)
}
template.render(context)
template.save(output_path)
defrender_grouped_table(
template_path: str,
output_path: str,
grouped_data: Dict[str, List[Dict]]
) -> None:
"""
Render table with grouped rows and subtotals.
Template:
{%tr for group_name, items in groups.items() %}
{{ group_name }} ({{ items|length }} items)
{%tr for item in items %}
| {{ item.name }} | {{ item.value }} |
{%tr endfor %}
Subtotal: {{ group_subtotals[group_name] }}
{%tr endfor %}
"""
template = DocxTemplate(template_path)
# Calculate subtotals
subtotals = {}
for group, items in grouped_data.items():
subtotals[group] = sum(item.get("value", 0) for item in items)
context = {
"groups": grouped_data,
"group_subtotals": subtotals,
"total": sum(subtotals.values())
}
template.render(context)
template.save(output_path)
classTableBuilder:
"""
Builder for complex table structures.
"""def__init__(self):
self.headers: List[str] = []
self.rows: List[Dict] = []
self.footer_row: Dict = {}
self.title: str = ""defset_title(self, title: str) -> 'TableBuilder':
"""Set table title."""self.title = title
returnselfdefset_headers(self, headers: List[str]) -> 'TableBuilder':
"""Set column headers."""self.headers = headers
returnselfdefadd_row(self, **kwargs) -> 'TableBuilder':
"""Add a data row."""self.rows.append(kwargs)
returnselfdefadd_rows_from_dicts(self, rows: List[Dict]) -> 'TableBuilder':
"""Add multiple rows from list of dicts."""self.rows.extend(rows)
returnselfdefadd_rows_from_dataframe(self, df: pd.DataFrame) -> 'TableBuilder':
"""Add rows from DataFrame."""self.headers = df.columns.tolist()
self.rows = df.to_dict('records')
returnselfdefset_footer(self, **kwargs) -> 'TableBuilder':
"""Set footer row (e.g., totals)."""self.footer_row = kwargs
returnselfdefauto_calculate_footer(self, columns: List[str], operation: str = "sum") -> 'TableBuilder':
"""Auto-calculate footer values."""for col in columns:
values = [row.get(col, 0) for row inself.rows ifisinstance(row.get(col), (int, float))]
if operation == "sum":
self.footer_row[col] = sum(values)
elif operation == "avg":
self.footer_row[col] = sum(values) / len(values) if values else0elif operation == "count":
self.footer_row[col] = len(values)
returnselfdefto_context(self) -> Dict[str, Any]:
"""Convert to template context."""return {
"table_title": self.title,
"headers": self.headers,
"rows": self.rows,
"footer": self.footer_row,
"has_footer": bool(self.footer_row),
"row_count": len(self.rows)
}
defrender(self, template_path: str, output_path: str) -> None:
"""Render to document."""
template = DocxTemplate(template_path)
template.render(self.to_context())
template.save(output_path)
# Example usage
builder = TableBuilder()
builder.set_title("Monthly Sales Report")
builder.set_headers(["Product", "Units", "Revenue", "Margin"])
builder.add_row(Product="Widget A", Units=100, Revenue=5000, Margin=25)
builder.add_row(Product="Widget B", Units=150, Revenue=7500, Margin=30)
builder.add_row(Product="Widget C", Units=75, Revenue=3750, Margin=22)
builder.auto_calculate_footer(["Units", "Revenue"], operation="sum")
# builder.render("sales_table_template.docx", "sales_report.docx")
5. Image Insertion
Adding Images to Templates:
"""
Insert images into templates with proper sizing.
"""from docxtpl import DocxTemplate, InlineImage
from docx.shared import Mm, Inches, Cm
from pathlib import Path
from typing importOptional, Unionfrom io import BytesIO
import requests
defadd_image_to_template(
template_path: str,
output_path: str,
image_path: str,
context: dict,
width: Optional[Union[Mm, Inches, Cm]] = None,
height: Optional[Union[Mm, Inches, Cm]] = None) -> None:
"""
Add an image to a template.
Template syntax:
{{ image }}
Args:
template_path: Path to template
output_path: Path for output
image_path: Path to image file
context: Additional context data
width: Image width (optional)
height: Image height (optional)
"""
template = DocxTemplate(template_path)
# Create InlineImage
image = InlineImage(
template,
image_path,
width=width,
height=height
)
# Add image to context
context["image"] = image
template.render(context)
template.save(output_path)
defadd_image_from_url(
template: DocxTemplate,
url: str,
width: Optional[Mm] = None) -> InlineImage:
"""
Create InlineImage from URL.
Args:
template: DocxTemplate instance
url: Image URL
width: Desired width
Returns:
InlineImage object
"""
response = requests.get(url)
response.raise_for_status()
image_stream = BytesIO(response.content)
return InlineImage(
template,
image_stream,
width=width
)
defrender_document_with_images(
template_path: str,
output_path: str,
data: dict,
images: dict) -> None:
"""
Render document with multiple images.
Template:
Company Logo: {{ logo }}
Product Images:
{% for product in products %}
{{ product.name }}: {{ product.image }}
{% endfor %}
"""
template = DocxTemplate(template_path)
# Process images
context = data.copy()
for key, image_info in images.items():
ifisinstance(image_info, str):
# Simple path
context[key] = InlineImage(template, image_info, width=Mm(50))
elifisinstance(image_info, dict):
# Dict with path and dimensions
context[key] = InlineImage(
template,
image_info["path"],
width=image_info.get("width"),
height=image_info.get("height")
)
template.render(context)
template.save(output_path)
classImageHandler:
"""
Handle images for template rendering.
"""def__init__(self, template: DocxTemplate):
self.template = template
self._images: dict = {}
defadd_image(
self,
key: str,
source: Union[str, BytesIO],
width: Optional[int] = None,
height: Optional[int] = None,
unit: str = "mm") -> 'ImageHandler':
"""
Add an image to the handler.
Args:
key: Context key for the image
source: File path or BytesIO stream
width: Width in specified units
height: Height in specified units
unit: Unit type ('mm', 'inches', 'cm')
"""# Convert unitsif unit == "mm":
w = Mm(width) if width elseNone
h = Mm(height) if height elseNoneelif unit == "inches":
w = Inches(width) if width elseNone
h = Inches(height) if height elseNoneelif unit == "cm":
w = Cm(width) if width elseNone
h = Cm(height) if height elseNoneelse:
w = h = Noneself._images[key] = InlineImage(
self.template,
source,
width=w,
height=h
)
returnselfdefadd_image_from_url(
self,
key: str,
url: str,
width: int = 50,
unit: str = "mm") -> 'ImageHandler':
"""Add image from URL."""
response = requests.get(url)
response.raise_for_status()
image_stream = BytesIO(response.content)
returnself.add_image(key, image_stream, width=width, unit=unit)
defget_context(self) -> dict:
"""Get images as context dictionary."""returnself._images.copy()
# Example usagedefcreate_product_catalog(
template_path: str,
output_path: str,
products: List[Dict]
) -> None:
"""Create product catalog with images."""
template = DocxTemplate(template_path)
handler = ImageHandler(template)
# Process products and add images
processed_products = []
for i, product inenumerate(products):
if"image_path"in product:
handler.add_image(
f"product_image_{i}",
product["image_path"],
width=60,
unit="mm"
)
product["image"] = handler._images[f"product_image_{i}"]
processed_products.append(product)
context = {
"catalog_title": "2026 Product Catalog",
"products": processed_products,
**handler.get_context()
}
template.render(context)
template.save(output_path)
6. Mail Merge and Batch Generation
Generating Multiple Documents:
"""
Generate multiple documents from a template with different data.
"""from docxtpl import DocxTemplate
from typing importList, Dict, Any, Iterator
from pathlib import Path
from concurrent.futures import ThreadPoolExecutor, as_completed
import csv
import json
import pandas as pd
defmail_merge_from_list(
template_path: str,
output_dir: str,
records: List[Dict[str, Any]],
filename_field: str = "id") -> List[str]:
"""
Generate documents for multiple records.
Args:
template_path: Path to template
output_dir: Directory for output files
records: List of data records
filename_field: Field to use for output filename
Returns:
List of generated file paths
"""
output_path = Path(output_dir)
output_path.mkdir(parents=True, exist_ok=True)
generated_files = []
for record in records:
# Load fresh template for each document
template = DocxTemplate(template_path)
# Generate filename
filename = f"{record.get(filename_field, 'document')}.docx"
file_path = output_path / filename
# Render and save
template.render(record)
template.save(str(file_path))
generated_files.append(str(file_path))
print(f"Generated {len(generated_files)} documents in {output_dir}")
return generated_files
defmail_merge_from_csv(
template_path: str,
csv_path: str,
output_dir: str,
filename_field: str = "id") -> List[str]:
"""
Generate documents from CSV data source.
Args:
template_path: Path to template
csv_path: Path to CSV file
output_dir: Directory for output files
filename_field: Field to use for output filename
Returns:
List of generated file paths
"""withopen(csv_path, 'r', newline='', encoding='utf-8') as f:
reader = csv.DictReader(f)
records = list(reader)
return mail_merge_from_list(template_path, output_dir, records, filename_field)
defmail_merge_from_excel(
template_path: str,
excel_path: str,
output_dir: str,
sheet_name: str = None,
filename_field: str = "id") -> List[str]:
"""
Generate documents from Excel data source.
Args:
template_path: Path to template
excel_path: Path to Excel file
output_dir: Directory for output files
sheet_name: Sheet to read (default: first sheet)
filename_field: Field to use for output filename
Returns:
List of generated file paths
"""
df = pd.read_excel(excel_path, sheet_name=sheet_name)
records = df.to_dict('records')
return mail_merge_from_list(template_path, output_dir, records, filename_field)
defmail_merge_parallel(
template_path: str,
output_dir: str,
records: List[Dict[str, Any]],
filename_field: str = "id",
max_workers: int = 4) -> List[str]:
"""
Generate documents in parallel for better performance.
Args:
template_path: Path to template
output_dir: Directory for output files
records: List of data records
filename_field: Field to use for output filename
max_workers: Maximum parallel workers
Returns:
List of generated file paths
"""
output_path = Path(output_dir)
output_path.mkdir(parents=True, exist_ok=True)
defgenerate_single(record: Dict) -> str:
"""Generate a single document."""
template = DocxTemplate(template_path)
filename = f"{record.get(filename_field, 'document')}.docx"
file_path = output_path / filename
template.render(record)
template.save(str(file_path))
returnstr(file_path)
generated_files = []
with ThreadPoolExecutor(max_workers=max_workers) as executor:
futures = {executor.submit(generate_single, r): r for r in records}
for future in as_completed(futures):
try:
result = future.result()
generated_files.append(result)
except Exception as e:
record = futures[future]
print(f"Error generating document for {record.get(filename_field)}: {e}")
print(f"Generated {len(generated_files)} documents")
return generated_files
classMailMergeGenerator:
"""
Full-featured mail merge generator.
"""def__init__(self, template_path: str):
self.template_path = template_path
self._validate_template()
def_validate_template(self) -> None:
"""Validate template file exists."""ifnot Path(self.template_path).exists():
raise FileNotFoundError(f"Template not found: {self.template_path}")
def_get_template_variables(self) -> List[str]:
"""Extract variable names from template."""
template = DocxTemplate(self.template_path)
returnlist(template.get_undeclared_template_variables())
defvalidate_data(self, records: List[Dict]) -> Dict[str, List]:
"""
Validate data against template variables.
Returns:
Dict with 'missing' and 'extra' variable lists
"""
template_vars = set(self._get_template_variables())
ifnot records:
return {"missing": list(template_vars), "extra": []}
data_vars = set(records[0].keys())
return {
"missing": list(template_vars - data_vars),
"extra": list(data_vars - template_vars)
}
defgenerate(
self,
output_dir: str,
records: List[Dict],
filename_pattern: str = "{id}",
parallel: bool = False,
max_workers: int = 4) -> Dict[str, Any]:
"""
Generate documents with full reporting.
Args:
output_dir: Output directory
records: Data records
filename_pattern: Pattern for filenames (e.g., "{name}_{date}")
parallel: Use parallel processing
max_workers: Number of parallel workers
Returns:
Generation report
"""
output_path = Path(output_dir)
output_path.mkdir(parents=True, exist_ok=True)
results = {
"total": len(records),
"successful": 0,
"failed": 0,
"files": [],
"errors": []
}
defprocess_record(record: Dict) -> Dict:
"""Process single record."""try:
template = DocxTemplate(self.template_path)
# Generate filename from pattern
filename = filename_pattern.format(**record) + ".docx"
file_path = output_path / filename
template.render(record)
template.save(str(file_path))
return {"success": True, "file": str(file_path)}
except Exception as e:
return {"success": False, "error": str(e), "record": record}
if parallel andlen(records) > 1:
with ThreadPoolExecutor(max_workers=max_workers) as executor:
futures = [executor.submit(process_record, r) for r in records]
for future in as_completed(futures):
result = future.result()
if result["success"]:
results["successful"] += 1
results["files"].append(result["file"])
else:
results["failed"] += 1
results["errors"].append(result)
else:
for record in records:
result = process_record(record)
if result["success"]:
results["successful"] += 1
results["files"].append(result["file"])
else:
results["failed"] += 1
results["errors"].append(result)
return results
# Example usagedefgenerate_personalized_letters():
"""Generate personalized letters for recipients."""
recipients = [
{
"id": "001",
"name": "John Smith",
"title": "Mr.",
"company": "Acme Corp",
"address": "123 Main St",
"city": "New York",
"offer_amount": "$50,000"
},
{
"id": "002",
"name": "Jane Doe",
"title": "Ms.",
"company": "TechStart Inc",
"address": "456 Oak Ave",
"city": "San Francisco",
"offer_amount": "$75,000"
}
]
generator = MailMergeGenerator("offer_letter_template.docx")
# Validate data
validation = generator.validate_data(recipients)
if validation["missing"]:
print(f"Warning: Missing variables: {validation['missing']}")
# Generate documents
results = generator.generate(
output_dir="generated_letters",
records=recipients,
filename_pattern="{id}_{name}",
parallel=True
)
print(f"Generated: {results['successful']}/{results['total']}")
if results["errors"]:
for error in results["errors"]:
print(f"Error: {error}")
# generate_personalized_letters()
Integration Examples
Database Integration
"""
Generate documents from database queries.
"""from docxtpl import DocxTemplate
from typing importList, Dictimport sqlite3
from sqlalchemy import create_engine, text
import pandas as pd
defgenerate_from_database(
template_path: str,
output_dir: str,
db_connection: str,
query: str,
filename_field: str = "id") -> List[str]:
"""
Generate documents from database query results.
Args:
template_path: Path to template
output_dir: Output directory
db_connection: Database connection string
query: SQL query to fetch data
filename_field: Field for output filename
Returns:
List of generated file paths
"""# Connect and fetch data
engine = create_engine(db_connection)
with engine.connect() as conn:
result = conn.execute(text(query))
records = [dict(row._mapping) for row in result]
# Generate documentsreturn mail_merge_from_list(template_path, output_dir, records, filename_field)
defgenerate_customer_reports(
template_path: str,
output_dir: str,
db_path: str) -> Dict:
"""Generate customer reports from SQLite database."""
conn = sqlite3.connect(db_path)
conn.row_factory = sqlite3.Row
# Fetch customers with their orders
query = """
SELECT
c.id,
c.name,
c.email,
c.address,
COUNT(o.id) as order_count,
SUM(o.total) as total_spent
FROM customers c
LEFT JOIN orders o ON c.id = o.customer_id
GROUP BY c.id
"""
cursor = conn.cursor()
cursor.execute(query)
records = []
for row in cursor.fetchall():
record = dict(row)
# Fetch order details for each customer
cursor.execute(
"SELECT * FROM orders WHERE customer_id = ?",
(record["id"],)
)
record["orders"] = [dict(r) for r in cursor.fetchall()]
records.append(record)
conn.close()
# Generate reports
generated = mail_merge_from_list(
template_path,
output_dir,
records,
filename_field="id"
)
return {
"total_customers": len(records),
"generated_reports": len(generated),
"files": generated
}
FastAPI Service
"""
Document generation service with FastAPI.
"""from fastapi import FastAPI, HTTPException, UploadFile, File
from fastapi.responses import FileResponse
from pydantic import BaseModel
from typing importDict, Any, List, Optionalfrom docxtpl import DocxTemplate
from pathlib import Path
import tempfile
import uuid
app = FastAPI(title="Document Generation Service")
# Template storage
TEMPLATES_DIR = Path("./templates")
OUTPUT_DIR = Path("./generated")
OUTPUT_DIR.mkdir(exist_ok=True)
classGenerateRequest(BaseModel):
"""Request model for document generation."""
template_name: str
data: Dict[str, Any]
output_filename: Optional[str] = NoneclassBatchGenerateRequest(BaseModel):
"""Request for batch generation."""
template_name: str
records: List[Dict[str, Any]]
filename_field: str = "id"@app.get("/templates")asyncdeflist_templates():
"""List available templates."""
templates = []
for f in TEMPLATES_DIR.glob("*.docx"):
templates.append({
"name": f.stem,
"filename": f.name
})
return {"templates": templates}
@app.post("/generate")asyncdefgenerate_document(request: GenerateRequest):
"""Generate a single document."""
template_path = TEMPLATES_DIR / f"{request.template_name}.docx"ifnot template_path.exists():
raise HTTPException(404, f"Template '{request.template_name}' not found")
try:
template = DocxTemplate(str(template_path))
template.render(request.data)
# Generate output filename
output_name = request.output_filename orf"{uuid.uuid4()}.docx"ifnot output_name.endswith(".docx"):
output_name += ".docx"
output_path = OUTPUT_DIR / output_name
template.save(str(output_path))
return FileResponse(
str(output_path),
media_type="application/vnd.openxmlformats-officedocument.wordprocessingml.document",
filename=output_name
)
except Exception as e:
raise HTTPException(500, f"Generation failed: {str(e)}")
@app.post("/generate/batch")asyncdefgenerate_batch(request: BatchGenerateRequest):
"""Generate multiple documents."""
template_path = TEMPLATES_DIR / f"{request.template_name}.docx"ifnot template_path.exists():
raise HTTPException(404, f"Template '{request.template_name}' not found")
generated = []
errors = []
for record in request.records:
try:
template = DocxTemplate(str(template_path))
template.render(record)
filename = f"{record.get(request.filename_field, uuid.uuid4())}.docx"
output_path = OUTPUT_DIR / filename
template.save(str(output_path))
generated.append(filename)
except Exception as e:
errors.append({
"record": record.get(request.filename_field),
"error": str(e)
})
return {
"generated": len(generated),
"failed": len(errors),
"files": generated,
"errors": errors
}
@app.post("/templates/upload")asyncdefupload_template(file: UploadFile = File(...)):
"""Upload a new template."""ifnot file.filename.endswith(".docx"):
raise HTTPException(400, "Only .docx files are supported")
template_path = TEMPLATES_DIR / file.filename
content = await file.read()
withopen(template_path, "wb") as f:
f.write(content)
return {"message": f"Template '{file.filename}' uploaded", "name": Path(file.filename).stem}
# Run with: uvicorn service:app --reload
Best Practices
1. Template Design
"""Best practices for template design."""# DO: Use meaningful variable names
good_context = {
"customer_name": "John Smith",
"invoice_date": "2026-01-17",
"total_amount": "$1,500.00"
}
# DON'T: Use cryptic names
bad_context = {
"cn": "John Smith",
"d": "2026-01-17",
"t": "$1,500.00"
}
# DO: Organize context with nested objects
organized_context = {
"customer": {
"name": "John Smith",
"email": "john@example.com",
"address": {
"street": "123 Main St",
"city": "New York"
}
},
"invoice": {
"number": "INV-001",
"date": "2026-01-17",
"items": [...]
}
}
# DO: Include computed valuesdefprepare_context(data: dict) -> dict:
"""Prepare context with computed values."""
context = data.copy()
# Add computed fieldsif"items"in context:
context["item_count"] = len(context["items"])
context["subtotal"] = sum(i["total"] for i in context["items"])
# Add display flags
context["has_discount"] = context.get("discount", 0) > 0return context
2. Error Handling
"""Robust error handling for template rendering."""from typing importTuple, Optionaldefsafe_render(
template_path: str,
output_path: str,
context: dict) -> Tuple[bool, Optional[str]]:
"""
Safely render template with error handling.
Returns:
Tuple of (success, error_message)
"""try:
# Validate template existsifnot Path(template_path).exists():
returnFalse, f"Template not found: {template_path}"# Load and render
template = DocxTemplate(template_path)
# Check for missing variables
required_vars = template.get_undeclared_template_variables()
missing = [v for v in required_vars if v notin context]
if missing:
returnFalse, f"Missing variables: {missing}"
template.render(context)
# Ensure output directory exists
Path(output_path).parent.mkdir(parents=True, exist_ok=True)
template.save(output_path)
returnTrue, Noneexcept Exception as e:
returnFalse, str(e)
3. Performance Optimization
"""Optimize batch document generation."""# DO: Use parallel processing for large batchesdefoptimized_batch_generation(
template_path: str,
records: List[Dict],
output_dir: str,
batch_size: int = 100) -> List[str]:
"""Generate documents in optimized batches."""from concurrent.futures import ThreadPoolExecutor
defprocess_batch(batch_records):
results = []
for record in batch_records:
template = DocxTemplate(template_path)
template.render(record)
output_path = Path(output_dir) / f"{record['id']}.docx"
template.save(str(output_path))
results.append(str(output_path))
return results
# Process in batches
all_results = []
batches = [records[i:i+batch_size] for i inrange(0, len(records), batch_size)]
with ThreadPoolExecutor(max_workers=4) as executor:
futures = [executor.submit(process_batch, b) for b in batches]
for future in futures:
all_results.extend(future.result())
return all_results
# Problem: Loop content not appearing# Solution: Verify loop syntax and data structure# CORRECT loop syntax in template:# {%tr for item in items %}# {{ item.name }}# {%tr endfor %}# Ensure data is a list
context = {
"items": [
{"name": "Item 1"}, # Must be dict or object
{"name": "Item 2"}
]
}