Day90 详情页加入购物车

知识点:
1.redis数据库使用

2.vuex的使用

 

一.详情页添加购物车

1.后端配置

1.1创建子应用 cart

cd luffy/apps
python ../../manage.py startapp cart

1.2注册子应用cart

INSTALLED_APPS = [
    'ckeditor',  # 富文本编辑器
    'ckeditor_uploader',  # 富文本编辑器上传图片模块

    'home',
    'users',
    'courses',
    'cart',
]

因为购物车中的商品(课程)信息会经常被用户操作,所以为了减轻服务器的压力,可以选择把购物车信息通过redis来存储.

1.3配置信息

# 设置redis缓存
CACHES = {
    # 默认缓存
    ....
    
    "cart":{
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://127.0.0.1:6379/3",
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
        }
    },
}

接下来商品信息存储以下内容:

course_id      购物车中的商品ID
course_expire  购物车中商品的有效期
is_selected    购物车中对应商品的勾选状态

# 上面三个数据,实际上存储到redis中,要以什么类型来存储呢?
# redis一共5种数据类型,我们就应该考虑到哪种数据类型保存上面的数据最方便我们读写.
cart_<user_id>: {
  "商品ID1":"有效期",
  "商品ID1":"有效期",
  "商品ID1":"有效期",
}
    
# 把已经勾选的商品ID记录到无序集合中
cart_selected_<user_id>:{
    "商品ID1",
    "商品ID2",
    "商品ID3",
}

1.4添加课程商品到购物车的API接口实现

cart/views.py视图,代码:

from django.shortcuts import render

# Create your views here.
from rest_framework.views import APIView
from courses.models import Course
from rest_framework.response import Response
from django_redis import get_redis_connection
from rest_framework.permissions import IsAuthenticated
from rest_framework import status
class CartAPIView(APIView):
    permission_classes = [IsAuthenticated]
    """购物车视图"""
    def post(self,request):
        """购物车添加商品"""
        # 获取客户端发送过来的课程ID
        course_id = request.data.get("course_id")
        # 验证课程ID是否有效
        try:
            Course.objects.get(pk=course_id,is_delete=False,is_show=True)
        except Course.DoesNotExist:
            return Response({"message":"当前课程不存在!"},status=status.HTTP_400_BAD_REQUEST)

        # 组装基本数据[课程ID,有效期]保存到redis
        redis = get_redis_connection("cart")
        # user_id = 1
        user_id = request.user.id
        try:
            # 添加一个成员到指定名称的hash数据中[如果对应名称的hash数据不存在,则自动创建]
            # hset(名称,键,值)
            redis.hset("cart_%s" % user_id, course_id, -1) # -1表示购买的课程永久有效

            # 添加一个成员到制定名称的set数据中[如果对应名称的set数据不存在,则自动创建]
            # sadd(名称,成员)
            redis.sadd("cart_selected_%s" % user_id, course_id )

        except:
            return Response({"message": "添加课程到购物车失败!请联系客服人员~"},status=status.HTTP_507_INSUFFICIENT_STORAGE)

        # 返回结果
        return Response({"message":"成功添加课程到购物车!"},status=status.HTTP_200_OK)

1.5 配置路由

1.5.1 总路由,代码:

urlpatterns = [
    ...
    path('cart/', include("cart.urls")),
]

1.5.2子应用路由cart/urls.py,代码:

from django.urls import path, re_path
from . import views
urlpatterns = [
    path(r"course/",views.CartAPIView.as_view()),
]

1.5.3 默认用户添加课程到购物车就已经勾选了商品,视图代码:

from django.shortcuts import render

# Create your views here.
from rest_framework.views import APIView
from courses.models import Course
from rest_framework.response import Response
from django_redis import get_redis_connection
from rest_framework.permissions import IsAuthenticated
from rest_framework import status
class CartAPIView(APIView):
    permission_classes = [IsAuthenticated]
    """购物车视图"""
    def post(self,request):
        """购物车添加商品"""
        # 获取客户端发送过来的课程ID
        course_id = request.data.get("course_id")
        # 验证课程ID是否有效
        try:
            Course.objects.get(pk=course_id,is_delete=False,is_show=True)
        except Course.DoesNotExist:
            return Response({"message":"当前课程不存在!"},status=status.HTTP_400_BAD_REQUEST)

        # 组装基本数据[课程ID,有效期]保存到redis
        redis = get_redis_connection("cart")
        # user_id = 1
        user_id = request.user.id
        # transation: 事务
        # 作用: 可以设置多个数据库操作看成一个整体,这个整理里面每一条数据库操作都成功了,事务才算成功,
        #      如果出现其中任意一个数据库操作失败,则整体一起失败!
        # 事务可以提供 提交事务 和 回滚事务 的功能
        # 不仅mysql中存在事务,在redis中也有事务的概念,但是叫"管道 pipeline"
        try:
            # 创建事务[管道]对象
            pipeline = redis.pipeline()
            # 开启事务
            pipeline.multi()
            # 添加一个成员到指定名称的hash数据中[如果对应名称的hash数据不存在,则自动创建]
            # hset(名称,键,值)
            pipeline.hset("cart_%s" % user_id, course_id, -1) # -1表示购买的课程永久有效

            # 添加一个成员到制定名称的set数据中[如果对应名称的set数据不存在,则自动创建]
            # sadd(名称,成员)
            pipeline.sadd("cart_selected_%s" % user_id, course_id )

            # 提交事务[如果不提交,则事务会自动回滚]
            pipeline.execute()

        except:
            return Response({"message": "添加课程到购物车失败!请联系客服人员~"},status=status.HTTP_507_INSUFFICIENT_STORAGE)

        # 返回结果
        return Response({"message": "成功添加课程到购物车!"}, status=status.HTTP_200_OK)

