文章目录
- 07-天天生鲜首页
- 04_静态index页面和IndexView的调度说明
- 05_首页数据缓存设置和获取
- 缓存作用
- index视图与Django的缓存配置
- 更新时删除缓存
- 缓存信息
- 总结
- 11_redis存储购物车记录分析
- 08-天天生鲜详情页
- 1. 商品详情与添加浏览记录
- 2. 用户历史浏览记录的添加
- 3. 获取同一SPU的其他规格的商品信息
- 09-天天生鲜列表页
- 10-天天生鲜商品搜索
- 01_商品搜索_全文检索概念
- 02_商品搜索_全文检索框架和引擎的安装和配置
- 03_商品搜索_生成索引文件
- 04_商品搜索_全文检索的使用
- 05_商品搜索_更改分词方式
- 11. 购物车记录添加
- 01_商品详情页js代码
- 02_购物车记录添加后台view
- 03_购物车记录添加前端js
- 12-天天生鲜购物车记录更新
- 01_购物车js_全选_全不选_商品的选中和不选中
- 02_购物车记录更新_后台view
07-天天生鲜首页
首页index.html 静态化
nginx访问静态页面配置
修改
goods.models.py
from django.contrib import admin
# Register your models here.
from django.core.cache import cache
from apps.goods.models import GoodsType, IndexPromotionBanner, IndexGoodsBanner, IndexTypeGoodsBanner
class BaseModelAdmin(admin.ModelAdmin):
def save_model(self, request, obj, form, change):
"""
当促销页被管理员修改时,会触发modelAdmin自动的save_model方法
在这里需要重新生成index静态文件
"""
super(BaseModelAdmin, self).save_model(request, obj, form, change)
# 发出任务让celery重新生成静态页面
from celery_tasks.tasks import generate_static_index_html
generate_static_index_html.delay()
# 更新首页的缓存
cache.delete('index_page_data')
def delete_model(self, request, obj):
"""管理员删除时候更新index页面"""
super(BaseModelAdmin, self).delete_model(request, obj)
# 发出任务让celery重新生成静态页面
from celery_tasks.tasks import generate_static_index_html
generate_static_index_html.delay()
# 更新首页的缓存
cache.delete('index_page_data')
class IndexPromotionBannerAdmin(BaseModelAdmin):
pass
class IndexGoodsBannerAdmin(BaseModelAdmin):
pass
class GoodsTypeAdmin(BaseModelAdmin):
pass
class IndexTypeGoodsBannerAdmin(BaseModelAdmin):
pass
admin.site.register(GoodsType, GoodsTypeAdmin)
admin.site.register(IndexGoodsBanner, IndexGoodsBannerAdmin)
admin.site.register(IndexPromotionBanner, IndexPromotionBannerAdmin)
admin.site.register(IndexTypeGoodsBanner, IndexTypeGoodsBannerAdmin)
04_静态index页面和IndexView的调度说明
05_首页数据缓存设置和获取
缓存作用
index视图与Django的缓存配置
class IndexView(View):
"""首页"""
def get(self, request):
"""
显示首页,如果访问的是/index的话直接调用视图函数去重新查询一遍
如果直接访问域名的话,那么加载的是celery服务器中已经渲染好的html代码,不需要数据库重新 查询
当管理员更新后台的时候,会自动celery重新生成静态html网页,不影响使用
"""
# 尝试从缓存中获取数据
context = cache.get('index_page_data')
if context is None:
print('设置缓存')
# 获取商品的种类信息
types = GoodsType.objects.all()
# 获取轮播图信息
banners = IndexGoodsBanner.objects.all().order_by('index')
# 获取促销信息
promotion_banners = IndexPromotionBanner.objects.all().order_by('index')
# 获取首页分类商品展示信息
for type in types:
image_banners = IndexTypeGoodsBanner.objects.filter(type=type, display_type=1).order_by('index')
title_banners = IndexTypeGoodsBanner.objects.filter(type=type, display_type=1).order_by('index')
type.image_banners = image_banners
type.title_banners = title_banners
# 上面查询出来的结果都一样,设置缓存
context = {
'types': types,
'goods_banners': banners,
'promotion_banners': promotion_banners,
}
## 防止model更新时忘记删除缓存,信息永远缓存
cache.set('index_page_data', context, 3600)
print('缓存成功')
# 获取首页购物车的数目
if request.user.is_authenticated:
conn = get_redis_connection('default')
cart_key = 'cart_%s' % request.user.id
cart_count = conn.hlen(cart_key)
else:
cart_count = 0
context.update(cart_count=cart_count)
return render(request, 'index.html', context=context)
更新时删除缓存
# Django的缓存配置
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379/9",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
}
}
}
缓存信息
总结
11_redis存储购物车记录分析
08-天天生鲜详情页
1. 商品详情与添加浏览记录
2. 用户历史浏览记录的添加
3. 获取同一SPU的其他规格的商品信息
class DetailView(View):
"""商品详情界面"""
def get(self, request, goods_id):
# 1.商品详情
try:
sku = GoodsSKU.objects.get(id=goods_id)
except GoodsSKU.DoesNotExist as e:
return redirect(reverse('goods:index'))
# 获取商品的分类信息
types = GoodsType.objects.all()
# 获取商品的评论信息
sku_order = OrderGoods.objects.filter(sku=sku).exclude(comment='')
# 获取新品信息
new_skus = GoodsSKU.objects.filter(type=sku.type).order_by('-create_time')[:2]
# 3.获取同一spu下面的其他商品
same_spu_skus = GoodsSKU.objects.filter(goods=sku.goods).exclude(id=goods_id)
# 获取首页购物车的数目
cart_count = 0
if request.user.is_authenticated:
conn = get_redis_connection('default')
cart_key = 'cart_%s' % request.user.id
cart_count = conn.hlen(cart_key)
# 2.浏览历史中添加
conn = get_redis_connection('default')
history_key = 'history_%s' % request.user.id
# 移除列表中的goods_id如果已经存在, 大于0表示从左移除几个,等于0表示移除所有存在的元素
conn.lrem(history_key, 0, goods_id)
# 左侧进行插入
conn.lpush(history_key, goods_id)
# 只保存用户最新浏览的5条数据
conn.ltrim(history_key, 0, 4)
context = {
'sku': sku,
'sku_order': sku_order,
'types': types,
'new_skus': new_skus,
'cart_count': cart_count,
'same_spu_skus': same_spu_skus,
}
return render(request, 'detail.html', context)
{% extends 'base_detail_list.html' %}
{% load staticfiles %}
{% block title %}天天生鲜-商品详情{% endblock title %}
{% block main_content %}
<div class="breadcrumb">
<a href="#">全部分类</a>
<span>></span>
<a href="#">{{ sku.type.name }}</a>
<span>></span>
<a href="#">商品详情</a>
</div>
<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 same_spu_skus %}
<li><a href="{% url 'goods:detail' sku.id %}">{{ sku.name }}</a></li>
{% endfor %}
</ul>
</div>
<div class="total">总价:<em>16.80元</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>
<div class="main_wrap clearfix">
<div class="l_wrap fl clearfix">
<div class="new_goods">
<h3>新品推荐</h3>
<ul>
{% for sku in new_skus %}
<li>
<a href="{% url 'goods:detail' sku.id %}"><img src="{{ sku.image.url }}"></a>
<h4><a href="{% url 'goods:detail' sku.id %}">{{ sku.name }}</a></h4>
<div class="prize">¥{{ sku.price }}</div>
</li>
{% endfor %}
</ul>
</div>
</div>
<div class="r_wrap fr clearfix">
<ul class="detail_tab clearfix">
<li id='tag_detail' class="active">商品介绍</li>
<li id="tag_comment">评论</li>
</ul>
<div class="tab_content" id="tab_detail">
<dl>
<dt>商品详情:</dt>
<dd>{{ sku.goods.detail|safe }}</dd>
</dl>
</div>
<div class="tab_content" id="tab_comment" style="display: none">
<dl>
{% for order in sku_orders %}
<dt>评论时间:{{ order.update_time }} 用户名:{{ order.order.user.username }}</dt>
<dd>评论内容:{{ order.comment }}</dd>
{% endfor %}
</dl>
</div>
</div>
</div>
{% endblock main_content %}
{% block bottom %}
<div class="add_jump"></div>
{% endblock bottom %}
{% block bottomfiles %}
<script type="text/javascript" src="{% static 'js/jquery-1.12.4.min.js' %}"></script>
<script type="text/javascript">
$('#tag_detail').click(function () {
$('#tag_comment').removeClass('active')
$(this).addClass('active')
$('#tab_detail').show()
$('#tab_comment').hide()
})
$('#tag_comment').click(function () {
$('#tag_detail').removeClass('active')
$(this).addClass('active')
$('#tab_detail').hide()
$('#tab_comment').show()
})
update_goods_amount()
// 计算商品的总价格
function update_goods_amount() {
// 获取商品的单价和数量
price = $('.show_pirze').children('em').text()
count = $('.num_show').val()
// 计算商品的总价
price = parseFloat(price)
count = parseInt(count)
amount = price*count
// 设置商品的总价,保存两位小数
$('.total').children('em').text(amount.toFixed(2)+'元')
}
// 增加商品的数量
$('.add').click(function () {
// 获取商品原有的数目
count = $('.num_show').val()
// 加1
count = parseInt(count)+1
// 重新设置商品的数目
$('.num_show').val(count)
// 更新商品的总价
update_goods_amount()
})
// 减少商品的数量
$('.minus').click(function () {
// 获取商品原有的数目
count = $('.num_show').val()
// 减1
count = parseInt(count)-1
if (count <= 0){
count = 1
}
// 重新设置商品的数目
$('.num_show').val(count)
// 更新商品的总价
update_goods_amount()
})
// 手动输入商品的数量
$('.num_show').blur(function () {
// 获取用户输入的数目
count = $(this).val()
// 校验count是否合法
if (isNaN(count) || count.trim().length==0 || parseInt(count) <=0){
count = 1
}
// 重新设置商品的数目
$(this).val(parseInt(count))
// 更新商品的总价
update_goods_amount()
})
// 获取add_cart div元素左上角的坐标
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_cart').click(function(){
// 获取商品id和商品数量
sku_id = $(this).attr('sku_id') // attr prop
count = $('.num_show').val()
{# 手动添加csrf进行ajax的post请求 #}
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>
{% endblock bottomfiles %}
09-天天生鲜列表页
# /list/类型id/页码/?sort=排序方式
class ListView(View):
"""列表页"""
def get(self, request, type_id, page):
# 先验证种类信息
try:
type = GoodsType.objects.get(id=type_id)
except GoodsType.DoesNotExist as e:
return redirect(reverse('goods:index'))
# 获取下拉的全部种类信息
types = GoodsType.objects.all()
# 获取排序的方式
sort = request.GET.get('sort')
if sort == 'price':
skus = GoodsSKU.objects.filter(type=type).order_by('price')
elif sort == 'hot':
skus = GoodsSKU.objects.filter(type=type).order_by('-sales')
else:
sort = 'default'
skus = GoodsSKU.objects.filter(type=type).order_by('-id')
# 对skus数据进行分页
paginator = Paginator(skus, 1)
# 获取第page页的内容
try:
page = int(page)
except Exception as e:
page = 1
if page > paginator.num_pages:
page = 1
# 获取第page页的page对象,废弃因为会加载所有的页码
skus_page = paginator.page(page)
# 控制限制的页码,只显示最多5个按钮
# 如果总页数小于5,显示[1-页码]
# 如果当前页是前三页,显示[1,2,3,4,5]
# 如果当前页是后三页,显示[4,5,6,7,8] num_pages-4 到num_oages+1
num_pages = paginator.num_pages
if num_pages < 5:
pages = range(1, num_pages + 1)
elif page <= 3:
pages = range(1, 6)
elif num_pages - page <= 2:
pages = range(num_pages - 4, num_pages + 1)
else:
pages = range(page - 2, page + 3)
# 通过page对象获取数据
# 获取新品信息
new_skus = GoodsSKU.objects.filter(type=type).order_by('-create_time')[:2]
# 获取首页购物车的数目
cart_count = 0
if request.user.is_authenticated:
conn = get_redis_connection('default')
cart_key = 'cart_%s' % request.user.id
cart_count = conn.hlen(cart_key)
context = {
"sort": sort,
"type": type,
"types": types,
"skus_page": skus_page, #会加载所有的页码
"new_skus": new_skus,
"cart_count": cart_count,
"pages": pages,
}
return render(request, 'list.html', context)
{% extends 'base_detail_list.html' %}
{% block title %}天天生鲜-商品列表{% endblock title %}
{% block main_content %}
<div class="breadcrumb">
<a href="#">全部分类</a>
<span>></span>
<a href="#">{{ type.name }}</a>
</div>
<div class="main_wrap clearfix">
<div class="l_wrap fl clearfix">
<div class="new_goods">
<h3>新品推荐</h3>
<ul>
{% for sku in new_skus %}
<li>
<a href="{% url 'goods:detail' sku.id %}"><img src="{{ sku.image.url }}"></a>
<h4><a href="{% url 'goods:detail' sku.id %}">{{ sku.name }}</a></h4>
<div class="prize">¥{{ sku.price }}</div>
</li>
{% endfor %}
</ul>
</div>
</div>
<div class="r_wrap fr clearfix">
<div class="sort_bar">
<a href="{% url 'goods:list' type.id 1 %}" {% if sort == 'default' %}class="active"{% endif %}>默认</a>
<a href="{% url 'goods:list' type.id 1 %}?sort=price" {% if sort == 'price' %}class="active"{% endif %}>价格</a>
<a href="{% url 'goods:list' type.id 1 %}?sort=hot" {% if sort == 'hot' %}class="active"{% endif %}>人气</a>
</div>
<ul class="goods_type_list clearfix">
{% for sku in skus_page %}
<li>
<a href="{% url 'goods:detail' sku.id %}"><img src="{{ sku.image.url }}"></a>
<h4><a href="{% url 'goods:detail' sku.id %}">{{ sku.name }}</a></h4>
<div class="operate">
<span class="prize">¥{{ sku.price }}</span>
<span class="unit">{{ sku.price}}/{{ sku.unite }}</span>
<a href="#" class="add_goods" title="加入购物车"></a>
</div>
</li>
{% endfor %}
</ul>
<div class="pagenation">
{% if skus_page.has_previous %}
<a href="{% url 'goods:list' type.id skus_page.previous_page_number %}?sort={{ sort }}"><上一页</a>
{% endif %}
{% for pindex in pages %}
{% if pindex == skus_page.number %}
<a href="{% url 'goods:list' type.id pindex %}?sort={{ sort }}" class="active">{{ pindex }}</a>
{% else %}
<a href="{% url 'goods:list' type.id pindex %}?sort={{ sort }}">{{ pindex }}</a>
{% endif %}
{% endfor %}
{% if skus_page.has_next %}
<a href="{% url 'goods:list' type.id skus_page.next_page_number %}?sort={{ sort }}">下一页></a>
{% endif %}
</div>
</div>
</div>
{% endblock main_content %}
10-天天生鲜商品搜索
01_商品搜索_全文检索概念
02_商品搜索_全文检索框架和引擎的安装和配置
settings.py
# 全文检索框架的配置
HAYSTACK_CONNECTIONS = {
'default': {
# 使用whoosh引擎
# 'ENGINE': 'haystack.backends.whoosh_backend.WhooshEngine',
'ENGINE': 'haystack.backends.whoosh_cn_backend.WhooshEngine',
# 索引文件路径
'PATH': os.path.join(BASE_DIR, 'whoosh_index'),
}
}
#设置分页显示的数据量
HAYSTACK_SEARCH_RESULTS_PER_PAGE = 1
# 当添加、修改、删除数据时,自动生成索引
HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor'
03_商品搜索_生成索引文件
python manage.py rebuild_index
04_商品搜索_全文检索的使用
05_商品搜索_更改分词方式
通过关键词查询,但无法通过详情、简介查询,所以更改其分词方式
添加ChineseAnalyzer.py
import jieba
from whoosh.analysis import Tokenizer, Token
class ChineseTokenizer(Tokenizer):
def __call__(self, value, positions=False, chars=False,
keeporiginal=False, removestops=True,
start_pos=0, start_char=0, mode='', **kwargs):
t = Token(positions, chars, removestops=removestops, mode=mode,**kwargs)
seglist = jieba.cut(value, cut_all=True)
for w in seglist:
t.original = t.text = w
t.boost = 1.0
if positions:
t.pos = start_pos + value.find(w)
if chars:
t.startchar = start_char + value.find(w)
t.endchar = start_char + value.find(w) + len(w)
yield t
def ChineseAnalyzer():
return ChineseTokenizer()
11. 购物车记录添加
01_商品详情页js代码
update_goods_amount()
// 计算商品的总价格
function update_goods_amount() {
// 获取商品的单价和数量
price = $('.show_pirze').children('em').text()
count = $('.num_show').val()
// 计算商品的总价
price = parseFloat(price)
count = parseInt(count)
amount = price*count
// 设置商品的总价,保存两位小数
$('.total').children('em').text(amount.toFixed(2)+'元')
}
// 增加商品的数量
$('.add').click(function () {
// 获取商品原有的数目
count = $('.num_show').val()
// 加1
count = parseInt(count)+1
// 重新设置商品的数目
$('.num_show').val(count)
// 更新商品的总价
update_goods_amount()
})
// 减少商品的数量
$('.minus').click(function () {
// 获取商品原有的数目
count = $('.num_show').val()
// 减1
count = parseInt(count)-1
if (count <= 0){
count = 1
}
// 重新设置商品的数目
$('.num_show').val(count)
// 更新商品的总价
update_goods_amount()
})
// 手动输入商品的数量
$('.num_show').blur(function () {
// 获取用户输入的数目
count = $(this).val()
// 校验count是否合法
if (isNaN(count) || count.trim().length==0 || parseInt(count) <=0){
count = 1
}
// 重新设置商品的数目
$(this).val(parseInt(count))
// 更新商品的总价
update_goods_amount()
})
02_购物车记录添加后台view
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 as e:
return JsonResponse({'res': 3, 'errmsg': '商品不存在'})
# 添加购物车记录, redis购物车使用的是hash类型保存的,用到hget获取key中的字典
conn = get_redis_connection('default')
cart_key = 'cart_%d' % user.id
cart_count = conn.hget(cart_key, sku_id)
if cart_count:
count += int(cart_count)
# sku_id存在即更新,不存在则新建
conn.hset(cart_key, sku_id, count)
# 验证商品的库存
if count > sku.stock:
return JsonResponse({'res': 4, 'errmsg': '商品库存不足'})
# 获取购物车的数量
total_count = conn.hlen(cart_key)
# 返回应答
return JsonResponse({'res': 5, 'total_count': total_count, 'errmsg': '添加成功'})
03_购物车记录添加前端js
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 as e:
return JsonResponse({'res': 3, 'errmsg': '商品不存在'})
# 添加购物车记录, redis购物车使用的是hash类型保存的,用到hget获取key中的字典
conn = get_redis_connection('default')
cart_key = 'cart_%d' % user.id
cart_count = conn.hget(cart_key, sku_id)
if cart_count:
count += int(cart_count)
# sku_id存在即更新,不存在则新建
conn.hset(cart_key, sku_id, count)
# 验证商品的库存
if count > sku.stock:
return JsonResponse({'res': 4, 'errmsg': '商品库存不足'})
# 获取购物车的数量
total_count = conn.hlen(cart_key)
# 返回应答
return JsonResponse({'res': 5, 'total_count': total_count, 'errmsg': '添加成功'})
{% block bottomfiles %}
<script type="text/javascript" src="{% static 'js/jquery-1.12.4.min.js' %}"></script>
<script type="text/javascript">
$('#tag_detail').click(function () {
$('#tag_comment').removeClass('active')
$(this).addClass('active')
$('#tab_detail').show()
$('#tab_comment').hide()
})
$('#tag_comment').click(function () {
$('#tag_detail').removeClass('active')
$(this).addClass('active')
$('#tab_detail').hide()
$('#tab_comment').show()
})
update_goods_amount()
// 计算商品的总价格
function update_goods_amount() {
// 获取商品的单价和数量
price = $('.show_pirze').children('em').text()
count = $('.num_show').val()
// 计算商品的总价
price = parseFloat(price)
count = parseInt(count)
amount = price*count
// 设置商品的总价,保存两位小数
$('.total').children('em').text(amount.toFixed(2)+'元')
}
// 增加商品的数量
$('.add').click(function () {
// 获取商品原有的数目
count = $('.num_show').val()
// 加1
count = parseInt(count)+1
// 重新设置商品的数目
$('.num_show').val(count)
// 更新商品的总价
update_goods_amount()
})
// 减少商品的数量
$('.minus').click(function () {
// 获取商品原有的数目
count = $('.num_show').val()
// 减1
count = parseInt(count)-1
if (count <= 0){
count = 1
}
// 重新设置商品的数目
$('.num_show').val(count)
// 更新商品的总价
update_goods_amount()
})
// 手动输入商品的数量
$('.num_show').blur(function () {
// 获取用户输入的数目
count = $(this).val()
// 校验count是否合法
if (isNaN(count) || count.trim().length==0 || parseInt(count) <=0){
count = 1
}
// 重新设置商品的数目
$(this).val(parseInt(count))
// 更新商品的总价
update_goods_amount()
})
// 获取add_cart div元素左上角的坐标
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_cart').click(function(){
// 获取商品id和商品数量
sku_id = $(this).attr('sku_id') // attr prop
count = $('.num_show').val()
alert(sku_id+":"+count)
{# 手动添加csrf进行ajax的post请求 #}
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>
{% endblock bottomfiles %}
12-天天生鲜购物车记录更新
01_购物车js_全选_全不选_商品的选中和不选中
cart.html 选中js文件
<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()
})
02_购物车记录更新_后台view
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 as e:
return JsonResponse({'res': 2, 'errmsg': '商品不存在'})
# 更新购物车数量
conn = get_redis_connection('default')
cart_key = 'cart_%d' % user.id
# sku_id存在即更新,不存在则新建
conn.hdel(cart_key, sku_id)
# 返回应答
return JsonResponse({'res': 3, 'message': '删除成功'})
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 as e:
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': '商品库存不足'})
# sku_id存在即更新,不存在则新建
conn.hset(cart_key, sku_id, count)
# 返回应答
return JsonResponse({'res': 5, 'errmsg': '更新成功'})
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 as e:
return JsonResponse({'res': 3, 'errmsg': '商品不存在'})
# 添加购物车记录, redis购物车使用的是hash类型保存的,用到hget获取key中的字典
conn = get_redis_connection('default')
cart_key = 'cart_%d' % user.id
cart_count = conn.hget(cart_key, sku_id)
if cart_count:
count += cart_count
# sku_id存在即更新,不存在则新建
conn.hset(cart_key, sku_id, count)
# 验证商品的库存
if count > sku.stock:
return JsonResponse({'res': 4, 'errmsg': '商品库存不足'})
# 获取购物车的数量
total_count = conn.hlen(cart_key)
# 返回应答
return JsonResponse({'res': 5, 'total_count': total_count, 'errmsg': '添加成功'})
class CartInfoView(LoginRequiredMixin, View):
"""购物车页面"""
def get(self, request):
# 获取登录的用户
user = request.user
# redis获取用户购物车信息
conn = get_redis_connection('default')
cart_key = 'cart_%s' % user.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添加属性
sku.amount = amount
sku.count = count
# 添加到列表
skus.append(sku)
total_count += count
total_price += amount
context = {
'skus': skus,
'total_count': total_count,
'total_price': total_price,
}
render(request, 'cart.html', context)