当前位置:网站首页>Day83: Luffy: add shopping cart & navigation bar shopping cart digital display & shopping cart page display

Day83: Luffy: add shopping cart & navigation bar shopping cart digital display & shopping cart page display

2020-11-09 16:59:09 iR-Poke

Catalog

1. Add cart + Verify login status

2. The small red circle number on the top right of the shopping cart icon

3.Vuex

4. Shopping cart page display - Back end interface

5. Shopping cart page display - front end

6. Solve a shopping cart number display confusion bug

1. Add cart + Verify login status

1. Add the whole idea of shopping cart

Shopping cart data should be saved to redis Medium : To save users id, Course id

On the course details page, users click Add cart :

Get the course of the current course id Go to the database and put the course id The corresponding information ( Need to be displayed in the shopping cart ) Make it into a dictionary , then json Serialize into a string and save to redis in

How to add shopping cart by clicking , Add cart data to redis in ??????

2. Add cart - Back end interface

1. Create a cart application , And configuration INSTALLAPP

python3 ../../ manage.py startapp cart

2. Add... To the total route cart

# lyapi/urls.py
path('cart/', include("cart.urls") ),

3.cart/urls.py

from django.urls import path,re_path
from . import views

urlpatterns = [
    path('add_cart/', views.AddCartView.as_view({'post':'add'}))

]

4.cart/views.py

from rest_framework.viewsets import Viewset
from django_redis import get_redis_connection
from course import models
from rest_framework.response import Response


class AddCartView(ViewSet):
    
    def add(self,request):
        course_id = request.data.get('course_id')
        user_id = 1 #  First put the user id Write dead 
        
        #  Go to redis There's data in it 
        conn = get_redis_connection('cart')
        
        #  Check the course id Is it legal 
        try:
            models.Course.objects.get(id=course_id)
        except:
            return Response({'msg':' The curriculum doesn't exist '},status=400)
      
        ''' Choose to store with the data type of the collection '''
        conn.sadd('cart_%s' % user_id,course_id)
        
        # vheader The small red number of the shopping cart on the right shows 
        cart_length = conn.scard('cart_%s' % user_id) #  Get the number of items 
        return Response({'msg':' Add success ','cart_length',cart_length})

5. Use a separate one for the shopping cart redis library

# dev.py
CACHES = {
    ......
    "cart":{
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://127.0.0.1:6379/3",
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
        },
    }
}

3. Add cart - front end

Click on the front end of the shopping cart , Send a request back

<!-- html -->
<div class="add-cart" @click="addCart"><img src="/static/img/cart-yellow.svg" alt=""> Add to cart </div>

Be careful : Add a shopping cart to verify that the user is logged in

axios It's an asynchronous request ,setting.js Inside check_login() Functions and Detail.vue Medium addCart It's synchronous , however addCart Add shopping cart need to verify the login status of ,

There will be : I haven't verified it yet token Well , Add a shopping cart and you've already detected it token The code location of .

So now we need to make two requests synchronous , Give Way token After verification, do other operations

It is troublesome to change asynchronous to synchronous , So we're directly Will verify token The operation of is written in addCart Add shopping cart method

// js     
addCart(){

        //  Get the front-end storage of token value 
        let token = localStorage.token || sessionStorage.token;
       
        //  If token The value is 
        if (token){
          
          //  verification token
          this.$axios.post(`${this.$settings.Host}/users/verify/`,{
              token:token,
            }).then((res)=>{

          //  Verification passed , You can add shopping carts 
            this.$axios.post(`${this.$settings.Host}/cart/add_cart/`,{
                //  Get the course id
                course_id:this.course_id,
              }).then((res)=>{
                //  Shopping cart added successfully , Print the information of successful addition 
                this.$message.success(res.data.msg);
              })
           //  The verification failed (token Wrong or token Be overdue )  Prompt the user to log in 
            }).catch((error)=>{

              this.$confirm(' You haven't signed in yet !!!?', '31s', {
                confirmButtonText: ' Go to login ',
                cancelButtonText: ' Cancel ',
                type: 'warning'
              }).then(() => {
                this.$router.push('/user/login');
              })
              
              //  Will expire token Clean up 
              sessionStorage.removeItem('token');
              sessionStorage.removeItem('username');
              sessionStorage.removeItem('id');
              localStorage.removeItem('token');
              localStorage.removeItem('username');
              localStorage.removeItem('id');
            })


        }
        // token Can't get 
        else {
          this.$confirm(' You haven't signed in yet !!!?', '31s', {
                confirmButtonText: ' Go to login ',
                cancelButtonText: ' Cancel ',
                type: 'warning'
              }).then(() => {
                this.$router.push('/user/login');
              })
        }



      },

