【愚公系列】2022年02月 Django商城项目 30-购物车功能实现
【摘要】 一、添加购物车 1.后端逻辑代码"""一 前后端需求分析需求 前端需要收集: 商品id,商品数量, 选中是可选的(默认就是选中) 如果用户登陆了则请求携带session id 如果用户未登陆了则不请求携带session id 后端的需求: 新增数据二 大体流程 接收数据 验证数据 数据保存 返回相应三 把详细思...
一、添加购物车
1.后端逻辑代码
"""
一 前后端需求分析需求
前端需要收集: 商品id,商品数量, 选中是可选的(默认就是选中)
如果用户登陆了则请求携带session id
如果用户未登陆了则不请求携带session id
后端的需求: 新增数据
二 大体流程
接收数据
验证数据
数据保存
返回相应
三 把详细思路完善一下(纯后端)
1.接收数据 sku_id,count,
2.验证数据
3根据用户是否登陆来保存
4登陆用户redis
4.1 连接redis
4.2 hash
4.3 set
4.4 返回相应
5未登录用户cookie
5.1 组织数据
5.2 对数据进行base64处理
5.3 设置cookie
5.4 返回相应
四 确定我们请求方式和路由
POST carts/
"""
# 新增购物车
def post(self,request):
# 1.接收数据 sku_id,count,
data = json.loads(request.body.decode())
sku_id=data.get('sku_id')
count=data.get('count')
# 2.验证数据
#2.1 判断参数是否齐全
if not all([sku_id,count]):
return http.JsonResponse({'code':RETCODE.PARAMERR,'errmsg':'参数不齐'})
#2.2 判断商品是否存在
try:
sku = SKU.objects.get(pk=sku_id)
except SKU.DoesNotExist:
return http.JsonResponse({'code':RETCODE.NODATAERR,'errmsg':"暂无次数据"})
#2.3 数量需要是数值
try:
count = int(count)
except Exception as e:
return http.JsonResponse({'code':RETCODE.PARAMERR,'errmsg':'参数错误'})
# 3根据用户是否登陆来保存
if request.user.is_authenticated:
# 4登陆用户redis
# 4.1 连接redis
redis_conn = get_redis_connection('carts')
# 4.2 hash
# redis_conn.hset('carts_%s'%request.user.id,sku_id,count)
#① 先创建管道
pl = redis_conn.pipeline()
pl.hincrby('carts_%s'%request.user.id,sku_id,count)
# 4.3 set
pl.sadd('selected_%s'%request.user.id,sku_id)
#③ 执行管道
pl.execute()
# 4.4 返回相应
return http.JsonResponse({'code':RETCODE.OK,'errmsg':'ok'})
else:
# 5未登录用户cookie
# 5.0 先获取cookie数据,
cookie_str = request.COOKIES.get('carts')
# 判断cookie数据是否存在
if cookie_str is None:
# 如果cookie数据不存在,先初始化一个 空的carts
carts = {}
else:
# 如果cookie数据存在,对数据进行解码
# 对数据进行base64解码
de = base64.b64decode(cookie_str)
# 再将bytes类型的数据转换为字典
carts = pickle.loads(de)
# 5.1 更新数据
if sku_id in carts:
#数量累加
origin_count = carts[sku_id]['count']
count += origin_count
# count = origin_count + count
carts[sku_id]={
'count':count,
'selected':True
}
# 5.2 对数据进行base64处理
# 5.2.1 将字典转换为bytes类型
d = pickle.dumps(carts)
# 5.2.2 将bytes类型的数据进行base64编码
# bytes
en = base64.b64encode(d)
# 5.3 设置cookie
response = http.JsonResponse({'code':RETCODE.OK,'errmsg':'ok'})
# set_cookie(key,string_value)
# response.set_cookie('carts',en)
response.set_cookie('carts',en.decode())
# 5.4 返回相应
return response
2.前台请求接口代码
// 加入购物车
add_cart(){
var url = this.host + '/carts/';
axios.post(url, {
sku_id: parseInt(this.sku_id),
count: this.sku_count
}, {
headers: {
'X-CSRFToken': getCookie('csrftoken')
},
responseType: 'json',
withCredentials: true
})
.then(response => {
if (response.data.code == '0') {
alert('添加购物车成功');
this.cart_total_count += this.sku_count;
} else { // 参数错误
alert(response.data.errmsg);
}
})
.catch(error => {
console.log(error.response);
})
},
3.实际效果
二、获取购物车
1.后端逻辑代码
"""
一 把需求写下来 (前端需要收集什么 后端需要做什么)
二 把大体思路写下来(后端的大体思路)
查询数据
展示
三 把详细思路完善一下(纯后端)
1.获取用户信息,根据用户信息进行判断
2.登陆用户redis查询
2.1 连接redis
2.2 hash {sku_id:count}
2.3 set [sku_id]
2.4 根据id查询商品详细信息
2.5 展示
3.未登录用户cookie查询
3.1 读取cookie
3.2 判断carts数据,如果有则解码数据,如果没有则初始化一个字典
{sku_id: {count:xxx,selected:xxxx}}
3.3 根据id查询商品信息信息
3.4 展示
1.获取用户信息,根据用户信息进行判断
2.登陆用户redis查询
2.1 连接redis
2.2 hash {sku_id:count}
2.3 set [sku_id]
3.未登录用户cookie查询
3.1 读取cookie
3.2 判断carts数据,如果有则解码数据,如果没有则初始化一个字典
{sku_id: {count:xxx,selected:xxxx}}
4.根据id查询商品信息信息
5.展示
四 确定我们请求方式和路由
GET carts/
"""
def get(self,request):
# 1.获取用户信息,根据用户信息进行判断
user = request.user
if user.is_authenticated:
# 2.登陆用户redis查询
# 2.1 连接redis
redis_conn = get_redis_connection('carts')
# 2.2 hash {sku_id:count}
sku_id_count=redis_conn.hgetall('carts_%s'%user.id)
# 2.3 set [sku_id]
selected_ids=redis_conn.smembers('selected_%s'%user.id)
# 将redis的数据统一为cookie的格式 v
# 将cookie的数据统一为redis的格式
#{sku_id: {count:xxx,selected:xxxx}}
carts = {}
# 对字典数据进行遍历,并且进行解包
for sku_id,count in sku_id_count.items():
#判断商品id是否在选中列表中
if sku_id in selected_ids:
selected=True
else:
selected=False
# 添加新数据
carts[int(sku_id)]={
'count':int(count),
'selected':selected
}
else:
# 3.未登录用户cookie查询
# 3.1 读取cookie
cookie_str = request.COOKIES.get('carts')
# 3.2 判断carts数据,如果有则解码数据,如果没有则初始化一个字典
if cookie_str is None:
carts = {}
else:
#将数据进行base64解码
de = base64.b64decode(cookie_str)
carts = pickle.loads(de)
#{sku_id: {count:xxx,selected:xxxx}}
# 4.根据id查询商品详细信息
ids = carts.keys()
# [1,2,3,4]
# 4.1 计算总价格
# 4.2 添加商品的选中状态和数量
skus = SKU.objects.filter(id__in=ids)
sku_list=[]
#总数量
total_count=0
#总价
total_amount=0
#{sku_id: {count:xxx,selected:xxxx}}
for sku in skus:
sku_list.append({
'id':sku.id,
'name':sku.name,
'count': carts.get(sku.id).get('count'),
'selected': str(carts.get(sku.id).get('selected')), # 将True,转'True',方便json解析
'default_image_url':sku.default_image.url,
'price':str(sku.price), # 从Decimal('10.2')中取出'10.2',方便json解析
'amount':str(sku.price * carts.get(sku.id).get('count')),
})
total_count += carts[sku.id]['count']
total_amount += (sku.price * carts[sku.id]['count'])
# 5.展示
context = {
'cart_skus': sku_list
}
return render(request,'cart.html',context=context)
2.前台页面代码
<ul class="cart_list_td clearfix" v-for="(cart_sku,index) in carts" v-cloak=v-cloak>
<li class="col01"><input type="checkbox" name="" v-model="cart_sku.selected" @change="update_selected(index)" /></li>
<li class="col02"><img src="{{ static('images/goods/goods003.jpg') }}" /></li>
<li class="col03">[[ cart_sku.name ]]</li>
<li class="col05">[[ cart_sku.price ]]元</li>
<li class="col06">
<div class="num_add">
<a @click="on_minus(index)" class="minus fl">-</a>
<input v-model="cart_sku.count" @blur="on_input(index)" type="text" class="num_show fl" />
<a @click="on_add(index)" class="add fl">+</a>
</div>
</li>
<li class="col07">[[ cart_sku.amount ]]元</li>
<li class="col08"><a @click="on_delete(index)">删除</a></li>
</ul>
// 初始化购物车数据并渲染界面
render_carts(){
// 渲染界面
this.carts = JSON.parse(JSON.stringify(cart_skus));
for (var i = 0; i < this.carts.length; i++) {
if (this.carts[i].selected == 'True') {
this.carts[i].selected = true;
} else {
this.carts[i].selected = false;
}
}
// 手动记录购物车的初始值,用于更新购物车失败时还原商品数量
this.carts_tmp = JSON.parse(JSON.stringify(cart_skus));
},
3.实际效果
三、更新购物车
1.后端逻辑代码
"""
一 把需求写下来 (前端需要收集什么 后端需要做什么)
前端: 要收集sku_id,count,selected 传递给后端
后端: 更新数据
二 把大体思路写下来(后端的大体思路)
指定更新哪里的数据
接收数据
验证数据
更新数据
返回相应
三 把详细思路完善一下(纯后端)
1.接收数据
2.验证数据
3.获取用户的信息
4.登陆用户更新redis数据
4.1 连接redis
4.2 hash
4.3 set
4.4 返回相应
5.未登录更新cookie数据
5.1 获取cart数据,并判断
5.2 更新指定数据
5.3 对字典数据进行处理,并设置cookie
5.4 返回相应
四 确定我们请求方式和路由
"""
def put(self,request):
# 1.接收数据
data = json.loads(request.body.decode())
# 2.验证数据
sku_id = data.get('sku_id')
count = data.get('count')
selected=data.get('selected')
# 2.验证数据
# 2.1 判断参数是否齐全
if not all([sku_id, count]):
return http.JsonResponse({'code': RETCODE.PARAMERR, 'errmsg': '参数不齐'})
# 2.2 判断商品是否存在
try:
sku = SKU.objects.get(pk=sku_id)
except SKU.DoesNotExist:
return http.JsonResponse({'code': RETCODE.NODATAERR, 'errmsg': "暂无次数据"})
# 2.3 数量需要是数值
try:
count = int(count)
except Exception as e:
return http.JsonResponse({'code': RETCODE.PARAMERR, 'errmsg': '参数错误'})
# 3.获取用户的信息
if request.user.is_authenticated:
# 4.登陆用户更新redis数据
# 4.1 连接redis
redis_conn = get_redis_connection('carts')
# 4.2 hash
redis_conn.hset('carts_%s'%request.user.id,sku_id,count)
# 4.3 set
if selected:
redis_conn.sadd('selected_%s'%request.user.id,sku_id)
else:
redis_conn.srem('selected_%s'%request.user.id,sku_id)
# 4.4 返回相应
cart_sku = {
'id': sku_id,
'count': count,
'selected': selected,
'name': sku.name,
'default_image_url': sku.default_image.url,
'price': sku.price,
'amount': sku.price * count,
}
return http.JsonResponse({'code':RETCODE.OK,'errmsg':'ok','cart_sku':cart_sku})
else:
# 5.未登录更新cookie数据
# 5.1 获取cart数据,并判断
cookie_str = request.COOKIES.get('carts')
if cookie_str is None:
carts = {}
else:
carts = pickle.loads(base64.b64decode(cookie_str))
# 5.2 更新指定数据
# {sku_id: {count:xxxx,selected:xxxx}}
if sku_id in carts:
carts[sku_id]={
'count':count,
'selected':selected
}
# 5.3 对字典数据进行处理,并设置cookie
en = base64.b64encode(pickle.dumps(carts))
# 5.4 返回相应
cart_sku = {
'id': sku_id,
'count': count,
'selected': selected,
'name': sku.name,
'default_image_url': sku.default_image.url,
'price': sku.price,
'amount': sku.price * count,
}
respnse = http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'ok', 'cart_sku': cart_sku})
respnse.set_cookie('carts',en)
return respnse
2.前台页面代码
// 减少操作
on_minus(index){
if (this.carts[index].count > 1) {
var count = this.carts[index].count - 1;
// this.carts[index].count = count; // 本地测试
this.update_count(index, count); // 请求服务器
}
},
// 增加操作
on_add(index){
var count = 1;
if (this.carts[index].count < 5) {
count = this.carts[index].count + 1;
} else {
count = 5;
alert('超过商品数量上限');
}
// this.carts[index].count = count; // 本地测试
this.update_count(index, count); // 请求服务器
},
// 更新购物车
update_count(index, count){
var url = this.host + '/carts/';
axios.put(url, {
sku_id: this.carts[index].id,
count: count,
selected: this.carts[index].selected
}, {
headers: {
'X-CSRFToken': getCookie('csrftoken')
},
responseType: 'json',
withCredentials: true
})
.then(response => {
if (response.data.code == '0') {
// this.carts[index].count = response.data.cart_sku.count; // 无法触发页面更新
Vue.set(this.carts, index, response.data.cart_sku); // 触发页面更新
// 重新计算界面的价格和数量
this.compute_total_selected_amount_count();
this.compute_total_count();
// 更新成功将新的购物车再次临时保存
this.carts_tmp = this.carts;
} else {
alert(response.data.errmsg);
this.carts[index].count = this.carts_tmp[index].count;
}
})
.catch(error => {
console.log(error.response);
this.carts[index].count = this.carts_tmp[index].count;
})
},
// 更新购物车选中数据
update_selected(index) {
var url = this.host + '/carts/';
axios.put(url, {
sku_id: this.carts[index].id,
count: this.carts[index].count,
selected: this.carts[index].selected
}, {
headers: {
'X-CSRFToken': getCookie('csrftoken')
},
responseType: 'json',
withCredentials: true
})
.then(response => {
if (response.data.code == '0') {
this.carts[index].selected = response.data.cart_sku.selected;
// 重新计算界面的价格和数量
this.compute_total_selected_amount_count();
this.compute_total_count();
} else {
alert(response.data.errmsg);
}
})
.catch(error => {
console.log(error.response);
})
},
3.实际效果
四、删除购物车
1.后端逻辑代码
"""
一 把需求写下来 (前端需要收集什么 后端需要做什么)
前端需要把商品id传递给后端
后端把指定的商品删除就可以
二 把大体思路写下来(后端的大体思路)
根据id进行删除
登陆用户删除redis
未登陆用户删除cookie
三 把详细思路完善一下(纯后端)
1.接收数据 sku_id
2.根据用户信息进行判断
3.登陆用户删除redis
3.1 连接redis
3.2 hash
3.3 set
3.4 返回相应
4.未登陆用户删除cookie
4.1 读取cookie中的数据,并且判断
4.2 删除数据
4.3 字典数据处理,并设置cookie
4.4 返回相应
四 确定我们请求方式和路由
"""
def delete(self,request):
# 1.接收数据 sku_id
data = json.loads(request.body.decode())
sku_id = data.get('sku_id')
# 2.根据用户信息进行判断
if request.user.is_authenticated:
# 3.登陆用户删除redis
# 3.1 连接redis
redis_conn = get_redis_connection('carts')
# 3.2 hash
redis_conn.hdel('carts_%s'%request.user.id,sku_id)
# 3.3 set
redis_conn.srem('selected_%s'%request.user.id,sku_id)
# 3.4 返回相应
return http.JsonResponse({'code':RETCODE.OK,'errmsg':'ok'})
else:
# 4.未登陆用户删除cookie
# 4.1 读取cookie中的数据,并且判断
cookie_str = request.COOKIES.get('carts')
if cookie_str is None:
carts = {}
else:
carts = pickle.loads(base64.b64decode(cookie_str))
# 4.2 删除数据
if sku_id in carts:
del carts[sku_id]
en = base64.b64encode(pickle.dumps(carts))
# 4.3 字典数据处理,并设置cookie
response = http.JsonResponse({'code':RETCODE.OK,'errmsg':'ok'})
response.set_cookie('carts',en.decode())
# 4.4 返回相应
return response
2.前台页面代码
// 删除购物车数据
on_delete(index){
var url = this.host + '/carts/';
axios.delete(url, {
data: {
sku_id: this.carts[index].id
},
headers: {
'X-CSRFToken': getCookie('csrftoken')
},
responseType: 'json',
withCredentials: true
})
.then(response => {
if (response.data.code == '0') {
this.carts.splice(index, 1);
// 重新计算界面的价格和数量
this.compute_total_selected_amount_count();
this.compute_total_count();
} else {
alert(response.data.errmsg);
}
})
.catch(error => {
console.log(error.response);
})
},
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)