Django 18购物商城项目
1、支付功能完善
1.1、向支付宝支付页面传递支付信息
order_detail.html (把get请求改成了post请求)
<form method="POST" enctype="multipart/form-data" action="{% url 'axf:alipay' %}">
{% csrf_token %}
<input name="orderid" type="hidden" value="{{ order.id }}">
<button type="submit" orderid="{{ order.id }}" id="alipay" class="btn btn-success btn-block">支付</button>
</form>
order_detail.js (直接post请求,不需要点击事件了,所以里面的内容都注解了)
1.2、pay.py修改
因为视图里需要用到AliPay的相关信息,所以把这部分数据单独封装成了一个函数,下面是pay.py里面的全部内容。
(同样注意,这里的两个密钥用你自己的,不要复制我的,复制我的用不了的)
from alipay import AliPay
def pay(order_id, price, subject, return_url=None, notify_url=None):
# 发起支付请求
order_string = my_alipay().api_alipay_trade_page_pay(
out_trade_no=order_id, # 订单号,多次请求不能一样
total_amount=str(price), # 支付金额
subject=subject, # 交易主题
return_url=return_url,
notify_url=notify_url
)
# return_url支付成功后 - 重定向到自己的网站
# notify_url支付成功后 - 异步发送支付结果到回调地址(地址需是服务器地址,否则无法接收到回调结果)
# return_url中最终需要返回 "success" 字符给支付宝,否则支付宝将一直请求该地址并发送回调结果
# notify_url支付前首先执行的,
return "https://openapi.alipaydev.com/gateway.do?" + order_string
def my_alipay():
alipay_public_key_string = """-----BEGIN PUBLIC KEY-----
MIfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDIgHnOn7LLILlKETd6BFRJ0GqgS2Y3mn1wMQmyh9zEyWlz5p1zrahRahbXAfCfSqshSNfqOmAQzSHRVjCqjsAw1jyqrXaPdKBmr90DIpIxmIyKXv4GGAkPyJ/6FTFY99uhpiq0qadD/uSzQsefWo0aTvP/65zi3eof7TcZ32oWpwIDAQAB
-----END PUBLIC KEY-----"""
app_private_key_string = """-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQCgaYHnBs4xmVUeJ06Wncybr4OOu0svQbD/g7kfud2aMPoB3UETNWs0lxu2CtzvdlUtLFSQSI2QfVeB2fVULILUzyh0HCHMa11/hdAuXBYOTP4Xhe5QlGUTNpBJjqLKsFxLc3PmVptHHRtHlhfdF4NOLdbPfBEB8Fw2oyTo2RhbwIDAQABAoGBAJRXvbemBXy8rYhLFVQX7aVztBeEgMzc1RAWAlaijZoP/MNIlutqlQ93Rjsc5J/WMIKr4i/jyHZ7GoOQGaedDmgORxhw2ji29vgKllsuHzAD5/JzFLIr4T1N//bU6gb7DbTml7dHyEHDLg/nsPVZFmKNg7h3SudPMnpobNPfi4eBAkEA3pTFkd7yzs60Bl559y2UHwYmMFPxmgNJspsVtynv4LS+Fb5a4xDJrMeAjJt16UGE4Xm1669jd5ReckHD7ddUTwJBALh/Lh1BYEYdTXANtIjSPOvKh7Jgc44aQ3nCQP92imwmvI3zIVlnXEXIVVNc1k6dzA1Ppgj8jBMAuLYItn+uOOECQQDU4oIcxJqDRpxUwyPwT/2ttpnr+z3HSoHAfChG6atuxjBQZ6JSLwpVYPMIiOA72tiXN2vSIgwGoTe8HD6jSyJtAkAEtmrtIGBfKhxyQkdcP1KDC1dP/Rq2hIE4uPeEDvkWLh8e2Rj++Z7nwWg8iuCGfY1awbASBrFlQt10+OAAfujBAkBJHaKdVyVmdK9Xqdb4IOopO511kOSIQBZwPnfgkFdBpRrPF+Bt4ROxBi9hP5UwKosONAhi508L5QZDgkgPht2w
-----END RSA PRIVATE KEY-----"""
# 实例化支付应用
alipay = AliPay(
appid="20161021007289",
app_notify_url=None,
app_private_key_string=app_private_key_string,
alipay_public_key_string=alipay_public_key_string,
sign_type="RSA",
)
return alipay
1.3、路由、视图
路由
视图
# 跳转去支付页面
def alipay(request):
if request.method == "POST":
order_id = request.POST.get("orderid")
print(order_id)
order = Order.objects.get(pk=order_id)
O_price = order.O_price
O_user = order.O_user.u_name
return redirect(pay(order_id,O_price, O_user, return_url='http://127.0.0.1:8000/axf/payreturn'))
# 支付成功后执行
def pay_return(request):
# 支付成功后的回调函数 -- 重定向自己的网站
# 同时在重定向之前会校验此次支付信息是否正确
params = request.GET.dict()
# 获取字典里的'sign'的值,然后移除这个数据
sign = params.pop('sign', None)
alipay = my_alipay()
status = alipay.verify(params, sign) # 返回 True or False
if status:
order_id = params.get('out_trade_no')
order = Order.objects.get(pk=order_id)
order.O_status = ORDER_STATUS_NOT_SEND
order.save()
data = {
'status': 200,
}
# 支付成功
return redirect(reverse('axf:mine'))
return HttpResponse('支付失败')
1.4、下单支付测试截图
成功支付,并把订单状态信息存入数据库
2、订单页面返回按钮
base_order.html
base_order.css
/*目前此css为止所有内容*/
header {
height: 1.5rem;
width: 100%;
background: yellow;
margin-bottom: 0.3rem;
text-align: center;
line-height: 1.5rem;
position: fixed;
z-index: 10;
border-top: 1px solid #f0f0f0;
}
.back {
background: url(/static/img/back.png) no-repeat;
background-size: 0.6rem;
margin-top: 0.5rem;
margin-left: 0.6rem;
height: 1rem;
z-index: 100;
position: fixed;
}
.back > span {
margin-top: 0.9rem;
margin-left: 0.6rem;
}
效果
3、收货地址
3.1、创建全国地名表
数据下载地址:https://pan.baidu.com/s/1ZqAjTw9FznavKGiy3Wqtog
提取码:roa7
里面有单表和多表,我们仅需要单表数据
3.1.1、建表pro_city_area
模型(mondels.py)
# 全国地名表
class City(models.Model):
id = models.IntegerField(primary_key=True, max_length=11)
name = models.CharField(max_length=40)
pid = models.IntegerField(max_length=11)
class Meta:
db_table = 'pro_city_area'
迁移:
python manage.py makemigrations
python manage.py migrate
3.1.2、插入城市信息数据
把表里所有的数据复制进去并运行(仅复制插入语句)
插入后查看表
3.1.2、表的结构
- pid
省级城市:pid为0
市级:pid除以10000为整数
区级:pid除以100为整数 - id
省级id = 市级pid+省序号(万位上加)
市级id = 区级pid+市序号(百位上加)
区级id = 区级pid+区序号(个位上加)
3.1.3、查询(测试城市能否正确展示并记录)
查表思路:
查询 | 思路 |
---|---|
所有省级 | 直接查pid=0的 |
对应的市级城市有哪些 | 省级的id=市级的pid |
对应的区级城市有哪些 | 市级的id=区级的pid |
3.2、城市级联查询的实现
order_addr.html(新建的,注意路径)
{% extends "bese_order.html" %}
{% load static %}
{% block ext_css %}
{{ block.super }}
<link rel="stylesheet" href="{% static 'axf/order/css/order_addr.css' %}">
{% endblock %}
{% block ext_js %}
{{ block.super }}
<script type="text/javascript" src="{% static 'axf/order/js/order_addr.js' %}"></script>
{% endblock %}
{% block footer %}
<div class="title">
<a href="#" onclick="javascript:history.back(-1);">
<div class="arrow-box nav-left">
返回
</div>
</a>
<p class="font">编辑收货地址</p>
</div>
{% endblock %}
{% block content %}
<div class="container">
<form method="POST" action="{% url 'axf:order_addrPCA' %}" class="bs-example bs-example-form" role="form">
{% csrf_token %}
<div class="input-group">
<span class="input-group-addon">收货人:</span>
<input name="order_name" type="text" class="form-control" placeholder="">
</div>
<br>
<div class="input-group">
<span class="input-group-addon">电 话:</span>
<input nam="order_tell" type="text" class="form-control" placeholder="">
</div>
<br>
<div class="input-group">
<span class="input-group-addon">收货时间:</span>
<input name="oder_time" type="text" class="form-control" placeholder="1">
<span class="input-group-addon">小时后收货</span>
</div>
<br>
<div class="input-group">
<span class="input-group-addon">收货地址:</span>
<select id="p">
<option value="1">请选择</option>
</select>
<select id="c">
<option value="1">请选择</option>
</select>
<select id="a">
<option value="1">请选择</option>
</select>
</div>
<br>
<div class="input-group">
<span class="input-group-addon">详细地址:</span>
<input id="addr_dail" name="order_detail" type="text" class="form-control" placeholder="">
</div>
<br>
<div>
<span id="checkbox">默认地址<input name="is_default" type="checkbox"></span>
</div>
<br>
<br>
<button type="submit" class="btn btn-success btn-block">保存</button>
</form>
</div>
{% endblock %}
order_addr.css(新建的)
.title {
width: 100%;
height: 50px;
background-color: #009688;
}
.font {
color: #fff;
line-height: 50px;
font-size: 20px;
text-indent: -2em;
text-align: center
}
.nav-left {
float: left;
}
.arrow-box{
width: 50px;
height: 26px;
position: relative;
border-radius: 10% 10%;
background: #fff;
text-align: center;
line-height: 26px;
top: 12px;
font-size: 14px;
left: 10px;
}
.container {
padding: 2rem 0.5rem;
overflow: auto;
height: 100%;
width: 100%;
position: fixed;
}
.input-group-addon{
font-size: 10px;
}
#p,#c,#a{
width: 33.3%;
padding: 6px 0px;
font-size: 10px;
font-weight: normal;
line-height: 1;
color: #555;
text-align: center;
background-color: #eee;
border: 1px solid #000000;
border-radius: 4px;
}
input[type="checkbox"]{
margin-left: 0.2rem;
margin-top: 0;
height: 0.5rem;
width: 0.5rem;
}
#checkbox{
float: right;
margin-right: 0.4rem;
}
order_addr.js(新建的,级联查询实现代码重点在里面)
$(function () {
var $p = $('#p');
var $c = $('#c');
var $a = $('#a');
// 点击p时获取省级城市名单
$.get("/axf/PCA/", {"p": 0}, function (data) {
console.log(data['ps_all']);
var ps_all = data['ps_all'];
for (var i = 0; i < ps_all.length; i++) {
$p.append('<option value="' + ps_all[i][0] + '">' + ps_all[i][1] + '</option>');
$c.hide();
$a.hide();
}
});
// p发生变化就移除c、a中的值,并获取新的
$p.change(function () {
if ($("#p option:selected").val() == 1) {
$c.hide();
$a.hide();
$("#addr_dail").val('');
} else {
$c.children().not(':eq(0)').remove();
$c.show();
$a.children().not(':eq(0)').remove();
$a.hide();
c_pid = $("#p option:selected").val();
console.log(c_pid);
// 返回选中数据
$.get("/axf/PCA/", {"c_pid": c_pid}, function (data) {
var cs_all = data['cs_all'];
for (var i = 0; i < cs_all.length; i++) {
$c.append('<option value="' + cs_all[i][0] + '">' + cs_all[i][1] + '</option>');
p_dail = $("#p option:selected").text();
$("#addr_dail").val(p_dail);
}
});
}
});
// c发生变化就移除a中的值,并获取新的
$c.change(function () {
if ($("#c option:selected").val() == 1) {
$a.hide();
$("#p option:selected").val();
$("#addr_dail").val(p_dail);
} else {
$a.children().not(':eq(0)').remove();
a_pid = $("#c option:selected").val();
console.log(a_pid);
// 返回选中数据
$.get("/axf/PCA/", {"a_pid": a_pid}, function (data) {
var as_all = data['as_all'];
if (as_all.length > 0) {
$a.show();
for (var i = 0; i < as_all.length; i++) {
$a.append('<option value="' + as_all[i][0] + '">' + as_all[i][1] + '</option>');
p_dail = $("#p option:selected").text();
c_dail = $("#c option:selected").text();
$("#addr_dail").val(p_dail + c_dail);
}
}
});
}
});
// a发生变化时,将p\c\a中的城市地名设为详细地址的值
$a.change(function () {
if ($("#a option:selected").val() == 1) {
$("#p option:selected").val();
$("#c option:selected").val();
$("#addr_dail").val(p_dail+ c_dail);
} else {
p_dail = $("#p option:selected").text();
c_dail = $("#c option:selected").text();
a_dail = $("#a option:selected").text();
$("#addr_dail").val(p_dail + c_dail + a_dail);
}
});
});
路由:
视图:
# p省级城市、c市级城市、a区县级城市
def order_addrPCA(request):
data = {}
if request.method == "GET":
return render(request, 'order/oder_addr.html', context=data)
else:
return render(request, 'order/oder_addr.html', context=data)
# oder_addr.html的异步请求
def PCA(request):
data = {}
if request.GET.get("p"):
ps = City.objects.filter(pid=0)
ps_all = []
for p in ps:
temp = []
temp.append(p.id)
temp.append(p.name)
ps_all.append(temp)
data['ps_all'] = ps_all
elif request.GET.get("c_pid"):
cs = City.objects.filter(pid=request.GET.get("c_pid"))
cs_all = []
for c in cs:
temp1 = []
temp1.append(c.id)
temp1.append(c.name)
cs_all.append(temp1)
data['cs_all'] = cs_all
elif request.GET.get("a_pid"):
a_s = City.objects.filter(pid=request.GET.get("a_pid"))
as_all = []
for a in a_s:
temp2 = []
temp2.append(a.id)
temp2.append(a.name)
as_all.append(temp2)
data['as_all'] = as_all
return JsonResponse(data)