Django 5.0中render_to_string无法正确访问上下文生成PDF问题
Django+WeasyPrint生成PDF时上下文数据无法解析的解决方案
问题核心
使用Django 5.0结合WeasyPrint转换HTML模板为PDF时,模板本身渲染正常,但generar_pdf视图中调用render_to_string时,无法遍历carro字典的items,也无法解析pedido模型属性(如{{pedido.id}}),导致PDF缺失表格等动态内容。
错误原因
通过表单隐藏字段传递的pedido、carro、total均为字符串格式,而非原有的模型实例或字典类型:
pedido变成了模型的字符串表示(例如<Pedido: Pedido object (1)>),无法访问模型属性carro变成了字典的字符串形式(例如{'1': {'nombre': ...}}),模板无法对其执行遍历操作- 模板引擎只能识别原生Python对象,无法解析这些字符串化的数据
解决方法
方法一:传递ID重新查询(推荐,安全防篡改)
优先从数据库或session获取真实数据,避免前端传递敏感内容:
- 修改模板表单:仅传递
pedido.id和total,购物车数据从session获取(假设购物车存储在session的carro键中)
<form id="pdf_form" method="post" action="{% url 'generar_pdf' %}" style="display: none;"> {% csrf_token %} <input type="hidden" name="pedido_id" value="{{ pedido.id }}"> <input type="hidden" name="total" value="{{ total }}"> </form>
- 更新视图逻辑:通过ID重新查询Pedido实例,从session读取购物车
from django.shortcuts import get_object_or_404 from .models import Pedido def generar_pdf(request): if request.method == "POST": # 通过ID获取真实的Pedido实例 pedido_id = request.POST.get("pedido_id") pedido = get_object_or_404(Pedido, id=pedido_id) # 从session获取购物车字典 carro_info = request.session.get('carro', {}) total_compra = request.POST.get("total") html_string = render_to_string( template_name='exito.html', context={"pedido": pedido, "carro": carro_info, "total": total_compra}, request=request ) html = HTML(string=html_string) pdf_file = html.write_pdf() response = HttpResponse(pdf_file, content_type='application/pdf') response['Content-Disposition'] = 'attachment; filename="mi_archivo.pdf"' return response
方法二:序列化传递数据(适合无法从session/数据库获取的场景)
如果必须通过前端传递carro字典,需先序列化再反序列化:
- 模板中序列化数据:使用Django的
json_script标签安全序列化字典
<form id="pdf_form" method="post" action="{% url 'generar_pdf' %}" style="display: none;"> {% csrf_token %} <input type="hidden" name="pedido_id" value="{{ pedido.id }}"> <input type="hidden" name="total" value="{{ total }}"> {{ carro|json_script:"carro-data" }} </form> <script> document.getElementById("pdf_button").addEventListener("click", function() { const form = document.getElementById("pdf_form"); // 创建隐藏字段传递序列化后的购物车数据 const carroInput = document.createElement('input'); carroInput.type = 'hidden'; carroInput.name = 'carro'; carroInput.value = JSON.stringify(JSON.parse(document.getElementById('carro-data').textContent)); form.appendChild(carroInput); form.submit(); }); </script>
- 视图中反序列化:将字符串转回字典
import json from django.shortcuts import get_object_or_404 from .models import Pedido def generar_pdf(request): if request.method == "POST": pedido_id = request.POST.get("pedido_id") pedido = get_object_or_404(Pedido, id=pedido_id) # 反序列化购物车字符串为字典,添加异常处理防止篡改 try: carro_info = json.loads(request.POST.get("carro", "{}")) except json.JSONDecodeError: carro_info = {} total_compra = request.POST.get("total") html_string = render_to_string( template_name='exito.html', context={"pedido": pedido, "carro": carro_info, "total": total_compra}, request=request ) html = HTML(string=html_string) pdf_file = html.write_pdf() response = HttpResponse(pdf_file, content_type='application/pdf') response['Content-Disposition'] = 'attachment; filename="mi_archivo.pdf"' return response
注意事项
- 禁止直接传递模型实例或字典的字符串表示,模板引擎无法识别这类数据
- 优先从数据库或session获取数据,避免前端篡改风险
- 确保
content_type设置为application/pdf,否则浏览器无法正确识别PDF文件
内容的提问来源于stack exchange,提问作者qseb59
相关产品推荐
相关产品推荐

