You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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获取真实数据,避免前端传递敏感内容:

  1. 修改模板表单:仅传递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>
  1. 更新视图逻辑:通过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字典,需先序列化再反序列化:

  1. 模板中序列化数据:使用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>
  1. 视图中反序列化:将字符串转回字典
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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 18:25:57