1.什么时候添加购物车记录
当用户浏览商品详情时添加购物车记录
detail.html
<div class="goods_detail_con clearfix">
<div class="goods_detail_pic fl"><img src="{{ sku.image.url }}"></div>
<div class="goods_detail_list fr">
<h3>{{ sku.name }}</h3>
<p>{{ sku.desc }}</p>
<div class="prize_bar">
<span class="show_pirze">¥<em>{{ sku.price }}</em></span>
<span class="show_unit">单 位:{{ sku.unite }}</span>
</div>
<div class="goods_num clearfix">
<div class="num_name fl">数 量:</div>
<div class="num_add fl">
<input type="text" class="num_show fl" value="1">
<a href="javascript:;" class="add fr">+</a>
<a href="javascript:;" class="minus fr">-</a>
</div>
</div>
<div>
<p>其他规格:</p>
<ul>
{% for sku in sama_spu_skus %}
<li><a href="{% url 'goods:detail' sku.id %}">{{ sku.name }}</a></li>
{% endfor %}
</ul>
</div>
<div class="total">总价:<em>16.8元</em></div>
<div class="operate_btn">
{% csrf_token %}
<a href="javascript:;" class="buy_btn">立即购买</a>
<a href="javascript:;" sku_id="{{ sku.id }}" class="add_cart" id="add_cart">加入购物车</a>
</div>
</div>
</div>
<script src="{% static 'js/jquery-1.12.4.min.js' %}"></script>
<script type="text/javascript">
$('#add_cart').click(function(){
// 获取商品id和数量
sku_id = $(this).attr('sku_id')
count = $('.num_show').val()
csrf = $('input[name="csrfmiddlewaretoken"]').val()
// alert(sku_id+':'+count)
//组织参数
params = {'sku_id': sku_id, 'count': count, 'csrfmiddlewaretoken': csrf}
// 发起ajax post请求, 访问/cart/add, 传递参数:sku_id count
$.post('{% url "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>
cart/urls.py
from django.conf.urls import url
from .views import CartAddView
urlpatterns = [
url(r'^add$', CartAddView.as_view(), name='add'), # 添加购物车
]
cart/views.py
from django.views.generic import View
from django.http import JsonResponse
from goods.models import GoodsSKU
from django_redis import get_redis_connection
class CartAddView(View):
"""添加购物车记录"""
def post(self, request):
user = request.user
if not user.is_authenticated():
# 用户未登录
return JsonResponse({'res': 0, 'errmsg': '请先登录'})
# 1.获取数据
sku_id = request.POST.get('sku_id')
count = request.POST.get('count')
# 2.数据校验
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': '商品不存在'})
# 3.业务处理
conn = get_redis_connection('default')
cart_key = 'cart_%d' % user.id
# 尝试先获取sku_id的值, --> hget cart_key
# 如果获取不到返回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对应的值, 如果sku_id不存在则添加, 存在则更新
conn.hset(cart_key, sku_id, count)
# 统计用户购物车中的商品条目
total_count = conn.hlen(cart_key)
# 4.返回响应
return JsonResponse({'res': 5, 'total_count': total_count, 'errmsg': '添加成功'})
2.什么时候获取购物车记录
使用到购物车中数据和访问购物车页面的时候需要获取购物车记录
cart/urls.py
from django.conf.urls import url
from .views import CartAddView, CartInfoView
urlpatterns = [
url(r'^add$', CartAddView.as_view(), name='add'), # 添加购物车
url(r'^$', CartInfoView.as_view(), name='show'), # 购物车页面显示
]
cart/views.py
from django.shortcuts import render
from django.views.generic import View
from goods.models import GoodsSKU
from django_redis import get_redis_connection
from utils.mixin import LoginRequiredMixin # 自定义的登录验证装饰器
class CartInfoView(LoginRequiredMixin, 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)
3.使用什么存储购物车记录
redis存储购物车记录
4.分析购物车存储的记录格式
hash
'cart_用户id':{'sku_id1': 商品数量, 'sku_id2': 商品数量...}
示例:
'cart_1': {'1': 2, '2', 4}
获取用户购物车中的商品条目数
hlen
5.更新,删除购物车中商品
当用户进入购物车页面后可以对购物车中商品数量进行修改,此时需要更新redis购物车记录
cart/cart.html
<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' %}">
{% for sku in skus %}
<ul class="cart_list_td clearfix">
<li class="col01"><input type="checkbox" name="sku_ids" value="{{ sku.id }}"></li>
<li class="col02"><img src="{{ sku.image.url }}"></li>
<li class="col03">{{ sku.name }}<br><em>{{ sku.price }}元/{{ sku.unit }}</em></li>
<li class="col04">{{ sku.unit }}</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>
<script src="{% static 'js/jquery-1.12.4.min.js' %}"></script>
<script>
// 计算被选中的商品的总件数和总价格
function update_page_info() {
// 获取所有被选中的商品
// $('.cart_list_td').find(':checked')
// 获取被选中的商品的父级元素
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)
// scherzi商品的小计
sku_ul.children('.col07').text(amount.toFixed(2)+'元')
}
// 商品的全选和全不选
$('.settlements').find(':checkbox').change(function () {
//获取全选的checkbox的选中状态
is_checked = $(this).prop('checked')
// 遍历商品对应的cehckbox, 设置这些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
// 如果被选中商品的总数量小于,所有商品数量, 将全选checkbox取消
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请求都是异步,不会等待回调函数执行,所以当我们更新页面时,拿不到total_count
// 发起ajax post请求, /cart/update, 传递参数:sku_id, count
$.post('{% url "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('{% url "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>
cart/urls.py
from django.conf.urls import url
from .views import CartAddView, CartInfoView, CartUpdateView, CartDeleteView
urlpatterns = [
url(r'^add$', CartAddView.as_view(), name='add'), # 添加购物车
url(r'^$', CartInfoView.as_view(), name='show'), # 购物车页面显示
url(r'^update$', CartUpdateView.as_view(), name='update'), # 更新购物车记录
url(r'^delete$', CartDeleteView.as_view(), name='delete'), # 删除购物车记录
]
cart/views.py
# 更新购物车
# ajax post
# 前端传递参数:商品id(sku_id),更新数量(count)
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')
# 2.数据校验
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)
# 计算用户购物车种商品总件数
vals = conn.hvals(cart_key)
total_count = 0
for val in vals:
total_count += int(val)
# 返回应答
return JsonResponse({'res': 5, 'total_count': total_count, 'ermsg': '更新成功'})
# 删除购物车记录
# ajax post
# 前端传递参数:商品id(sku_id)
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)
# 计算用户购物车种商品总件数
vals = conn.hvals(cart_key)
total_count = 0
for val in vals:
total_count += int(val)
# 返回应答
return JsonResponse({'res': 3, 'total_count': total_count, 'errmsg': '删除成功'})