2. The small red circle number on the top right of the shopping cart icon

We have returned the length of the shopping cart in the back end , At the front end, we can get the length of the cart

// Detail.vue 
this.$axios.post(`${this.$settings.Host}/cart/add_cart/`,{
                //  Get the course id
                course_id:this.course_id,
              }).then((res)=>{
                //  Shopping cart added successfully , Print the information of successful addition 
                this.$message.success(res.data.msg);
      
                 //  Get the length of the shopping cart sent to the back end 
                 this.cart_length = res.data.cart_length
              })

Now there's a problem , We are cart_length Data properties are in Detail In the component , But the little red circle in the top right shopping cart is vheader In the component . Data between different components is not interoperable

The first way of thinking :vheader Components are detail Subcomponent of component , We can go through vue The parent-child pass value to achieve .

That's the question

If we visit the actual combat class page That is to say /course, stay course Component is not to display the shopping cart small red circle ?

But in course Component we didn't get the length of the cart at all . So the red circle numbers don't show up at all . So the idea of father son value passing doesn't work .

3.Vuex

because For some data , Need to be shared instantly across multiple components , So according to the above question , We lead to vuex

1. install Vuex

npm install -S vuex

2. hold vuex Sign up to vue in

stay src Create under directory store Catalog , And in store Create one in the directory index.js file ,index.js File code

// store/index.js
import Vue from 'vue'
import Vuex from 'vuex'


Vue.use(Vuex)

  export default new Vuex.Store({
  state: { //  Data warehouse , similar vue Inside data
    cart_length: 0 //  Shopping cart data 
  },
   
  mutations: { //  Data manipulation method , similar vue Inside methods
    add_cart (state, cart_length) {
      state.cart_length = cart_length; //  Modify the total number of items in the shopping cart 
    }
  }
})

3. mount store object

Top up index.js Created in the store Object registered to main.js Of vue in .

// main.js

import Vue from 'vue'
import App from './App'
import router from './router'
import store from './store';  //  introduce 


new Vue({
  el: '#app',
  router,
  store,    //  mount 
  components: { App },
  template: '<App/>'
})

4.Vheader Component read store The data of ( Read the length of the cart )

stay Vheader.vue In the head assembly , You can read it directly store The data in it

<router-link to="/">
    <b>{{$store.state.cart_length}}</b>
    <img src="@/assets/shopcart.png" alt="">
    <span> The shopping cart  </span>
</router-link>

5.Detail Component modification store The data of ( Modify cart length )

When the user clicks add cart , Trigger addCart Medium post Method , Change the length of the shopping cart

this.$axios.post(`${this.$settings.Host}/cart/add_cart/`,{
    course_id:this.course_id,
}).then((res)=>{
    this.$message.success(res.data.msg);
    //  The length of the shopping cart obtained from the back end does not exist in the data attribute of the current component , It's stored in vuex in 
    this.$store.commit('add_cart', res.data.cart_length) ; // commit Used to trigger mutation The method declared in 
})

6. About page refresh ,vuex Data loss problem

problem :vuex The data in is stored in memory , As soon as the page is refreshed ,vuex There's no data in

Solution : When the user clicks refresh , We can monitor the user refresh action , You can do some actions on the page before refreshing .

When you click refresh , Let's save the data to sessionStorage or localStorage in ,

After the page is refreshed , And get the data back to vuex in . In this way, the page can be refreshed , The data has not been lost .

1. Click refresh , Save the data to sessionStorage in

// app.vue
<script>
export default {
  name: 'App',
  created() {
    //  Before refreshing the page cart_length The data is stored in sessionStorage in 
    window.addEventListener('beforeunload',()=>{
      console.log(' Page to refresh !!!, Save the data !!!!');
      sessionStorage.setItem('cart_length',this.$store.state.cart_length);

    })
  }
}
</script>

