黄色网页视频 I 影音先锋日日狠狠久久 I 秋霞午夜毛片 I 秋霞一二三区 I 国产成人片无码视频 I 国产 精品 自在自线 I av免费观看网站 I 日本精品久久久久中文字幕5 I 91看视频 I 看全色黄大色黄女片18 I 精品不卡一区 I 亚洲最新精品 I 欧美 激情 在线 I 人妻少妇精品久久 I 国产99视频精品免费专区 I 欧美影院 I 欧美精品在欧美一区二区少妇 I av大片网站 I 国产精品黄色片 I 888久久 I 狠狠干最新 I 看看黄色一级片 I 黄色精品久久 I 三级av在线 I 69色综合 I 国产日韩欧美91 I 亚洲精品偷拍 I 激情小说亚洲图片 I 久久国产视频精品 I 国产综合精品一区二区三区 I 色婷婷国产 I 最新成人av在线 I 国产私拍精品 I 日韩成人影音 I 日日夜夜天天综合

django 商城項(xiàng)目之購物車以及python中的一些redis命令

系統(tǒng) 1967 0

最近在用django restframe框架做一個(gè)商城項(xiàng)目,有一個(gè)關(guān)于購物車的業(yè)務(wù)邏輯,是用cookie和redis存儲(chǔ)的購物車信息,在這里記錄一下。

完成一個(gè)商城項(xiàng)目,如果不做一個(gè)購物車,就是十分可惜的。我們先來分析一下業(yè)務(wù)邏輯,參照,京東、淘寶等大型電商網(wǎng)站,可以發(fā)現(xiàn),對(duì)于登錄用戶以及未登錄用戶,都是可以使用購物車功能。所以首先我們將這兩種情況區(qū)分開來,采用不同的存儲(chǔ)方式。先來看一下已登錄用戶,購物車其實(shí)類似我們?cè)谟斡[網(wǎng)頁時(shí)的收藏功能,用于收藏用戶喜歡的一些商品,用戶使用頻率較高,所以我們應(yīng)該優(yōu)先使用內(nèi)存型的數(shù)據(jù)庫redis進(jìn)行存儲(chǔ),這樣查詢起來會(huì)更快。確定了使用什么數(shù)據(jù)庫,我們還要在思考一下用什么形式存儲(chǔ)數(shù)據(jù)。我使用的是2.x版本的redis,共有string,hash,set,zset,list等幾種存儲(chǔ)格式。hash類似python中的字典,其他幾種格式也和python中對(duì)應(yīng)的list,set,string類似。需要額外注意的是,redis中所有數(shù)據(jù)都是以bytes方式存儲(chǔ)。再來看一下我們的需求,對(duì)于一個(gè)購物車,我們可以看到商品以及商品數(shù)量,以及是否勾選商品。對(duì)于商品,我們可以只存儲(chǔ)其商品id,需要用到商品信息時(shí)在進(jìn)行查詢,而對(duì)于勾選狀態(tài),我們則需要用一個(gè)額外的字段存儲(chǔ),由于每個(gè)人的購物車都應(yīng)該是獨(dú)立的個(gè)體,所有我們可以用用戶的id進(jìn)行存儲(chǔ),我們會(huì)發(fā)現(xiàn)要存儲(chǔ)上述信息,我們只使用一種存儲(chǔ)格式是很難完成的,所以我們可以考慮用兩個(gè)分別存儲(chǔ)。商品及數(shù)量我們可以考慮使用hash格式進(jìn)行存儲(chǔ),用key存儲(chǔ)商品id,value存儲(chǔ)商品數(shù)量,用戶id進(jìn)行區(qū)分不同的購物車,而對(duì)于勾選狀態(tài),我們可以用set進(jìn)行存儲(chǔ),對(duì)于不同的商品只有勾選和未勾選狀態(tài),我們可以考慮將已經(jīng)勾選的商品的id進(jìn)行存儲(chǔ),在set內(nèi)的商品即為勾選,不在的即為未勾選。登錄用戶搞定了我們?cè)賮砜纯次吹卿浻脩?,未登錄用戶的話,?yīng)該只在本機(jī)使用,而在登錄時(shí)進(jìn)行合并處理,所以我們沒有必要存儲(chǔ)到數(shù)據(jù)庫中,同時(shí),在登錄時(shí)要進(jìn)行合并操作,我們可以想到cookie,在登錄請(qǐng)求時(shí),游覽器會(huì)自己帶上cookie,所以我們可以考慮用cookie存儲(chǔ),存儲(chǔ)格式可以用一個(gè)嵌套的大字典。分析完畢,就開始進(jìn)行真正的操作把。

