商城——購物車模塊


  在項目中添加商城購物結算模塊:

# 在項目目錄下執行如下命令
$ python3 manage.py startapp shopping

  將應用注冊到settings.py中:

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'Course.apps.CourseConfig',
    'Login.apps.LoginConfig',
    'shopping.apps.ShoppingConfig',
    'rest_framework'    # 注意要注冊rest_framework
]

  將shopping添加到路由分發/LuffyCity/LuffyCity/settings.py:

from django.contrib import admin
from django.urls import path, include, re_path
from django.views.static import serve
from LuffyCity import settings

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/course/', include("Course.urls")),
    path('api/shop/', include("shopping.urls")),   # 順序早於api/很重要
    path('api/', include("Login.urls")),

    # media路徑配置
    # path('media/(?P<path>.*)', serve, {'document_root': settings.MEDIA_ROOT}),
    re_path('media/(?P<path>.*)', serve, {'document_root': settings.MEDIA_ROOT})
]

一、添加購物車接口

  前端添加購物車,傳給購物車接口的信息會包含course_id、price_policy_id,把購物車的數據放入redis中。保存在redis中的信息其實就是會在前端展示的對應信息。

  設計redis保存的購物車信息主要是:商品名、商品圖片、商品價格策略對應價格、用戶id、商品id,如下所示:

{
    SHOPPINGCAR_USERID_COURSE_ID: {
        "id": '',
        "title": '',
        "course_img": '',
        "price_policy_dict": {
            price_policy_id: "{valid_period, price}",
            price_policy_id2: "{valid_period, price}",
            price_policy_id3: "{valid_period, price}"
        },
        "default_price_policy_id": 1
    }
}

  由於前端顯示時,時常需要一個默認的價格,所以還配置一個默認價格策略id字段。

1、購物車路由

  創建商城路由文件:shopping/urls.py

from django.urls import path
from .views import ShoppingCarView

urlpatterns = [
    path('shopping_car', ShoppingCarView.as_view()),   # 購物車
]

2、購物車視圖

  購物車視圖處理添加購物車的post請求,主要操作:獲取前端傳遞的數據和user_id、校驗數據的合法性、構建欲存入redis的key和value、將購物信息寫入redis數據庫中。

from rest_framework.views import APIView
from rest_framework.response import Response
from utils.base_response import BaseResponse
from utils.my_auth import LoginAuth
from utils.redis_pool import POOL
from Course.models import Course
import json
import redis


SHOPPINGCAR_KEY = "SHOPPINGCAR_%s_%s"
CONN = redis.Redis(connection_pool=POOL)    # redis連接


class ShoppingCarView(APIView):
    authentication_classes = [LoginAuth, ]    # 登錄認證

    def post(self, request):
        res = BaseResponse()
        # 1.獲取前端傳遞的數據及user_id
        course_id = request.data.get("course_id", "")
        price_policy_id = request.data.get("price_policy_id", "")
        user_id = request.user.pk    # LoginAuth返回的user_obj即為request.user
        # 2.校驗數據的合法性
        # 2.1.校驗課程id合法性
        course_obj = Course.objects.filter(id=course_id).first()
        if not course_obj:
            res.code = 1040
            res.error = "課程id不合法"
            return Response(res.dict)
        # 2.2 校驗價格策略id是否合法
        price_policy_queryset = course_obj.price_policy.all()
        price_policy_dict = {}
        for price_policy in price_policy_queryset:
            price_policy_dict[price_policy.id] = {
                "price": price_policy.price,   # 價格
                "valid_period": price_policy.valid_period,   # 周期
                "valid_period_display": price_policy.get_valid_period_display()   # 周期中文
            }
        print("價格策略", price_policy_id, price_policy_dict)
        if price_policy_id not in price_policy_dict:
            res.code = 1041
            res.error = "價格策略id不合法"
            return Response(res.dict)
        # 3.構建redis Key
        key = SHOPPINGCAR_KEY % (user_id, course_id)
        # 4.構建redis數據結構
        course_info = {
            "id": course_obj.id,
            "title": course_obj.title,
            "course_img": str(course_obj.course_img),    # 解決字典類型不一致的問題
            # 所有放入redis的字典最好做一下json.dumps,防止出現格式問題或中文亂碼
            "price_policy_dict": json.dumps(price_policy_dict, ensure_ascii=False),
            "default_price_policy_id": price_policy_id
        }
        # 5.寫入redis
        CONN.hmset(key, course_info)
        res.data = "加入購物車成功"
        return Response(res.dict)

3、添加購物車接口測試

  首先調用登錄接口獲取token:

  

   然后配置購物車header:

  

  隨后調用添加購物車接口

  

二、查看購物車接口

  通過redis拿到購物車的所有信息進行展示。

1、查看購物車接口視圖

from rest_framework.views import APIView
from rest_framework.response import Response
from utils.base_response import BaseResponse
from utils.my_auth import LoginAuth
from utils.redis_pool import POOL
from Course.models import Course
import json
import redis


SHOPPINGCAR_KEY = "SHOPPINGCAR_%s_%s"       # 第一個值user_id,第二個值course_id
CONN = redis.Redis(connection_pool=POOL)    # redis連接


class ShoppingCarView(APIView):
    authentication_classes = [LoginAuth, ]    # 登錄認證
    def post(self, request):...

    def get(self, request):
        res = BaseResponse()
        # 1.拼接redis KEY
        user_id = request.user.pk
        shopping_car_key = SHOPPINGCAR_KEY % (user_id, "*")   # redis支持模糊匹配
        # 2.去redis中讀取數據
        # 2.1 匹配所有的keys
        all_keys = CONN.scan_iter(shopping_car_key)    # 都匹配,返回的是生成器
        ret = []
        for key in all_keys:
            ret.append(CONN.hgetall(key))
        print(ret)
        # 3.構建數據結構展示
        res.data = ret
        return Response(res.dict)

(1)scan_iter(match=None, count=None)

  查看所有元素-迭代器

(2)hgetall(name)

  獲取name對應hash的所有鍵值。

2、查看購物車接口測試

  

三、更新購物車接口

  在更新購物車時,同樣需要前端提供course_id和price_policy_id,校驗數據合法后,更新redis中的default_price_policy_id字段。

1、更新購物車視圖

from rest_framework.views import APIView
from rest_framework.response import Response
from utils.base_response import BaseResponse
from utils.my_auth import LoginAuth
from utils.redis_pool import POOL
from Course.models import Course
import json
import redis


SHOPPINGCAR_KEY = "SHOPPINGCAR_%s_%s"       # 第一個值user_id,第二個值course_id
CONN = redis.Redis(connection_pool=POOL)    # redis連接

class ShoppingCarView(APIView):
    authentication_classes = [LoginAuth, ]    # 登錄認證

    def post(self, request):...

    def get(self, request):...

    def put(self, request):
        # 更新購物車:前端需要提供course_id、price_policy_id
        res = BaseResponse()
        # 1.獲取前端傳過來的數據及user_id
        course_id = request.data.get("course_id", "")
        price_policy_id = request.data.get("price_policy_id", "")
        user_id = request.user.pk
        # 2.校驗數據的合法性
        # 2.1 course_id是否合法
        key = SHOPPINGCAR_KEY % (user_id, course_id)
        if not CONN.exists(key):    # exists檢查名字是否存在
            res.code = 1043
            res.error = "課程id不合法"
            return Response(res.dict)
        # 2.2 price_policy_id是否合法
        price_policy_dict = json.loads(CONN.hget(key, "price_policy_dict"))
        if str(price_policy_id) not in price_policy_dict:
            res.code = 1044
            res.error = "價格策略不合法"
            return Response(res.dict)
        # 3.更新redis中的default_price_policy_id字段
        CONN.hset(key, "default_price_policy_id", price_policy_id)
        res.data = "更新成功"
        return Response(res.dict)

2、更新購物車測試

  更新token,發送put請求:

  

 

   發送get請求,驗證更新結果:

   

四、刪除購物車接口

  考慮可能會批量刪除,前端傳遞過來的數據應該是用一個列表course_list,來保存一個一個course_id。循環校驗這些課程id是否合法,再刪除redis中的數據。

1、刪除購物車視圖

class ShoppingCarView(APIView):
    """代碼省略"""
    def delete(self, request):
        # course_list = [course_id, ]
        res = BaseResponse()
        # 1.獲取前端傳來的數據及user_id
        course_list = request.data.get("course_list", "")
        user_id = request.user.pk
        # 2.校驗course_id是否合法
        for course_id in course_list:
            key = SHOPPINGCAR_KEY % (user_id, course_id)
            if not CONN.exists(key):   # 如果key不存在
                res.code = 1045
                res.error = "課程id不合法"
                return Response(res.dict)
            # 3.刪除redis數據
            CONN.delete(key)
        res.data = "刪除成功"
        return Response(res.dict)

 

2、刪除購物車測試

  更新token,發送delete請求:

  

 

  發送get請求,驗證刪除結果:

  

 

五、總結和優化

  這些接口要進一步優化,應該在最外層套一層try,同時應該添加大量的日志打印功能,方便后期運維。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM