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

Django向Postmark API传列表渲染全部订单项问题求解

问题描述

我们正在为学生开发基于Django框架的全新在线商店程序,其中completed checkout视图需要向学生对应的辅导员发送邮件,请求审批本次采购订单。
邮件发送选用第三方服务Postmark,而非Django原生邮件能力,目的是实现邮件投递追踪,同时避免SMTP相关配置问题。Postmark提供REST API,支持传入JSON、字典或列表类型数据填充邮件模板。

Postmark模板片段

模板中变量注入点示例:

<td width="60%" class="align-left purchase_item">{{product__name}}</td>
<td width="40%" class="align-right purchase_item">{{product__point_price}}</td>

完整模板代码:

<table class="purchase_content" width="100%" cellpadding="0" cellspacing="0">
        <tr>
          <th class="purchase_heading">
            <p class="align-left">Description</p>
          </th>
          <th class="purchase_heading">
            <p class="align-right">Amount</p>
          </th>
        </tr>
        {{#each receipt_details}}
        <tr>
          <td width="60%" class="align-left purchase_item">{{product__name}}</td>
          <td width="40%" class="align-right purchase_item">{{product__point_price}}</td>
        </tr>
        {{/each}}
        <tr>
          <td width="80%" class="purchase_footer" valign="middle">
            <p class="purchase_total purchase_total--label">Total Points</p>
          </td>
          <td width="20%" class="purchase_footer" valign="middle">
            <p class="purchase_total">{{points}}</p>
          </td>
        </tr>
      </table>

故障现象

当前向API传递列表类型数据时,通过索引取值(如"description": receipt_details[0]['product__name'])可正常发起请求,但仅能传递单个商品数据。需求是将列表中所有携带product__name键的商品项全部传递给模板,让receipt_details循环块渲染出全部订单商品,指定索引的方式无法满足需求。该问题已排查4天,需在下周前上线。

现有问题代码

def checkoutcomplete(request):
    customer = request.user
    order = Order.objects.get(customer= customer , complete = False, )
    favorites = Favorite.objects.filter(customer = customer.id)
    today_date = datetime.datetime.today()    
    today_date_strf = datetime.datetime.strftime(today_date,"%m/%d/%Y" )
    items = order.orderitem_set.all() 
    data = items.values("product__name","quanity","product__point_price", "order_id")
    data_list = list(data)
    print(data_list)
   
    try:
        student = Student.objects.get(email = customer.email)
        counselor = User.objects.get(id = student.counselor_id)
        points = K8RunningPoints.objects.filter(student_ps = student.studentpsid).aggregate(Sum("running_total"))['running_total__sum']
        #POST MARK API 
        url = "https://api.postmarkapp.com/email/withTemplate"
        payload = json.dumps({
        "From": "",
        "To": "",
        "TemplateId": 28132290,
        
        "TemplateModel": {
        "counselor_name": "Fellow Counselor",
        "student_name": student.student_name,
        "receipt_id": str(order.transaction_id),
        "date": today_date_strf,
        "points": order.get_cart_total,
        "receipt_details": [
        {
            "product__name": data_list,
            "amount": "amount_Value"
        }
            ]
        },
        "MessageStream": "outbound"
        })
        headers = {
        'Content-Type': 'application/json',
        'X-Postmark-Server-Token': settings.POSTMARK["TOKEN"]
            }
        response = requests.request("POST", url, headers=headers, data=payload)
        print(response.text)
        
    except Exception as err1:
        print(err1, "Student user not found.")
        points = 0
        counselor = ""
        student = ""
        #POST MARK API 
        url = "https://api.postmarkapp.com/email/withTemplate"
        payload = json.dumps({
        "From": "",
        "To": "",
        "TemplateId": 28132290,
        
        "TemplateModel": {
        "counselor_name": "Fellow Counselor",
        "student_name": "test-student",
        "receipt_id": str(order.transaction_id),
        "date": today_date_strf,
        "points": order.get_cart_total,
        "receipt_details": [
        {
            "description": receipt_details['product__name'],
            "amount": "amount_Value"
        }
            ]
        },
        "MessageStream": "outbound"
        })
        headers = {
        'Content-Type': 'application/json',
        'X-Postmark-Server-Token': settings.POSTMARK["TOKEN"]
            }

        response = requests.request("POST", url, headers=headers, data=payload)
        print(response.text)


    if response.status_code == 200:
        print("Response OK, closing order")
        Order.objects.filter(customer= customer , complete = False).update(complete = False)
    else:
        print(response, "Response Error, not closing order")    
   
    return render(request, 'store/checkout_complete.html', {'favorites': favorites,'points': points }) 

解决方案

问题核心是传给Postmark的receipt_details字段结构不符合模板循环的要求,同时代码里存在几处低级错误,按以下步骤修正即可:

  1. 修正数据结构:Postmark模板里的{{#each receipt_details}}要求receipt_details本身是对象数组,数组里的每一个元素对应一行商品,元素内的属性名要和模板里的变量名(product__name、product__point_price)完全一致。你之前的写法要么把整个商品列表塞进了单个数组元素的属性里,要么引用了未定义的变量,结构完全不对。
  2. 修正方法调用错误:order.get_cart_total是类方法,传参时必须加括号order.get_cart_total(),否则传给API的是方法引用而非实际积分值。
  3. 修正订单状态逻辑:支付完成后应该把订单标记为已完成,你当前代码里update(complete = False)等于没修改状态,要改成update(complete = True)。
  4. 消除重复代码:try/except块里重复写了两遍API请求逻辑,很容易出现改漏的问题,把公共参数抽出来统一构造即可。

修正后核心代码

首先构造符合要求的商品明细数组:

# 从查询结果直接组装模板需要的明细数组
receipt_details = []
for item in data_list:
    receipt_details.append({
        "product__name": item["product__name"],
        "product__point_price": item["product__point_price"]
    })

# 初始化模板公共参数
template_model = {
    "counselor_name": "Fellow Counselor",
    "student_name": "test-student",
    "receipt_id": str(order.transaction_id),
    "date": today_date_strf,
    "points": order.get_cart_total(),
    "receipt_details": receipt_details
}

# 能查到对应学生信息就覆盖对应字段
try:
    student = Student.objects.get(email = customer.email)
    counselor = User.objects.get(id = student.counselor_id)
    points = K8RunningPoints.objects.filter(student_ps = student.studentpsid).aggregate(Sum("running_total"))['running_total__sum']
    template_model["student_name"] = student.student_name
    # 这里填辅导员的真实邮箱
    to_email = counselor.email
except Exception as err1:
    print(err1, "Student user not found.")
    points = 0
    # 测试场景下填测试邮箱即可
    to_email = "test@example.com"

# 统一构造请求
url = "https://api.postmarkapp.com/email/withTemplate"
payload = json.dumps({
    # 这里要填Postmark后台验证过的发件人邮箱
    "From": "shop@yourschool.edu",
    "To": to_email,
    "TemplateId": 28132290,
    "TemplateModel": template_model,
    "MessageStream": "outbound"
})
headers = {
    'Content-Type': 'application/json',
    'X-Postmark-Server-Token': settings.POSTMARK["TOKEN"]
}
response = requests.request("POST", url, headers=headers, data=payload)
print(response.text)

# 修正订单状态更新逻辑
if response.status_code == 200:
    print("Response OK, closing order")
    Order.objects.filter(customer= customer , complete = False).update(complete = True)
else:
    print(response, "Response Error, not closing order")

验证要点

  • 发请求前可以打印payload检查结构,确认receipt_details是平铺的对象数组,每个对象都带product__name和product__point_price字段,没有额外嵌套
  • From字段必须是你Postmark账号里已经完成验证的发件签名域名下的邮箱,否则API会返回权限错误
  • 如果需要展示商品数量,直接在组装receipt_details时加入"quanity": item["quanity"]字段,同时在模板对应位置加{{quanity}}变量即可

内容的提问来源于stack exchange,提问作者ZoocMan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 14:54:19