P72 商品详情页js代码 购物车模块
1.添加商品到购物车
在详情页面上有一个加入商品到购物车,处理增加与减少部分的js,点击事件 在商品的详情页detail.html中去定义js
# 计算商品的总价格 <script type="text/javascript"> # 进行调用 update_goods_amount() # 定义点击事件 function update_goods_amount() { # 获取商品的单价和数量 # 计算商品的总价 # 设置商品的总价 } # 增加和减少还需要处理 # 增加商品的数量
# 减少商品的数量
# 手动输入商品的数量 </script>
# 获取商品的单价和数量 页面过来之后需要计算一下总价格,价格×数量即为总价格 # show_pirze是它的class,em是它的子标签,标签中的内容即为单价 price = $('.show_pirze').children('em').text() # num_show是它的class,value值为它的数量 count = $('.num_show').val()
此时拿到的price和count都是字符串,要进行数字转化
# price是小数 price = parseFloat(price) # count是整数 count= parseInt(count)
# 此时计算总价 amount = price * count
# 设置商品的总价格 # 总价这里有个类class为total # 在设置的时候需要注意:整数和小数相乘之后,小数点的位数可能不是两位 # 设置的时候需要将其设置为两位的小数,amount.toFixed(2)将小数转换成字符串,并且将小数保留几位数字,比如这里保留两位,则是2,最后加上元 $('.total').children('em').text(amount.toFixed(2) + '元')
################################## 要先获取原有的数目,加1之后,再做设置 $('.add').click(function() { # 获取商品原有的数目 count = $('.num_show').val() # 点击增加之后,该值count值加1 count = parseInt(count)+1 # 重新设置商品的数目 $('.num_show').val(count) # 此时总价也要变,更新商品的总价 update_goods_amount() })
################################## 要先获取原有的数目,加1之后,再做设置 $('.minus').click(function() { # 获取商品原有的数目 count = $('.num_show').val() # 点击增加之后,该值count值加1 count = parseInt(count)-1
# 不能减少到负数,这里需要加一步判断 if (count <= 0) { count = 1 } # 重新设置商品的数目 $('.num_show').val(count) # 此时总价也要变,更新商品的总价 update_goods_amount() })
################################## # 手动输入商品的数量 blur()在失去焦点的时候执行一个方法 $('.num_show').blur(function (){ # 获取用户输入的数量 #this即为输入框 count = $(this).val() # 校验count是否合法,三种不合法的情况 # case1: 如果返回的不是数字则isNaN(count)会返回真 # case2: 有可能输入了一堆空格,把获取到的内容去除两边的空格,看它的长度是多少 # js中去除空格的函数:count.trim(),取其长度count.trim().length # case3: 有可能输入小于等于0的数字 if (isNaN(count) || count.trim().length == 0 || parseInt(count) <= 0){ count = 1 } # 重新设置商品的数目 # 处理小数的情况,使用parseInt强制转换成整数 $(this).val(parseInt(count)) # 更新商品的总价 update_goods_amount() })
P73 购物车记录添加后台view
选择完商品之后,点击“加入购物车”,页面不跳转,需要请求地址 添加商品到购物车: 点击“加入购物车”,详情页不动,此时如何将请求发出去? 1、请求方式,采用ajax(ajax发起请求的时候要返回什么数据?json) 传参方式: get传参数:/cart/add?sku_id=1&count=3 post传参数:{'sku_id': 1, 'count': 3} url传参数:URL配置时捕获参数 如何选择传参方式: 如果涉及到数据的修改(新增,更新,删除),采用post 若干只涉及到数据的获取,采用get 2、添加商品到购物车,需要发哪些请求参数过去? 传递参数包括:商品id(sku_id)、商品数量(count)
cart/views.py
from django.views.generic import View from django-redis import get_redis_connection
# 地址: /cart/add class CartAddView(View): """购物车记录添加""" def post(self, request): """购物车记录添加""" pass
urls.py
path('add', CartAddView.as_view(), name='add'),
# 地址: /cart/add class CartAddView(View): """购物车记录添加""" def post(self, request): """购物车记录添加""" user = request.user if not user.is_authenticated(): # 用户未登录 return JsonResponse({'res': 0, 'errmsg': '请先登录'})
# 接收数据 sku_id = request.POST.get('sku_id') count = request.POST.get('count')
# 数据校验 if not all([sku_id, count]): # 'res': 0代表数据不完整 return JsonResponse({'res': 1, 'errmsg': '数据不完整'}) # 校验添加的商品数量 try: count = int(count) except Exception as e: # 说明不是一个有效数字,数目出错 return JsonResponse({'res': 2, 'errmsg': '商品数目出错'})
# 校验商品是否存在 try: sku = GoodsSKU.objects.get(id=sku_id) except GoodsSKU.DoesNotExist: # 商品不存在 return JsonResponse({'res': 3, 'errmsg': '商品不存在'})
# 业务处理: 添加购物车记录 # 添加购物车的几种情况 # 用户的购物车中已经有两条记录'cart_1': {'1': 3, '2': 5} # 随后用户又往购物车中添加了2件id为1的商品,sku_id=1, count=2,此时添加的时候应该如何处理? # 查看购物车中有没有该商品,若有则做累加,若没有则添加;
conn = get_redis_connection('default') cart_key = 'cart_%d' %user.id #尝试获取sku_id的值 从一个hash中获取一个属性的值 ---》 hget cart_key 属性 # redis.StrictRedis中找到一个方法 hget # 如果sku_id在hash中不存在,hget返回None cart_count = conn.hget(cart_key, sku_id) if cart_count: count += int(cart_count)
# 在添加之前要考虑商品的库存 # 校验商品的库存 if count > sku.stock: return JsonResponse({'res': 4, 'errmsg': '商品库存不足'})
# 无论购物车中有没有该商品,其最终存入的都是count值 # 设置hash中sku_id对应的值 # hset(name, key, value)<-----传参:hashkey,属性名字,对应的值 # hset--->如果商品sku_id不存在则进行添加操作,如果存在则进行更新操作 conn.hset(cart_key, sku_id, count) # 获取用户购物车中的条目数
# 返回应答 return JsonResponse({'res': 5, 'meeage': '添加成功'})
用户未登录的情况下无法添加购物车
P74 购物车记录添加前端js
此时添加购物车视图已经完成,接下来写前端代码,发起ajax post请求,将数据传过来 在detail.html中,
# 需要获取sku_id,则动态地添加一个属性sku_id="{{ sku.id }}" <a href="javascript:;" class="add_cart" sku_id="{{ sku.id }}" id="add_cart">加入购物车</a> $(".add_cart").click(function(){ # 获取商品id和商品数量 sku_id = $(this).attr('sku_id') count = $('.num_show').val() # alert测试 # alert(sku_id + ':' + count) # 在发起请求之前组织字典参数 params = {'sku_id': sku_id, 'count': count} # 发起ajax post请求,访问/cart/add, 传递参数: sku_id count # function (data) 对应的回调函数,data是后台返回的json数据 $.post('/cart/add', params, function (data){ if (data.res == 5){ // 添加成功 $(".add_jump").css({'left':$add_y+80,'top':$add_x+10,'display':'block'}) $(".add_jump").stop().animate({ 'left': $to_y+7, 'top': $to_x+7}, "fast", function() { $(".add_jump").fadeOut('fast',function(){ // 重新设置用户购物车中商品的条目数,即购物车中的商品总个数 $('#show_count').html(data.cart_count); }); }); } else{ // 添加失败 alert(data.errmsg) } }) })
进行测试,网页中检查-->Network # 报错403 1.在加购物车之前添加{% csrf_token %} 2.在ajax post中获取参数时,获取到csrfmiddlewaretoken的值【可在加了csrf_token标签之后通过查看页面源码获取】 csrf = $('input[name="csrfmiddlewaretoken"]').val() 3.组织参数中加入该键值对 params = {'sku_id': sku_id, 'count': count, 'csrfmiddlewaretoken': csrf}
###############################################django防止CSRF的方式: 1. 默认打开csrf中间件 2. 表单post提交数据时加上{% csrf_token %}标签 防御原理: 1. 渲染模板文件时在页面生成一个名字叫做csrfmiddlewaretoken的隐藏域 2. 服务器交给浏览器保存一个名字为csrftoken的cookie信息 3. 提交表单时,两个值都会发给服务器,服务器进行比对,如果一样,则csrf验证通过,否则失败 ##############################################
加购完之后,我的购物车应该显示相应的条目数 在views.py中 # 获取用户购物车中的条目数 cart_count = conn.hlen(cart_key)
# 返回应答 return JsonResponse({'res': 5, 'cart_count': cart_count, 'message': '添加成功'})
在post请求中: $(".add_jump").fadeOut('fast',function(){ // 重新设置用户购物车中商品的条目数,即购物车中的商品总个数 $('#show_count').html(data.cart_count); });
// 获取add_cart a标签左上角的坐标【加入购物车】 var $add_x = $('#add_cart').offset().top; var $add_y = $('#add_cart').offset().left;
// 获取show_count div元素左上角的坐标【我的购物车】 var $to_x = $('#show_count').offset().top; var $to_y = $('#show_count').offset().left;
添加成功之后做了一个动画add_jump
P75 购物车记录添加_小结
至此,购物车添加完成,在处理添加的时候,如果用户未登录则无法进行添加操作,这里为什么没有继承utils下的mixin.py中的LoginRequiredMixin? ajax发起的请求都在后台,在浏览器中看不到效果,即使浏览器访问了登录页面,也无法感知,因此需要自己进行判断并返回相应的json
前后端分离开发时: 确定前端是否传递数据,传递什么数据,什么格式 确定前端访问的方式(get, post) 确定返回给前端的什么数据,什么格式
P76 购物车页面显示
127.0.0.1:8000/static/cart.html 购物车页面展示
需要用户登陆后才能进行展示,继承LoginRequiredMixin
from utils.mixin import LoginRequiredMixin
class CartInfoView(LoginRequiredMixin, View): """购物车页面显示""" def get(self, request): """显示""" # 获取登录的用户 user = request.user # 获取用户购物车中商品的信息 (在redis中存储) conn = get_redis_connection('default') cart_key = 'cart_%s' %(user.id)
# 购物车记录的格式是 'cart_用户id':{'sku_id1': 商品数目, 'sku_id2': 商品数目} 要获取其中所有的数据:hgetall(name)<----根据名字来获取key,返回一个Python的dict # {'商品id': 商品数量} cart_dict = conn.hgetall(cart_key) # 并用一个列表接收查询到的信息 skus = [] # 保存用户购物车中商品的总数目和总价格 total_count = 0 # 总件数 total_price = 0 # 总价格
# 页面要获取每个商品的信息 # 可以拿到商品id获取到商品信息 # 遍历获取商品的信息,可以通过遍历字典每个元素cart_dict.items()<----键和值 for sku_id, count in cart_dict.items(): # 根据商品的id获取商品的信息 sku = GoodsSKU.objects.get(id=sku_id) # 计算商品的小计 商品有了,则很容易获取到价格,将计算得到的值传给模板 amount = sku.price * int(count) # amount与一个商品对象关联,考虑商品首页中动态的给一个对象增加属性,参考type # 动态给SKU对象增加一个属性amount,保存商品的小计 sku.amount = amount # 用户购物车中的数量也要获取,并传给模板 # 动态给SKU对象增加一个属性amount,保存购物车中对应商品的数量 sku.count = count
# 添加到列表 skus.append(sku)
# 需要计算购物车中的商品总件数和总价格 总件数和条目数不一样 # 累加计算商品的总数目和总价格 total_count += int(count) total_price += amount # 组织上下文 context = { 'total_count': total_count, 'total_count': total_count, 'skus': skus } # 使用模板 return render(request, 'cart.html', context)
re_path(r'^$', CartInfoView.as_view(), name='show') # 购物车页面显示
购物车信息的获取 1.购物车中商品的数量 2.购物车中每个商品的信息 3.总计以及商品的总数量
P77 购物车js-全选-全不选-商品的选中和不选中
jQuery的选择器找checkbox input标签是ul的子集元素(ul的孙子。。。) $('.settlements').find(':checkbox')<---全选的checkbox
选择器:所有被选中的input元素$(':checked')
# 获取所有被选中的商品的checkbox $('.cart_list_td').find(':checked') 目的:获取商品的ul,以此获取其中的小计和数量,从而进行累加计算 所有被选中的商品的checkbox有个父级元素ul parent和parents两者区别:parent是找它老爸,parents是它老爸及其以上 # 获取所有被选中的商品所在的ul元素,此时获取到的ul元素对应的商品一定是被选中的 $('.cart_list_td').find(':checked').parents('ul')
cart.html下方添加 1.定义函数一:计算被选中的商品的总件数和总价格 2.定义函数二:商品的全选和全不选,调用函数一获取选中的商品的总件数和总价格,更新页面的信息 3.定义函数三:商品对应的checkbox状态发生改变时,设置全选checkbox的状态,即当任一商品的不被勾选时,全选随之取消。 不管是哪个商品的checkbox,只要一改变都需要去判断一下全选是否要被选中,因此需要去绑定每个商品的checkbox的绑定事件,只要一改变就需要去做 {% block bottomfiles %} <script src="{% static 'js/jquery-1.12.4.min.js' %}"></script> <script> // 计算被选中的商品的总件数和总价格 function update_page_info() {
# 分别保存商品的总件数和总价格 total_count = 0 total_price = 0 // $('.cart_list_td')《---选中和没选中的都找到了 # 获取所有被选中的商品所在的ul元素,此时获取到的ul元素对应的商品一定是被选中的 # each()进行遍历,循环遍历之后可以获取到商品的总件数和总价格 $('.cart_list_td').find(':checked').parents('ul').each(function () { # $(this)对应一个商品的ul元素 # 计算商品的总件数和总价格,则需要获取商品的数目和小结 # $(this)对应一个商品的ul元素,要获取到ul中对应的商品数目在num_show里面【input元素,其class为num_show,其value值是商品的数目】 count = $(this).find('.num_show').val() # 获取商品的小计,在li元素里面,li是ul的儿子,因此可以使用children(),根据类来找 amount = $(this).find('.col07').text() # 获取完之后需要进行累加,求得总和 # 累加计算商品的总件数和总价格,将count累加到total_count上【注:count和amount是字符串,需要进行转换】 count = parseInt(count) amount = parseFloat(amount) total_count += count total_price += amount }) // 设置被选中的商品的总件数和总价格 // 在em中设置总价格,在b元素中设置商品的总件数,即找到settlements【ul元素,其类为settlements】 // 计算之后,小数点个数可能不是两位,使用toFixed(2)进行设置 $(.'settlements').find('em').text(total_price.toFixed(2)) $(.'settlements').find('b').text(total_count) } //商品的全选和全不选 // 当其进行改变的时候执行方法change $('.settlements').find(':checkbox').change(function () { // 获取全选的checkbox的选中状态,即获取checkbox的属性 is_checked = $(this).prop('checked') // 返回True或者False // 遍历商品对应的checkbox,设置这些checkbox的选中状态和全选的checkbox保持一致 // 每个商品都在一个ul中,先找到对应的ul,再找其中的checkbox each()实现遍历 $('.cart_list_td').find(':checkbox').each(function () { //每个$(this)就是checkbox,prop设置获取到的属性与全选的选中状态is_checked保持一致 //此时可以实现商品的选中,全选和全不选 $(this).prop('checked', is_checked) // 总计和商品件数也要随之改变,计算被选中的商品的总件数和总价格 }) // 更新页面的信息 update_page_info() })
// 商品对应的checkbox状态发生改变时,设置全选checkbox的状态 // 在function中判断全选是否应该被选中 // 思路一:找到购物车页面中一共有几个checkbox,再找到一共有几个被选中,被选中的数目小于所有checkbox的总和,则说明有商品未被选中,此时【全选】被选中,否则不被选中 $('.cart_list_td').find(':checkbox').change(function () { // 获取页面上所有商品的数目 // 分析,每个商品都在一个ul中,class为cart_list_td // 找到cart_list_td对应的ul,length:找到几个ul就有几件商品 all_len = $('.cart_list_td').length
// 获取页面上被选中的商品的数目 checked_len = $('.cart_list_td').find(':checked').length is_checked = true if (checked_len < all_len){ is_checked = false } $('.settlements').find(':checkbox').prop('checked', is_checked)
/* if (checked_len < all_len) { $('.settlements').find(':checkbox').prop('checked', false) } else{ $('.settlements').find(':checkbox').prop('checked', true) }*/ // 更新页面的信息 update_page_info() }) </script>
{% endblock bottomfiles %}
P78 购物车记录更新-后台view
此时的全选、全不选未涉及到与后台的交互,而加数量和减数量以及手动输入数量则涉及到与后台的交互,该部分实际上都是在更新用户购物车中商品的数量 cart的views.py中 # 更新商品的数量 # 页面发起加数量或减数量后,页面不进行跳转,因此前端采用ajax发起post请求 # 前端要给后端传参数:商品id(sku_id) 更新的商品数量(count)【购物车中存储的内容】 # /cart/update class CartUpdateView(View): """购物车记录更新""" def post(self, request): """购物车记录更新""" # 在接收数据之前进行用户登录校验 user = request.user if not user.is_authenticated(): # 用户未登录 return JsonResponse({'res': 0, 'errmsg': '请先登录'}) # 接收数据 sku_id = request.POST.get('sku_id') count = request.POST.get('count')
# 数据校验 if not all([sku_id, count]): # 'res': 0代表数据不完整 return JsonResponse({'res': 1, 'errmsg': '数据不完整'}) # 校验添加的商品数量 try: count = int(count) except Exception as e: # 说明不是一个有效数字,数目出错 return JsonResponse({'res': 2, 'errmsg': '商品数目出错'})
# 校验商品是否存在 try: sku = GoodsSKU.objects.get(id=sku_id) except GoodsSKU.DoesNotExist: # 商品不存在 return JsonResponse({'res': 3, 'errmsg': '商品不存在'})
# 业务处理:更新购物车记录 conn = get_redis_connection('default') cart_key = 'cart_%d' %(user.id) # 校验商品的库存 if count > sku.stock: return JsonResponse({'res': 4, 'errmsg': '商品库存不足'})
# 更新 conn.hset(cart_key, sku_id, count)
# 返回应答 return JsonResponse({'res': 5, 'errmsg': '更新成功'})
path('update', CartUpdateView.as_view(), name='update')
P79 购物车记录更新-前端数目增加js
点击加号绑定增加按钮的点击事件----一点击就会发送一个请求 $('.add').click()<---找到增加的a标签并绑定点击事件click cart.html中 {% block bottomfiles %} <script src="{% static 'js/jquery-1.12.4.min.js' %}"></script> <script> ...
// 定义函数 计算商品的小计 // 需要计算哪个小计,则把商品所在的ul元素传过来就可以,此时就可以在里面获取商品的价格和数量 function update_goods_amount(sku_ul) { // 获取商品的价格和数量 // 将$(this)改为sku_ul即可,你获取到的是哪个商品,需要把外面的元素传过来,就可以拿到它的数量 count = sku_ul.find(".num_show").val() // 价格在其子元素中的col05,此时可以使用children price = sku_ul.children('.col05').text() // 计算商品的小计 amount = parseInt(count)*parseFloat(price) // 设置商品的小计 sku_ul.children('.col07').text(amount.toFixed(2)+'元') } // 购物车商品数量的增加 点击之后需要执行function方法 // 在view中分析可知,前端需要传给后端商品id和商品数量 $('.add').click(function () { // 获取商品的id和商品的数量,分析页面可知 // $(this)为点击的a标签,该标签下面的input标签中的value值即为商品数量 count = $(this).next().val() // 往input标签中添加自定义属性sku_id = "{{ sku.id }}" sku_id = $(this).next().attr('sku_id') // 此时已经获取到商品id和商品数量,发起的是post请求,要记得csrf验证 // 在<ul class="settlements"></ul>中加上{% csrf_token %} // 在发参数之前,先获取到csrf csrf = $('input[name="csrfmiddlewaretoken"]').val() // 在发送请求之前 组织参数 // 原来的数目是count,点击加号,则是在原来的基础上加一 count = parseInt(count) + 1 params = { 'sku_id': sku_id, 'count': count, 'csrfmiddlewaretoken': csrf } // 发起ajax post请求,访问/cart/update, 传递参数:sku_id count // 在function函数中会返回一个数据,在这里面可以做判断 // 在更新之前定义一个变量 error_update 如果更新成功则该值为False,如果更新失败则该值为True error_update = false
// *step4a的获取页面上购物车商品的总件数<---更新成功后 total = 0 $.post('/cart/update', params, function (data) { // 在views中如果成功则res==5 if (data.res == 5){ // 更新成功 error_update = false // *step4b获取页面上购物车商品的总件数 total = data.total_count } else{ // 更新失败 弹出错误信息 error_update = true alert(data.errmsg) } // 经过更新之后就可以判断更新是否成功 if (error_update == false) { // 成功的话: // 1.需要重新设置商品的数目,即数字要加一, // 2.小计也要改变 // 3. 获取商品对应的checkbox的选中状态,如果被选中,更新页面信息
// 1.重新设置商品的数目,需要设置为count+1的值 $(this).next().val(count) // 2.计算商品的小计 可以单独写一个函数计算商品的小计 调用该函数即可 // $(this)是a标签,ul是a标签的父元素,这里sku_ul=$(this).parents('ul') update_goods_amount($(this).parents('ul')) // 3. 获取商品对应的checkbox的选中状态,如果被选中,更新页面信息 // 获取其父级中的ul,找到ul中的checkbox,找到其属性的值prop('checked') is_checked = $(this).parents('ul').find(':checkbox').prop('checked') // 如果is_checked成立,则更新页面信息 if (is_checked){ // 更新页面信息 // 即计算被选中商品的总件数以及总价格 只需要调用update_page_info即可实现更新 update_page_info() }
// 截止目前位置的效果已经满足上述三种要求,当未选中但是加减商品时,页面左上角的总件数【全部商品xx件】需要改变 // 4.更新页面上购物车商品的总件数《---去后台views中的更新后面进行计算 // *step4c设置页面上购物车商品的总件数 要设置的地方在【全部商品】em标签中的内容 $('.total_count').children('em').text(total) } }) })
views.py
... # 更新
# 计算用户购物车中商品的总件数,此时并不是条目数,而是商品的总件数{'1': 5, '2': 3},其中条目数为2,总件数为8 # 需要获取cart_key中的所有的value值获取到hash中有一个value值 取一个hash中的所有的value值 # hvals(name) <---哈希中的所有值作为列表返回 # 调用conn的方法拿到vals total_count = 0 vals = conn.hvals(cart_key)
for val in vals: total_count += int(val)
# 返回应答 放入json中传给前端 return JsonResponse({'res': 5, 'total_count': total_count, 'errmsg': '更新成功'})
# 去前端js代码中,如何获取到total_count
再进行测试的时候增加完后为0,原因: 首先在*step4a中定义total为0,要在*step4c中设置值,需要在回调函数$.post('/cart/update'...)执行完成之后,才会给total赋值,即*step4b, 现在直接是0则说明没有等待回调执行。 默认发起的ajax请求都是异步的,不会等回调函数执行,因此最终在 *step4c设置页面上购物车商品的总件数时,会设置为0 这里在发起请求之前设置ajax请求为同步 $.ajaxSettings.async = false<---之后发起的ajax请求则为同步的,这个设置会影响全局的,因此在发完请求之后在将其设置为true
$.post('/cart/update', params, function (data){...}
$.ajaxSettings.async = true 在这样设置之后,ajax请求发出之后,回调完成之前,代码会一直阻塞在这里,最终就可以获取到该值
除了total需要发起一个同步的请求之外,还有其他地方也需要,这里的error_update也需要同步。 如果是异步的话,error_update值永远为初值,未获取到更新后的真实值,而其后面的操作都是依赖于这个请求的更新结果的
P80 购物车记录更新-前端数目减少-手动输入js
购物车商品数量的减少 增加与减少同理,这里绑定的类是minus
获取商品的数量 增加是在输入框的前面,而减少是在输入框的后面,把其中所有的next()改成prev()
count = parseInt(count) - 1 如果减小到0直接return if (count <= 0){ return } 其余操作相同
这里可以把增加购物车数量和减少购物车数量抽象出来:更新购物车中商品的数量 function update_remote_cart_info(sku_id, count) { // 获取到csrf
// 发起请求之前组织参数 }
// 获得焦点事件 // 保存用户输入之前购物车中商品的数目 var pre_count = 0 $('.num_show').focus(function () { pre_count = $(this).val() })
手动输入修改
手动输入的参数可能是非法的,这里需要绑定失去焦点事件 输入框的class是num_show $('.num_show').blur(function () {...}) 其中的$(this)就是input框 // 获取商品的id和商品的数目 var count = $(this).val() var sku_id = $(this).attr('sku_id')
// 判断用户输入的商品数目是否合法 if (isNaN(count) || count.trim().length==0 || parseInt(count)<=0){ // 设置商品的数目为用户输入之前的数目 // 一进来需要先记录原来的值,输入的内容不合法就不做改变,直接返回原来的值 $(this).val(pre_count) return } // 输入值count通过了上述三种校验情况,可能是一个数字,因此在更新之前将count转化为数字 // 更新购物车记录 count = parseInt(count) update_remote_cart_info(sku_id, count) // 设置更新 if (error_update == false){ // 重新设置商品的数目 $(this).val(count) // 更新商品的小计 update_sku_amount($(this).parents('ul')) // 判断是否更新选中商品的总件数和总金额 is_checked = $(this).parents('ul').find(':checkbox').prop('checked') if (is_checked){ // 更新页面信息 update_page_info() } // 更新页面购物车商品的总件数 $('.total_count').children('em').text(total_count) } else{ // 如果更新失败了,例如超过商品的库存 // 设置商品的数目为用户输入之前的数目 $(this).val(pre_count) }
关于购物车数量的更新---增加,减少和手动输入,三者处理过程类似,都是通过ajax post请求访问/cart/update,区别在于一些参数校验和获取值的时候不同
P81 购物车记录删除-后台view
购物车页面上每个商品后面都有一个删除,这部分涉及到购物车记录的删除 # 删除购物车记录 # 采用ajax post请求 # 前端需要传递的参数: 商品id(sku_id) # /cart/delete class CartDeleteView(View): """购物车记录删除""" def post(self, request): user = request.user if not user.is_authenticated: return JsonResponse({'res': 0, 'errmsg': '请先登录'})
# 接收参数 sku_id = request.POST.get('sku_id')
# 数据校验 if not sku_id: return JsonResponse({'res': 1, 'errmsg': '无效的商品id'})
# 校验商品是否存在 try: sku = GoodsSKU.objects.get(id=sku_id) except GoodsSKU.DoesNotExist: return JsonResponse({'res': 2, 'errmsg': '商品不存在'})
# 业务处理: 删除购物车记录 conn = get_redis_connection('default') cart_key = 'cart_%d' % user.id
# 删除 hdel conn.hdel(cart_key, sku_id)
# 计算用户购物车中商品的总件数{'1':5,'2':3} total_count = 0 vals = conn.hvals(cart_key) for val in vals: total_count += int(val)
return JsonResponse({'res': 3, 'total_count': total_count, 'message': '删除成功'})
url中配置 path('delete', CartDeleteView.as_view(), name='delete')
P82 购物车记录删除-前端js
获取删除的点击事件,绑定a标签的点击事件,执行对应的方法 $('.cart_list_td').children('col08').children('a').click(function () { // 前端需要传给后端商品id // 这里则需要在前端去获取对应商品id // $(this)<---a标签,a标签的父元素ul中的input标签的自定义属性sku_id sku_id = $(this).parents('ul').find('num_show').attr('sku_id') // 删除之前拿出csrf // 在发参数之前,先获取到csrf csrf = $('input[name="csrfmiddlewaretoken"]').val() // 在发送请求之前 组织参数 params = { 'sku_id': sku_id, 'csrfmiddlewaretoken': csrf }
// 获取商品所在的ul元素 sku_ul = $(this).parents('ul') // 发起ajax post请求,访问/cart/delete, 传递参数:sku_id 执行回调函数 // $.post('/cart/delete', params, function (data) { // 回调函数,成功之后返回的是3 // 所以这里去判断,如果data.res返回的是3则说明删除成功 if (data.res == 3) { // 删除成功,成功之后要移除页面上商品所在的ul元素 // 如果直接在这里删除的话,这里的$(this)就不是删除的a标签了,这里在外面上面获取一下其ul标签 // sku_ul.remove()<--- remove()方法是移除自身及其子元素 // sku_ul.empty()<--- remove()方法是移除其子元素不包括自身 // 这里是需要移除自身及其子元素,使用remove方法 sku_ul.remove()
// 移除之后,其合计要根据选中或未选中进行判断 // 获取sku_ul中商品的选中状态 is_checked = sku_ul.find(':checkbox').prop('checked') // is_checked为真表示被选中了,则页面中的合计需要更新 if (is_checked) { // 更新页面信息 update_page_info() }
// 左上角的【全部商品】,不管有没有选中都需要进行更新《---需要在views中获取并传过来 // 重新设置页面上购物车中商品的总件数 $('.total_count').children('em').text(data.total_count) } else{ // 失败则直接弹出错误信息 alert(data.errmsg) }
}) })
views.py # 计算用户购物车中商品的总件数{'1':5,'2':3} total_count = 0 vals = conn.hvals(cart_key) for val in vals: total_count += int(val) return JsonResponse({'res': 3, 'total_count': total_count, 'message': '删除成功'})
P83 购物车模块-小结
第一部分: 计算被选中的商品的总件数和总价格 计算商品的小计 商品的全选和全不选 商品对应的checkbox状态发生改变时,设置全选checkbox的状态
以上js不涉及和后台的交互
第二部分: 涉及到后台交互,购物车商品数量的更新 在该部分分析了 前端的请求方式,应该传过来的参数
与更新视图相关的js《---添加,减少,手动输入
第三部分: 购物车记录的删除 |