在項目中添加商城購物結算模塊:
# 在項目目錄下執行如下命令 $ 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,同時應該添加大量的日志打印功能,方便后期運維。