2.前端详情页添加购物车

Course.vue

//template 代码:
<div @click="cartAddHander" class="add-cart"><img src="@/assets/cart-yellow.svg" alt="">加入购物车</div> //data数据: data(){ return { token:sessionStorage.token || localStorage.token, user_id:sessionStorage.user_id || localStorage.user_id, user_name:sessionStorage.user_name || ocalStorage.user_name,} // methods 方法 // 添加商品课程到购物车 cartAddHander(){ // 1. 判断用户是否已经登录了. if(!this.token){ this.$confirm("对不起,您尚未登录!请登录",'提示').then(() => { this.$router.push("/login"); }); } // 2. 发起请求 this.$axios.post(this.$settings.Host+`/carts/course/`,{ course_id: this.course_id, },{ headers:{ // 注意:jwt后面必须有且只有一个空格!!!! "Authorization":"jwt " + this.token } }).then(response=>{ // 获取购物城中商品总数 // 添加购物车成功! this.$message(response.data.message,"提示!",{ duration: 2000, // 单位: 毫秒 }); }).catch(error=>{ console.log(error.response); }) }

3.后端增加返回一个购物车的商品课程总数

  # 返回结果,返回购物车中的商品数量
        count = redis.hlen("cart_%s" % user_id)

4.前端展示商品课程的总数

获取商品总数是在头部组件中使用到,并展示出来,但是我们后面可以在购物车中,或者商品课程的详情页中修改购物车中商品总数,因为对于一些数据,需要在多个组件中共享,这种情况,我们可以使用本地存储来完成,但是也可以通过vuex组件来完成这个功能。

4.1 安装vuex

npm install -S vuex

4.2 把vuex注册到vue中

4.2.1在src目录下创建store目录,并在store目录下创建一个index.js文件,index.js文件代码:

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex);

export const store = new Vuex.Store({
  // 数据仓库,类似vue里面的data
  state: {

  },
  // 数据操作方法,类似vue里面的methods
  mutations: {

  }
});

4.2.2 把上面index.js中创建的store对象注册到main.js的vue中。

// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'

import router from './routers/index';
import store from './store/index';

// 手动的自定义全局配置
import settings from "./settings"
Vue.prototype.$settings = settings;

// elementUI 导入
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
// 调用插件
Vue.use(ElementUI);

import "../static/css/reset.css"

import axios from 'axios'; // 从node_modules目录中导入包

// 允许ajax发送请求时附带cookie
axios.defaults.withCredentials = true;

Vue.prototype.$axios = axios; // 把对象挂载vue中


Vue.config.productionTip = false;

// 导入gt极验
import '../static/js/gt.js';

// vue-video视频播放插件
require('video.js/dist/video-js.css');
require('vue-video-player/src/custom-theme.css');
import VideoPlayer from 'vue-video-player'
Vue.use(VideoPlayer);

/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  store,
  components: { App },
  template: '<App/>'
})

4.2.3接下来,我们就可以在组件使用到store中state里面保存的共享数据了.

先到vuex中添加数据,store/inde.js,代码

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex);

export default new Vuex.Store({
  // 数据仓库,类似vue里面的data
  state: {
    // 购物车数据
    cart:{
      count:0,
    }
  },
  // 数据操作方法,类似vue里面的methods
  mutations: {

  }
});

4.2.4在Header.vue头部组件中,直接读取store里面的数据

<b class="goods-number">{{this.$store.state.cart.count}}</b>
// this是可以省略不写。
<b class="goods-number">{{$store.state.cart.count}}</b>

4.2.5 我们可以在Detail.vue课程详情的组件中, 修改商品总数。

// 添加商品课程到购物车
      cartAddHander(){
        // 1. 判断用户是否已经登录了.
        if(!this.token){
          this.$confirm("对不起,您尚未登录!请登录",'提示').then(() => {
            this.$router.push("/login");
          });
        }

        // 2. 发起请求
        this.$axios.post(this.$settings.Host+`/carts/course/`,{
          course_id: this.course_id,
        },{
          headers:{
            // 注意:jwt后面必须有且只有一个空格!!!!
            "Authorization":"jwt " + this.token
          }
        }).then(response=>{

          // 获取购物城中商品总数
          // this.$store.state.cart.count = response.data.count;
          this.$store.commit("addcart",response.data);
          // 添加购物车成功!
          this.$message(response.data.message,"提示!",{
            duration: 2000, // 单位: 毫秒
          });

        }).catch(error=>{

          console.log(error.response);
        })
      }

4.3 在store/index.js中新增mutations的方法,代码:

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex);

export default new Vuex.Store({
  // 数据仓库,类似vue里面的data
  state: {
    // 购物车数据
    cart:{
      count: 0,
      // course_list: [], // 购物车里面的商品列表信息
    }
  },
  // 数据操作方法,类似vue里面的 methods
  mutations: {
    // data是调用方法,传递的购物车相关的参数
    addcart(state,data){
      // 修改商品课程的总数
      state.cart.count = data.count;
      // state.cart.course_list = data.course_list;
    }
  }
});

 

 

 

 

posted @ 2019-05-17 22:27  addit  Views(57)  Comments(0Edit  收藏  举报