· 阅读约 8 分钟
使用 JSON 在 Python / Django 中生成动态 PDF
将版式与 Django 业务逻辑分离:把 JSON 发给 QuartzAPI 并流式返回 PDF——无需 ReportLab 或 WeasyPrint C 依赖。
In Python environments, generating PDFs with tools like ReportLab means writing procedural code for layout positioning, fonts, and line breaks. Alternatives like WeasyPrint pull in heavy system C libraries (Cairo, Pango) that complicate Docker deployments and server setup.
With QuartzAPI, Python developers separate layout design from backend business logic entirely: Django prepares a JSON dictionary; the Visual Builder owns the PDF template.
Prerequisites
- Python 3.8+
requestslibrary (pip install requests)- Django 3.2+ / 4.x / 5.x
- A QuartzAPI account, API key, and template code (e.g.
ORDER_SUMMARY)
Step 1: create the API wrapper utility
Create a utility module (pdf_generator.py) in your Django app.
Call generate-document, then download the PDF bytes
(the first response is JSON with documentId / downloadUrl).
# utils/pdf_generator.py
import requests
from django.conf import settings
QUARTZ_GENERATE_URL = (
"https://backend.quartzapi.com/index.php?r=api/v1-jobs/generate-document"
)
QUARTZ_DOWNLOAD_URL = (
"https://backend.quartzapi.com/index.php?r=api/v1-documents/download"
)
class PDFGenerationError(Exception):
"""Custom exception for PDF generation errors."""
pass
def render_pdf_from_template(template_code: str, data: dict) -> bytes:
"""
Sends data to QuartzAPI and returns raw PDF bytes.
"""
headers = {
"Authorization": f"Bearer {settings.QUARTZ_API_KEY}",
"Content-Type": "application/json",
}
payload = {
"templateCode": template_code,
"outputFormat": "pdf",
"data": data,
}
try:
generate = requests.post(
QUARTZ_GENERATE_URL,
json=payload,
headers=headers,
timeout=30,
)
generate.raise_for_status()
body = generate.json()
result = body.get("result") or {}
document_id = result.get("documentId")
download_url = result.get("downloadUrl") or (
f"{QUARTZ_DOWNLOAD_URL}&uid={document_id}"
)
pdf = requests.get(
download_url,
headers={"Authorization": f"Bearer {settings.QUARTZ_API_KEY}"},
timeout=30,
)
pdf.raise_for_status()
return pdf.content
except requests.exceptions.RequestException as e:
raise PDFGenerationError(f"Failed to generate PDF: {str(e)}") from e
Add your API key to Django settings.py (prefer env vars in production):
# settings.py
import os
QUARTZ_API_KEY = os.environ.get("QUARTZ_API_KEY", "")
Step 2: implement the Django view
Build a view that loads models, shapes the JSON (typically under master /
master.items), and returns an HttpResponse with
application/pdf.
# views.py
from django.http import HttpResponse
from django.views import View
from .utils.pdf_generator import render_pdf_from_template, PDFGenerationError
class ExportOrderPDFView(View):
def get(self, request, order_id):
# 1. Prepare dynamic JSON (real apps: Order.objects.get(pk=order_id))
order_payload = {
"master": {
"order_id": f"ORD-{order_id}",
"status": "PAID",
"currency": "EUR",
"shipping_address": "123 Tech Street, Milan, Italy",
"total_amount": 199.00,
"items": [
{
"sku": "SDK-01",
"name": "REST API License",
"qty": 1,
"price": 99.00,
},
{
"sku": "SUP-02",
"name": "Priority Support Ticket",
"qty": 2,
"price": 50.00,
},
],
}
}
# 2. Render PDF bytes via API
try:
pdf_bytes = render_pdf_from_template("ORDER_SUMMARY", order_payload)
except PDFGenerationError as err:
return HttpResponse(str(err), status=500)
# 3. Stream binary PDF to the browser
response = HttpResponse(pdf_bytes, content_type="application/pdf")
response["Content-Disposition"] = (
f'attachment; filename="order_{order_id}.pdf"'
)
return response
Align field names with template bindings. Background on the JSON-to-PDF pattern: generate PDF invoices from JSON.
Step 3: configure URL routing
Wire the view in your app’s urls.py:
# urls.py
from django.urls import path
from .views import ExportOrderPDFView
urlpatterns = [
path(
"orders/<int:order_id>/pdf/",
ExportOrderPDFView.as_view(),
name="export_order_pdf",
),
]
Summary
With this pattern your Django app stays free of bloated system-level C dependencies like Cairo or Pango, deployment containers stay small, and PDF templates can be updated visually without restarting WSGI/ASGI workers.
| Approach | Layout code | Ops / Docker |
|---|---|---|
| ReportLab | Procedural X/Y in Python | Pure Python, but layout in repo |
| WeasyPrint | HTML/CSS + system libs | Cairo, Pango, fonts in image |
| QuartzAPI | Visual Builder cloud | requests only |
Conclusions
Treat PDF generation as an HTTP integration, not a graphics subsystem inside Django. Keep models and serializers in Python; keep typography and page breaks in QuartzAPI.
Ready to drop ReportLab / WeasyPrint?
Sign up for the QuartzAPI Beta, create an order template, and plug
render_pdf_from_template into your export view.
Ready-to-use snippets
- Generate:
POST https://backend.quartzapi.com/index.php?r=api/v1-jobs/generate-documentwithtemplateCode+data. - Auth:
Authorization: Bearer {settings.QUARTZ_API_KEY}. - Download:
result.downloadUrlorGET …/v1-documents/download&uid=…. - Docs: Web API documentation.