P94 用户中心——订单页面
本来更新商品库存实在添加商品之后,在使用乐观锁的时候将其提到前面来
# todo: 返回受影响的行数res, 0为失败
res = GoodsSKU.objects.filter(id=sku_id, stock=orgin_stock).update(stock=new_stock, sales=new_sales) # 乐观锁
if res == 0: # 返回0表示更新失败
if i == 2: # 尝试到第三次
transaction.savepoint_rollback(save_id)
return JsonResponse({'res': 8, 'errmsg': '下单失败2'})
continue
# TODO: 向df_order_goods表中添加一条记录
OrderGoods.objects.create(order=order,
sku=sku,
count=count,
price=sku.price
)
假如将其放在添加一条记录之后,如下:
# TODO: 向df_order_goods表中添加一条记录
OrderGoods.objects.create(order=order,
sku=sku,
count=count,
price=sku.price
)
# todo: 返回受影响的行数res, 0为失败
res = GoodsSKU.objects.filter(id=sku_id, stock=orgin_stock).update(stock=new_stock, sales=new_sales) # 乐观锁
if res == 0: # 返回0表示更新失败
if i == 2: # 尝试到第三次
transaction.savepoint_rollback(save_id)
return JsonResponse({'res': 8, 'errmsg': '下单失败2'})
continue
分析会出现的情况,假如依旧以两个用户同时买一件商品,库存数量为3的场景进行分析。
点击完之后有一次一定是成功的,此时把这条记录往表中加了一次 ,第二次做尝试,成功之后又加了一次,这是下单成功,表中的信息重复了这一单在表中添加了两次。
因此需要将乐观锁放到前面,如果没有下单成功则不会往表中添加信息,此时不会出现重复。这就是为什么将更新放到添加记录的前面,防止尝试的时候内容是重复的。
在提交订后,目前place_order.html中的js代码部分是打印了创建成功。
这里在订单提交成功之后,会弹出一个弹窗,写着订单提交成功,紧接着3s之后跳转到一个页面,
在这个页面我们就可以让它跳转到用户中心的提交订单页面,在下单成功之后,我们就会让你跳转到用户中心的提交订单页面《---这个页面对应的地址是/user/order
{% block bottom %}
<div class="popup_con">
<div class="popup">
<p>订单提交成功!</p>
</div>
<div class="mask"></div>
</div>
{% endblock bottom %}
$.post('/order/commit', params, function (data) {
// 进行处理
if (data.res == 5){
{#alert('订单创建成功')#}
localStorage.setItem('order_finish',2);
$('.popup_con').fadeIn('fast', function() {
setTimeout(function(){
$('.popup_con').fadeOut('fast',function(){
window.location.href = '/user/order/1';
});
},3000)
});
}
else{
alert(data.errmsg)
}
})
这里进行重新下单测试,在测试之前,将之前进行测试的订单信息都删除
select * from df_order_info;
select * from df_order_goods;
在清除这两个表中数据的时候,先删除df_order_goods中的数据,再删除df_order_info中的数据,否则会报外键错误《---有外键约束
delete from df_order_goods;
delete from df_order_info;
此时两个表均为空,重新下订单
下单之后会提示订单成功,3s之后跳转到了用户中心--全部订单页面,在做用户模块时,仅做了展示。
该页面需要的信息
订单的创建时间,订单的编号,订单的状态,实付订单总额,以及订单中每个商品的信息,包括数目以及小计,除此之外,信息是分页显示的获取第几页的内容,需要告知页码,此时去修改URL
在user.urls中
path('order/(?P<page>\d+)', UserOrderView.as_view(), name='order') # 用户中心-订单页
在user/views.py中的UserOrderView捕获参数,接收页码page
# /user/order
class UserOrderView(LoginRequiredMixin, View):
"""用户中心-订单页"""
def get(self, request, page):
# 获取用户的订单信息
return render(request, 'df_user/user_center_order.html')
在base.html和用户中心页base_user_center.html中修改{% url 'user:order' 1 %},默认是获取第一页的订单信息
在views.py中要拿到的内容
首先需要将用户中所有的内容查出来
from django.core.paginator import Paginator
# /user/order
class UserOrderView(LoginRequiredMixin, View):
"""用户中心-订单页"""
def get(self, request, page):
# 获取用户的订单信息
# 先获取用户的信息
user = request.user
# 获取该用户的所有订单
orders = OrderInfo.objects.filter(user=user)
# 页面中还需要订单商品的信息
# 遍历获取订单商品的信息
for order in orders:
# 根据order_id查询订单商品信息
# order_skus是一个查询集,遍历出来的每个内容都是一个OrderGoods,在OrderGoods中包含属性count和price,在显示时还有一个商品的小计,可以通过这两个属性计算得到小计
order_skus = OrderGoods.objects.filter(order_id=order.order_id)
# 可以遍历order_skus计算商品的小计
for order_sku in order_skus:
# 计算商品的小计
amount = order_sku.count * order_sku.price
# 动态给order_sku增加属性amount,保存订单商品的小计
order_sku.amount = amount
# <--此时订单order_sku中的小计都计算出来了
# 这里处理支付状态标题*
# 动态给order增加属性, 保存订单状态标题
order.status_name = OrderInfo.ORDER_STATUS[order.order_status]
# 除此之外,订单商品的查询集是根order相关的,也要把它保存起来
# 方式:动态给order增加属性,保存订单商品的信息
order.order_skus = order_skus
# <--当遍历完之后,页面需要的所有信息都获取到了
# 当查询完之后需要进行分页
# 对orders进行分页,每页显示一条信息
paginator = Paginator(orders, 1)
# 接下来就是获取第xx页的内容
# 页码通过get传过来
# 先要判断其是否合法
# 处理页码
try:
page = int(page)
except Exception as e:
page = 1
if page > paginator.num_pages or page <= 0:
page = 1
# 获取第page页的Page实例对象
order_page = paginator.page(page)
# todo: 进行页码的控制,页面上最多显示5个页码
# 1. 总数不足5页,显示全部
# 2. 如当前页是前3页,显示1-5页
# 3. 如当前页是后3页,显示后5页
# 4. 其他情况,显示当前页的前2页,当前页,当前页的后2页
num_pages = paginator.num_pages
if num_pages < 5:
pages = range(1, num_pages)
elif page <= 3:
pages = range(1, 6)
elif num_pages - page <= 2:
pages = range(num_pages-4, num_pages+1)
else:
pages = range(page-2, page+3)
# 组织上下文
context = {'order_page': order_page,# pager对象
'pages': pages, # 页面范围控制
'page': 'order' # 告诉我们显示的是用户中心的哪一个页面
}
# 使用模板
return render(request, 'df_user/user_center_order.html', context)
处理user_center_order.html
注意点1:
<td width="15%">{{ order.total_price |add:order.transit_price }}(含运费:{{ order.transit_price }})元</td>
注意点2:支付状态展示的是序号,要展示状态则在order/models.py的OrderInfo中定义字典:
ORDER_STATUS = {
1: '待支付',
2: '待发货',
3: '待收货',
4: '待评价',
5: '已完成'
}
# 这里处理支付状态标题*<----已经在views.py中处理,动态增加属性
# 动态给order增加属性, 保存订单状态标题
order.status_name = OrderInfo.ORDER_STATUS[order.order_status]
在页面上展示的时候
<td width="15%">{{ order.status_name }}</td>
刷新页面,展示成功
重新下一单,订单提交成功,在进行跳转的时候报错
需要去修改place_order.html中的跳转地址'/user/order/1'
此时会跳转到第一页,之前的订单
要显示最新的订单在user.views.UserOrderView中获取orders是按照创建时间倒叙排序
orders = OrderInfo.objects.filter(user=user).order_by('-create_time')
此时最新的订单页面展示的是最新订单。
P95 订单支付——支付宝简介
此时订单页面显示完成,接下来应该是点击【去付款】,进行支付操作。
这里介绍支付宝的支付
支付宝开放平台
https://open.alipay.com/platform/home.htm
P96 订单支付——网站对接支付宝流程图
此时用户中心可以显示用户的全部订单,当用户点击去付款的时候,就会去付款,付款的时候就会访问我们的网站
这里的【去付款】应该写我们网站的地址,点击去付款是向我们的网站发起一个请求,网站再去调用alipay的接口
/order/pay代表订单支付,在网站对应的后台,一个地址就应该有一个对应的处理函数。
在视图函数中我们就知道用户要进行付款了,在我们函数内部与支付宝平台进行对接,他是要支付,所以我们在这里面调用支付宝的一个接口alipay.trade.page.pay【PC场景下单并支付】,帮助我们生成一个支付的订单页面。
在生成支付的订单时,我们需要传递一个参数过去包括三个必传的,和两个可选的
订单id
总金额
订单标题
return_url
notify_url
这两个URL是django网站的地址
传入这些参数之后,支付宝平台会帮你生成一个支付订单,在处理完成之后,最终会返回一个应答
这个应答:返回一个支付页面的地址
拿到支付页面的地址之后给客户用,在客户的浏览器上要把页面显示出来。
因此拿到支付页面的地址有一个过程,该过程就是把用户引导到页面,让用户的浏览器能够打开支付页面。
接下来用户到支付页面登录账号,输入支付密码,这一过程和我们的网站无关,我们负责将用户引导至支付页面,下面是用户和支付宝的交互:用户登录支付宝,输入支付密码,点击确认支付。
当用户和支付宝进行交互的时候,我们无法确认用户是否支付完毕《---这里考虑上面提到的两个URL:return_url和 notify_url
只要用户付完款之后一点击确认支付,传过去的参数中传过去地址return_url,那么支付宝的平台在用户支付完成之后就会马上同步访问网站的return_url,并传递参数,告诉网站用户支付的结果《---此为同步访问
异步访问网站的notify_url,并传递参数,告诉网站用户支付的结果《---此为异步访问
支付宝在传递支付结果的时候是以异步通知为准
网站如果想让支付宝平台访问,需要有公网的IP,单纯的127.0.0.1支付宝无法访问。
这里没有公网IP的情况下,如何获取支付的结果?
目前我们可以访问支付宝,但是支付宝无法访问我们的网站。
这里有个alipay.trade.query统一收单线下交易查询接口,虽然return_url和 notify_url这两个地址不起作用,但是我们获取支付结果的话可以通过网站自己去访问支付宝,调用支付宝的接口alipay.trade.query,这个接口在处理完之后就会返回用户支付的结果
PS:最后5min是精髓《--总结整个支付流程
P97 订单支付——订单支付代码
目前支付流程已经明朗,如果我去付款的话,访问网站的地址/order/pay需不需要传参数?
需要传递订单id,用户要支付的是哪个订单,需要把订单id传过来,这一步涉及到传参数:订单id
传递参数的方式分析:
用户点击去付款,需要给django传递参数,因为用户支付完之后,会有一个支付的交易编号,我们最终要把支付的交易编号保存到表中,所以这里采用post。
在django网站中调用支付宝接口alipay.trade.page.pay,原生的参数需要自己调用,自己传参,这里有一个SDK的Python包:
https://github.com/fzlee/alipay/blob/master/README.zh-hans.md
别人封装好的,已经生成预付订单需要使用的签名
安装
pip3 install python-alipay-sdk --upgrade
在代码中使用包
截图:采用网络请求方式
生成自己电脑的公钥和私钥
使用openssl
openssl
OpenSSL> genrsa -out app_private_key.pem 2048 # 私钥 gen生成rsa代表算法 -out代表输出一个秘钥文件,输出到app_private_key.pem中
OpenSSL> rsa -in app_private_key.pem -pubout -out app_public_key.pem # 导出公钥 -in代表输入 输入刚才生成的私钥文件的内容,对它进行输出,输出到app_public_key.pem公钥文件中《---生成一个与私钥文件对应的公钥文件
OpenSSL> exit
# 查看私钥内容
cat app_private_key.pem
# 查看公钥内容
cat app_public_key.pem
接下来去沙箱环境中设置公钥,拷贝刚才生成的公钥内容到沙箱中
之后会看到有:查看支付宝公钥,将该公钥保存到项目的文件中
该公钥与订单相关,在订单下新建文件,
order/alipay_public_key.pem<---将支付宝的公钥放到文件该文件中,代表支付宝的公钥
注:这里在文件的开头和结尾加上-----BEGIN PUBLIC KEY-----和-----END PUBLIC KEY-----
加密还需要电脑的私钥,将其获取到,放入order文件夹下order/app_private_key.pem
请求的时候使用自己的私钥加密,收到信息之后使用支付宝的公钥解密
创建初始化对象
appid=""<---沙箱中的APPid
# notify_url
# 应用文件中私钥的路径
# 支付宝的公钥路径
# 签名的类型、算法RSA或RSA2
#debug=False代表不是调试模式,即访问的是实际环境地址,如果是沙箱的话,要改成True
# 电脑网站支付:需要调用alipay.trade.page.pay接口,
这里帮我们封装了一个函数,函数名为alipay.api_alipay_trade_page_pay接口(按照命名转化规则进行转化),该方法是上面初始化对象的一个方法,该方法内部帮我们调用支付宝的支付接口,传参:订单id、总金额、订单标题、return_url、notify_url,最终会返回一个order_string。电脑网站支付,需要跳转到xxx页面,该页面就是支付页面。
以上为支付的使用,接下来分析在views中订单支付的视图该如何实现
# 对应地址: /order/pay
# 请求方式:ajax post
# 前端传递的参数:订单id(order_id)
class OrderPayView(View):
'''订单支付'''
def post(self, request):
'''订单支付'''
# 关于订单支付,同样的当用户未登录时不能支付
# 校验用户是否登录
user = request.user
if not user.is_authenticated():
return JsonResponse({'res': 0, 'errmsg': '用户未登录'})
# 接收参数 订单id
order_id = request.POST.get('order_id')
# 校验参数
# 先判断有没有订单id,处理没有订单id的情况
if not order_id:
return JsonResponse({'res': 0, 'errmsg': '无效的订单id'})
# 查询一下订单id是不是确实存在
# 订单id到底是不是该用户的
# 支付结果需要对接支付宝,这里判断支付方式到底是不是支付宝
# 查看支付状态
# 满足以上条件且订单确实存在,则说明该订单为有效订单
try:
order = OrderInfo.objects.get(order_id=order_id,
user=user,
pay_method=3,
order_status=1)
except OrderInfo.DoesNotExist:
return JsonResponse({'res': 2, 'errmsg': '订单错误'})
# 业务处理:使用Python sdk调用支付宝的支付接口
# 使用我们安装的包
# 先去初始化一个对象
alipay = AliPay(...)
# 调用对象的方法---调用支付接口
# 电脑支付接口《---拿过来并传入必要的参数
# 电脑网站支付,需要跳转到https://openapi.alipay.com/gateway.do? + order_string<---实际地址
# https://openapi.alipaydev.com/gateway.do? + order_string<---沙箱地址
# 我们在模型类中使用的类型是Decimal,这里拿过来的也是Decimal类型
total_pay = order.total_price + order.transit_price
# 直接放在这里的话,内部都会将数据转化成json,Decimal是不能被序列化的,如果直接写Decimal是会报错的
# 这里将其直接转化成字符串
order_string = alipay.api_alipay_trade_page_pay(
out_trade_no="order_id, # 订单id
total_amount=str(total_pay), # 支付总金额,应该等于总金额+运费,在上面进行计算
subject='天天生鲜%s' %order_id, # 标题
return_url=None,
notify_url=None # 可选, 不填则使用默认notify url
)
# 返回应答
pay_url = 'https://openapi.alipaydev.com/gateway.do?' + order_string
# 最终返回数据,将地址带回去
# 在代码中可以将用户引导到该地址pay_url
return JsonResponse({'res': 3, 'pay_url': pay_url})
# 业务处理:使用Python sdk调用支付宝的支付接口
# 使用我们安装的包
# 导入包
from alipay import AliPay
# 进行初始化
# 关于path,借助settings.py以及os
from django.conf import settings
import os
# app_private_key_string = os.path.join(settings.BASE_DIR, 'apps/order/app_private_key.pem')
# alipay_public_key_string = os.path.join(settings.BASE_DIR, 'apps/order/alipay_public_key.pem')
alipay = AliPay(
appid="2016092700608687", # 应用id《---沙箱中的id
app_notify_url=None, # 默认回调url《---这里是127.0.0.1,所以即使传了支付宝也访问不了,这里不能写空 '',必须写None,代表不传
app_private_key_string=app_private_key_string,
# 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
alipay_public_key_string=alipay_public_key_string,
sign_type="RSA2", # RSA 或者 RSA2《---官网推荐RSA2
debug=True # 默认False, 此处沙箱模拟True
)
# 整个过程:初始化之后,开始调用接口,最终要把支付页面地址返回
对应的地址是/order/pay,进行URL配置
path('pay', OrderPayView.as_view(), name='pay') # 订单支付
# 至此后端结束
# 分析前端user_center_order.html
在页面上点击去付款,会访问支付宝支付页面地址,这里也有可能显示:已完成、待评价,这样就不应该访问该地址,所以需要进行相应的判断。《--根据支付状态
这里应该绑定【去付款】出class="oper_btn"的点击事件
{% load static %} 《---导入js
{% block bottomfiles %}
<script src="{% static 'js/jquery-1.12.4.min.js' %}"></script>
<script>
$('.oper_btn').click(function () {
// 点击之后,有可能不需要支付
// 获取status,进行判断,在对应的元素下面自定义属性status
status = $(this).attr('status')
// 获取订单id,在对应的元素下面自定义属性order_id
order_id = $(this).attr('order_id')
if (status == 1){
// 进行支付
csrf = $('input[name="csrfmiddlewaretoken"]').val()
// 组织参数
params = {'order_id': order_id, 'csrfmiddlewaretoken': csrf}
// 发起ajax post请求, 访问/order/pay, 传递参数:order_id
$.post('{% url 'order:pay' %}', params, function (data) {
if (data.res == 3){
// 引导用户到支付页面 如果使用location订单页面就找不到了,重新打开页面使用 window.open
window.open(data.pay_url)
// 浏览器访问order/check,获取支付交易结果
// ajax post 传递参数:order_id
$.post('/order/check', params, function () {
if (data.res == 3){
alert('支付成功')
//重新加载
location.reload()
}
else{
alert(data.errmsg)
}
})
}
else{
alert(data.errmsg)
}
})
}
else if (status == 4){
//其他情况
// 跳转到评价页面
location.href = "/order/comment/"+order_id
}
})
</script>
{% endblock bottomfiles %}
P93 订单支付——获取支付结果
点击去支付,会跳转到支付宝的支付页面,要登录账号
进入沙箱找到支付的账号
在支付页面输入用户密码并进行付款,付款成功之后在用户的页面上会出现付款成功,但是目前我们的网站还不知道用户已经付款。这个是支付宝给用户看的,我们的网站并不知道结果,因为我们没有提供地址return_url、notify_url,支付宝无法告知我们是支付结果,但是网站又想获取支付结果,所以需要自己去调用支付宝的交易查询接口alipay.trade.query,获取用户支付结果,最终告诉用户是否支付成功,给用户显示支付结果。
如果用户的浏览器不访问我们的页面,我们就无法访问它,不能告诉用户支付结果。
用户如果不访问我们,便无法告诉用户支付结果。这里需要再添加一部分,需要让用户的浏览器去访问我们的网站,查询支付结果到底有没有成功。
【用户浏览器,访问/order/check获取交易支付结果】《----用户只要进行支付操作,就将交易结果显示给用户。
只要把用户引导到支付页面,写一段js代码,让他往服务器发一段请求,发请求的目的就是为了获取支付结果
// 浏览器访问/order/check,获取支付交易的结果《---考虑这里需不需要传参数?
// ajax post 传递参数:order_id
在回调函数对应data,data中应该告知我们有没有支付成功,根据data的返回结果告知用户该订单支付有没有成功
编写对应的视图函数,在初始化之前都与查询支付结果一致,最后调用的支付宝接口不同
# 请求方式: ajax post
# 前端请求的参数: 订单id(order_id)
# order/pay
class OrderPayView(View):
"""订单支付"""
def post(self, request):
# 用户是否登录
user = request.user
if not user.is_authenticated:
return JsonResponse({'res': 0, 'errmsg': '用户未登录'})
# 接收参数
order_id = request.POST.get('order_id')
# 校验参数
if not order_id:
return JsonResponse({'res': 1, 'errmsg': '无效的订单'})
try:
order = OrderInfo.objects.get(order_id=order_id,
user=user,
pay_method=3,
order_status=1)
except OrderInfo.DoesNotExist:
return JsonResponse({'res': 2, 'errmsg': '订单错误'})
# 业务处理:使用python sdk调用支付宝的查询接口
# alipay初始化
app_private_key_string = open("apps/order/app_private_key.pem").read()
alipay_public_key_string = open("apps/order/alipay_public_key.pem").read()
# app_private_key_string == """
# -----BEGIN RSA PRIVATE KEY-----
# base64 encoded content
# -----END RSA PRIVATE KEY-----
# """
#
# alipay_public_key_string == """
# -----BEGIN PUBLIC KEY-----
# base64 encoded content
# -----END PUBLIC KEY-----
# """
# app_private_key_string = os.path.join(settings.BASE_DIR, 'apps/order/app_private_key.pem')
# alipay_public_key_string = os.path.join(settings.BASE_DIR, 'apps/order/alipay_public_key.pem')
alipay = AliPay(
appid="2016092700608687", # 应用id
app_notify_url=None, # 默认回调url
app_private_key_string=app_private_key_string,
# 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
alipay_public_key_string=alipay_public_key_string,
sign_type="RSA2", # RSA 或者 RSA2
debug=True # 默认False, 此处沙箱模拟True
)
# 调用支付宝的交易查询接口
while True:
response = alipay.api_alipay_trade_query(order_id)
# response = {
# "trade_no": "2017032121001004070200176844", # 支付宝交易号
# "code": "10000", # 接口调用是否成功
# "invoice_amount": "20.00",
# "open_id": "20880072506750308812798160715407",
# "fund_bill_list": [
# {
# "amount": "20.00",
# "fund_channel": "ALIPAYACCOUNT"
# }
# ],
# "buyer_logon_id": "csq***@sandbox.com",
# "send_pay_date": "2017-03-21 13:29:17",
# "receipt_amount": "20.00",
# "out_trade_no": "out_trade_no15",
# "buyer_pay_amount": "20.00",
# "buyer_user_id": "2088102169481075",
# "msg": "Success",
# "point_amount": "0.00",
# "trade_status": "TRADE_SUCCESS", # 支付结果
# "total_amount": "20.00"
# }
# 查询接口调用是否成功
code = response.get('code')
# 接口调用是否成功 && 支付是否成功
if code == '10000' and response.get('trade_status') == 'TRADE_SUCCESS':
# 支付成功
# 获取支付宝交易号
trade_no = response.get('trade_no')
# 更新订单状态
order.trade_no = trade_no
order.order_status = 4 # 待评价
order.save()
# 返回应答
return JsonResponse({'res': 3, 'message': '支付成功'})
elif code == '40004' or (code == '10000' and response.get('trade_status') == 'WAIT_BUYER_PAY'):
# 等待买家付款
import time
time.sleep(5)
continue
else:
# 支付出错
return JsonResponse({'res': 4, 'errmsg': '支付失败'})
注:当code返回值为'40004' 时,需要进行处理
处理js
$.post('/order/check', params, function () {
if (data.res == 3){
alert('支付成功')
//重新加载
location.reload()
}
else{
alert(data.errmsg)
}
})
配置order/chexk的URL,进行测试
P98 订单评论代码走读
目前页面已经可以查询支付结果,页面上已经是待评价,但是后面依旧显示【去付款】,此时应该显示【待评论】,点击之后会跳转到相应的评论页面。查看源代码可以看到,此时td中的自定义属性status=4,这部分可以通过在页面中添加js,根据状态status的不同,改成不同的内容。
评论的代码:遍历每个获取到的a标签,遍历的时候要获取到自定义属性status,
如果status=1,则设置为去支付,
如果status=4,则设置为去评价,
如果status=5,则设置为已完成。
订单评论URL的设置:
path('comment/(?P<order_id>.+)', CommentView.as_view(), name='comment') # 订单评价
下面如果用户去点击它,不同的状态对应不同的处理
{% block bottomfiles %}
<script src="{% static 'js/jquery-1.12.4.min.js' %}"></script>
<script>
$('.oper_btn').each(function () {
// 获取status
status = $(this).attr('status')
if (status == 1){
$(this).text('去支付')
}
else if (status == 4){
$(this).text('去评价')
}
else if (status == 5){
$(this).text('已完成')
}
})
$('.oper_btn').click(function () {
// 获取status
status = $(this).attr('status')
// 获取订单id
order_id = $(this).attr('order_id')
if (status == 1){
// 进行支付
csrf = $('input[name="csrfmiddlewaretoken"]').val()
// 组织参数
params = {'order_id': order_id, 'csrfmiddlewaretoken': csrf}
// 发起ajax post请求, 访问/order/pay, 传递参数:order_id
$.post('{% url 'order:pay' %}', params, function (data) {
if (data.res == 3){
// 引导用户到支付页面
window.open(data.pay_url)
// 浏览器访问order/check,获取支付交易结果
// ajax post 传递参数:order_id
$.post('/order/check', params, function () {
if (data.res == 3){
alert('支付成功')
//重新加载
location.reload()
}
else{
alert(data.errmsg)
}
})
}
else{
alert(data.errmsg)
}
})
}
else if (status == 4){
//其他情况
// 跳转到评价页面
location.href = "/order/comment/"+order_id
}
})
</script>
{% endblock bottomfiles %}
测试js代码无响应时:检查元素--》network
评价页面的地址:/order/comment/order_id
点击去评价---》跳转到对应的页面 /order/comment/order_id,去路由系统中查找对应的views函数。
分析评价页面显示内容:
基本信息和订单页面显示基本一致,只需 在下方添加一个评论的输入框
view中的类视图:【get基与订单页面显示时的处理过程一致,无需详述】
class OrderCommentView(LoginRequiredMixin, View):
def get(self, request, order_id):
"""展示评论页"""
...
在页面order_comment.html中,前面商品显示和订单页面一致,在下方每个商品对应一个评论的输入框,评论框中定义了一个元素input隐藏域
name="sku_{{ forloop.counter }}"
该元素对应的值:评论的时候需要接收是哪一个商品的评论,隐藏域对应的值是跟这个商品关联的商品id。商品对应的评论内容对应在输入的文本框中,文本框中对应的内容是 name="content_{{ forloop.counter }}"
将其通通放置在表单中,在表单中还包含了两个隐藏域
其一是订单id,你需要对那个订单中的商品信息进行评论,在其中写一个隐藏域为 name="order_id",其值为订单id---》value="{{ order.order_id }}"
<input type="hidden" name="order_id" value="{{ order.order_id }}">
以及订单中有几个商品total_count,其值为订单中跟订单关联的商品的长度,即订单中商品的个数
<input type="hidden" name="total_count" value="{{ order.order_skus|length }}">
用户输入完后点击提交,订单中的商品都会被提交。
点击提交之后,代表向当前地址发起一个post请求,在对应的视图中会有一个对应的post函数,处理评论内容
在该部分,首先要在地址中获取到订单的id,获取到之后进行数据校验,订单中有几个商品就应该对应多少评论,将total_count值进行接收,接收之后就可以获取到评论一共有多少条。
# 循环获取订单中商品的评论内容《---注:每个商品对应的名字是不一样的,通过forloop.counter1234往下数,遍历几次就应该有几个,在处理的时候也应该通过遍历拿到对应的值,每找到一个就应该获取商品的id,以及对应的评论内容,将其赋值给order_goods的comment,并做一个更新。
如果获取到了所有的订单商品的评论且进行了更新,则将该订单的状态设置为已完成,进行更新,最终将页面跳转到订单页面
拿到数字之后需要循环获取商品的评论,从1到total_count+1,获取到的 第一次就是1,在循环中获取被评论的商品id,名字对应的值是商品的id,获取到值之后即可获取到这是哪一条商品的评论。获取到评论内容sku_1对应下面的content_1,其余同理。没有内容则返回空。
根据order和sku_id查询到order_goods中,并将评论内容写到对应的order_goods.comment中,如果出错,则continue,继续看下一个。如果查到了,则需要将order_goods中的内容修改为接收到的content,并进行保存。
进行测试:下单,评论
此时商品详情页的评论部分应该会显示评论内容(此时在商品介绍下面显示的,进行修改),在detail.html页面写一段js代码
此时对应的元素中添加id="tag_detail",id="tag_comment"
<ul class="detail_tab clearfix">
<li class="active" id="tag_detail">商品介绍</li>
<li id="tag_comment">评论</li>
</ul>
<div class="tab_content" id="tab_detail">
<dl>
<dt>商品详情:</dt>
<dd>{{ sku.goods.detail|safe }}</dd>
</dl>
</div>
# style="display: none;默认不显示
<div class="tab_content" id="tab_comment" style="display: none;">
<dl>
{% for sku in sku_orders %}
<dt>评论时间: {{ sku.update_time }} 用户名:{{ sku.order.user.username }}</dt>
<dd>评论内容: {{ sku.comment }}</dd>
{% empty %}
暂时没有评论信息
{% endfor %}
</dl>
</div>
商品介绍和评论只显示一个,且点击哪一个,哪一个应该处于激活状态
class='active'
// 商品介绍与评论tag的切换
$('#tag_detail').click(function () {
$('#tag_comment').removeClass('active')
$(this).addClass('active')
$('#tab_detail').show()
$('#tab_comment').hide()
})
$('#tag_comment').click(function () {
$('#tag_detail').removeClass('active')
$(this).addClass('active')
$('#tab_comment').show()
$('#tab_detail').hide()
})