增刪改查四個(gè)邏輯,首先來看看新增把。對(duì)于新增購物車,我們需要接受的參數(shù)為商品id和數(shù)量,而勾選狀態(tài)可以默認(rèn)為勾選,添加購物車后可以在進(jìn)行修改,這里的商品我們采用sku的形式進(jìn)行存儲(chǔ)。新增操作的話是post請(qǐng)求,我們可以在視圖類中定義一個(gè)post方法來接受新增請(qǐng)求,這里的視圖類我們繼承的是APIView。這里我只寫視圖方法,對(duì)于序列化器就不做描寫。首先我們應(yīng)該接受前端傳過來的參數(shù),并進(jìn)行校驗(yàn),校驗(yàn)完成后,對(duì)用戶登錄狀態(tài)進(jìn)行判斷(我是采用JWT來進(jìn)行用戶登錄狀態(tài)存儲(chǔ)),對(duì)于不同用戶,采用不同方式存儲(chǔ)購物車信息。

?

             def post(self, request):
        # 獲取參數(shù),校驗(yàn)參數(shù) (使用序列化器)
        serializer = CartSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
            
     # 取出驗(yàn)證后的參數(shù) sku_id = serializer.validated_data['sku_id'] count = serializer.validated_data['count'] selected = serializer.validated_data['selected'] # 判斷用戶是否登錄,這里不明白的可以看一下我之前寫的django中的user驗(yàn)證 try: user = request.user except Exception: user = None if user is not None and user.is_authenticated: # 登錄,將數(shù)據(jù)存到redis 默認(rèn)勾選 redis_conn = get_redis_connection('cart') # 建立redis鏈接 pl = redis_conn.pipeline() # 建立管道,一次發(fā)送所有redis命令,不用多次連接redis pl.hincrby('cart_%s'%user.id, sku_id, count) # 插入域名為'cart_%s'%user.id,key為sku_id,value為count的數(shù)據(jù),域不存在會(huì)自己創(chuàng)建 if selected: pl.sadd('cart_select_%s'%user.id, sku_id) # 若勾選,則會(huì)將商品id加入集合,若集合不存在則創(chuàng)建 pl.execute() # 將命令一次執(zhí)行 return Response(serializer.validated_data, status=status.HTTP_201_CREATED) # 返回相應(yīng)給前端 else: # 未登錄,將數(shù)據(jù)存到cookie cart_dict = request.COOKIES.get('cart') # 從cookie中拿到購物車數(shù)據(jù) if cart_dict is not None: cookie_cart = pickle.loads(base64.b64decode(cart_dict.encode())) # 若存在,將數(shù)據(jù)轉(zhuǎn)化為字典 else: cookie_cart = {} # 若不存在,建立一個(gè)新的字典 if sku_id in cookie_cart:
          # 若商品已在購物車中,則將數(shù)據(jù)進(jìn)行更新 cookie_cart[sku_id]['count'] += count cookie_cart[sku_id]['selected'] = selected else:
          # 若不存在,則建立新的數(shù)據(jù) cookie_cart[sku_id] = { 'count':count, 'selected':selected } cart_cookie = base64.b64encode(pickle.dumps(cookie_cart)).decode() # 將數(shù)據(jù)進(jìn)行加密,并轉(zhuǎn)化為字符串 response = Response(serializer.validated_data, status=status.HTTP_201_CREATED) response.set_cookie('cart', cart_cookie, max_age=constants.CART_COOKIE_EXPIRES) # 給相應(yīng)設(shè)置cookie return response

?

