django项目开发之cart页面

cart页面

1.购物车页面添加商品

我们在app上购买商品,都是先在商品页选择商品添加到购物车,然后从购物车结算,考虑一下怎么实现这个功能。

1.1 表的关联

商品,购物车,用户之间,可以转换为表的关联,购物车相当于关系表的角色,用户和商品之间成为多对多的关系。
首先去models.py写购物车的模型

class Cart(models.Model):
    c_user=models.ForeignKey(User)
    c_goods=models.ForeignKey(Goods)

    c_goods_num=models.IntegerField(default=1)
    c_is_select=models.BooleanField(default=True)
    class Meta:
        db_table = 'sxw_cart'

在新版本的django中,外键必须添加on_delete=models.CASCADE属性,否则会报错。外键关联到用户和商品,采用级联删除,当购物车中删除的时候,用户也删除相关信息。
执行数据迁移。

1.2 获取商品id,数量num

当点击购物车的时候,添加一个点击事件,这个事件发起了一个ajax请求,通过js,把goodsid传到后台,

market.js

 $(".addShopping").click(function(){
     
        console.log('add');
        var $add=$(this);
        var goodsid=$add.attr('goodsid');
        $.get('/sxw/addtocart/',{
     'goodsid':goodsid},function(data){
     
            console.log(data);
            if (data['status'] === 302){
     
                window.open('/sxw/login/',target="_self");
            }else if(data['status']===200){
     
                $add.prev('span').html(data['c_goods_num']);
            }
        })
    })

1.3 将商品id和num传到后台

上面的步骤已经取到了商品的id和num,接下来使用ajax传到后台。先将数据传到/sxw/addtocart/
function(data)是一个回调函数,作用是当ajax请求发起后,在后台接受到请求后,会主动调用这个函数,把数据放到data里。
写一个视图函数,因为是ajax请求,所以返回json会比较好。

def add_to_cart(request):
    goodsid=request.GET.get('goodsid')
    #获取到商品id,根据用户和商品id,获取到购物车数据
    carts=Cart.objects.filter(c_user=request.user).filter(c_goods_id=goodsid)
    #如果购物车中有该商品,该商品数量加1;如果没有,将该商品id加入购物车
    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)

最后,添加下url

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

1.4 从购物车移除商品

从购物车移除商品的做法与添加一样。
market.js

$(".subShopping").click(function(){
     
    console.log('sub');
    var $sub=$(this);
    var goodsid=$sub.attr('goodsid');
    $.get('/sxw/subtocart/',{
     'goodsid':goodsid},function(data){
     
        console.log(data);
        if (data['status'] === 302){
     
            window.open('/sxw/login/',target="_self");
        }else if(data['status']===200){
     
            $sub.prev('span').html(data['c_goods_num']);
        }
    })
})

视图函数

def sub_to_cart(request):
    goodsid = request.GET.get('goodsid')
    carts = Cart.objects.filter(c_user=request.user).filter(c_goods_id=goodsid)
    data = {
     
        'status': 200,
        'msg': 'add success',
    }
    if carts.exists():
        cart_obj = carts.first()
        if cart_obj.c_goods_num > 1:
            cart_obj.c_goods_num = cart_obj.c_goods_num - 1
            cart_obj.save()
            data['c_goods_num'] = cart_obj.c_goods_num
        else:
            cart_obj.delete()
            data['c_goods_num'] = 0

    return JsonResponse(data=data)

配置路由

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

2 购物车页面

2.1 cart视图函数及页面

def cart(request):
    carts=Cart.objects.filter(c_user=request.user)
    is_all_select= not carts.filter(c_is_select=False).exists()

    data={
     
        'title':'购物车',
        'carts':carts,
        'is_all_select':is_all_select,
        'total_price':get_total_price()
    }
    return render(request,'cart.html',context=data)

cart.html

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

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

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

{
     % endblock%}

{
     % block content %}
