zoukankan      html  css  js  c++  java
  • 路飞学城项目之加入购物车接口

    路飞学城项目之加入购物车接口

    购物车的 Redis 结构设计

    存储结构设计

    第一种

    以 luffy_shopping_car 作为外键,里面是用户购物车字典,字典的键为用户名 ID ,字典的值是课程字典,字典的键是课 ID,字典的值是课程信息。

    {
        luffy_shopping_car:{
            6:{
                11:{
                    'title':'21天入门到放弃',
                    'src':'xxx.png'
                },
                12:{
                    'title':'21天入门到放弃',
                    'src':'xxx.png'
                }
            }
        }
    }
    

    第二种

    luffy_shopping_car_用户ID_课程ID 为外键,里面为课程信息。

    {
        luffy_shopping_car_6_11:{
            'title':'21天入门到放弃',
             'src':'xxx.png'
        },
        luffy_shopping_car_6_12:{
            'title':'21天入门到放弃',
             'src':'xxx.png'
        },
        luffy_shopping_car_6_14:{
            'title':'21天入门到放弃',
             'src':'xxx.png'
        }
    }
    

    最后选择第二种,因为此版本更方便进行删除与更改。

    Redis 中的购物车操作

    引入 redis 包,并连接。

    import redis
    
    conn = redis.Redis(host="127.0.0.1", port=6379)
    
    1. 删除所有键
      conn.flushall()
      
    2. 添加课程
      redis_key = "luffy_shopping_car_%s_%s"%(6, 13)
      conn.hmset(redis_key, {"title":"23天从入门到放弃", "src":"x3.png"})
      
    3. 删除课程
      conn.delete('luffy_shopping_car_7_12')
      
    4. 修改课程
      conn.hset('luffy_shopping_car_6_12', 'src', 'x1.png')
      
    5. 查看所有课程
      for item in conn.scan_iter("luffy_shopping_car_6_*", count=10):
          course = conn.hgetall(item)
      
    6. 查看课程中的某个信息
      course_title = conn.hget("luffy_shopping_car_6_12", 'title')
      

    接口逻辑

    前期工作

    配置文件设置

    settings.py 文件中:

    用 redis 作为缓存的配置

    # 用redis作为缓存的配置
    CACHES = {
        "default": {
            "BACKEND": "django_redis.cache.RedisCache",
            "LOCATION": "redis://127.0.0.1:6379",
            "OPTIONS": {
                "CLIENT_CLASS": "django_redis.client.DefaultClient",
                "CONNECTION_POOL_KWARGS": {"max_connections": 100},
    			# "PASSWORD": "1234", # 有时候还需要密码
            }
        }
    }
    

    购物车关键字的设置

    # 购物车关键字的设置
    SHOPPING_CAR_KEY = "luffy_shopping_car_%s_%s"
    

    URL 设置

    from api.views import shoppingcar
    
    url(r'^shoppingcar/$', shoppingcar.ShoppingCarViewSet.as_view()),
    

    登录认证

    基于 rest framework 的认证类,文件为 api/auth/auth.py

    from rest_framework.authentication import BaseAuthentication
    from rest_framework.exceptions import AuthenticationFailed
    from api import models
    
    # 登录认证
    class LufyyAuth(BaseAuthentication):
        def authenticate(self, request):
            token = request.query_params.get('token')
            obj = models.UserToken.objects.filter(token=token).first()
    
            if not obj:
                raise AuthenticationFailed({'code':1001, 'error':'认证失败'})
    
            return (obj.user.user, obj)
    

    响应体基础类

    用于返回数据的封装。文件为 api/utils/response.py

    class BaseResponse(object):
        """
        响应体基础类
        """
    
        def __init__(self):
            self.data = None
            self.code = 1000
            self.error = None
    
        @property
        def dict(self):
            return self.__dict__
    
    

    购物车逻辑实现

    所用到的包。

    from utils.response import BaseResponse
    from api.auth.auth import LufyyAuth
    from api import models
    from utils.exception import PricePolicyInvalid
    from django.core.exceptions import ObjectDoesNotExist
    from django.conf import settings
    import json
    from django_redis import get_redis_connection
    

    认证类和链接

    先将 rest framework 的认证类包含进去并连接上 redis 。

    class ShoppingCarViewSet(APIView):
        authentication_classes = [LufyyAuth, ]
        conn = get_redis_connection("default")
    

    添加到购物车

    使用的是 post 方法。而且整个逻辑应该放到 try expect 模块中处理。

    def post(self, request, *args, **kwargs):
    	ret = BaseResponse()
        ...
    
    1. 获取用户提交的课程ID和价格策略
    course_id = int(request.data.get('courseid'))
    policy_id = int(request.data.get('policyid'))
    
    1. 获取专题课信息
    course = models.Course.objects.get(id=course_id)
    

    此时如果得不到相应的课程对象,应该抛出 ObjectDoesNotExist 异常。

    except ObjectDoesNotExist as e:
    	ret.code = 2001
    	ret.error = "课程不存在"
    
    1. 获得该课程相关的所有价格策略

    得到课程的所有价格策略并放到一个字典中。

    price_policy_list = course.price_policy.all()
    price_policy_dict = {}
    
    for price_policy_item in price_policy_list:
    	price_policy_dict[price_policy_item.id] = {
    		"period": price_policy_item.valid_period,
    		"period_display": price_policy_item.get_valid_period_display(),
    		"price": price_policy_item.price
    	}
    
    1. 判断用户提交的价格策略是否合法

    如果不合法就抛出异常,此处的 PricePolicyInvalid 异常是自己定义的:

    class PricePolicyInvalid(Exception):
        def __init__(self, msg):
            self.msg = msg
    
    
    if policy_id not in price_policy_dict:
    	# 价格策略不合法
    	raise PricePolicyInvalid("价格策略不合法")
    except PricePolicyInvalid as e:
    	ret.code = 2001
    	ret.error = e.msg
    
    1. 将购物信息添加到redis中

    应该在此处建立之前提到过得 redis 购物车数据结构。

    car_key = settings.SHOPPING_CAR_KEY % (request.auth.user_id, course_id, )
    	car_dict = {
    		"title": course.title,
    		"img": course.course_img,
    		"default_policy": policy_id,
    		"policy": json.dumps(price_policy_dict)
    	}
    	self.conn.hmset(car_key, car_dict)
    	ret.data = "添加成功"
    

    最后,如果还发生其他异常,进行捕获并返回响应体。

    except Exception as e:
    	ret.code = 1001
    	ret.error = "获取购物车失败"
    
    return Response(ret.dict)
    

    从购物车中删除课程

    删除使用的是 delete 方法。

    从前端获取所有课程的 ID ,并将这些 ID 拼成 SHOPPING_CAR_KEY 的形式,组成一个列表,直接用 delete(*[]) 的方式删除所有键值。

    
    def delete(self, request, *args, **kwargs):
    	"""
    	删除购物车中的课程
    	:param request:
    	:param args:
    	:param kwargs:
    	:return:
    	"""
    	ret = BaseResponse()
    
    	try:
    		course_id_list = request.data.get('courseids')
    		key_list = [settings.SHOPPING_CAR_KEY%(request.auth.user_id, course_id, ) for course_id in course_id_list]
    		self.conn.delete(*key_list)
    	except Exception as e:
    		ret.code = 1002
    		ret.error = "删除失败"
    

    修改购物车的课程

    使用的是 patch 方法。

    1. 获取价格策略ID和课程ID
    course_id = int(request.data.get('courseid'))
    policy_id = str(request.data.get('policyid'))
    
    1. 拼接课程的 key

    有错的时候可以通过异常抛出的方式来处理,还可以直接返回 return ,此处用的是第二种方式。

    key = settings.SHOPPING_CAR_KEY%(request.auth.user_id, course_id)
    
    if not self.conn.exists(key):
    	ret.code = 1002
    	ret.error = "购物车中不存在此课程"
    	return Response(ret.dict)
    
    1. redis中获取所有的价格策略
    policy_dict = json.loads(self.conn.hget(key, 'policy').decode('utf-8'))
    
    if policy_id not in policy_dict:
    	ret.code = 1003
    	ret.error = "价格策略不合法"
    	return Response(ret.dict)
    
    1. 在购物车中修改该课程的默认价格策略
    self.conn.hset(key, 'default_policy', policy_id)
    
    ret.data = "修改成功"
    

    完整代码:

    def patch(self,  request, *args, **kwargs):
    	"""
    	修改购物车的课程
    	:param request:
    	:param args:
    	:param kwargs:
    	:return:
    	"""
    	ret = BaseResponse()
    
    	try:
    		# 1. 获取价格策略ID和课程ID
    		course_id = int(request.data.get('courseid'))
    		policy_id = str(request.data.get('policyid'))
    
    		# 2. 拼接课程的key
    		key = settings.SHOPPING_CAR_KEY%(request.auth.user_id, course_id)
    
    		if not self.conn.exists(key):
    			ret.code = 1002
    			ret.error = "购物车中不存在此课程"
    			return Response(ret.dict)
    
    		# 3. redis中获取所有的价格策略
    		policy_dict = json.loads(self.conn.hget(key, 'policy').decode('utf-8'))
    
    		if policy_id not in policy_dict:
    			ret.code = 1003
    			ret.error = "价格策略不合法"
    			return Response(ret.dict)
    
    		# 4. 在购物车中修改该课程的默认价格策略
    		self.conn.hset(key, 'default_policy', policy_id)
    
    		ret.data = "修改成功"
    
    	except Exception as e:
    		ret = 1004
    		ret.error = "修改失败"
    
    	return Response(ret.dict)
    

    查看购物车中的所有课程

    使用的是 get 方法。

    使用通配符来构建该用户的所有课程 key ,然后通过 scan_iter 函数进行遍历,并返回所有课程信息。

    def get(self, request, *args, **kwargs):
    	"""
    	查看购物车中的所有商品
    	:param request:
    	:param args:
    	:param kwargs:
    	:return:
    	"""
    	ret = BaseResponse()
    
    	try:
    		key_match = settings.SHOPPING_CAR_KEY%(request.auth.user_id, "*")
    
    		course_list = []
    
    		for key in self.conn.scan_iter(key_match, count=10):
    			info = {
    				"title": self.conn.hget(key, "title").decode('utf-8'),
    				"img": self.conn.hget(key, "img").decode('utf-8'),
    				"policy": json.loads(self.conn.hget(key, "policy").decode('utf-8')),
    				"default_policy": self.conn.hget(key, "default_policy").decode('utf-8'),
    			}
    			course_list.append(info)
    
    		ret.data = course_list
    
    	except Exception as e:
    		ret.code = 1002
    		ret.error = "获取失败"
    
    	return Response(ret.dict)
    
    

    GitHub 地址:https://github.com/protea-ban/oldboy/tree/master/s9day112

  • 相关阅读:
    Hdoj 3697 Selecting courses 【贪心】
    nginx 反向代理
    嵌入式交叉编译环境搭建
    OpenWrt编译
    OpenWrt for vmware 从openwrt.org下载10.03.1 或是自己下载最新的源码进行编译生成x86 vmdk格式
    ubuntu centos debina
    openWRT
    C++ classics
    tomcat配置访问日志,访问首页主目录
    apache http配置https
  • 原文地址:https://www.cnblogs.com/banshaohuan/p/10211310.html
Copyright © 2011-2022 走看看