然后來看一下獲取的邏輯,以get請(qǐng)求進(jìn)行請(qǐng)求,獲取不需要額外的參數(shù),通過用戶id查到商品的id和數(shù)量以及勾選狀態(tài),然后從數(shù)據(jù)庫查到具體的商品信息,返回給前端即可。

                def get(self, request):
        try:
            user = request.user
        except Exception:
            user = None
            
     # 判斷用戶登錄狀態(tài) if user is not None and user.is_authenticated: redis_conn = get_redis_connection('cart') redis_cart = redis_conn.hgetall('cart_%s'%user.id) # 獲取購物車信息 redis_cart_selected = redis_conn.smembers('cart_select_%s'%user.id) # 獲取勾選狀態(tài) cart_dict = {}
       # 由于redis中所有信息都是bytes類型,所以我們需要進(jìn)行轉(zhuǎn)化 for sku_id, count in redis_cart.items(): cart_dict[int(sku_id)] = { 'count':int(count), 'selected':sku_id in redis_cart_selected } else: cart_dict = request.COOKIES.get('cart') # 從cookie中獲取購物車信息 if cart_dict is not None: cart_dict = pickle.loads(base64.b64decode(cart_dict.encode())) else: cart_dict = {} skus = SKU.objects.filter(id__in=cart_dict.keys()) for sku in skus: sku.count = cart_dict[sku.id]['count'] sku.selected = cart_dict[sku.id]['selected'] serializer = CartSKUSerializer(skus, many=True) return Response(serializer.data)

然后是修改的邏輯,以put形式請(qǐng)求,商品數(shù)量和勾選狀態(tài)我們可以修改,所以我們需要接受這兩個(gè)參數(shù),并對(duì)redis或者cookie進(jìn)行修改并返回

                def put(self, request):
        serializer = CartSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        sku_id = serializer.validated_data['sku_id']
        count = serializer.validated_data['count']
        selected = serializer.validated_data['selected']
        try:
            user = request.user
        except Exception:
            user = None
        if user is not None and user.is_authenticated:
            redis_conn = get_redis_connection('cart')
            pl = redis_conn.pipeline() # 對(duì)于要進(jìn)行多次的redis操作,我們就考慮使用管道
            pl.hset('cart_%s'%user.id, sku_id, count) # 對(duì)哈希表進(jìn)行數(shù)據(jù)插入,如果字段存在,就進(jìn)行覆蓋
            if selected:
                pl.sadd('cart_selected_%s'%user.id, sku_id) # 如果勾選,就在set中加入商品id
            else:
                pl.srem('cart_selected_%s'%user.id, sku_id) # 如果未勾選,則刪除該商品id,若id不存在,則忽略操作
            pl.execute()
            return Response(serializer.validated_data)
        else:
            cart_dict = request.COOKIES.get('cart')
            if cart_dict is not None:
                cookie_cart = pickle.loads(base64.b64decode(cart_dict.encode()))
            else:
                cookie_cart = {}
            cookie_cart[sku_id] = {
                'count':count,
                'selected':selected
            }
            cart_cookie = base64.b64encode(pickle.dumps(cookie_cart)).decode() # 加密cookie
            response = Response(serializer.validated_data, status=status.HTTP_201_CREATED)
            response.set_cookie('cart', cart_cookie, max_age=constants.CART_COOKIE_EXPIRES) # 在響應(yīng)中設(shè)置新的cookie返回給前端
            return response

          