2.. After the page is refreshed , Take data from sessionStorage Take it out and put it in vuex in

// vheader.vue
 created() {


    if (this.$store.state.cart_length === 0) {
      let cart_length = sessionStorage.getItem('cart_length');
      this.$store.commit('add_cart', cart_length);
    }
  },

7. About redis Exception capture

In order to ensure that the system log records can be followed up redis Part of the , We can also add information about redis Exception capture

# utils/exceptions.py
from rest_framework.views import exception_handler

from django.db import DatabaseError
from rest_framework.response import Response
from rest_framework import status

from redis import RedisError #  introduce redis abnormal 

import logging
logger = logging.getLogger('django')


def custom_exception_handler(exc, context):
    """
     Custom exception handling 
    :param exc:  Exception class 
    :param context:  The context in which the exception was thrown 
    :return: Response The response object 
    """
    #  call drf Framework native exception handling method 
    response = exception_handler(exc, context)

    if response is None:
        view = context['view']  #  The function or method where the error occurred 
        if isinstance(exc, DatabaseError) or isinstance(exc, RedisError):
            #  Database exception /redis abnormal 
            logger.error('[%s] %s' % (view, exc))
            response = Response({'message': ' Server internal error '}, status=status.HTTP_507_INSUFFICIENT_STORAGE)

    return response
redis Exception capture and error logging

4. Shopping cart page display - Back end interface

1. Add cart - Course validity

1. Set a default on the back end The period of validity

2. When adding a shopping cart, save the validity period to redis in : Previous redis The data storage structure is a collection , But now the collection can't meet our needs . To use Hash data type storage .

The hash data type structure is as follows :

'''
        user_id:{
            course_id:expire,
            course_id:expire,
        }
        
'''
# cart/views.py


class AddCartView(ViewSet):

    def add(self,request):
       ......

        expire = 0  #  The period of validity : Permanent 
        ''' Save the user's corresponding course id And validity period '''
        conn.hset('cart_%s' % user_id,course_id,expire)
        ''' Length of shopping cart for storing users '''
        cart_length = conn.hlen('cart_%s' % user_id)

          ......

In the code above , We can see that it was set up twice conn Connect , This is not very good , So we use a redis The pipe pipe

pipe = conn.pipeline() #  Create pipes 
pipe.multi()

#  Put the next two instructions in the pipe 
''' Save the user's corresponding course id And validity period '''
pipe.hset('cart_%s' % user_id,course_id,expire)
''' Length of shopping cart for storing users '''
cart_length = pipe.hlen('cart_%s' % user_id)

pipe.execute() #  Execute the two instructions above 

2. Shopping cart list - Back end interface

