购物车

  • 购物车时多对多的关系

    • 用户
    • 商品
  • 订单

问题:一个购物车有多个商品,只买其中一部分商品,如何将这多个商品变成一个订单
答:一个订单对应的是多个已经购买的商品,一个商品只对应一个订单,而不是一个商品可以对应多个订单,比如两个人同时在一家店都买了两个苹果,那么第一个人买的苹果和第二个人买的苹果不是相同的。

  • 表关系

    • 订单表

      • 属于哪个用户
    • 订单商品表

      • 订单商品表的数据从购物车里下单的来,下单时,将购物车中对应的商品删除,放入订单表中
    • 地址

      • 每个订单对应一个地址
      • 一个地址可以对应多个订单
      • 订单会级联收获地址表
    • 优惠卷
  • 添加购物车

    • 需要用户

      • 如果用户未登录,直接跳转到登录界面
    • 需要商品

      • 传递商品唯一标识
    • 添加的合法性

      • 新商品首次加入购物车,数据不存在,创建购物车数据
      • 此商品数据在购物车中存在,直接将数量加一。

购物车模块表单之间的关系图:

这里的关系图只是其中一部分,还有其他比如评价表,优惠卷,积分表表等等,可以额外扩展。

下面我们先来实现,Goods,Cart,User,order,ordergoods这五张表。

Models

Cart

class Cart(models.Model):
    c_user = models.ForeignKey(peiqi1user)
    c_goods = models.ForeignKey(Goods)
    c_goods_num = models.IntegerField(default=1)
    c_is_select = models.BooleanField(default=True)
    
    class Meta:
        db_table = 'peiqi1_cart'

然后迁移数据库。

接着我们要将商城中商品的数量控制按钮完善好:
market.css:

.subShopping .addShopping {
    font-size: 0.4rem;
    font-weight: 100;
}

market.html:(76-80)给按钮标签添加选择器

 <section>
        <button class="subShopping">—</button>
        <span>0</span>
        <button class="addShopping">+</button>
</section>

在market.js中添加点击事件:

$(function () {
    $('#all_types').click(function(){
        console.log('全部类型');

        var $all_types_container = $('#all_types_container'); /* 找到all_types_container的div标签 */

        $all_types_container.show();  /* 显示 */

        var $all_type = $(this);  /* 找到箭头的位置 */

        var $span = $all_type.find('span').find('span'); /* 取出箭头对应的class */

        $span.removeClass('glyphicon-chevron-down').addClass('glyphicon-chevron-up');


        /* 打开全部分类时,收起综合排序 */
        var $sort_rule_container = $('#sort_rule_container');

        $sort_rule_container.slideUp();

        var $sort_rule = $('#sort_rule');

        var $span_sort_rule = $sort_rule.find('span').find('span');

        $span_sort_rule.removeClass('glyphicon-chevron-up').addClass('glyphicon-chevron-down');

    })

    $('#all_types_container').click(function () {
        var $all_type_container = $(this);

        $all_type_container.hide();

        var $all_type = $('#all_types');  /* 找到箭头的位置 */

        var $span = $all_type.find('span').find('span'); /* 取出箭头对应的class */

        $span.removeClass('glyphicon-chevron-up').addClass('glyphicon-chevron-down');

        })

    $('#sort_rule').click(function(){
        console.log('排序规则');

        var $sort_rule_container = $("#sort_rule_container");

        $sort_rule_container.slideDown() /* 下弹特效显示 */
        var $sort_rule = $(this);

        var $span = $sort_rule.find('span').find('span');

        $span.removeClass('glyphicon-chevron-down').addClass('glyphicon-chevron-up');

        /* 打开综合排序,自动关闭全部分类 */
        var $all_type_container = $('#all_types_container');

        $all_type_container.hide();

        var $all_type = $('#all_types');  /* 找到箭头的位置 */

        var $span_all_type = $all_type.find('span').find('span'); /* 取出箭头对应的class */

        $span_all_type.removeClass('glyphicon-chevron-up').addClass('glyphicon-chevron-down');
    })

    $('#sort_rule_container').click(function () {  /* 点击事件,上拉收起菜单 */
        var $sort_rule_container = $(this);
        $sort_rule_container.slideUp();

        var $sort_rule = $('#sort_rule');

        var $span = $sort_rule.find('span').find('span');

        $span.removeClass('glyphicon-chevron-up').addClass('glyphicon-chevron-down');
    })
    $('.subShopping').click(function (){
        console.log('sub');
    })
    $('.addShopping').click(function (){
        console.log('add');
    })
})

