【DailyFresh】课程记录4---购物车模块

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《---添加,减少,手动输入

第三部分:
购物车记录的删除
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值