<div id="cart">
    <h3>购物车</h3>
    <div class="full">
        <section>
            <ul>
                <li>收货人:aimo</li>
                <li>电话:110</li>
                <li>地址:杭州西湖里</li>
            </ul>
            <section class="bill">
                <p>闪送超市</p>
                <p>¥0起送,22:00前满¥30免运费</p>
                <a href="#">凑单专区</a>
            </section>


            <section class="delivery">
                <span>收货时间</span>
                <span>一小时送达</span>
                <a href="#">可预订></a>
            </section>
            <section class="delivery">
                <span>收货备注</span> <input type="text" placeholder="可输入100字以内特殊要求内容"/>
            </section>
            <ul>
                {
     % for cart in carts %}
                    <li class="menulist" cartid="{
     { cart.id }}">
                        <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 class="subShopping">-</button>
                            <span>{
     {
     cart.c_goods_num}}</span>
                            <button class="addShopping">+</button>
                        </section>

                    </li>
                {
     % endfor%}
            </ul>
            <li class="payTheBill">
                <div class="all_select">
                    <span>
                        {
     % if is_all_select %}
                        <span></span>
                        {
     % else %}
                        <span></span>
                        {
     % endif %}
                    </span>
                </div>
                <p>
                    <span>全选</span>
                    <span>共计:</span>
                    <span id="total_price">{
     {
     total_price}}</span>
                </p>
                <span id="make_order">下单</span>
            </li>
        </section>
    </div>
</div>

{
     % endblock %}

2.2 购物车状态变更

购物车全选

购物车默认状态全选按钮是选中,此时内部所有商品都是选中的;如果全选按钮未选中,内部商品中只要存在未选中的,全选就应该是未选中。
点击全选,原状态是选中,全选和所有商品都变成未选中;原状态是未选中,全选和所有商品都变成选中
点击单个商品,商品由选中变成未选中,全选一定变成未选中;商品由未选中变成选中,那全选的默认状态就是未选中,也可能变成选中。

购物车还应该会根据选中商品的数量和价格,自动计算出订单总价。

在views_helps中写一个函数来计算商品总价

def get_total_price():
    carts=Cart.objects.filter(c_is_select=True)
    total=0
    for cart in carts:
        total += cart.c_goods_num * cart.c_goods.price
    total=float('%.2f'%total)
    return total

cart.js

