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字段结构不符合模板循环的要求,同时代码里存在几处低级错误,按以下步骤修正即可:
- 修正数据结构:Postmark模板里的
{{#each receipt_details}}要求receipt_details本身是对象数组,数组里的每一个元素对应一行商品,元素内的属性名要和模板里的变量名(product__name、product__point_price)完全一致。你之前的写法要么把整个商品列表塞进了单个数组元素的属性里,要么引用了未定义的变量,结构完全不对。 - 修正方法调用错误:
order.get_cart_total是类方法,传参时必须加括号order.get_cart_total(),否则传给API的是方法引用而非实际积分值。 - 修正订单状态逻辑:支付完成后应该把订单标记为已完成,你当前代码里
update(complete = False)等于没修改状态,要改成update(complete = True)。 - 消除重复代码: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
相关产品推荐
相关产品推荐