最后是刪除操作,刪除需要接受的是對(duì)應(yīng)的商品id,然后將對(duì)應(yīng)的數(shù)據(jù)刪除即可

                def delete(self, request):
            
     # 考慮到參數(shù)少,并不需要將數(shù)據(jù)返回,所以這里我自行對(duì)參數(shù)進(jìn)行校驗(yàn),這樣比寫序列化器代碼量更少 sku_id = request.data.get('sku_id', None) if not sku_id and not isinstance(sku_id, int): return Response('請(qǐng)求方式錯(cuò)誤',status=status.HTTP_400_BAD_REQUEST) if not SKU.objects.filter(id=sku_id).first(): return Response('商品不存在',status=status.HTTP_400_BAD_REQUEST) try: user = request.user except Exception: user = None if user is not None and user.is_authenticated: redis_conn = get_redis_connection('cart') pl = redis_conn.pipeline() pl.hdel('cart_%s'%user.id, sku_id) # 刪除單個(gè)域,如不存在則忽略操作 pl.srem('cart_selected_%s'%user.id, sku_id) pl.execute() return Response(status=status.HTTP_204_NO_CONTENT) else: response = Response(status=status.HTTP_204_NO_CONTENT) cart_dict = request.COOKIES.get('cart') if cart_dict: cookie_cart = pickle.loads(base64.b64decode(cart_dict.encode())) else: cookie_cart = {} if sku_id in cookie_cart: del cookie_cart[sku_id] cart_cookie = base64.b64encode(pickle.dumps(cookie_cart)).decode() response.set_cookie('cart', cart_cookie, max_age=constants.CART_COOKIE_EXPIRES) return response

由于涉及到登錄和未登錄,所以也就涉及到用戶購物車合并的問題,而cookie中信息是最新的信息,我們合并時(shí)就以cookie中信息為準(zhǔn),在合并完成后,會(huì)刪除cookie信息進(jìn)行重置。由于合并購物車不涉及業(yè)務(wù)邏輯,僅僅在登錄或注冊(cè)等邏輯時(shí)觸發(fā),所以不必寫額外的視圖,而是寫成工具函數(shù)的形式,哪里需要觸發(fā),就調(diào)用該函數(shù)

            def merge_cart_cookie_to_redis(request, response, user):
    """
    合并請(qǐng)求用戶的購物車數(shù)據(jù),將未登錄保存在cookie里的保存到redis中
    遇到cookie與redis中出現(xiàn)相同的商品時(shí)以cookie數(shù)據(jù)為主,覆蓋redis中的數(shù)據(jù)
    :param request: 用戶的請(qǐng)求對(duì)象
    :param response: 響應(yīng)對(duì)象,用于清楚購物車cookie
    :param user: 當(dāng)前登錄的用戶
    :return:
    """
            
  # 首先在cookie中獲取標(biāo)準(zhǔn)信息,若未登錄狀態(tài)下沒有添加商品到購物車cookie中也就沒有購物車,直接返回響應(yīng),不需要做合并處理 cart_dict = request.COOKIES.get('cart') if not cart_dict: return response cookie_cart = pickle.loads(base64.b64decode(cart_dict.encode())) redis_conn = get_redis_connection('cart') redis_cart = redis_conn.hgetall('cart_%s'%user.id) # 從redis中取出過期的信息,并進(jìn)行轉(zhuǎn)化 cart = {} for sku_id, count in redis_cart.items(): cart[int(sku_id)] = int(count)
   # 首先將商品id和數(shù)量進(jìn)行更新,并將selected為真的存在add的列表中,為假的存在remove的列表中,下面可以一次進(jìn)行操作 redis_cart_selected_add = [] redis_cart_selected_remove = []
   # 更新商品id和數(shù)量 for sku_id, count_selected_dict in cookie_cart.items(): cart[sku_id] = count_selected_dict['count'] if count_selected_dict['selected']: redis_cart_selected_add.append(sku_id) else: redis_cart_selected_remove.append(sku_id) if cart: pl = redis_conn.pipeline() pl.hmset('cart_%s' % user.id, cart) if redis_cart_selected_add: pl.sadd('cart_selected_%s' % user.id, *redis_cart_selected_add) # sadd可以直接添加一組數(shù)據(jù) if redis_cart_selected_remove: pl.srem('cart_selected_%s' % user.id, *redis_cart_selected_remove) # srem可以直接刪除一組數(shù)據(jù) pl.execute() response.delete_cookie('cart') return response

至此,整個(gè)邏輯完成。

新人寫博客鍛煉自己,有錯(cuò)誤歡迎大家指出,我的QQ:595395786?。?!


更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號(hào)聯(lián)系: 360901061

您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對(duì)您有幫助就好】

您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對(duì)您有幫助,請(qǐng)用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長(zhǎng)會(huì)非常 感謝您的哦?。。?/p>

發(fā)表我的評(píng)論
最新評(píng)論 總共0條評(píng)論