文章目录
- 一、添加购物车
- 二、获取购物车
- 三、更新购物车
- 四、删除购物车
一、添加购物车
1.后端逻辑代码
代码语言:javascript
复制"""
一 前后端需求分析需求
前端需要收集: 商品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.前台请求接口代码
代码语言:javascript
复制// 加入购物车
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.后端逻辑代码
代码语言:javascript
复制"""
一 把需求写下来 (前端需要收集什么 后端需要做什么)
二 把大体思路写下来(后端的大体思路)
查询数据
展示
三 把详细思路完善一下(纯后端)
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.前台页面代码
代码语言:javascript
复制<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>
代码语言:javascript
复制// 初始化购物车数据并渲染界面
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.后端逻辑代码
代码语言:javascript
复制"""
一 把需求写下来 (前端需要收集什么 后端需要做什么)
前端: 要收集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.前台页面代码
代码语言:javascript
复制 // 减少操作
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.后端逻辑代码
代码语言:javascript
复制"""
一 把需求写下来 (前端需要收集什么 后端需要做什么)
前端需要把商品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.前台页面代码
代码语言:javascript
复制// 删除购物车数据
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);
})
},
五、合并购物车
代码语言:javascript
复制"""
我们登陆的时候合并 (普通登陆/QQ登陆的时候都能合并)
将cookie数据合并到redis中
1.获取到cookie数据
carts: {1:{count:10,selected:True},3:{count:10,selected:True}}
2.读取redis的数据
hash: {2:20,3:20}
set {2,3}
3.合并之后形成新的数据
3.1 对cookie数据进行遍历
合并的原则:
① cookie中有的,redis中没有的,则将cookie中的数据添加到redis中
② cookie中有的,redis中也有,count怎么办?
count以 cookie为主
③ 选中状态以cookie为主
hash 新增一个 {1:10}
hash 更新一个 {3:10}
选中的增加一个 [1,3]
选中的减少一个 []
4.把新的数据更新到redis中
{2:20,3:10,1:10}
{2,1}
5.删除cookie数据
"""
import base64
import pickle
from django_redis import get_redis_connection
def merge_cookie_to_redis(request,user,response):
# 1获取到cookie数据
cookie_str = request.COOKIES.get('carts')
if cookie_str is None:
return response
else:
carts = pickle.loads(base64.b64decode(cookie_str))
# carts: {1:{count:10,selected:True},3:{count:10,selected:True}}
#2.初始化数据
# 2.1 hash的字典数据最终要更新到redis中的 {sku_id:count,sku_id:count}
cookie_hash = {}
# 2.2 选中的列表和未选中的列表
selected_ids = []
remove_selected_ids=[]
# 3.合并之后形成新的数据
# 3.1 对cookie数据进行遍历
for sku_id,count_selected_dict in carts.items():
cookie_hash[sku_id]=count_selected_dict['count']
if count_selected_dict['selected']:
selected_ids.append(sku_id)
else:
remove_selected_ids.append(sku_id)
# 4.把新的数据更新到redis中
redis_conn = get_redis_connection('carts')
# {2:20,3:10,1:10}
# user = request.user
redis_conn.hmset('carts_%s'%user.id,cookie_hash)
# {2,1}
if len(selected_ids) > 0:
redis_conn.sadd('selected_%s'%user.id,*selected_ids)
if len(remove_selected_ids)>0:
redis_conn.srem('selected_%s'%user.id,*remove_selected_ids)
# 5.删除cookie数据
response.delete_cookie('carts')
return response