DJANGO-天天生鲜项目从0到1-010-购物车-购物车操作页面(勾选+删改)
本项目基于B站UP主‘神奇的老黄’的教学视频‘天天生鲜Django项目’,视频讲的非常好,推荐新手观看学习
https://www.bilibili.com/video/BV1vt41147K8?p=1
购物车页面展示
分析购物车页面需要后台查询的数据有哪些,最基础的是购物车中goods对象,还有一些比如购物车中每个商品的小计金额、所有商品的总数目以及总价。虽然这些信息通过前台js代码也可以进行计算展示,但是最好是后台查询数据时一并计算一起发给前端,减轻前端的计算压力。
class CartView(LoginRequiredMixin, View):
'''购物车视图类'''
template_name = 'cart/cart.html'
def get(self, request):
'''显示购物车'''
user = request.user
# 获取购物车信息
connect = get_redis_connection('default')
cart_key = 'cart_%d'%(user.id)
cart = connect.hgetall(cart_key)
# 返回给前端的goods_list
goods_list = []
total_count = 0
total_amount = 0
for goods_id, count in cart.items():
goods = Goods.objects.get(id=int(goods_id))
# 小计
amount = goods.price * int(count)
# 给goods对象新增属性
goods.amount = amount
goods.count = int(count)
# 将商品添加至列表中
goods_list.append(goods)
# 合计
total_count += int(count)
total_amount += amount
# 组织上下文
context = {
'goods_list': goods_list,
'total_count': total_count,
'total_amount': total_amount,
}
return render(request, self.template_name, context)
对应HTML模板:
{% extends 'base_no_cart.html' %}
{% load static %}
{% block title %}天天生鲜-购物车{% endblock title %}
{% block infoname %}购物车{% endblock infoname %}
{% block body %}
<div class="total_count">全部商品<em>{{ total_count }}</em>件</div>
<ul class="cart_list_th clearfix">
<li class="col01">商品名称</li>
<li class="col02">商品单位</li>
<li class="col03">商品价格</li>
<li class="col04">数量</li>
<li class="col05">小计</li>
<li class="col06">操作</li>
</ul>
<form method="post" action="{% url 'order:place' %}">
{% csrf_token %}
{% for goods in goods_list %}
<ul class="cart_list_td clearfix">
<li class="col01"><input type="checkbox" name="goods_ids" value="{{ goods.id }}" checked></li>
<li class="col02"><a href="{% url 'goods:detail' goods.id %}"><img src="{{ goods.image.url }}"></a></li>
<li class="col03"><a href="{% url 'goods:detail' goods.id %}">{{ goods.name }}<br><em>{{ goods.price }}元/{{ goods.uom }}</em></a></li>
<li class="col04">{{ goods.uom }}</li>
<li class="col05">{{ goods.price }}元</li>
<li class="col06">
<div class="num_add">
{% csrf_token %}
<a href="javascript:;" class="add fl">+</a>
<input type="text" goods_id="{{ goods.id }}" class="num_show fl" value="{{ goods.count }}">
<a href="javascript:;" class="minus fl">-</a>
</div>
</li>
<li class="col07">{{ goods.amount }}元</li>
<li class="col08"><a href="javascript:;" class="delete">删除</a></li>
</ul>
{% endfor %}
<ul class="settlements">
<li class="col01"><input type="checkbox" name="" checked=""></li>
<li class="col02">全选</li>
<li class="col03">合计(不含运费):<span>¥</span><em>{{ total_amount }}</em><br>共计<b>{{ total_count }}</b>件商品</li>
<li class="col04"><input type="submit" value='去结算'/></li>
</ul>
</form>
{{ errmsg }}
{% endblock body %}
{% block endfiles %}
<script src="{% static 'js/jquery-1.12.4.min.js'%}"></script>
<script>
//全选按钮
$('.settlements').find(':checkbox').change(function(){
//获取全选checkbox的全选状态
is_checked = $(this).prop('checked')
//遍历设置商品的checkbox
$('.cart_list_td').find(':checkbox').each(function(){
$(this).prop('checked', is_checked)
})
//刷新总价格和总数量
update_page_info()
}) //单个CheckBox监听
$('.cart_list_td').find(':checkbox').change(function(){
//获取全部商品的数目
all_count = $('.cart_list_td').length
//获取被选中商品的数目
checked_count = $('.cart_list_td').find(':checked').length
//判断两者数目是否相等,不相等则设置全选为未选中,否则设置为选中
$('.settlements').find(':checkbox').prop('checked', all_count == checked_count)
//更新总数量和价格
update_page_info()
}) //加号点击事件
$('.add').click(function(){
count = update_count($(this).next(), 1)
goods_id = $(this).next().attr('goods_id')
//发送ajax请求
send_ajax_change(goods_id, count)
}) //减号点击事件
$('.minus').click(function(){
count = update_count($(this).prev(), -1)
goods_id = $(this).prev().attr('goods_id')
//发送ajax请求
send_ajax_change(goods_id, count)
}) //手动修改数量
$('.num_show').blur(function(){
//获取数量
count = $(this).val()
if(isNaN(count) || count.trim().length==0 || parseInt(count) <=0){
count = 1
$(this).val(parseInt(count))
}
//提交ajax请求
goods_id = $(this).attr('goods_id')
count = parseInt(count)
send_ajax_change(goods_id, count)
}) //删除按钮
$('.delete').click(function(){
//获取商品所在的ul元素
goods_ul = $(this).parents('.cart_list_td')
//获取商品id
goods_id = goods_ul.find('.num_show').attr('goods_id')
send_ajax_delete(goods_id)
//移除界面数据
goods_ul.remove()
//刷新总数量和总价格
update_page_info()
}) //数量加减
function update_count(num_show, num){
//获取原数量
count = parseInt(num_show.val())
//计算新数量
count += num
if (count <= 0){
count = 1
}
//重新设置数量
num_show.val(count)
return count
} //更新选中商品的总价格和总数量
function update_page_info(){
var total_amount = 0
var total_count = 0
$('.cart_list_td').find(':checked').parents('ul').each(function(){
count = parseInt($(this).find('.num_show').val())
amount = parseFloat($(this).find('.col07').text())
total_count += count
total_amount += amount
})
//更新总件数和价格
$('.settlements').find('em').text(total_amount.toFixed(2))
$('.settlements').find('b').text(total_count)
} //发送ajax请求,修改购物车信息
function send_ajax_change(goods_id, count){
csrf = $('input[name="csrfmiddlewaretoken"]').val()
parameter = {
'goods_id': goods_id,
'count': count,
'csrfmiddlewaretoken': csrf
}
$.post('change/', parameter, function(data){
//回调函数
if(data.status == 'S'){
//刷新全部商品件数
$('.total_count').children('em').text(data.total_count)
//刷新小计,找到传进来的goods_id对应的那一行input,找到其父节点下的小计节点
amount = parseInt(data.amount)
$('input[goods_id='+parameter.goods_id+']').parents('.cart_list_td').children('.col07').text(amount.toFixed(2)+'元')//刷新总数量和总价格
update_page_info()
}
else{
alert(data.errmsg)
//将数量重置为改变前
$('input[goods_id='+parameter.goods_id+']').val(data.count)
}
})
} //发送ajax请求,删除商品
function send_ajax_delete(goods_id){
csrf = $('input[name="csrfmiddlewaretoken"]').val()
parameter = {
'goods_id': goods_id,
'csrfmiddlewaretoken': csrf
}
$.post('delete/', parameter, function(data){
if(data.status == 'S'){
//刷新总数量
total_count = parseInt(data.total_count)
$('.total_count').children('em').text(total_count)
}else{
alert(data.errmsg)
}
})
}
</script>
{% endblock endfiles%}
全选框和勾选框
以下Js代码知识点基于jquery
1. 获取子标签可通过find和children,对应的获取父标签为parents和parent:
.find('selector'),遍历当前元素集合中每个元素的后代。不管是多少代后代。如find('元素名')、find('#id')、find('.class')、find(':type')等
.children(selector),遍历当前元素集合中的第一代后代,只有儿子辈。如children('元素名')、children('#id')、children('.class')、children(':type')等
2. 勾选框的勾选和取消动作对应的是change方法
3. 获取标签的属性可通过attr 与 prop获取:
在jQuery中attr
表示HTML文档节点属性,而prop
则表示JS对象属性。
例如,下面这个 HTML 元素:<input type="text" value="Name:">
拥有两个 attributes。
一旦浏览器解析该代码,HTMLInputElement 对象就会被创建,并且该对象会拥有很多 properties,如:accept、accessKey、align、alt、attributes、autofocus、baseURI、checked、childElementCount、ChildNodes、children、classList、className、clientHeight 等等。
对于某个 DOM 节点对象,properties 是该对象的所有属性,而 attributes 是该对象对应元素(标签)的属性。当一个 DOM 节点为某个 HTML 元素所创建时,其大多数 properties 与对应 attributes 拥有相同或相近的名字。
但是对于表单元素的checked
、selected
、disabled
等属性,attribute的checked、selected、disabled就是表示该属性初始状态的值,property的checked、selected、disabled才表示该属性实时状态的值(值为true或false)。因此得使用prop()
函数来设置或获取checked
、selected
、disabled
等属性
//更新选中商品的总价格和总数量
function update_page_info(){
var total_amount = 0
var total_count = 0
$('.cart_list_td').find(':checked').parents('ul').each(function(){
count = parseInt($(this).find('.num_show').val())
amount = parseFloat($(this).find('.col07').text())
total_count += count
total_amount += amount
})
//更新总件数和价格
$('.settlements').find('em').text(total_amount.toFixed(2))
$('.settlements').find('b').text(total_count)
}
//全选按钮
$('.settlements').find(':checkbox').change(function(){
//获取全选checkbox的全选状态
is_checked = $(this).prop('checked')
//遍历设置商品的checkbox
$('.cart_list_td').find(':checkbox').each(function(){
$(this).prop('checked', is_checked)
})
//刷新总价格和总数量
update_page_info()
})
//单个CheckBox监听
$('.cart_list_td').find(':checkbox').change(function(){
//获取全部商品的数目
all_count = $('.cart_list_td').length
//获取被选中商品的数目
checked_count = $('.cart_list_td').find(':checked').length
//判断两者数目是否相等,不相等则设置全选为未选中,否则设置为选中
$('.settlements').find(':checkbox').prop('checked', all_count == checked_count)
//更新总数量和价格
update_page_info()
})
实时修改购物车中商品数量
以下Js代码知识点基于jquery:
1. .next()获取下一标签,.prev()获取上一标签
2. input框的失去焦点时会调用触发.blur()方法
3. 校验数量时:
isNaN(x):如果 x 是特殊的非数字值 NaN(或者能被转换为这样的值),返回的值就是 true。如果 x 是正常的数字,则返回 false。NaN,该值表示一个非法的数字
.trim().length:去掉空格后的长度
parseInt(string, radix):将字符串解析为数字,只有字符串中的第一个数字组合会被返回。parseInt("10"); //返回 10 parseInt("10.56"); //返回 10
4. 在django中提交ajax的post请求时,也需要经过django自带的CSRF认证,可以在html模板代码中随意位置(习惯写在触发ajax请求的按钮或者链接上)加上{% CSRF_TOKEN%},加上了之后,浏览器将其解析hidden的input代码:
<input type="hidden" name="csrfmiddlewaretoken" value="9BdYDF3097s9UGgYJ6L8aGmqkL9DJCgMZehs5xT4Ebdrlvdg74KMXuEvcQpksO6C">
通过
csrf = $('input[name="csrfmiddlewaretoken"]').val()
获取值,并在post参数中加入 'csrfmiddlewaretoken': csrf,注意这里的key值'csrfmiddlewaretoken'名字固定不能更改
5. toFixed() 方法可把 Number 四舍五入为指定小数位数的数字。
//加号点击事件
$('.add').click(function(){
count = update_count($(this).next(), 1)
goods_id = $(this).next().attr('goods_id')
//发送ajax请求
send_ajax_change(goods_id, count)
}) //减号点击事件
$('.minus').click(function(){
count = update_count($(this).prev(), -1)
goods_id = $(this).prev().attr('goods_id')
//发送ajax请求
send_ajax_change(goods_id, count)
})
//手动修改数量
$('.num_show').blur(function(){
//获取数量
count = $(this).val()
if(isNaN(count) || count.trim().length==0 || parseInt(count) <=0){
count = 1
$(this).val(parseInt(count))
}
//提交ajax请求
goods_id = $(this).attr('goods_id')
count = parseInt(count)
send_ajax_change(goods_id, count)
})
//数量加减
function update_count(num_show, num){
//获取原数量
count = parseInt(num_show.val())
//计算新数量
count += num
if (count <= 0){
count = 1
}
//重新设置数量
num_show.val(count)
return count
}
//发送ajax请求,修改购物车信息
function send_ajax_change(goods_id, count){
csrf = $('input[name="csrfmiddlewaretoken"]').val()
parameter = {
'goods_id': goods_id,
'count': count,
'csrfmiddlewaretoken': csrf
}
$.post('change/', parameter, function(data){
//回调函数
if(data.status == 'S'){
//刷新全部商品件数
$('.total_count').children('em').text(data.total_count)
//刷新小计,找到传进来的goods_id对应的那一行input,找到其父节点下的小计节点
amount = parseInt(data.amount)
$('input[goods_id='+parameter.goods_id+']').parents('.cart_list_td').children('.col07').text(amount.toFixed(2)+'元')//刷新总数量和总价格
update_page_info()
}
else{
alert(data.errmsg)
//将数量重置为改变前
$('input[goods_id='+parameter.goods_id+']').val(data.count)
}
})
}
新增CartChangeView变更购物车商品数量
class CartChangeView(View):
'''购物车改变视图'''
def post(self, request):
user = request.user
context = {
'status': 'E',
'errmsg': ''
}
# 判断是否登录
if not user.is_authenticated:
context['errmsg'] = '用户未登录!'
return JsonResponse(context) # 获取数据
goods_id = request.POST.get('goods_id')
count = request.POST.get('count') # 校验数据
if not all([goods_id, count]):
context['errmsg'] = '数据不完整!'
return JsonResponse(context)
# 商品是否存在
try:
goods = Goods.objects.get(id=int(goods_id))
except Goods.DoesNotExist:
context['errmsg'] = '商品不存在!'
return JsonResponse(context)
# 数量格式是否正确
try:
count = int(count)
except Exception as e:
context['errmsg'] = '数量格式不正确!'
return JsonResponse(context) # 更新redis数据库
connect = get_redis_connection('default')
cart_key = 'cart_%d'%(user.id)
# 校验库存
if count > goods.onhand:
context['errmsg'] = '库存不足!'
# 返回原数量
context['count'] = int(connect.hget(cart_key, goods_id))
return JsonResponse(context) # 更新值
connect.hset(cart_key, goods_id, count) # 重新计算总件数
total_count = 0
values = connect.hvals(cart_key)
for val in values:
total_count += int(val)
# 上下文
context['amount'] = goods.price * count
context['total_count'] = total_count
context['status'] = 'S'
return JsonResponse(context)
删除购物车中商品
以下Js代码知识点基于jquery:
1. .remove() 方法删除被选元素及其子元素。
//删除按钮
$('.delete').click(function(){
//获取商品所在的ul元素
goods_ul = $(this).parents('.cart_list_td')
//获取商品id
goods_id = goods_ul.find('.num_show').attr('goods_id')
send_ajax_delete(goods_id)
//移除界面数据
goods_ul.remove()
//刷新总数量和总价格
update_page_info()
})
//发送ajax请求,删除商品
function send_ajax_delete(goods_id){
csrf = $('input[name="csrfmiddlewaretoken"]').val()
parameter = {
'goods_id': goods_id,
'csrfmiddlewaretoken': csrf
}
$.post('delete/', parameter, function(data){
if(data.status == 'S'){
//刷新总数量
total_count = parseInt(data.total_count)
$('.total_count').children('em').text(total_count)
}else{
alert(data.errmsg)
}
})
}
新增CartDeleteView删除购物车商品
class CartDeleteView(View):
'''购物车删除视图'''
def post(self, request):
user = request.user
context = {
'status': 'E',
'errmsg': ''
}
if not user.is_authenticated:
context['errmsg'] = '用户未登录!'
return JsonResponse(context)
# 接收数据
goods_id = request.POST.get('goods_id') # 校验数据
try:
goods_id = int(goods_id)
goods = Goods.objects.get(id=goods_id)
except Exception as e:
context['errmsg'] = '商品不存在'
return JsonResponse(context) # 删除购物车数据
connect = get_redis_connection('default')
cart_key = 'cart_%d'%(user.id)
connect.hdel(cart_key, goods_id) # 重新获取商品数量
vals = connect.hvals(cart_key)
total_count = 0
for val in vals:
total_count += int(val) # 上下文
context['total_count'] = total_count
context['status'] = 'S'
return JsonResponse(context)
DJANGO-天天生鲜项目从0到1-010-购物车-购物车操作页面(勾选+删改)的更多相关文章
- django天天生鲜项目
.后台admin管理天天生鲜商品信息 models里 from django.db import modelsfrom tinymce.models import HTMLField #需要pip安装 ...
- DJANGO-天天生鲜项目从0到1-007-首页静态化与缓存
本项目基于B站UP主‘神奇的老黄’的教学视频‘天天生鲜Django项目’,视频讲的非常好,推荐新手观看学习 https://www.bilibili.com/video/BV1vt41147K8?p= ...
- python 天天生鲜项目
python 天天生鲜项目 django版:https://github.com/Ivy-1996/fresh flask版:https://github.com/Ivy-1996/flask-fre ...
- Django之天天生鲜项目
准备工作 1.配置settings.py内置文件 注意: AUTH_USER_MODEL配置参数要在第一次迁移数据库之前配置,否则可能django的认证系统工作不正常 2.创建应用 3.配置主路由 一 ...
- DJANGO-天天生鲜项目从0到1-012-订单-用户订单页面
本项目基于B站UP主‘神奇的老黄’的教学视频‘天天生鲜Django项目’,视频讲的非常好,推荐新手观看学习 https://www.bilibili.com/video/BV1vt41147K8?p= ...
- DJANGO-天天生鲜项目从0到1-011-订单-订单提交和创建
本项目基于B站UP主‘神奇的老黄’的教学视频‘天天生鲜Django项目’,视频讲的非常好,推荐新手观看学习 https://www.bilibili.com/video/BV1vt41147K8?p= ...
- DJANGO-天天生鲜项目从0到1-009-购物车-Ajax实现添加至购物车功能
本项目基于B站UP主‘神奇的老黄’的教学视频‘天天生鲜Django项目’,视频讲的非常好,推荐新手观看学习 https://www.bilibili.com/video/BV1vt41147K8?p= ...
- DJANGO-天天生鲜项目从0到1-009-搜索功能实现(django-haystack+whoosh+jieba)
本项目基于B站UP主‘神奇的老黄’的教学视频‘天天生鲜Django项目’,视频讲的非常好,推荐新手观看学习 https://www.bilibili.com/video/BV1vt41147K8?p= ...
- DJANGO-天天生鲜项目从0到1-006-首页-内容展示
本项目基于B站UP主‘神奇的老黄’的教学视频‘天天生鲜Django项目’,视频讲的非常好,推荐新手观看学习 https://www.bilibili.com/video/BV1vt41147K8?p= ...
随机推荐
- 黎活明8天快速掌握android视频教程--15_采用Pull解析器解析和生成XML内容
1.该项目主要有下面的两个作用 (1)将xml文件解析成对象的List对象,xml文件可以来自手机本地,也可以来自服务器返回的xml数据 (2)强list对象保存成xml文件,xml保存到手机的内存卡 ...
- SpringBoot——项目启动时读取配置及初始化资源
介绍 在开发过程中,我们有时候会遇到非接口调用而出发程序执行任务的一些场景,比如我们使用quartz定时框架通过配置文件来启动定时任务时,或者一些初始化资源场景等触发的任务执行场景. 方法一:注解 ...
- No configuration file found and no output filename configured via Cli option.报错
webpack手动配置webpack.config.js文件,打包时出现的报错,可以试试这种解决方案 报错如下: No configuration file found and no output f ...
- win7旗舰版安装 oracle 10g 不能进入图形界面的问题
前阵子重装了系统,把dell机器自带的win7 64位(家庭版已升级旗舰版,装ORACLE正常)换回了32位系统,前两天因为一些软件开发的问题,需要把以前做的一个系统重新架起来,数据库用的是oracl ...
- 2020 最新省市区 sql
一个基于有赞的 area.js 生成的sql area.js 简单的写了一个js 生成了sql语句 sql文件 完整代码
- 《UNIX环境高级编程》(APUE) 笔记第十一章 - 线程
11 - 线程 Github 地址 1. 线程概念 典型的 UNIX进程 可以看成只有一个 控制线程 :一个进程在某一时刻只能做一件事情.有了 多个控制线程 ,就可以把进程设计成在某一时刻能够做不止一 ...
- ORA-04063: package body "DBSNMP.BSLN_INTERNAL" has errors
ORA-04063: package body "DBSNMP.BSLN_INTERNAL" has errors 问题描述: 警告日志出现报错: Sun Jun 21 00:00 ...
- LeetCode 第 196 场周赛 (题目:5452-5455,这是参加过最坑的周赛,暴力n^2居然可以过)
5452. 判断能否形成等差数列 给你一个数字数组 arr . 如果一个数列中,任意相邻两项的差总等于同一个常数,那么这个数列就称为 等差数列 . 如果可以重新排列数组形成等差数列,请返回 tru ...
- Azure Web App (一)发布你的Net Core Web 项目
一,引言 今天我们看一下Azure上的一个服务-----Web 应用,我们都知道云计算的三大模式:Iaas(基础设施即服务),Paas(平台即服务),Saas(软件即服务). Iass,其实就是虚拟主 ...
- css3 文本行的斑马线
背景知识 CSS 渐变, background-size ,“条纹背景”,“灵活的背景定位 难题 几年前,在刚刚获得 :nth-child() / :nth-of-type() 伪类之后,我们最常用其 ...