django-luffycity-购物车接口
一 基本功能
-添加购物车
-详见代码
-修改课程价格策略
-put或者patch
{"course_id": "1", "policy_id": "1"}
-查看购物车
-删除购物车数据
-购物车数据放在哪?
-放到redis中,不需要创建mysql的表来存储数据了
二 实现代码
2.1相关表单建立
from django.db import models from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.fields import GenericForeignKey from django.contrib.contenttypes.fields import GenericRelation # Create your models here. # class CourseType(models.Model): class UserInfo(models.Model): name = models.CharField(max_length=64) pwd = models.CharField(max_length=32) class Token(models.Model): user = models.OneToOneField(to=UserInfo) token = models.CharField(max_length=64) class Category(models.Model): nid = models.AutoField(primary_key=True) name = models.CharField(max_length=64) class Course(models.Model): """专题课程""" # unique=True 唯一性约束 name = models.CharField(max_length=128, unique=True) course_img = models.CharField(max_length=255) brief = models.TextField(verbose_name="课程概述", max_length=2048) level_choices = ((0, '初级'), (1, '中级'), (2, '高级')) # 默认值为1 ,中级 level = models.SmallIntegerField(choices=level_choices, default=1) pub_date = models.DateField(verbose_name="发布日期", blank=True, null=True) period = models.PositiveIntegerField(verbose_name="建议学习周期(days)", default=7) # help_text 在admin中显示的帮助信息 order = models.IntegerField("课程顺序", help_text="从上一个课程数字往后排") status_choices = ((0, '上线'), (1, '下线'), (2, '预上线')) status = models.SmallIntegerField(choices=status_choices, default=0) # 用于GenericForeignKey反向查询,不会生成表字段,切勿删除 price_policy = GenericRelation("PricePolicy") category = models.ForeignKey(to='Category',to_field='nid',null=True) def __str__(self): return self.name class Meta: verbose_name_plural = "专题课" class CourseDetail(models.Model): """课程详情页内容""" course = models.OneToOneField("Course", on_delete=models.CASCADE) hours = models.IntegerField("课时") # 课程的标语 口号 course_slogan = models.CharField(max_length=125, blank=True, null=True) # video_brief_link = models.CharField(verbose_name='课程介绍', max_length=255, blank=True, null=True) # why_study = models.TextField(verbose_name="为什么学习这门课程") # what_to_study_brief = models.TextField(verbose_name="我将学到哪些内容") # career_improvement = models.TextField(verbose_name="此项目如何有助于我的职业生涯") # prerequisite = models.TextField(verbose_name="课程先修要求", max_length=1024) # 推荐课程 # related_name 基于对象的反向查询,用于替换表名小写_set recommend_courses = models.ManyToManyField("Course", related_name="recommend_by", blank=True) teachers = models.ManyToManyField("Teacher", verbose_name="课程讲师") def __str__(self): return "%s" % self.course class Meta: verbose_name_plural = "课程详细" class PricePolicy(models.Model): """价格与有课程效期表""" content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) # 关联course or degree_course object_id = models.PositiveIntegerField() content_object = GenericForeignKey('content_type', 'object_id') # course = models.ForeignKey("Course") valid_period_choices = ((1, '1天'), (3, '3天'), (7, '1周'), (14, '2周'), (30, '1个月'), (60, '2个月'), (90, '3个月'), (180, '6个月'), (210, '12个月'), (540, '18个月'), (720, '24个月'), ) valid_period = models.SmallIntegerField(choices=valid_period_choices) price = models.FloatField() class Meta: unique_together = ("content_type", 'object_id', "valid_period") verbose_name_plural = "价格策略" def __str__(self): return "%s(%s)%s" % (self.content_object, self.get_valid_period_display(), self.price) class Teacher(models.Model): """讲师、导师表""" name = models.CharField(max_length=32) image = models.CharField(max_length=128) brief = models.TextField(max_length=1024) def __str__(self): return self.name class Meta: verbose_name_plural = "讲师"
2.2自定义response与Exception信息
class MyResponse(): def __init__(self): self.status = 100 self.msg = None @property def get_dic(self): return self.__dict__ class CommonException(Exception): def __init__(self,status,msg): self.status =status self.msg = msg
2.3登录认证组件
from rest_framework.authentication import BaseAuthentication from api import models from rest_framework.exceptions import AuthenticationFailed class LoginAuth(BaseAuthentication): def authenticate(self, request): token = request.GET.get('token') ret = models.UserToken.objects.filter(token=token).first() if ret: # 有值说明认证通过,返回两个值 return ret.user, ret else: raise AuthenticationFailed('认证失败,没有登录')
2.4views
from rest_framework.views import APIView from rest_framework.response import Response from api import models from api.utils.commonUtils import MyResponse from rest_framework.viewsets import ViewSetMixin from django.core.exceptions import ObjectDoesNotExist from django.conf import settings from api.utils.MyAuth import LoginAuth from api.utils.commonUtils import CommonException from django_redis import get_redis_connection import json # 需要登录之后才能操作,写一个认证组件 class ShoppingCart(APIView): authentication_classes = [LoginAuth] conn = get_redis_connection() def post(self, request, *args, **kwargs): response = MyResponse() # 课程id,价格策略id # {"course_id": "1", "policy_id": "1"} # 放到redis中key值 shoppingcart_userid_courseid # 0 取出课程id,价格策略id course_id = str(request.data.get('course_id')) policy_id = str(request.data.get('policy_id')) # 1 校验课程是否合法 try: course = models.Course.objects.get(pk=course_id) # 2 获取所有价格策略(通过课程拿出所有价格策略) policy_price_all = course.price_policy.all() # 3 从redis中取出当前登录用户的购物车 shopping_byte = self.conn.get('shoppingcart_%s' % request.user.pk) if shopping_byte: shopping_cart = json.loads(shopping_byte) else: shopping_cart = {} # 循环构造出价格策略大字典 policy = {} for policy_price in policy_price_all: ''' { "period":3, "period_display":"3天", "price":200 }, ''' policy_one = { 'period': policy_price.pk, 'period_display': policy_price.get_valid_period_display(), 'price': policy_price.price } policy[str(policy_price.pk)] = policy_one # 判断价格策略是否合法,不再字典中,就不合法 if policy_id not in policy: # 不合法 raise CommonException(102, '价格策略不合法,你不是人') # 判断传入的课程id是否在购物车中 if course_id in shopping_cart: # 更新一下默认价格策略 shopping_cart[course_id]['default_policy'] = policy_id response.msg = '更新成功' else: shopping_course = { 'title': course.name, 'img': course.course_img, 'default_policy': policy_id, 'policy': policy } # 添加到购物车 shopping_cart[course_id] = shopping_course response.msg = '添加成功' # 写入redis self.conn.set('shoppingcart_%s' % request.user.pk, json.dumps(shopping_cart)) except ObjectDoesNotExist as e: response.status = 101 response.msg = '该课程不存在,你可能是爬虫' except CommonException as e: response.status = e.status response.msg = e.msg except Exception as e: response.status = 400 response.msg = '未知错误' print(str(e)) return Response(response.get_dic) def put(self,request,*args,**kwargs): response=MyResponse() # 0 取出课程id,价格策略id course_id = str(request.data.get('course_id')) policy_id = str(request.data.get('policy_id')) try: shopping_byte = self.conn.get('shoppingcart_%s' % request.user.pk) if shopping_byte: shopping_cart = json.loads(shopping_byte) else: shopping_cart = {} if course_id not in shopping_cart: raise CommonException(102,'要修改的课程不存在') course_detail=shopping_cart.get(course_id) if policy_id not in course_detail['policy']: raise CommonException(103, '价格策略不合法') course_detail['default_policy']=policy_id response.msg='修改成功' self.conn.set('shoppingcart_%s' % request.user.pk, json.dumps(shopping_cart)) except ObjectDoesNotExist as e: response.status = 101 response.msg = '该课程不存在,你可能是爬虫' except CommonException as e: response.status = e.status response.msg = e.msg except Exception as e: response.status = 400 response.msg = '未知错误' print(str(e)) return Response(response.get_dic) def get(self,request,*args,**kwargs): response=MyResponse() try: shopping_byte = self.conn.get('shoppingcart_%s' % request.user.pk) if shopping_byte: shopping_cart = json.loads(shopping_byte) else: shopping_cart = {} response.data=shopping_cart except Exception as e: response.status = 400 response.msg = '未知错误' print(str(e)) return Response(response.get_dic) def delete(self, request, *args, **kwargs): response=MyResponse() course_id=request.data.get('course_id') try: shopping_byte = self.conn.get('shoppingcart_%s' % request.user.pk) if shopping_byte: shopping_cart = json.loads(shopping_byte) else: shopping_cart = {} shopping_cart.pop(course_id,None) self.conn.set('shoppingcart_%s' % request.user.pk, json.dumps(shopping_cart)) except Exception as e: response.status = 400 response.msg = '未知错误' print(str(e)) return Response(response.get_dic)
2.5redis配置
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": 1000} # "PASSWORD": "123", } } }