Django框架实现可运营电商网站(二)-- 前台部分

26 篇文章 3 订阅
20 篇文章 0 订阅

文章目录

后台和前台基础部分有大量的相似之处,所以我已经讲过的就会一带而过,有疑问可以通过查看后台博客解决疑惑(https://blog.csdn.net/weixin_39561473/article/details/86775213)

1 前台模板修改

这里提供一个我找的前台模板,大家也可以自己去找,每个人审美不同的,我觉得好的,大家不一定觉得。方法都是一样的。
链接:https://pan.baidu.com/s/1ulgLs1SwuIfww_NUx6XYPA
提取码:5yry

  1. 把静态文件放在前台的app中(buyers)的static下的区分目录下,把网页放在前台app的templates下的区分目录下
  2. 制作模板
  3. 修改静态路径
  4. 在视图和路由中关联所有页面
  5. 修改跳转链接

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 }} &nbsp;x&nbsp; {{ 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>&nbsp; 确认支付</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确认订单,所以需要正则
在这里插入图片描述

网页路由绑定

在这里插入图片描述
效果如下:
在这里插入图片描述

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值