class AddCartView(ViewSet):

    def cart_list(self,request):
        user_id = 1  #  user id Write death first 
        conn = get_redis_connection('cart') #  obtain cart Corresponding redis Library object 
        
        #  The course corresponding to the current user id from redis To remove 
        ret = conn.hgetall('cart_%s' % user_id)  #  It's packaged into a dictionary { Course id, The period of validity },dict {b'1': b'0', b'2': b'0'}
        cart_data_list = []
        try:
            for cid, eid in ret.items():# cid: Course id eid: The period of validity 
                '''redis There are bytes in it   So to decode '''
                course_id = cid.decode('utf-8') 
                expire_id = eid.decode('utf-8')

                course_obj = models.Course.objects.get(id=course_id)
               '''
                The shopping cart data required by the front end includes 
               1. Course name 
               2. Course cover 
               3. Price of course 
               4. Course validity 
               so  We create our own data structure to store what the front end needs 
               '''
                cart_data_list.append({
                    'name':course_obj.name,
                    'course_img':contains.SERVER_ADDR + course_obj.course_img.url , #  The picture path is a relative path , We turn it into an absolute path 
                    'price':course_obj.price,
                    'expire_id':expire_id
                })
        except Exception:
            logger.error(' Failed to get cart data ')
            return Response({'msg':' There's something wrong with the background database , Please contact the Administrator '},status=status.HTTP_507_INSUFFICIENT_STORAGE)

       #  Respond the data to the front end 
        return Response({'msg':'xxx','cart_data_list':cart_data_list})

5. Shopping cart page display - front end

1. The initial interface of the front end of the shopping cart

...

2. take cart The component is registered on the route

import Vue from 'vue'
import Cart from '@/components/Cart'

Vue.use(Router)

export default new Router({
  mode:'history',
  routes: [
   
    {
      path: '/cart/',   
      component: Cart
    },

  ]
})

3. About cart Components and cartitem Components

In the shopping cart page , The whole shopping cart is a component (cart Components ), Then each shopping cart data to be displayed is a sub component (cartitem Components )

<!-- cart.vue html part  -->
<div class="cart_course_list">
    <CartItem v-for="(value,index) in cart_data_list" :key="index" :cart="value"></CartItem> <!-- 001 :cart  The parent component passes values to the child components  -->
</div>
// cart.vue js part 
<script>
import CartItem from "./common/CartItem"
export default {
    name: "Cart",
    data(){
      return {
        cart_data_list:[],
      }
    },
    methods:{

    },
  created() {
    let token = sessionStorage.token || localStorage.token;
    if (token){

      this.$axios.get(`${this.$settings.Host}/cart/add_cart/`) //  Get shopping cart data 
      .then((res)=>{
        this.cart_data_list = res.data.cart_data_list
      })
      .catch((error)=>{
        this.$message.error(error.response.data.msg);
      })

    }else {
      this.$router.push('/user/login');
    }


  },
  components:{

      CartItem,
    }
}
</script>

The parent component holds its own value cart_data_list Pass to each subcomponent for rendering ( The parent component passes values to the child components )

// cartitem.vue
<template>
    <div class="cart_item">
      <div class="cart_column column_1">
        <el-checkbox class="my_el_checkbox" v-model="checked"></el-checkbox>
      </div>
      <div class="cart_column column_2">
        <img :src="cart.course_img" alt="">
        <span><router-link to="/course/detail/1">{{cart.name}}</router-link></span>
      </div>
      <div class="cart_column column_3">
        <el-select v-model="cart.expire_id" size="mini" placeholder=" Please select the purchase validity period " class="my_el_select">
          <el-option label="1 Months in effect " value="30" key="30"></el-option>
          <el-option label="2 Months in effect " value="60" key="60"></el-option>
          <el-option label="3 Months in effect " value="90" key="90"></el-option>
          <el-option label=" Permanent validity " value="0" key="0"></el-option>
        </el-select>
      </div>
      <div class="cart_column column_4">¥{{cart.price}}</div>
      <div class="cart_column column_4"> Delete </div>
    </div>
</template>

<script>
export default {
    name: "CartItem",
    data(){
      return {
        checked:false,

      }
    },
    props:['cart', ] // 002: The child component accepts the value passed by the parent component 
}
</script>

6. Solve a shopping cart number display confusion bug

1. Page refresh caused by vuex Data reset

resolvent : Save data to before page refresh SessionStorage

// App.vue
<script>
export default {
  name: 'App',
  created() {
    window.addEventListener('beforeunload',()=>{
  sessionStorage.setItem('cart_length',this.$store.state.cart_length);
    })
  }
}
</script>

2. The number of small red circles in shopping cart displayed on different pages is inconsistent

When the component is loaded , Will execute vheader Medium created Method , Get sessionStorage Value

let cart_length = sessionStorage.getItem('cart_length');
this.$store.commit('add_cart',cart_length);

One of them is : Only when the page is refreshed , To get sessionStorage The value of is stored in vuex in ,

If the page doesn't refresh , Users click to add shopping cart ( here vuex The length of the stored cart has changed due to the addition of shopping operations ),

When our component reloads , If it's sessionStorage Value , In fact, it is still the original value .

We should put the value operation after refreshing the page

 

created(){
    if (this.$store.state.cart_length === 0){ //  If the shopping cart has no data 
      let cart_length = sessionStorage.getItem('cart_length'); //  Just go to sessionStorage Of the data 
      this.$store.commit('add_cart',cart_length); //  And store the data in vuex in 
    }
  },

 

版权声明
本文为[iR-Poke]所创,转载请带上原文链接,感谢