商城——购物车模块
在项目中添加商城购物结算模块:
# 在项目目录下执行如下命令 $ 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,同时应该添加大量的日志打印功能,方便后期运维。