$(function (){
     
    $(".confirm").click(function(){
     
        console.log("change state");
        var $confirm=$(this);
        var $li=$confirm.parents("li");
        var cartid=$li.attr('cartid');
        $.getJSON("/sxw/changecartstate/",{
     'cartid':cartid},function (data){
     
            console.log(data);
            if (data['status']===200){
     
                $("#total_price").html(data['total_price']);
                if(data['c_is_select']){
     
                    $confirm.find("span").find("span").html("√");
                }else{
     
                    $confirm.find("span").find("span").html("");
                }
                if (data['is_all_select']){
     
                    $(".all_select span span").html("√");
                }else{
     
                    $(".all_select span span").html("");
                }
            }
        })
    })
    $(".subShopping").click(function(){
     
        var $sub=$(this);
        var $li =$sub.parents("li");
        var cartid=$li.attr("cartid");
        $.getJSON("/sxw/subshopping/",{
     "cartid":cartid},function(data){
     
            console.log(data);
            if (data['status']===200){
     
                 $("#total_price").html(data['total_price']);
                if (data['c_goods_num']>0){
     
                    var $span =$sub.next("span");
                    $span.html(data['c_goods_num']);
                }else{
     
                    $li.remove();
                }
            }
        })
    })
    $(".all_select").click(function(){
     
        var $all_select=$(this);
        var select_list=[];
        var unselect_list=[];
        $(".confirm").each(function(){
     
            var $confirm=$(this);
            var cartid =$confirm.parents("li").attr("cartid");
            if($confirm.find("span").find("span").html().trim()){
     
                select_list.push(cartid);
            }else{
     
                unselect_list.push(cartid);
            }
        })
        console.log(select_list);
        console.log(unselect_list);
        if(unselect_list.length > 0 ){
     
            $.getJSON('/sxw/allselect/',{
     'cart_list':unselect_list.join('#')},function(data){
     
                console.log(data);
                if(data['status']===200){
     
                    $(".confirm").find("span").find("span").html("√");
                    $all_select.find("span").find("span").html("√");
                    $("#total_price").html(data['total_price']);
                }
            })
        }else{
     
            if (select_list.length > 0){
     
                $.getJSON('/sxw/allselect/',{
     'cart_list':select_list.join('#')},function(data){
     
                console.log(data);
                if(data['status']===200){
     
                    $(".confirm").find("span").find("span").html("");
                    $all_select.find("span").find("span").html("");
                    $("#total_price").html(data['total_price']);
                }
            })
            }
        }
    })

views.py

def change_cart_state(request):
    cart_id=request.GET.get('cartid')
    cart_obj=Cart.objects.get(pk=cart_id)
    cart_obj.c_is_select=not cart_obj.c_is_select
    cart_obj.save()
    is_all_select=not Cart.objects.filter(c_user=request.user).filter(c_is_select=False).exists()
    data={
     
        'status':200,
        'msg':'change ok',
        'c_is_select':cart_obj.c_is_select,
        'is_all_select':is_all_select,
        'total_price': get_total_price(),
    }
    return JsonResponse(data=data)


def sub_shopping(request):
    cart_id = request.GET.get('cartid')
    cart_obj = Cart.objects.get(pk=cart_id)
    data = {
     
        'status':200,
        'msg':'ok',
    }
    if cart_obj.c_goods_num > 1:
        cart_obj.c_goods_num = cart_obj.c_goods_num - 1
        cart_obj.save()
        data['c_goods_num']=cart_obj.c_goods_num
    else:
        cart_obj.delete()
        data['c_goods_num']=0
    data['total_price'] = get_total_price()
    return JsonResponse(data=data)


def all_select(request):
    cart_list=request.GET.get('cart_list')
    cart_list=cart_list.split('#')
    carts=Cart.objects.filter(id__in=cart_list)
    for cart_obj in carts:
        cart_obj.c_is_select = not cart_obj.c_is_select
        cart_obj.save()

    print(cart_list)
    data={
     
        'status':200,
        'msg':'ok',
        'total_price':get_total_price(),
    }
    return JsonResponse(data=data)

配置路由

url(r'^changecartstate/',views.change_cart_state,name='change_cart_state'),
url(r'^subshopping/',views.sub_shopping,name='sub_shopping'),
url(r'^allselect/',views.all_select,name='all_select'),

3 登陆检测中间件

有些页面不需要用户登录,而有些页面需要用户登录才具有相应权限,如果每次都去判断用户是否登陆,不仅代码量大,耦合性也低。鉴于每一次实现功能,都要判断用户是否登陆状态,我们可以使用中间件来选择请求哪些页面需要登陆。当然,也可以使用装饰器。

这里,我们创建一个logincheckmiddleware.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 User
REQUIRE_LOGIN=[
    '/sxw/cart/',
    '/sxw/orderdetail/',
    '/sxw/orderlistnotpay/',
]
REQUIRE_LOGIN_JSON=[
    '/sxw/addtocart/',
    '/sxw/changecartstate/',
    '/sxw/makeorder/',
]
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:
                try:
                    user=User.objects.get(pk=user_id)
                    request.user=user
                except:
                    data={
     
                        'status':301,
                        'msg':'user not avaliable'
                    }
                    return JsonResponse(data=data)
                    #return redirect(reverse('sxw:login'))
            else:
                data = {
     
                    'status': 301,
                    'msg': 'user not login'
                }
                return JsonResponse(data=data)
                #return redirect(reverse('sxw:login'))
        if request.path in REQUIRE_LOGIN:
            user_id = request.session.get('user_id')
            if user_id:
                try:
                    user = User.objects.get(pk=user_id)
                    request.user = user
                except:
                    return redirect(reverse('sxw:login'))
            else:
                return redirect(reverse('sxw:login'))

写好中间件,要在settings中注册。

你可能感兴趣的:(web开发,python,django)