生成购物车数据,即商品数据和用户的关系,哪个商品,登录的是哪个用户,然后生成一条购物车数据,还要考虑这条数据在我们的购物车中有没有,有的话改变数量,没有就重新建。

商品的唯一标识是id,用户的唯一标识id、token、session都可以。

下面我们要实现购物车中商品数量的改变,就要在js中获得对应商品的id:

market.html:

<button class="subShopping">—</button>
<span>0</span>
<button goodsid="{{ goods.id }}" class="addShopping">+</button>

market.js:

    $('#sort_rule_container').click(function () {  /* 点击事件,上拉收起菜单 */
        var $sort_rule_container = $(this);
        $sort_rule_container.slideUp();

        var $sort_rule = $('#sort_rule');

        var $span = $sort_rule.find('span').find('span');

        $span.removeClass('glyphicon-chevron-up').addClass('glyphicon-chevron-down');
    })
    $('.subShopping').click(function (){
        console.log('sub');
    })
    $('.addShopping').click(function (){
        console.log('add');
        
        var $add = $(this);
        var goodsid = $add.attr('goodsid');  /* 获得goodsid */
        /*将goodsid发送给后端*/
        $.get('/peiqi1/addtocart/', {'goodsid':goodsid}, function(data){
            console.log(data);
        })
    })
})

添加url:

    url(r'^addtocart/', views.addtocart, name='add_to_cart'),

函数封装判断用户登录

要改动购物车中商品的数量,保证用户要登录

所以我们要验证用户是在登录状态下对购物车进行操作:
views: redirect重定向的行为是浏览器才能识别详细请看下方浏览器行为介绍

def addtocart(request):

    goodsid = request.GET.get('goodsid')

    # print(request.user)
    user_id = request.session.get('user_id')

    if user_id:
        return HttpResponse('ADD SUCESS')
    else:  
        # 因为我们是利用ajax请求来返回数据,所以直接使用redirect  ajax不会识别出这个是重定向请求,所以只会在控制台打印出html代码
        data = {
          'status': 302,
          'msg': 'not login'
      }
        return JsonResponse(data=data)

market.js:

$(function () {
    $('#all_types').click(function(){
        console.log('全部类型');

        var $all_types_container = $('#all_types_container'); /* 找到all_types_container的div标签 */

        $all_types_container.show();  /* 显示 */

        var $all_type = $(this);  /* 找到箭头的位置 */

        var $span = $all_type.find('span').find('span'); /* 取出箭头对应的class */

        $span.removeClass('glyphicon-chevron-down').addClass('glyphicon-chevron-up');


        /* 打开全部分类时,收起综合排序 */
        var $sort_rule_container = $('#sort_rule_container');

        $sort_rule_container.slideUp();

        var $sort_rule = $('#sort_rule');

        var $span_sort_rule = $sort_rule.find('span').find('span');

        $span_sort_rule.removeClass('glyphicon-chevron-up').addClass('glyphicon-chevron-down');

    })

    $('#all_types_container').click(function () {
        var $all_type_container = $(this);

        $all_type_container.hide();

        var $all_type = $('#all_types');  /* 找到箭头的位置 */

        var $span = $all_type.find('span').find('span'); /* 取出箭头对应的class */

        $span.removeClass('glyphicon-chevron-up').addClass('glyphicon-chevron-down');

        })

    $('#sort_rule').click(function(){
        console.log('排序规则');

        var $sort_rule_container = $("#sort_rule_container");

        $sort_rule_container.slideDown() /* 下弹特效显示 */
        var $sort_rule = $(this);

        var $span = $sort_rule.find('span').find('span');

        $span.removeClass('glyphicon-chevron-down').addClass('glyphicon-chevron-up');

        /* 打开综合排序,自动关闭全部分类 */
        var $all_type_container = $('#all_types_container');

        $all_type_container.hide();

        var $all_type = $('#all_types');  /* 找到箭头的位置 */

        var $span_all_type = $all_type.find('span').find('span'); /* 取出箭头对应的class */

        $span_all_type.removeClass('glyphicon-chevron-up').addClass('glyphicon-chevron-down');
    })

    $('#sort_rule_container').click(function () {  /* 点击事件,上拉收起菜单 */
        var $sort_rule_container = $(this);
        $sort_rule_container.slideUp();

        var $sort_rule = $('#sort_rule');

        var $span = $sort_rule.find('span').find('span');

        $span.removeClass('glyphicon-chevron-up').addClass('glyphicon-chevron-down');
    })
    $('.subShopping').click(function (){
        console.log('sub');
    })
    $('.addShopping').click(function (){
        console.log('add');

        var $add = $(this);
        var goodsid = $add.attr('goodsid');  /* 获得goodsid */
        /*将goodsid发送给后端*/
        $.get('/peiqi1/addtocart/', {'goodsid':goodsid}, function(data){
            console.log(data);

            if (data['status'] === 302){   /* 如果状态是302,那么就 */
                window.open('/peiqi1/login/', target='_self')
            }
        })
    })
})

82-93行判断如果返回302,也就是用户未登录的状态下,那么就重定向到登录界面

中间件封装判断用户登录

这里我们也可以用中间件封装用户登录
新建middleware 的python包,然后在该文件夹中新建Middleware的python文件
middleware.py:

from django.http import JsonResponse
from django.shortcuts import redirect
from django.urls import reverse
from django.utils.deprecation import MiddlewareMixin

from App.models import peiqi1user

# 需要验证登录的接口
REQUIRE_LOGIN = [
    '/peiqi1/addtocart/',
]


class LoginMiddleware(MiddlewareMixin):
    def process_request(self, request):
        if request.path in REQUIRE_LOGIN:  # 如果登录的用户在列表中就执行下方登录操作

            user_id = request.session.get('user_id')  # 获取用户登录状态信息

            if user_id:  # 如果user_id存在
                try:
                    user = peiqi1user.objects.get(pk=user_id)

                    request.user = user  # 这样之后的操作就不需要去数据库中进行查询
                except:  # 出现异常则重新登录
                    # return redirect(reverse('peiqi1:login'))
                    data = {
                      'status': 302,
                      'msg': 'user not avaliable',
                    }
                    return JsonResponse(data=data)
            else:   # 如果id不存在则返回登陆界面
                # return redirect(reverse('peiqi1:login'))
                data = {
                    'status': 302,
                    'msg': 'user not login',
                }
                return JsonResponse(data=data)

然后在settings中注册该文件:
srttings.py:

MIDDLEWARE = [
    'debug_toolbar.middleware.DebugToolbarMiddleware',
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'middleware.middleware.LoginMiddleware',
]

这样我们只要能执行addtocart函数的对象,都是经过中间件验证的登录成功的对象:
views:

def addtocart(request):

    goodsid = request.GET.get('goodsid')

    print(request.user)  # 打印出用户

    data = {
        'status': 200,

    }

    return JsonResponse(data=data)

商品数量增/删实现

增实现

url:

url(r'^addtocart/', views.addtocart, name='add_to_cart'),

在改动商品数量之前我们还要判断商品是否已经存在在购物车中:
views:

def addtocart(request):
    goodsid = request.GET.get('goodsid')
    # 从数据库中取出对应用户的购物车
    carts = Cart.objects.filter(c_user=request.user).filter(c_goods_id=goodsid)
    if carts.exists():  # 如果商品在购物车表单中存在
        cart_obj = carts.first()
        cart_obj.c_goods_num = cart_obj.c_goods_num + 1
    else:
        cart_obj = Cart()
        cart_obj.c_goods_id = goodsid  # 添加到商品表单中
        cart_obj.c_user = request.user

    cart_obj.save()

    data = {
        'status': 200,
        'msg': 'add_success',
        'c_goods_num': cart_obj.c_goods_num
    }

    return JsonResponse(data=data)

测试:
当我们点击加号时,控制台会返回添加成功

并且当我们观察数据库时,发现cart表中生成了对应的商品,及其数量:

减实现

url:

url(r'^subtocart/', views.subtocart, name='sub_to_cart'),

views:
记得subtocart函数要放入中间件的用户验证中!!!否则会报TypeError: 'AnonymousUser' object is not iterable

def subtocart(request):
    goodsid = request.GET.get('goodsid')
    # 从数据库中取出对应用户的购物车
    carts = Cart.objects.filter(c_user=request.user).filter(c_goods_id=goodsid)
    if carts.exists():  # 如果商品在购物车表单中存在
        cart_obj = carts.first()
        data = {
            'status': 200,
            'msg': 'sub_success',
            'c_goods_num': cart_obj.c_goods_num
        }
        if cart_obj.c_goods_num > 1:  # 如果数量大于1就减少1
            cart_obj.c_goods_num = cart_obj.c_goods_num - 1
            cart_obj.save()
            data['c_goods_num'] = cart_obj.c_goods_num
            return JsonResponse(data=data)
        else:  # 如果数量小于等于1就从数据库中删除
            cart_obj.delete()
            data['c_goods_num'] = 0
            return JsonResponse(data=data)
    else:  # 如果数量为0,则直接返回0
        return JsonResponse(data={'status': 200, 'msg': 'sub_success', 'c_goods_num': 0})

market.js:

    $('.subShopping').click(function (){
        console.log('sub');
        var $sub =$(this);
        var goodsid = $sub.attr('goodsid');
        $.get('peiqi1/subtocart/',{'goodsid':goodsid}, function (data){
            console.log(data);
            if (data['status'] === 302){   /* 如果状态是302,那么就重定向到登陆界面 */
                window.open('/peiqi1/login/',target='_self');
            }else if(data['status'] === 200){
                $sub.next('span').html(data['c_goods_num']);   /*前端前端对应商品的数量标签*/
            }
        })

    })

前端商品数量同步显示

现在我们要将商品的数量同步显示在用户的前端页面上:
market.js:

    $('.addShopping').click(function (){
        console.log('add');

        var $add = $(this);
        var goodsid = $add.attr('goodsid');  /* 获得goodsid */
        /*将goodsid发送给后端*/
        $.get('/peiqi1/addtocart/', {'goodsid':goodsid}, function(data){
            console.log(data);

            if (data['status'] === 302){   /* 如果状态是302,那么就重定向到登陆界面 */
                window.open('/peiqi1/login/', target='_self');
            }else if(data['status']===200){
                $add.prev('span').html(data['c_goods_num']);  /*前端前端对应商品的数量标签*/
            }
        })
    })

测试:

Cart页面检查是否登录

只有登录的用户可以进入购物车页面,未登录者跳转到登录页面

之前我们在中间件中实现了ajax中验证用户是否登录
为了实现这个功能,我们需要在中间件中添加一个新的功能,用来验证用户在访问一个普通页面时是否登录:

REQUIRE_LOGIN_JSON = [
    '/peiqi1/addtocart/',
]

REQUIRE_LOGIN = [
    '/peiqi1/cart/',
]


class LoginMiddleware(MiddlewareMixin):
    def process_request(self, request):
        if request.path in REQUIRE_LOGIN_JSON:  # 如果登录的用户在列表中就执行下方登录操作

            user_id = request.session.get('user_id')  # 获取用户登录状态信息

            if user_id:  # 如果user_id存在
                try:
                    user = peiqi1user.objects.get(pk=user_id)

                    request.user = user  # 这样之后的操作就不需要去数据库中进行查询
                except:  # 出现异常则重新登录
                    # return redirect(reverse('peiqi1:login'))
                    data = {
                      'status': 302,
                      'msg': 'user not avaliable',
                    }
                    return JsonResponse(data=data)
            else:   # 如果id不存在则返回登陆界面
                # return redirect(reverse('peiqi1:login'))
                data = {
                    'status': 302,
                    'msg': 'user not login',
                }
                return JsonResponse(data=data)

        if request.path in REQUIRE_LOGIN:
            user_id = request.session.get('user_id')  # 获取用户登录状态信息

            if user_id:  # 如果user_id存在
                try:
                    user = peiqi1user.objects.get(pk=user_id)

                    request.user = user  # 这样之后的操作就不需要去数据库中进行查询
                except:  # 出现异常则重新登录
                    return redirect(reverse('peiqi1:login'))

            else:  # 如果id不存在则返回登陆界面
                return redirect(reverse('peiqi1:login'))

Cart页面的搭建

下面我们要将商品在cart页面中展示出来:
views:

def cart(request):
    
    carts = Cart.objects.filter(c_user=request.user)
    
    data = {
        'title': '购物车',
        'carts': carts
    }
    
    return render(request, 'main/cart.html', context=data)

cart.css:

/* 底部图标 */
footer .cart span {
    background: url(/static/img/cart_selected.png) no-repeat;
    background-size: 0.513889rem;
}

footer .cart dd{
    color: orange;
}



/*
    Container
*/

#cart {
    padding: 1.5rem 0;
    z-index: +15;
    width: 100%;
    background: #fafafa;
    overflow: hidden;
}

h3 {
    text-align: center;
    position: fixed;
    z-index: +100;
    width: 100%;
    border-bottom: 0.04rem solid lightgrey;
    line-height: 1.5rem;
    background: yellow;
    top: 0;
}



.full > section {
    background: lightpink;
}

.full > section > ul {
    border: 0.2rem dashed lightgreen;
    border-width: 0.1rem 0;
    margin-bottom: 0.2rem;
}

.full > section > ul > li {
    padding-left: 0.3rem;
    line-height: 0.8rem;
    font-size: 0.375rem;
}

.clear:after {
    content: "";
    display: block;
    visibility: hidden;
    clear: both;
    height: 0;
}

.full > section > ul > li > div > p:last-child {
    float: right;
    width: 78%;
}

.full > section > ul > li > div > p:last-child > span {
    padding: 0.15rem;
}

.infoJustify {
    float: left;
    width: 20%;
    height: 0.8rem;
    overflow: hidden;
    text-align: justify;
}

.infoJustify > b {
    display: inline-block;
    width: 100%;
}

.change {
    float: right;
    padding-right: 0.2rem;
}

/*
    闪送超市
 */
.bill {
    line-height: 0.75rem;
    position: relative;
    border-bottom: 0.04rem solid lightgrey;
}

.bill > p {
    padding: 0 0.3rem;
    font-size: 0.3rem;
    color: green;
}

.bill > p:first-child:before {
    width: 0.2rem;
    height: 0.3rem;
    background: yellow;
    content: ".";
    color: yellow;
    margin-right: 0.2rem;
}

.bill > a {
    border: 0.05rem solid orangered;
    position: absolute;
    border-radius: 0.3rem;
    padding: 0 0.3rem;
    font-size: 0.35rem;
    line-height: 0.65rem;
    top: 0.2rem;
    right: 0.5rem;
}

/*************收货时间*****收货备注************/
.delivery {
    line-height: 1.5rem;
    border-bottom: 0.04rem solid lightgrey;
    font-size: 0.4rem;
    padding: 0 0.3rem;
}

.delivery > span:first-child {
    padding-right: 0.3rem;
}

.delivery > span:nth-child(2) {
    color: orangered;
}

.delivery > a:last-child {
    float: right;
}

.delivery > input {
    height: 0.8rem;
    line-height: 0.8rem;
    border-radius: 0.1rem;
    border-width: 0.04rem;
    width: 70%;
}

/**************menu***************/
.menuList {
    border-bottom: 0.04rem solid lightgrey;
    height: 2.5rem;
    position: relative;
}

.confirm, .all_select{
    padding: 0.95rem 0;
    width: 15%;
    height: 2.5rem;
    display: inline-block;
    box-sizing: border-box;
    text-align: center;
    float: left;
}

.confirm > span, .all_select>span{
    box-sizing: border-box;
    border: 0.04rem solid orange;
    background: white;
    display: inline-block;
    width: 0.6rem;
    height: 0.6rem;
    overflow: hidden;
    border-radius: 50%;
    line-height: 0.6rem;
}

.confirm > span > span, .all_select>span>span {
    background: yellow;
    font-size: 0.5rem;
    display: block;
}

.menuList > a {
    width: 84%;
    display: inline-block;
    font-size: 0.4rem;
    line-height: 1rem;
}

.menuList > a > img {
    margin-top: 0.25rem;
    width: 25%;
    height: 100%;
    float: left;
}

.menuList > a > p {
    width: 70%;
    height: 1rem;
    float: right;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.menuList > section {
    position: absolute;
    right: 0.4rem;
    bottom: 0.4rem;
    height: 0.75rem;
    border-radius: 1rem;
}

.menuList > section > button {
    background: white;
    border: 1px solid orange;
    border-radius: 1111px;
    color: red;
    display: inline-block;
    text-align: center;
    line-height: 0.65rem;
    font-weight: 900;
    width: 0.75rem;
    height: 0.75rem;
}

.menuList > section > span {
    display: inline-block;
    width: 0.5rem;
    text-align: center;
    line-height: 0.5rem;
    font-size: 0.4rem;
}

.presentPrice:before {
    content: "¥";
    font-size: 0.33rem;
}

/*************payTheBill 买单*************/
.payTheBill {
    height: 1.5rem;
    position: relative;
}

.payTheBill .all_select {
    width: 10%;
    padding-top: 0.4rem;
    padding-left: 0.4rem;
}

.payTheBill > p {
    line-height: 1.5rem;
    text-indent: 0.3rem;
}

.payTheBill > p > span:first-child {
    padding-right: 0.8rem;
}

.payTheBill > p > span:last-child {
    padding-left: 0.3rem;
    color: red;
}

.payTheBill > p > span:last-child:before {
    content: "¥";
    font-size: 0.35rem;
}

.payTheBill > a {
    background: yellow;
    position: absolute;
    right: 0;
    line-height: 1.5rem;
    top: 0;
    padding: 0 0.7rem;
}

cart.html:

{% extends 'base_main.html' %}
{% load static %}

{% block ext_css %}
    {{ block.super }}
    <link rel="stylesheet" href="{% static 'peiqi1/main/css/cart.css' %}">
{% endblock %}

{% block content %}
    <div id="cart">
        <h3>Cart</h3>
        <div class="full">
            <section>
                <ul>
                    <li>收&nbsp;&nbsp;货&nbsp;&nbsp;人:&nbsp;PEIQI</li>
                    <li>电&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;话:&nbsp;123456789</li>
                    <li>地&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;址:&nbsp;CHINA</li>
                </ul>
                <div class="bill">
                    <p>闪送超市</p>
                    <p>1元起送,满30免运费,22:00前可送达</p>
                    <a href="">凑单专区</a>
                </div>

                <div class="delivery">
                    <span>收获时间</span>
                    <span>一小时之内送达</span>
                    <a href="#">可预订&gt;</a>
                </div>

                <div>
                    <div class="delivery">
                        <span>收货备注</span>
                        <input type="text" placeholder="可输入100字以内的特殊要求">
                    </div>
                </div>

                <ul>
                    {% for cart in carts %}
                    <li class="menuList">
                    <div class="confirm">
                        <span>
                            {% if cart.c_is_select %}  {# 判断购物车中商品是否被选中 #}
                                <span>√</span>
                            {% else %}
                                <span></span>
                            {% endif %}
                        </span>
                    </div>
                        <a href="#">
                            <img src="{{ cart.c_goods.productimg }}" alt="{{ cart.c_goods.productlongname }}">
                        <p>{{ cart.c_goods.productlongname }}</p>
                            <p class="presentPrice">{{ cart.c_goods.price}}</p>
                        </a>

                    <section>
                        <button>-</button>
                        <span>{{ cart.c_goods_num }}</span>
                        <button>+</button>
                    </section>
                    </li>
                    {% endfor %}

                </ul>

                <div class="payTheBill">
                <div class="confirm">
                    <span>
                        <span></span>
                    </span>
                </div>
                <p>
                    <span>全选</span>
                    <span>共计:</span>
                    <span>0</span>
                </p>

                <a href="#">下单</a>
            </section>

        </div>
    </div>
{% endblock %}

测试:

Cart页面的点击事件

下面我们要实现点击事件
新建cart.js

cart.html导入cart.js:

{% block ext_js %}
{{ block.super }}
    <script type="text/javascript" src="{% static 'peiqi1/main/js/cart.js'%}"></script>
{% endblock %}

为了方便管理cart页面的点击事件,我们不采用之前直接给每一个标签都添加id,而是在最小父标签添加id

<li class="menuList" cartid="{{ cart.id }}">

cart.js:

$(function(){
    $('.confirm').click(function(){
        console.log('change state');
        /*修改选中状态*/
        var $confirm = $(this);

        var $li = $confirm.parents('li');

        var cartid = $li.attr('cartid');
        /*利用ajax,找到对应标签之后发送给服务器修改状态*/
        $.getJSON('/peiqi1/changecartstate/',{'cartid': cartid}, function (data){
            console.log(data);
            
            if (data['status']===200){
                if(data['c_is_select']){
                    $confirm.find('span').find('span').html('√');
                }else{
                    $confirm.find('span').find('span').html('');
                }
            }
        })
    })
})

添加修改商品是否被选中状态的url:

url(r'^changecartstate/', views.change_cart_state, name='changecartstate'),

因为这个url需要是用户登录状态,所以我们要在中间件上加上该url:
middleware:

REQUIRE_LOGIN_JSON = [
    '/peiqi1/addtocart/',
    '/peiqi1/changecartstate/',
]

views:

def change_cart_state(request):
    # 从ajax获得商品id,但是这里有个隐藏漏洞,这里没有验证这个cart_id是用户已经存在的。
    cart_id = request.GET.get('cartid')
    # 去数据库中匹配对应的商品id
    cart_obj = Cart.objects.get(pk=cart_id)
    # 将原本选中的状态取反
    cart_obj.c_is_select = not cart_obj.c_is_select
    # 记得保存,如果不保存,数据库的状态不会改变
    cart_obj.save()
    data = {
        'status':200,
        'msg':'change ok',
        'c_is_select':cart_obj.c_is_select
    }
    return JsonResponse(data=data)

测试:


小技巧

js获取jquery对象属性

  • js获取jquery对象属性

    • attr

      • 可以获取任意属性
    • prop

      • 只能获取内置属性,自定义的属性不认

项目中多块逻辑有相同操作

例如我们开发很多接口都要求用户登录,那这个操作就可以封装起来。

  • 封装函数
  • 装饰器
  • 中间件

    浏览器行为

  • 重定向——是浏览器才能识别的请求,所以只有浏览器才能识别301,302是重定向

而比如移动端,客户端实际上识别不了301是什么意思。

  • 跨域也是浏览器的行为
最后修改:2024 年 03 月 13 日
如果觉得我的文章对你有用,请随意赞赏