商品详情页detail.html添加加入购物车按钮
<a href="javascript:;" sku_id="{{ sku.id }}" class="add_cart" id="add_cart">加入购物车</a> .... <script> $('#add_cart').click(function(){ // 获取商品id和商品数量 sku_id = $(this).attr('sku_id') // attr prop count = $('.num_show').val() csrf = $('input[name="csrfmiddlewaretoken"]').val() // 组织参数 params = {'sku_id':sku_id, 'count':count, 'csrfmiddlewaretoken':csrf} // 发起ajax post请求,访问/cart/add, 传递参数:sku_id count $.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.total_count); }); }); } else{ // 添加失败 alert(data.errmsg) } }) }) </script>
视图函数views.py中添加add功能
from django.shortcuts import render from django.views.generic import View from django.http import JsonResponse from goods.models import GoodsSKU from django_redis import get_redis_connection # Create your views here. # 添加商品到购物车: # 1)请求方式,采用ajax post # 如果涉及到数据的修改(新增,更新,删除), 采用post # 如果只涉及到数据的获取,采用get # 2) 传递参数: 商品id(sku_id) 商品数量(count) # ajax发起的请求都在后台,在浏览器中看不到效果 # /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]): 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 # 先尝试获取sku_id的值 -> hget cart_key 属性 # 如果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':'商品库存不足'}) # 设置hash中sku_id对应的值 # hset->如果sku_id已经存在,更新数据, 如果sku_id不存在,添加数据 conn.hset(cart_key, sku_id, count) # 计算用户购物车商品的条目数 total_count = conn.hlen(cart_key) # 返回应答 return JsonResponse({'res':5, 'total_count':total_count, 'message':'添加成功'})
购物车页面
跳转到购物车
base.html中
<a href="{% url 'cart:show' %}" class="cart_name fl">我的购物车</a>
视图函数views.py添加CartInfoView视图功能
# /cart/ from utils.mixin import LoginRequiredMinxin # 登录校验 class CartInfoView(LoginRequiredMinxin, View): '''购物车页面显示''' def get(self, request): '''显示''' # 获取登录的用户 user = request.user # 获取用户购物车中商品的信息 conn = get_redis_connection('default') cart_key = 'cart_%d'%user.id # {'商品id':商品数量, ...} cart_dict = conn.hgetall(cart_key) skus = [] # 保存用户购物车中商品的总数目和总价格 total_count = 0 total_price = 0 # 遍历获取商品的信息 for sku_id, count in cart_dict.items(): # 根据商品的id获取商品的信息 sku = GoodsSKU.objects.get(id=sku_id) # 计算商品的小计 amount = sku.price*int(count) # 动态给sku对象增加一个属性amount, 保存商品的小计 sku.amount = amount # 动态给sku对象增加一个属性count, 保存购物车中对应商品的数量 sku.count = count # 添加 skus.append(sku) # 累加计算商品的总数目和总价格 total_count += int(count) total_price += amount # 组织上下文 context = {'total_count':total_count, 'total_price':total_price, 'skus':skus} # 使用模板 return render(request, 'cart.html', context)
模板cart.html
{% extends 'layout/base_no_cart.html' %} {% load staticfiles %} {% block title %}天天生鲜-购物车{% endblock title %} {% block page_title %}购物车{% endblock page_title %} {% 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=""> {% for sku in skus %} <ul class="cart_list_td clearfix"> <li class="col01"><input type="checkbox" name="sku_ids" value="{{ sku.id }}" checked></li> <li class="col02"><img src="{{ sku.image.url }}"></li> <li class="col03">{{ sku.name }}<br><em>{{ sku.price }}元/{{ sku.unite }}</em></li> <li class="col04">{{ sku.unite }}</li> <li class="col05">{{ sku.price }}元</li> <li class="col06"> <div class="num_add"> <a href="javascript:;" class="add fl">+</a> <input type="text" sku_id="{{ sku.id }}" class="num_show fl" value="{{ sku.count }}"> <a href="javascript:;" class="minus fl">-</a> </div> </li> <li class="col07">{{ sku.amount }}元</li> <li class="col08"><a href="javascript:;">删除</a></li> </ul> {% endfor %} <ul class="settlements"> {% csrf_token %} <li class="col01"><input type="checkbox" name="" checked=""></li> <li class="col02">全选</li> <li class="col03">合计(不含运费):<span>¥</span><em>{{ total_price }}</em><br>共计<b>{{ total_count }}</b>件商品</li> <li class="col04"><input type="submit" value="去结算"></li> </ul> </form> {% endblock body %} {% block bottomfiles %} <script src="{% static 'js/jquery-1.12.4.min.js' %}"></script> <script> // 计算被选中的商品的总件数和总价格 function update_page_info() { // 获取所有被选中的商品的checkbox // 获取所有被选中的商品所在的ul元素 total_count = 0 total_price = 0 $('.cart_list_td').find(':checked').parents('ul').each(function () { // 获取商品的数目和小计 count = $(this).find('.num_show').val() amount = $(this).children('.col07').text() // 累加计算商品的总件数和总价格 count = parseInt(count) amount = parseFloat(amount) total_count += count total_price += amount }) // 设置被选中的商品的总件数和总价格 $('.settlements').find('em').text(total_price.toFixed(2)) $('.settlements').find('b').text(total_count) } // 计算商品的小计 function update_goods_amount(sku_ul) { // 获取商品的价格和数量 count = sku_ul.find('.num_show').val() price = sku_ul.children('.col05').text() // 计算商品的小计 amount = parseInt(count)*parseFloat(price) // 设置商品的小计 sku_ul.children('.col07').text(amount.toFixed(2)+'元') } // 商品的全选和全不选 $('.settlements').find(':checkbox').change(function () { // 获取全选的checkbox的选中状态 is_checked = $(this).prop('checked') // 遍历商品的对应的checkbox,设置这些checkbox的选中状态和全选的checkbox保持一致 $('.cart_list_td').find(':checkbox').each(function () { $(this).prop('checked', is_checked) }) // 更新页面的信息 update_page_info() }) // 商品对应的checkbox状态发生改变时,设置全选checkbox的状态 $('.cart_list_td').find(':checkbox').change(function () { // 获取页面上所有商品的数目 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) // 更新页面的信息 update_page_info() }) // 更新购物车中商品的数量 error_update = false total = 0 function update_remote_cart_info(sku_id, count) { csrf = $('input[name="csrfmiddlewaretoken"]').val() // 组织参数 params = {'sku_id':sku_id, 'count':count, 'csrfmiddlewaretoken':csrf} // 设置ajax请求为同步 $.ajaxSettings.async = false // 发起ajax post请求,访问/cart/update, 传递参数:sku_id count // 默认发起的ajax请求都是异步的,不会等回调函数执行 $.post('/cart/update', params, function (data) { if (data.res == 5){ // 更新成功 error_update = false total = data.total_count } else{ // 更新失败 error_update = true alert(data.errmsg) } }) // 设置ajax请求为异步 $.ajaxSettings.async = true } // 购物车商品数量的增加 $('.add').click(function () { // 获取商品的id和商品的数量 sku_id = $(this).next().attr('sku_id') count = $(this).next().val() // 组织参数 count = parseInt(count)+1 // 更新购物车记录 update_remote_cart_info(sku_id, count) // 判断更新是否成功 if (error_update == false){ // 重新设置商品的数目 $(this).next().val(count) // 计算商品的小计 update_goods_amount($(this).parents('ul')) // 获取商品对应的checkbox的选中状态,如果被选中,更新页面信息 is_checked = $(this).parents('ul').find(':checkbox').prop('checked') if (is_checked){ // 更新页面信息 update_page_info() } // 更新页面上购物车商品的总件数 $('.total_count').children('em').text(total) } }) // 购物车商品数量的减少 $('.minus').click(function () { // 获取商品的id和商品的数量 sku_id = $(this).prev().attr('sku_id') count = $(this).prev().val() // 校验参数 count = parseInt(count)-1 if (count <= 0){ return } // 更新购物车中的记录 update_remote_cart_info(sku_id, count) // 判断更新是否成功 if (error_update == false){ // 重新设置商品的数目 $(this).prev().val(count) // 计算商品的小计 update_goods_amount($(this).parents('ul')) // 获取商品对应的checkbox的选中状态,如果被选中,更新页面信息 is_checked = $(this).parents('ul').find(':checkbox').prop('checked') if (is_checked){ // 更新页面信息 update_page_info() } // 更新页面上购物车商品的总件数 $('.total_count').children('em').text(total) } }) // 记录用户输入之前商品的数量 pre_count = 0 $('.num_show').focus(function () { pre_count = $(this).val() }) // 手动输入购物车中的商品数量 $('.num_show').blur(function () { // 获取商品的id和商品的数量 sku_id = $(this).attr('sku_id') count = $(this).val() // 校验参数 if (isNaN(count) || count.trim().length==0 || parseInt(count)<=0){ // 设置商品的数目为用户输入之前的数目 $(this).val(pre_count) return } // 更新购物车中的记录 count = parseInt(count) update_remote_cart_info(sku_id, count) // 判断更新是否成功 if (error_update == false){ // 重新设置商品的数目 $(this).val(count) // 计算商品的小计 update_goods_amount($(this).parents('ul')) // 获取商品对应的checkbox的选中状态,如果被选中,更新页面信息 is_checked = $(this).parents('ul').find(':checkbox').prop('checked') if (is_checked){ // 更新页面信息 update_page_info() } // 更新页面上购物车商品的总件数 $('.total_count').children('em').text(total) } else{ // 设置商品的数目为用户输入之前的数目 $(this).val(pre_count) } }) // 删除购物车中的记录 $('.cart_list_td').children('.col08').children('a').click(function () { // 获取对应商品的id sku_id = $(this).parents('ul').find('.num_show').attr('sku_id') 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) { if (data.res == 3){ // 删除成功,异常页面上商品所在的ul元素 sku_ul.remove() // 获取sku_ul中商品的选中状态 is_checked = sku_ul.find(':checkbox').prop('checked') if (is_checked){ // 更新页面信息 update_page_info() } // 重新设置页面上购物车中商品的总件数 $('.total_count').children('em').text(data.total_count) } else{ alert(data.errmsg) } }) }) </script> {% endblock bottomfiles %}
视图函数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]): 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) # 计算用户购物车中商品的总件数 {'1':5, '2':3} total_count = 0 vals = conn.hvals(cart_key) for val in vals: total_count += int(val) # 返回应答 return JsonResponse({'res':5, 'total_count':total_count, 'message':'更新成功'}) # 删除购物车记录 # 采用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':'删除成功'})