后台和前台基础部分有大量的相似之处,所以我已经讲过的就会一带而过,有疑问可以通过查看后台博客解决疑惑(https://blog.csdn.net/weixin_39561473/article/details/86775213)
1 前台模板修改
这里提供一个我找的前台模板,大家也可以自己去找,每个人审美不同的,我觉得好的,大家不一定觉得。方法都是一样的。
链接:https://pan.baidu.com/s/1ulgLs1SwuIfww_NUx6XYPA
提取码:5yry
- 把静态文件放在前台的app中(buyers)的static下的区分目录下,把网页放在前台app的templates下的区分目录下
- 制作模板
- 修改静态路径
- 在视图和路由中关联所有页面
- 修改跳转链接
1.1 模板制作
观察我给的模板页面,所有的页面都被分为了一下几个部分:
紫色框中内容基本一致,除了title和个别css文件数量不同,橙色框中是
所以将紫色框中的代码抽取出来作为模板,head部分当然要包含所有网页要用的css和 js包。
在templates中新建一个html网页为base.html
然后将共性代码放入base.html,并添加个性定位标记
修改所有的页面,继承模板,套用标记块,以下图方法修改所有页面
1.2 静态链接
修改所有的链接,基本都在模板页,这也体现了模板页的优势,分散了很多地方。注意别漏了,推荐使用Ctrl+R
1.3 在视图和路由中关联所有页面
前台视图文件:
from django.shortcuts import render
def index(request):
return render(request,'buyers/index.html',locals())
def login(request):
return render(request,'buyers/login.html',locals())
def cart(request):
return render(request,'buyers/cart.html',locals())
def products(request):
return render(request,'buyers/products.html',locals())
def product_details(request):
return render(request,'buyers/product-details.html',locals())
# Create your views here.
前台的url就有些不同了,因为大家在访问首页的时候直接输入网址就行,而不用加任何app名子。目前几个页面,除了购物车需要放在buyers app的子路由文件中,其他的页面都可以放在主路由文件中。
主路由文件:
子路由文件:
1.4 修改跳转链接
注意在base也上有很多的按钮,需要修改这些按钮的跳转页为我们设置的路由:
1.5 运行项目
现在就可以运行项目查看效果了
1.6 收集静态文件
为了方便以后部署,之前在做后台富文本编辑器的时候收集过一次,
方法一致,先注释了静态路径,添加静态根路径。
使用收集命令后再还原静态路径,注释静态根路径备用。
按ctrl+alt+r,输入以下命令
如果问你是否覆盖选择yes,收集后还原配置文件:
2 建模
用户建模涉及一下几个表:
- 用户表:用户名,邮箱,密码(目前只需要这些,后期还可以扩充)
- 地址表:收件人,电话,地址,地址所属用户(用户地址可以很多,给父母,给自己买东西地址不同)
- 邮件验证表:验证码,邮箱,时间(注册用户需要通过邮件验证码)
- 购物车表:商品id,商品名称,商品价格,商品照片,商品数量,购买用户
- 订单页面:订单编号,订单时间,订单状态,总计,订单用户,订单地址
- 订单商品:商品id,商品名称,商品价格,商品照片,商品数量,所属订单
在buyers app中model文件中建立模型
from django.db import models
class Buyer(models.Model):
username = models.CharField(max_length = 32)
email = models.EmailField(blank=True, null=True)
password = models.CharField(max_length = 32)
class Address(models.Model):
address = models.TextField()
phone = models.CharField(max_length = 32)
username = models.CharField(max_length = 32)
buyer = models.ForeignKey(Buyer,on_delete = True)
class EmailValid(models.Model):
value = models.CharField(max_length = 32)
email_address = models.EmailField()
times = models.DateTimeField()
class BuyCar(models.Model):
goods_id = models.CharField(max_length=32)
goods_name = models.CharField(max_length=32)
goods_price = models.FloatField()
goods_picture = models.ImageField(upload_to="image")
goods_num = models.IntegerField()
user = models.ForeignKey(Buyer, on_delete=True)
class Order(models.Model):
order_num = models.CharField(max_length=32)
order_time = models.DateTimeField(auto_now=True)
order_statue = models.CharField(max_length=32)
total = models.FloatField()
user = models.ForeignKey(Buyer,on_delete=True)
order_address = models.ForeignKey(Address,on_delete=True)
class OrderGoods(models.Model):
good_id = models.IntegerField()
good_name = models.CharField(max_length=32)
good_price = models.FloatField()
good_num = models.IntegerField()
goods_picture = models.ImageField()
order = models.ForeignKey(Order,on_delete=True)
检查、生成、同步表:
按ctrl+alt+r,输入以下三个命令
check
makemigrations
migarate
成功后表同步成功,可以用pycharm自带的数据库工具进行查看
3 注册与登陆
3.1 注册页面
3.1.1 样式
模板没有提供注册页,所以复制登录页进行修改。
绑定视图函数和路由后,样式变为:
至于邮箱验证注册的方法与逻辑参照我之前的博客(https://blog.csdn.net/weixin_39561473/article/details/86654561)
这里就粗略的写了:
3.1.2 需要导入的所有包:
from django.shortcuts import render,HttpResponseRedirect
from django.core.mail import EmailMultiAlternatives
from django.http import JsonResponse
import random,datetime,time,hashlib
from Buyers.models import *
from EarphoneMall.settings import EMAIL_HOST_USER
3.1.3 密码加密函数:
#加密函数
def lockpw(pw):
md5 = hashlib.md5()
md5.update(pw.encode())
result = md5.hexdigest()
return result
3.1.4 邮箱发送函数:
#发送邮件函数
def sendMessage(request):
result = {"status": "error","data":""}
if request.method=='GET' and request.GET:
receiver = request.GET.get('email')
try:
subject = "耳机商城的邮件"
text_content = ""
value = getRandomData()
html_content = """
<div>
<p>
尊敬的耳机商城用户,您的用户验证码是:%s,打死不要告诉别人。
</p>
</div>
"""%value
message = EmailMultiAlternatives(subject,text_content,EMAIL_HOST_USER,[receiver])
message.attach_alternative(html_content,"text/html")
message.send()
except Exception as e:
result["data"] = str(e)
else:
result["status"] = "success"
result["data"] = "验证码已发送"
e = EmailValid()
e.email_address = receiver
e.value = value
e.times = datetime.datetime.now()
e.save()
finally:
return JsonResponse(result)
发送效果如图:
3.1.5 注册函数:
#注册页面
def register(request):
result = {"status": "error", "data": ""}
if request.method == 'POST' and request.POST:
email = request.POST.get('email')
username = request.POST.get('username')
massage = request.POST.get('massage')
pwd = request.POST.get('password')
db_email = EmailValid.objects.filter(email_address=email).first()
if db_email:
if massage == db_email.value:
now = time.mktime(
datetime.datetime.now().timetuple()
)
db_now = time.mktime(db_email.times.timetuple())
if now - db_now > 86400:
result['data'] = '验证码过期'
db_email.delete()
else:
b = Buyer()
b.username = username
b.email = email
b.password = lockpw(pwd)
b.save()
db_email.delete()
return HttpResponseRedirect('/login/') #注册成功跳转到登录页
else:
result['data'] = '验证码错误'
else:
result['data'] = '邮箱不匹配'
return render(request, 'buyers/register.html', locals())
3.1.6 路由配置:
这样注册页面就完成了,可以自己尝试注册
3.2 登陆页面
前台登陆与后台登陆完全一致,也需要下发cookie和session
3.2.1 修改登陆界面样式
form添加属性,添加csrf防御,提交按钮改为input,链接到注册页面
3.2.2 修改登陆的视图函数
前台登陆逻辑与后台完全一致,不过多加了一个cookies:user-id,这是为了后面绑定用户与商品、购物车关系的。
# 登陆
def login(request):
result = {'statue': 'error', 'date': ''}
if request.POST and request.method == 'POST':
email = request.POST.get('email')
user = Buyer.objects.filter(email=email).first()
if user:
pwd = lockpw(request.POST.get('password'))
if pwd == user.password:
response = HttpResponseRedirect('/') #跳转到首页
response.set_cookie('user_id', user.id, max_age=3600) #下发cookie
response.set_cookie('username', user.username, max_age=3600) #下发cookie
request.session['username'] = user.username #上传session
return response
else:
result['date'] = '密码错误'
else:
result['date'] = '用户名不存在'
return render(request, 'buyers/login.html', locals())
3.2.3 设置cookies
再视图函数上面添加cookie装饰器,这里与后台的也完全一致
#COOKIE验证装饰器
def cookieVerify(fun):
def inner(request,*args,**kwargs):
username = request.COOKIES.get("username")
session = request.session.get("username") #获取session
user = Buyer.objects.filter(username = username).first()
if user and session == username: #校验session
return fun(request,*args,**kwargs)
else:
return HttpResponseRedirect("/login/")
return inner
然后给需要的页面添加cookie验证,例如购物车等,首页不需要,因为你即使不登陆也是可以浏览商品的。
3.3 登出逻辑
3.3.1 登出页面效果
由于模板没有提供登出的按钮,那就只能自己写一个了
对头像按钮进行修改
这里也体现了设置session的好处,通过session有没有进行判断是否用户登陆了,没登陆的时候通过if标签判断,显示登陆按钮,点击后跳转到登录页。而登陆后显示用户名,点击头像后进入用户主页(还没做,先空着)。
这个按钮再base.html页面中
代码如下:
css样式:
<style>
.loginbotton {
font-size: 16px;
width:40px;
display: inline-block;
margin-left: -2px;
margin-top:2px
}
</style>
html代码:
{% if request.session.username %}
<div class="nav-item">
<a href= class="text-primary">
<i class="fas fa-user-circle fa-2x"></i>
{{ request.session.username }}
</a>
<a class="loginbotton" href="">登出</a>
</div>
{% else %}
<div class="nav-item">
<a href="/login/" class="text-primary">
<i class="fas fa-user-circle fa-2x"></i>
<span class="loginbotton" href="">登陆</span>
</a>
</div>
{% endif %}
实现效果如下:
登陆前:
登陆后:
3.3.2 登出逻辑
首先是视图函数
这个与后台的也是一样的,点击登出后删除cookie和session就行了。
#登出
def logout(request):
response = HttpResponseRedirect("/") #登出后跳转至首页
response.delete_cookie("user_id") #删除cookie
response.delete_cookie('username')
del request.session["username"] #删除session
return response
然后绑定路由
页面按钮关联路由
这样登陆登出也实现了。
4 首页、模板页样式修改
4.1 首页
主要是换图和改文字,全是英文很头疼,因为我们收集静态文件的缘故,图片需要放在根目录的静态文件里 ,而不是app的静态文件中。
4.2 模板页
模板页主要是修改英文说明为中文。
例如图中部分
修改完成后:
修改之后会发现,右侧关于耳机类型就是我们存在数据库中额类型表,点击类型后会跳转到该类型的商品页,所以接下来开始制作商品列表页。
5 商品列表与商品详情
5.1 商品列表
由于在模板页我们按照后台的商品类别对商品进行了分类,所以视图函数处理时也需要对商品进行区分,这里就需要用到正则来帮助匹配商品的id了
5.1.1 修改页面的关联的路由
这里需要按照分类的id进行修改,写错了就会类别与商品无法完全对应
修改商品展示页的路由,这时候可以发现,我们的主路由已经包含了非常多的路由了,很不方便进行修改查询,所以我们将商品的路由单独提取出来,并以列表的方式链接:
5.1.2 商品页视图函数
这里还有一点,我对网页做了修改,添加了一个全部商品,并把路由绑在了/0/上:
因为我们并没有id为0的类型,所以视图函数需要对传入的数进行判断,如果为0则取全部商品,如果不为0,按照类型取商品:
视图函数注意以下几点
- 判断点击的是全部商品还是类型商品
- 全部类型因为类型表中是没有的,需要自己写
- 需要通过外链获取到图片
- 因为图片是分离的,所以需要一个列表来容纳每个商品
代码如下:
代码有个小细节,就是返回值里我把local()改成了{‘data’:data,‘type’:type},local返回的是所有变量信息,可是我们用到的只有data这个列表和type,所以可以用字典的形式返回单个或多个变量,而不是所有变量。
#商品列表
def products(request,num):
num = int(num)
if num == 0:
type = {'label':'全部耳机','description':'所有商品,尽情挑选,蓝牙、头戴、入耳、线材应有尽有'}
goods = Goods.objects.all()
else:
type = Types.objects.get(id=num) #取出这个类型的详情
goods = Goods.objects.filter(types=num) #取出这个类型的全部商品
data = []
for i in goods:
img = i.image_set.first().img_path #取出商品的第一张图片路径
data.append({'img':img,'goods':i}) #将每个商品图片和信息的字典写到data列表中
return render(request,'buyers/products.html',{'data':data,'type':type})
5.1.3 商品页页面修改
我删除了好多没用的东西,大家也可以酌情修改,模板就是用来修改的
首先是类型展示在页面上:
修改后效果如下:
商品列表
商品列表使用for循环依次遍历出来
效果如下:
而且点击不同的类型显示的商品也不相同。
5.2 商品详情页
点击商品查看商品详情,所以还是需要使用正则匹配点击的商品。
5.2.1 修改视图函数
既然是详情页,那肯定需要商品的所有信息,还有商品所有图片。
一般详情页还会有商品推荐之类的东西,所以也要获取一些其他商品,模板提供了3个位置,所以从库中取出3个商品用作推广。
这里有个细节,就是我在取三个商品的时候使用了order_by(’-goods_now_price’),order_by是对取出的数据进行排序,排序的依据就是括号里的内容,而 - 代表逆序,也就是我取出的商品按照价钱从高到低取了3个。
#商品详情页
def product_details(request,id):
id = int(id)
goods = Goods.objects.get(id=id)
imgs = goods.image_set.all()
showGoods = Goods.objects.all().order_by('-goods_now_price')[0:3]
data = []
for i in showGoods:
img = i.image_set.first().img_path # 取出商品的第一张图片路径
data.append({'img': img, 'goods': i}) # 将每个商品图片和信息的字典写到data列表中
return render(request,'buyers/product-details.html',locals())
5.2.2 修改路由
5.2.3 修改页面
这个需要修改的地方有点多
方法都是一致的,就是把视图函数获取的数据渲染到页面上,熟能生巧
图片
商品概述修改
详情修改
这里可以自由发挥,我只保留了详情、问答和参数,参数理论上也是保存在数据库中的,方法都是一样的,只是商品表多了一些字段,有兴趣的可以去试试,做到以假乱真。
最底下还有个商品推荐,也就是从视图中取出来的那三个最贵的商品
效果:
最终效果
我还自己修改了很多css样式,这个就是看个人喜好了。
6 购物车
购物车涉及到以下几点
- 数量的修改:增加,减少,手动输入,小计,总计
- 数量修改后后台数据的获取,对数据库的修改
首先,设置模板右侧购物车按钮的路径,跳转至购物车页
6.1 视图函数
@cookieVerify
def cart(request):
userId = request.COOKIES.get('user_id')
buycarGoos = BuyCar.objects.filter(user=userId)
alltotal = 0
data = []
for i in buycarGoos:
goods = Goods.objects.get(id=i.goods_id)
total = i.goods_num * i.goods_price
alltotal += total
data.append({'total':total,'goods':i,'js':goods.goods_id})
return render(request,'buyers/cart.html',locals())
首先需要通过列表将小计与商品对应传入数据库。
6.2 路由
6.3 页面设置
这里就很复杂了,主要的复杂在于三个框数据间的关系,模板提供了点击加减数字发生变化,但是特别坑,占用了name这个属性,所以只能将计就计。
6.3.1 数据渲染
首先解决购物车数据展示在页面
6.3.2 点击增加
由于模板提供了点击增加与减少,但是并没有关联,数量变化和小计总计价钱间的关系,首先找到模板的js文件,global.js
对器逻辑修改,增加小计和总计减的关系:
我添加的所有类名:
修改增加按钮逻辑
在global.js中
$("input[name='qty_plus']").on("click", function(e)
{
e.preventDefault();
// Get the field name
var fieldName = $(this).parent().parent().find("input[name='"+$(this).attr("data-field")+"']");
// Get its current value
var currentVal = parseInt($(fieldName).val());
// If is not undefined
if (!isNaN(currentVal))
{
// Increment
$(fieldName).val(currentVal + 1);
var goods_allprice = $(this).parent().parent().parent().parent().children('td').eq(4).children('strong');
var goods_price = $(this).parent().parent().parent().parent().children('td').eq(2).children('strong').text();
var num = $(this).parent().parent().children('input').val();
goods_allprice.text(Number(goods_price)*Number(num));
var newtoal = Number($('.alltotal').text())+Number(goods_price);
$('.alltotal').text(newtoal);
}
else
{
// Otherwise put a 1 there
$(fieldName).val(1);
}
});
修改减少按钮逻辑
在global.js中
// This button will decrement the value till 1
$("input[name='qty_minus']").on("click", function(e)
{
e.preventDefault();
// Get the field name
var fieldName = $(this).parent().parent().find("input[name='"+$(this).attr("data-field")+"']");
// Get its current value
var currentVal = parseInt($(fieldName).val());
// If it isn't undefined or its greater than 1
if (!isNaN(currentVal) && currentVal > 1)
{
// Decrement one
$(fieldName).val(currentVal - 1);
var goods_allprice = $(this).parent().parent().parent().parent().children('td').eq(4).children('strong');
var goods_price = $(this).parent().parent().parent().parent().children('td').eq(2).children('strong').text();
var num = $(this).parent().parent().children('input').val();
goods_allprice.text(Number(goods_price)*Number(num));
var newtoal = Number($('.alltotal').text())-Number(goods_price);
$('.alltotal').text(newtoal)
}
else
{
// Otherwise put a 1 there
var newtoal = Number($('.alltotal').text())-Number(0);
$('.alltotal').text(newtoal);
$(fieldName).val(1);
}
});
给原有页添加js逻辑,用来进行手动输入数据,小计与总计变化关系
购物车页
<script>
$(function () {
$('.quantity').focus(function () {
num1 = $(this).val();
});
$('.quantity').blur(function () {
var goods_allprice = $(this).parent().parent().parent().children('td').eq(4).children('strong');
var goods_price = $(this).parent().parent().parent().children('td').eq(2).children('strong').text();
var num2 = $(this).val();
if(Number(num2) >= 1){
var num = num2-num1;
goods_allprice.text(Number(goods_price)*Number(num2));
var newtoal = Number($('.alltotal').text())+Number(goods_price)*Number(num);
$('.alltotal').text(newtoal)
}
else {
num2 = $(this).val(1);
var num=1-num1;
goods_allprice.text(Number(goods_price));
var newtoal = Number($('.alltotal').text())+Number(goods_price)*Number(num);
$('.alltotal').text(newtoal)
}
})
})
</script>
6.4 删除按钮
这个很简单
6.4.1 视图函数
增加一个视图函数
#删除购物车函数
@cookieVerify
def delete_car_goods(request,id):
userId = request.COOKIES.get('user_id')
goods = BuyCar.objects.filter(user=int(userId),id=int(id))
goods.delete()
return HttpResponseRedirect("/buyers/cart/")
6.4.2 路由
因为要判断删除的是哪个,所以肯定会有正则
6.4.3 页面链接
6.5 跳转至确认订单页面
我们修改数量之后,在点击确认订单按钮的同时,也需要修改购物车数据库中商品的数量。
这里有一个坑,就是模板的js占用了商品数量的name属性,所以我们不能修改name属性,也就无法区分每个商品
经过我研究,所有的数量input框的name都是quantity,那传到前台会不会是个列表呢,经过debug之后,发现还真是,那就简单多了。
6.5.1 修改页面表单
form属性 提交按钮 还有csrf防御
因为我们点击去提交按钮后会跳转至其他页面,这时候就用到form标签的另一个属性ation了,action就是用作跳转的,我已经写好了确认订单页的路由。
6.5.2 确认订单视图函数
首先需要获取到修改过后的商品数量,传过来的quantity列表中有几个元素,就代表购物车里有几个商品,顺序与数据库的顺序是一致的,所以可以利用这一点,加上for循环递增的i,来依次取出数量,存入数据库。
@cookieVerify
def enterorder(request):
alltotal = 0
data = []
userId = request.COOKIES.get('user_id')
if request.method == 'POST' and request.POST:
countLIST = request.POST.getlist('quantity')
for i in range(0,len(countLIST)):
buycar = BuyCar.objects.filter(user=userId)[i]
buycar.goods_num = countLIST[i]
buycar.save()
total = int(buycar.goods_price)*int(buycar.goods_num)
data.append({'total':total,'goods':buycar})
alltotal += total
return render(request,'buyers/enterorder.html',locals())
6.5.3 路由修改
6.5.4 页面修改
样式:
我把没用的都删除了,邮寄方式目前只是个摆设,在后期版本会补全很多细节功能。
不过点击顺丰或者普通,总价发生变化还是可以通过js实现的:
<script>
$('#sf').click(function () {
var alltotal= $('#alltotal').text();
$('#alltotal').text(Number(alltotal)+15)
});
$('#free').click(function () {
var alltotal= $('#alltotal').text();
$('#alltotal').text(Number(alltotal)-15)
})
</script>
7 确认支付页面
点击确认订单按钮之后,提交页面所有填写的信息,模板没有提供确认支付页面,所以我用确认订单页面修改了一下
7.1 确认支付的视图函数
首先要将购物车里的数据放到订单表中,生成订单单号,还要将购物车中的商品存入订单商品表中,还有邮寄地址也需要存入数据库中。
#确认支付
def enterpay(request):
if request.POST and request.method == 'POST':
alltotal = 0
goods_list = []
userId = request.COOKIES.get('user_id')
#取出购物车中用户确定要购买的商品
buycar = BuyCar.objects.filter(user=userId)
for goods in buycar:
total = int(goods.goods_price) * int(goods.goods_num)
goods_list.append({'total': total, 'goods': goods})
alltotal += total
#把地址存入地址表
address = Address()
address.address = request.POST.get('address')
address.username = request.POST.get('name')
address.phone = request.POST.get('phone')
address.buyer = Buyer.objects.get(id = userId)
address.save()
#在订单表中生成订单
order = Order()
# 订单编号 日期(年月日时分秒) + 随机 + 用户id
now = datetime.datetime.now()
order.order_num = now.strftime("%Y%m%d%H%M%S") + str(random.randint(10000, 99999)) + userId
# 状态 未支付 1 支付成功 2 配送中 3 交易完成 4 已取消 0
order.order_time = now
order.order_statue = 1
order.total = alltotal
order.user = Buyer.objects.get(id = userId)
order.order_address = address
order.save()
#订单商品
for good in goods_list: #循环保存订单当中的商品
g = good["goods"]
g_o = OrderGoods()
g_o.good_id = g.goods_id
g_o.good_name = g.goods_name
g_o.good_price = g.goods_price
g_o.good_num = g.goods_num
g_o.goods_picture = g.goods_picture
g_o.order = order
g_o.save()
return render(request,'buyers/enterpay.html',locals())
7.2 路由设置
7.3 页面设置
修改了以下确认清单页
<!-- Content -->
<div class="content-right">
<div class="container m-t-100 m-b-100">
<div class="row">
<div class="col-sm-12">
<h1 class="text-xs-center">确认订单</h1>
<div class="separator-one"></div>
</div>
</div>
<div class="row m-t-70">
<form>
<div class="col-xs-12 col-sm-6 col-md-7">
<div class="row">
<div class="col-sm-12">
<h3 class="m-tb-30 text-xs-center" style="margin-top: 0" >你的订单</h3>
<p>订单编号:<span>{{ order.order_num }}</span></p>
<table class="table table-hover table-condensed">
<tbody>
{% for goods in goods_list %}
<tr>
<td class="col-sm-2 hidden-xs">
<a href="http://demo.intelcoder.net/staro/checkout.html#" class="thumbnail m-b-0">
<img src="/static/{{ goods.goods.goods_picture }}" alt="">
</a>
</td>
<td>{{ goods.goods.goods_num }} x {{ goods.goods.goods_name }}</td>
<td class="text-right">{{ goods.total }}RMB</td>
</tr>
{% endfor %}
</tbody>
<tfoot>
<tr>
<td colspan="2" class="b-t-0 text-right"><h3 class="m-b-0">总计</h3></td>
<td class="b-t-0 text-right"><h3 id="alltotal" class="m-b-0">{{ order.total }}RMB</h3></td>
</tr>
</tfoot>
</table>
</div>
</div>
</div>
<div class="col-xs-12 col-sm-6 col-md-5">
<div class="row">
<div class="col-sm-12">
<h3 class="m-xs-t-30 m-b-30 text-xs-center">邮寄方式</h3>
<div class="form-group form-group-default">
<div class="radio">
<label style="font-weight: bolder;font-size: 18px;color: black">
普通快递
</label>
<label class="pull-right">免运费</label>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-sm-12">
<h3 class="m-tb-30 text-xs-center">支付方式</h3>
<div class="form-group form-group-default">
<div class="radio">
<label style="font-weight: bolder;font-size: 18px;color: black">
支付宝
</label>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-sm-12">
<h3 class="m-tb-30 text-xs-center">邮寄地址</h3>
<div class="form-group form-group-default required">
<label>姓名</label>
<p style="color: black" class="form-control">{{ address.username }}</p>
</div>
<div class="form-group form-group-default required">
<label>手机号</label>
<p style="color: black" class="form-control">{{ address.phone }}</p>
</div>
<div class="form-group form-group-default required">
<label>地址</label>
<p style="color: black" class="form-control">{{ address.address }}</p>
</div>
</div>
</div>
</div>
<div class="col-xs-12 col-sm-12 col-md-12 text-center">
<div class="separator-two m-b-30"></div>
<a href="/buyers/payVerify/{{ order.id }}/" class="btn btn-success"><i class="fas fa-shopping-bag"></i> 确认支付</a>
<p class="small m-t-20"><a href="/buyers/cart/"><i class="fas fa-angle-left"></i> 取消支付</a></p>
</div>
</form>
</div>
</div>
</div>
<!-- Content -->
效果如下:
7.5 支付
这里说一下 只能用支付宝进行支付目前(沙箱支付宝并不能真的付钱),微信需要营业执照,银联贼麻烦。
支付宝沙箱环境申请:
关于申请支付宝沙箱啥的参考这个(https://blog.csdn.net/lzz781699880/article/details/81045135)我就不多说了。
7.5.1 支付函数
跳转函数需要两个参数,一个是订单号,一个是总价
#支付跳转函数
from alipay import AliPay
def paydata(order_num,count):
alipay_public_key_string = '''-----BEGIN PUBLIC KEY-----
你的沙箱公钥
-----END PUBLIC KEY-----'''
app_private_key_string = '''-----BEGIN RSA PRIVATE KEY-----
你的沙箱密钥
-----END RSA PRIVATE KEY-----'''
alipay = AliPay(
appid="你的沙箱支付宝app的id", # 支付宝app的id
app_notify_url=None, # 回调视图
app_private_key_string=app_private_key_string, # 私钥字符
alipay_public_key_string=alipay_public_key_string, # 公钥字符
sign_type="RSA2", # 加密方法
)
order_string = alipay.api_alipay_trade_page_pay(
out_trade_no=str(order_num),
total_amount=str(count), #将Decimal类型转换为字符串交给支付宝
subject="商贸商城",
return_url=None,
notify_url=None # 可选, 不填则使用默认notify url
)
return "https://openapi.alipaydev.com/gateway.do?" + order_string
7.5.2 调用支付函数
视图函数中
调用刚才的支付跳转函数
#支付
def payVerify(request,num):
order = Order.objects.get(id=int(num))
order_num = order.order_num
order_count = order.total
url = paydata(order_num,order_count)
return HttpResponseRedirect(url)
路由设置
因为需要根据订单id确认订单,所以需要正则
网页路由绑定
效果如下: