考试第二部分:MySQL数据库
6. MySQL中char和varchar的区别(1分)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
char是定长,varchar是变长。
char的查询速度比varchar要快。
7. MySQL中varchar(50)的50表示什什么意思?(1分)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
是字符长度。一个中文,也是一个字符。
8. left join、right join以及inner join的区别?(2分)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
left join,表示左连接,以左表为基准,如果左表有不匹配的,显示为空
right join,表示右连接,以右表为基准,如果右表有不匹配的,显示为空
inner join,表示内连接,只显示2个表条件符合的记录,不匹配的不显示
9. MySQL组合索引(2分)
where⼦子句句中有a、b、c 三个查询条件,创建⼀一个组合索引 abc(a,b,c),那么如下那中情况会命 中索引:
a. where (a)
b. where (b)
c. where (c)
d. where (a,b)
e. where (b,c)
f. where (a,c)
g. where (a,b,c)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
a,d,f,g 会命中索引
解释:
索引有2个功能:加快查询和约束。
这里的约束指的是唯一索引,联合唯一索引。
索引遵循的原则: 最左前缀原则
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
你可以认为联合索引是闯关游戏的设计 例如你这个联合索引是state/city/zipCode 那么state就是第一关 city是第二关, zipCode就是第三关 你必须匹配了第一关,才能匹配第二关,匹配了第一关和第二关,才能匹配第三关 你不能直接到第二关的 索引的格式就是第一层是state,第二层才是city 索引是因为B+树结构 所以查找快 如果单看第三列 是非排序的。 多列索引是先按照第一列进行排序,然后在第一列排好序的基础上再对第二列排序,如果没有第一列的话,直接访问第二列,那第二列肯定是无序的,直接访问后面的列就用不到索引了。 所以如果不是在前面列的基础上而是但看后面某一列,索引是失效的。
简而言之,只要where条件包含最左边的字段,那么它就会用到组合索引,反之亦然!
如果创建了组合索引,但是却没有命中,这是浪费磁盘空间。因为索引也占用磁盘!
10. 假设学⽣生Student和教师Teacher关系模型如下:(4分) Student(学号、姓名、性别、类型、身份证号) Teacher(教师号、姓名、性别、类型、身份证号、工资)
其中,学⽣生表中类别为“本科生”和“研究生”两类;性别为“男”和“女”两类。
a. 性别为女的所有学生。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
select * from Student where 性别='女'
b. 学生表中类别分别对应的个数。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
select 类型,count(1) from Student group by 类型
c.工资少于10000的女教师的身份证和姓名。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
select 身份证,姓名 from Teacher where 性别= '女' and 类型='研究生' and 工资 < 10000
d. 研究生教师平均工资、最⾼高和最低工资。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
select AVG(工资),MAX(工资),MIN(工资) from Teacher wherer 类型='研究生'
11. 根据如下表结构建表:(2分)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
CREATE TABLE `t1` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(32) NOT NULL, `balance` decimal(10,2) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
12. 根据如下表查询每个⽤用户第⼀一次下订单的时间。(2分)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# 第一次下单时间,就是时间最早的 select name,MIN(order_time) from table group by name
13. 有⼀一个订单系统包含订单信息、商品信息、价格信息且还要⼀一些状态,如何设计表结构(2分)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#最简单的设计 商品表 - id - 名称 - 价格 - 描述信息 订单表 - id - 订单号(唯一) - 商品id - 用户id 用户表 - id - username - password
14. 有如下表:(3分)
products(商品表) columns为 id、name、price
orders(商城订单表) columns为 id、reservations_id、product_id、quantity(数量量)
reservations(酒店订单表) columns为 id、user_id、price、created_at
ps:这个一个真实面试题!
应用场景:比如万达酒店,需要订购商品,比如红酒,红木家具...
a. 各个商品的售卖情况,需要字段:商品名、购买总数、商品收⼊入(单价*数量量)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
SELECT products. NAME, sum(orders.quantity), products.price * sum(orders.quantity) FROM orders LEFT JOIN products ON products.id = orders.product_id GROUP BY orders.product_id
b. 所有用户在2018-01-01至2018-02-01下单次数、下单金额、商城下单次数、商城下单金额
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# 注意:最后的期限要加1天。因为23:59:59也是属于当天的 SELECT count(1), sum(reservations.price), sum(orders.quantity), products.price * sum(orders.quantity) FROM reservations LEFT JOIN orders ON orders.reservations_id = reservations.id LEFT JOIN products ON products.id = orders.product_id WHERE reservations.created_at BETWEEN 2018-01-01 AND reservations.created_at '2018-02-02'
c. 历月下单用户数:下单1次的用户数、下单2次的用户数、下单3次及以上的用户数
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# 下单1次的用户数 select DATE_FORMAT(created_at,'%Y-%m'),user_id,count(1) from reservations group by DATE_FORMAT(created_at,'%Y-%m'),user_id having count(user_id) = 1; # 下单2次的用户数 select DATE_FORMAT(created_at,'%Y-%m'),user_id,count(1) from reservations group by DATE_FORMAT(created_at,'%Y-%m'),user_id having count(user_id) = 1; # 下单3次及以上的用户数 select DATE_FORMAT(created_at,'%Y-%m'),user_id,count(1) from reservations group by DATE_FORMAT(created_at,'%Y-%m'),user_id having count(user_id) >= 3;
15. 根据表写SQL语句句:(5分)
• 查询所有同学的学号、姓名、班级名称。(1分)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
select student.sid,student.sname,class.caption from student left jon class on class.cid = student.class_id
• 查询没有学⽣生的所有班级。(2分)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
select class.caption from student left jon class on class.cid = student.class_id where class.cid is null
• 查询有学⽣生的所有班级的名称和学数量量。(2分)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
select class.caption,count(1) from student left jon class on class.cid = student.class_id
一、DRF用户认证
流程图
请求到达视图的时候,需要进行认证。
认证是在中间件之后的。如果一旦认证失败,则返回信息给用户
启动项目luffcity,访问购物车
注意:要启动redis,否则提示获取购物车数据失败
添加认证
购物车需要登录才能查看,登录成功后,返回一个token
修改views目录下的auth.py
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from rest_framework.response import Response from rest_framework.views import APIView from rest_framework.viewsets import ViewSetMixin from api import models from api.utils.response import BaseResponse import uuid class AuthView(ViewSetMixin,APIView): def login(self,request,*args,**kwargs): """ 用户登陆认证 :param request: :param args: :param kwargs: :return: """ response = BaseResponse() # 默认状态 try: user = request.data.get('username') pwd = request.data.get('password') # 验证用户和密码 obj = models.Account.objects.filter(username=user,password=pwd).first() if not obj: response.code = 10002 response.error = '用户名或密码错误' else: uid = str(uuid.uuid4()) # 生成唯一id response.code = 99999 response.data = uid except Exception as e: response.code = 10005 response.error = '操作异常' return Response(response.dict)
请确保已经生成了表api_account,并添加了一条记录
如果没有,请参考昨天的文档!
测试用户和密码
查看返回结果
输入正确的用户名和密码
查看返回结果,返回一个随机码。这个就是token
9999表示登录成功
既然token已经生成了,并返回给了客户端。那么服务器如何验证客户端的token是否合法呢?
答案是,服务器需要保存token。推荐加一个有效期,比如微信公众号的token有效期为8个小时!
增加token表
修改models.py,增加token表
它和用户表是一对一的关系!
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation from django.contrib.contenttypes.models import ContentType from django.db.models import Q from django.utils.safestring import mark_safe from django.db import models import hashlib # ######################## 课程相关 ######################## class CourseCategory(models.Model): """课程大类, e.g 前端 后端...""" name = models.CharField(max_length=64, unique=True) def __str__(self): return "%s" % self.name class Meta: verbose_name_plural = "01.课程大类" class CourseSubCategory(models.Model): """课程子类, e.g python linux """ category = models.ForeignKey("CourseCategory") name = models.CharField(max_length=64, unique=True) def __str__(self): return "%s" % self.name class Meta: verbose_name_plural = "02.课程子类" class DegreeCourse(models.Model): """学位课程""" name = models.CharField(max_length=128, unique=True) course_img = models.CharField(max_length=255, verbose_name="缩略图") brief = models.TextField(verbose_name="学位课程简介", ) total_scholarship = models.PositiveIntegerField(verbose_name="总奖学金(贝里)", default=40000) # 2000 2000 mentor_compensation_bonus = models.PositiveIntegerField(verbose_name="本课程的导师辅导费用(贝里)", default=15000) period = models.PositiveIntegerField(verbose_name="建议学习周期(days)", default=150) # 为了计算学位奖学金 prerequisite = models.TextField(verbose_name="课程先修要求", max_length=1024) teachers = models.ManyToManyField("Teacher", verbose_name="课程讲师") # 用于GenericForeignKey反向查询, 不会生成表字段,切勿删除 # coupon = GenericRelation("Coupon") # 用于GenericForeignKey反向查询,不会生成表字段,切勿删除 degreecourse_price_policy = GenericRelation("PricePolicy") def __str__(self): return self.name class Meta: verbose_name_plural = "03.学位课" class Teacher(models.Model): """讲师、导师表""" name = models.CharField(max_length=32) role_choices = ((0, '讲师'), (1, '导师')) role = models.SmallIntegerField(choices=role_choices, default=0) title = models.CharField(max_length=64, verbose_name="职位、职称") signature = models.CharField(max_length=255, help_text="导师签名", blank=True, null=True) image = models.CharField(max_length=128) brief = models.TextField(max_length=1024) def __str__(self): return self.name class Meta: verbose_name_plural = "04.导师或讲师" class Scholarship(models.Model): """学位课程奖学金""" degree_course = models.ForeignKey("DegreeCourse") time_percent = models.PositiveSmallIntegerField(verbose_name="奖励档位(时间百分比)", help_text="只填百分值,如80,代表80%") value = models.PositiveIntegerField(verbose_name="奖学金数额") def __str__(self): return "%s:%s" % (self.degree_course, self.value) class Meta: verbose_name_plural = "05.学位课奖学金" class Course(models.Model): """专题课/学位课模块表""" name = models.CharField(max_length=128, unique=True) course_img = models.CharField(max_length=255) sub_category = models.ForeignKey("CourseSubCategory") course_type_choices = ((0, '付费'), (1, 'VIP专享'), (2, '学位课程')) course_type = models.SmallIntegerField(choices=course_type_choices) # 不为空;学位课的某个模块 # 为空;专题课 degree_course = models.ForeignKey("DegreeCourse", blank=True, null=True, help_text="若是学位课程,此处关联学位表") brief = models.TextField(verbose_name="课程概述", max_length=2048) level_choices = ((0, '初级'), (1, '中级'), (2, '高级')) 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) # order = models.IntegerField("课程顺序", help_text="从上一个课程数字往后排") attachment_path = models.CharField(max_length=128, verbose_name="课件路径", blank=True, null=True) status_choices = ((0, '上线'), (1, '下线'), (2, '预上线')) status = models.SmallIntegerField(choices=status_choices, default=0) template_id = models.SmallIntegerField("前端模板id", default=1) coupon = GenericRelation("Coupon") # 用于GenericForeignKey反向查询,不会生成表字段,切勿删除 price_policy = GenericRelation("PricePolicy") asked_question = GenericRelation("OftenAskedQuestion") def __str__(self): return "%s(%s)" % (self.name, self.get_course_type_display()) def save(self, *args, **kwargs): if self.course_type == 2: if not self.degree_course: raise ValueError("学位课程必须关联对应的学位表") super(Course, self).save(*args, **kwargs) class Meta: verbose_name_plural = "06.专题课或学位课模块" class CourseDetail(models.Model): """课程详情页内容""" course = models.OneToOneField("Course") 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) 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 = "07.课程或学位模块详细" class OftenAskedQuestion(models.Model): """常见问题""" content_type = models.ForeignKey(ContentType) # 关联course or degree_course object_id = models.PositiveIntegerField() content_object = GenericForeignKey('content_type', 'object_id') question = models.CharField(max_length=255) answer = models.TextField(max_length=1024) def __str__(self): return "%s-%s" % (self.content_object, self.question) class Meta: unique_together = ('content_type', 'object_id', 'question') verbose_name_plural = "08. 常见问题" class CourseOutline(models.Model): """课程大纲""" course_detail = models.ForeignKey("CourseDetail") title = models.CharField(max_length=128) # 前端显示顺序 order = models.PositiveSmallIntegerField(default=1) content = models.TextField("内容", max_length=2048) def __str__(self): return "%s" % self.title class Meta: unique_together = ('course_detail', 'title') verbose_name_plural = "09. 课程大纲" class CourseChapter(models.Model): """课程章节""" course = models.ForeignKey("Course") chapter = models.SmallIntegerField(verbose_name="第几章", default=1) name = models.CharField(max_length=128) summary = models.TextField(verbose_name="章节介绍", blank=True, null=True) pub_date = models.DateField(verbose_name="发布日期", auto_now_add=True) class Meta: unique_together = ("course", 'chapter') verbose_name_plural = "10. 课程章节" def __str__(self): return "%s:(第%s章)%s" % (self.course, self.chapter, self.name) class CourseSection(models.Model): """课时目录""" chapter = models.ForeignKey("CourseChapter") name = models.CharField(max_length=128) order = models.PositiveSmallIntegerField(verbose_name="课时排序", help_text="建议每个课时之间空1至2个值,以备后续插入课时") section_type_choices = ((0, '文档'), (1, '练习'), (2, '视频')) section_type = models.SmallIntegerField(default=2, choices=section_type_choices) section_link = models.CharField(max_length=255, blank=True, null=True, help_text="若是video,填vid,若是文档,填link") video_time = models.CharField(verbose_name="视频时长", blank=True, null=True, max_length=32) # 仅在前端展示使用 pub_date = models.DateTimeField(verbose_name="发布时间", auto_now_add=True) free_trail = models.BooleanField("是否可试看", default=False) class Meta: unique_together = ('chapter', 'section_link') verbose_name_plural = "11. 课时" def __str__(self): return "%s-%s" % (self.chapter, self.name) class Homework(models.Model): chapter = models.ForeignKey("CourseChapter") title = models.CharField(max_length=128, verbose_name="作业题目") order = models.PositiveSmallIntegerField("作业顺序", help_text="同一课程的每个作业之前的order值间隔1-2个数") homework_type_choices = ((0, '作业'), (1, '模块通关考核')) homework_type = models.SmallIntegerField(choices=homework_type_choices, default=0) requirement = models.TextField(max_length=1024, verbose_name="作业需求") threshold = models.TextField(max_length=1024, verbose_name="踩分点") recommend_period = models.PositiveSmallIntegerField("推荐完成周期(天)", default=7) scholarship_value = models.PositiveSmallIntegerField("为该作业分配的奖学金(贝里)") note = models.TextField(blank=True, null=True) enabled = models.BooleanField(default=True, help_text="本作业如果后期不需要了,不想让学员看到,可以设置为False") class Meta: unique_together = ("chapter", "title") verbose_name_plural = "12. 章节作业" def __str__(self): return "%s - %s" % (self.chapter, self.title) # class CourseReview(models.Model): # """课程评价""" # enrolled_course = models.OneToOneField("EnrolledCourse") # about_teacher = models.FloatField(default=0, verbose_name="讲师讲解是否清晰") # about_video = models.FloatField(default=0, verbose_name="内容实用") # about_course = models.FloatField(default=0, verbose_name="课程内容通俗易懂") # review = models.TextField(max_length=1024, verbose_name="评价") # disagree_number = models.IntegerField(default=0, verbose_name="踩") # agree_number = models.IntegerField(default=0, verbose_name="赞同数") # tags = models.ManyToManyField("Tags", blank=True, verbose_name="标签") # date = models.DateTimeField(auto_now_add=True, verbose_name="评价日期") # is_recommend = models.BooleanField("热评推荐", default=False) # hide = models.BooleanField("不在前端页面显示此条评价", default=False) # # def __str__(self): # return "%s-%s" % (self.enrolled_course.course, self.review) # # class Meta: # verbose_name_plural = "13. 课程评价(购买课程后才能评价)" # # # class DegreeCourseReview(models.Model): # """学位课程评价 # 为了以后可以定制单独的评价内容,所以不与普通课程的评价混在一起,单独建表 # """ # enrolled_course = models.ForeignKey("EnrolledDegreeCourse") # course = models.ForeignKey("Course", verbose_name="评价学位模块", blank=True, null=True, # help_text="不填写即代表评价整个学位课程", limit_choices_to={'course_type': 2}) # about_teacher = models.FloatField(default=0, verbose_name="讲师讲解是否清晰") # about_video = models.FloatField(default=0, verbose_name="视频质量") # about_course = models.FloatField(default=0, verbose_name="课程") # review = models.TextField(max_length=1024, verbose_name="评价") # disagree_number = models.IntegerField(default=0, verbose_name="踩") # agree_number = models.IntegerField(default=0, verbose_name="赞同数") # tags = models.ManyToManyField("Tags", blank=True, verbose_name="标签") # date = models.DateTimeField(auto_now_add=True, verbose_name="评价日期") # is_recommend = models.BooleanField("热评推荐", default=False) # hide = models.BooleanField("不在前端页面显示此条评价", default=False) # # def __str__(self): # return "%s-%s" % (self.enrolled_course, self.review) # # class Meta: # verbose_name_plural = "14. 学位课评价(购买课程后才能评价)" class PricePolicy(models.Model): """价格与有课程效期表""" content_type = models.ForeignKey(ContentType) # 关联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 = "15. 价格策略" def __str__(self): return "%s(%s)%s" % (self.content_object, self.get_valid_period_display(), self.price) # ################################### 优惠券相关 ################################# class Coupon(models.Model): """优惠券生成规则""" name = models.CharField(max_length=64, verbose_name="活动名称") brief = models.TextField(blank=True, null=True, verbose_name="优惠券介绍") coupon_type_choices = ((0, '立减'), (1, '满减券'), (2, '折扣券')) coupon_type = models.SmallIntegerField(choices=coupon_type_choices, default=0, verbose_name="券类型") money_equivalent_value = models.IntegerField(verbose_name="等值货币") off_percent = models.PositiveSmallIntegerField("折扣百分比", help_text="只针对折扣券,例7.9折,写79", blank=True, null=True) minimum_consume = models.PositiveIntegerField("最低消费", default=0, help_text="仅在满减券时填写此字段") content_type = models.ForeignKey(ContentType, blank=True, null=True) object_id = models.PositiveIntegerField("绑定课程", blank=True, null=True, help_text="可以把优惠券跟课程绑定") content_object = GenericForeignKey('content_type', 'object_id') quantity = models.PositiveIntegerField("数量(张)", default=1) open_date = models.DateField("优惠券领取开始时间") close_date = models.DateField("优惠券领取结束时间") valid_begin_date = models.DateField(verbose_name="有效期开始时间", blank=True, null=True) valid_end_date = models.DateField(verbose_name="有效结束时间", blank=True, null=True) # coupon_valid_days = models.PositiveIntegerField(verbose_name="优惠券有效期(天)", blank=True, null=True, # help_text="自券被领时开始算起") date = models.DateTimeField(auto_now_add=True) class Meta: verbose_name_plural = "31. 优惠券生成记录" def __str__(self): return "%s(%s)" % (self.get_coupon_type_display(), self.name) class CouponRecord(models.Model): """优惠券发放、消费纪录""" coupon = models.ForeignKey("Coupon") account = models.ForeignKey("Account", verbose_name="拥有者") number = models.CharField(max_length=64, unique=True) status_choices = ((0, '未使用'), (1, '已使用'), (2, '已过期')) status = models.SmallIntegerField(choices=status_choices, default=0) get_time = models.DateTimeField(verbose_name="领取时间", help_text="用户领取时间") used_time = models.DateTimeField(blank=True, null=True, verbose_name="使用时间") # order = models.ForeignKey("Order", blank=True, null=True, verbose_name="关联订单") # 一个订单可以有多个优惠券 order_id = models.IntegerField(verbose_name='关联订单ID') class Meta: verbose_name_plural = "32. 用户优惠券" def __str__(self): return '%s-%s-%s' % (self.account, self.number, self.status) class Account(models.Model): username = models.CharField("用户名", max_length=64, unique=True) email = models.EmailField( verbose_name='邮箱', max_length=255, unique=True, blank=True, null=True ) password = models.CharField('密码', max_length=128) class Meta: verbose_name_plural = "33. 用户表" class UserToken(models.Model): user = models.OneToOneField(to='Account') token = models.CharField(max_length=36) class Meta: verbose_name_plural = "34. token表"
使用2个命令,生成表
python manage.py makemigrations
python manage.py migrate
修改views目录下的auth.py,保存token到数据库中
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from rest_framework.response import Response from rest_framework.views import APIView from rest_framework.viewsets import ViewSetMixin from api import models from api.utils.response import BaseResponse import uuid class AuthView(ViewSetMixin,APIView): def login(self,request,*args,**kwargs): """ 用户登陆认证 :param request: :param args: :param kwargs: :return: """ response = BaseResponse() # 默认状态 try: user = request.data.get('username') pwd = request.data.get('password') # 验证用户和密码 obj = models.Account.objects.filter(username=user,password=pwd).first() if not obj: response.code = 10002 response.error = '用户名或密码错误' else: uid = str(uuid.uuid4()) # 生成唯一id # 保存到数据库中,update_or_create表示更新或者创建 # user=obj,这个是判断条件。当条件成立,更新token字段,值为uid # 当条件不成立时,增加一条记录。注意:增加时,有2个字段,分别是user和token models.UserToken.objects.update_or_create(user=obj, defaults={'token': uid}) response.code = 99999 response.data = uid except Exception as e: response.code = 10005 response.error = '操作异常' return Response(response.dict)
再次发送POST请求,输入正确的用户名和密码
查看表api_usertoken,发现和返回结果是一样的!
再发送一次
表的数据随之更新
增加认证
不光购物车会用到用户认证,结算中心也需要用到认证,还有其他的视图,也同样需要登录才能使用。
所以,这个认证类需要放到utils里面
在utils目录中新建文件auth.py
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from rest_framework.authentication import BaseAuthentication from rest_framework.exceptions import AuthenticationFailed from api import models class LuffyAuthentication(BaseAuthentication): def authenticate(self, request): """ 用户认证 :param request: :return: """ # 获取get参数中的token token = request.query_params.get('token') # 判断token是否在数据库中 token_obj = models.UserToken.objects.filter(token=token).first() if not token_obj: # 认证失败 raise AuthenticationFailed({'code':1008,'error':'认证失败'}) # 认证成功 # return 必须返回2个参数,请参考源码解析 # 这里的token_obj.user,表示UserToken表中的user字段 # token_obj就是UserToken表的一条记录,也就是一个object return (token_obj.user,token_obj)
修改views目录下的shoppingcart.py,导入utils下的auth模块
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from rest_framework.views import APIView from rest_framework.viewsets import ViewSetMixin from rest_framework.response import Response from api import models from api.utils.response import BaseResponse import json import redis from django.conf import settings from api.utils.auth import LuffyAuthentication # redis连接 CONN = redis.Redis(host=settings.REDIS_SERVER.get('host'),port=settings.REDIS_SERVER.get('port')) # print(settings.REDIS_SERVER.get('host')) SHOPPING_CAR = {} USER_ID = 1 # 用户id # SHOPPING_CAR = { # 1:{ # 2:{ # 'title':'xxxx', # 'price':1, # 'price_list':[ # {'id':11,}, # {'id':22}, # {'id':33}, # ] # }, # 3:{}, # 5:{} # }, # 2:{}, # 3:{}, # } class ShoppingCartView(ViewSetMixin,APIView): # 开启认证,指定认证类 authentication_classes = [LuffyAuthentication,] def list(self, request, *args, **kwargs): """ 查看购物车信息 :param request: :param args: :param kwargs: :return: """ ret = {'code':10000,'data':None,'error':None} try: # request.user和request.auth是源码返回的 # 如果自定义认证类返回了一个元组,元组里面有2个值。 # 它会覆盖上面2个值,request.user和request.auth print(request.user) # 认证类返回的第一个值 print(request.auth) # 认证类返回的第二个值 # 获取token print('shopping',request.query_params.get('token')) shopping_car_course_list = [] # pattern = "shopping_car_%s_*" % (USER_ID,) pattern = "shopping_car_%s_%s" % (USER_ID,'*',) user_key_list = CONN.keys(pattern) for key in user_key_list: temp = { 'id': CONN.hget(key, 'id').decode('utf-8'), 'name': CONN.hget(key, 'name').decode('utf-8'), 'img':CONN.hget(key, 'img').decode('utf-8'), 'default_price_id':CONN.hget(key, 'default_price_id').decode('utf-8'), 'price_policy_dict': json.loads(CONN.hget(key, 'price_policy_dict').decode('utf-8')) } shopping_car_course_list.append(temp) ret['data'] = shopping_car_course_list except Exception as e: # print(e) ret['code'] = 10005 ret['error'] = '获取购物车数据失败' # print(ret) # print(json.dumps(ret)) return Response(ret) def create(self, request, *args, **kwargs): """ 加入购物车 :param request: :param args: :param kwargs: :return: """ """ 1. 接受用户选中的课程ID和价格策略ID 2. 判断合法性 - 课程是否存在? - 价格策略是否合法? 3. 把商品和价格策略信息放入购物车 SHOPPING_CAR 注意:用户ID=1 """ # 1.接受用户选中的课程ID和价格策略ID """ 相关问题: a. 如果让你编写一个API程序,你需要先做什么? - 业务需求 - 统一数据传输格式 - 表结构设计 - 程序开发 b. django restful framework的解析器的parser_classes的作用? 根据请求中Content-Type请求头的值,选择指定解析对请求体中的数据进行解析。 如: 请求头中含有Content-type: application/json 则内部使用的是JSONParser,JSONParser可以自动去请求体request.body中 获取请求数据,然后进行 字节转字符串、json.loads反序列化; c. 支持多个解析器(一般只是使用JSONParser即可) """ course_id = request.data.get('courseid') policy_id = request.data.get('policyid') # 2. 判断合法性 # - 课程是否存在? # - 价格策略是否合法? # 2.1 课程是否存在? course = models.Course.objects.filter(id=course_id).first() if not course: return Response({'code': 10001, 'error': '课程不存在'}) # 2.2 价格策略是否合法? price_policy_queryset = course.price_policy.all() price_policy_dict = {} for item in price_policy_queryset: temp = { 'id': item.id, 'price': item.price, 'valid_period': item.valid_period, 'valid_period_display': item.get_valid_period_display() } price_policy_dict[item.id] = temp print(price_policy_dict,type(price_policy_dict)) print(policy_id,type(policy_id)) if policy_id not in price_policy_dict: return Response({'code': 10002, 'error': '傻×,价格策略别瞎改'}) # 3. 把商品和价格策略信息放入购物车 SHOPPING_CAR """ 购物车中要放: 课程ID 课程名称 课程图片 默认选中的价格策略 所有价格策略 { shopping_car_1_1:{ id:课程ID name:课程名称 img:课程图片 defaut:默认选中的价格策略 price_list:所有价格策略 }, } """ pattern = "shopping_car_%s_%s" % (USER_ID, '*',) keys = CONN.keys(pattern) if keys and len(keys) >= 1000: return Response({'code': 10009, 'error': '购物车东西太多,先去结算再进行购买..'}) # key = "shopping_car_%s_%s" %(USER_ID,course_id,) key = "shopping_car_%s_%s" % (USER_ID, course_id,) print(key,'1111111111') CONN.hset(key, 'id', course_id) CONN.hset(key, 'name', course.name) CONN.hset(key, 'img', course.course_img) CONN.hset(key, 'default_price_id', policy_id) CONN.hset(key, 'price_policy_dict', json.dumps(price_policy_dict)) CONN.expire(key, 60 * 60 * 24) # 有效期,单位秒。表示一天 return Response({'code': 10000, 'data': '购买成功'}) def destroy(self,request,*args,**kwargs): """ 删除购物车中的某个课程 :param request: :param args: :param kwargs: :return: """ response = BaseResponse() try: # courseid = request.GET.get('courseid') courseid = request.data.get('courseid') print(courseid) # key = "shopping_car_%s_%s" % (USER_ID,courseid) key = "shopping_car_%s_%s" % (USER_ID, courseid,) CONN.delete(key) response.data = '删除成功' except Exception as e: response.code = 10006 response.error = '删除失败' return Response(response.dict) def update(self,request,*args,**kwargs): """ 修改用户选中的价格策略 :param request: :param args: :param kwargs: :return: """ """ 1. 获取课程ID、要修改的价格策略ID 2. 校验合法性(去redis中) """ response = BaseResponse() try: course_id = request.data.get('courseid') policy_id = str(request.data.get('policyid')) if request.data.get('policyid') else None # key = 'shopping_car_%s_%s' %(USER_ID,course_id,) key = "shopping_car_%s_%s" % (USER_ID, course_id,) if not CONN.exists(key): response.code = 10007 response.error = '课程不存在' return Response(response.dict) price_policy_dict = json.loads(CONN.hget(key, 'price_policy_dict').decode('utf-8')) if policy_id not in price_policy_dict: response.code = 10008 response.error = '价格策略不存在' return Response(response.dict) CONN.hset(key,'default_price_id',policy_id) CONN.expire(key, 20 * 60) # 有效期20分钟 response.data = '修改成功' except Exception as e: response.code = 10009 response.error = '修改失败' return Response(response.dict)
参数说明:
CONN.expire 表示设置有效期,单位是秒。60 * 60 * 24,表示一天
使用postman,发送get请求
提示认证失败
发送一个错误的token
提示认证失败!注意:这里直接被认证组件拦截了,并没有到达视图
发送一个正确的token,从数据库里面copy一下
返回code为10000,表示认证成功!
查看Pycharm控制台输出:
Account object
UserToken object
shopping c8aa8609-fb14-43ea-a6cf-96b2c2469b01
上面2个值,就被自定义类覆盖了!
既然得到了用户对象,那么常量USER_ID就可以删除了
修改shoppingcart.py
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from rest_framework.views import APIView from rest_framework.viewsets import ViewSetMixin from rest_framework.response import Response from api import models from api.utils.response import BaseResponse import json import redis from django.conf import settings from api.utils.auth import LuffyAuthentication # redis连接 CONN = redis.Redis(host=settings.REDIS_SERVER.get('host'),port=settings.REDIS_SERVER.get('port')) class ShoppingCartView(ViewSetMixin,APIView): # 开启认证,指定认证类 authentication_classes = [LuffyAuthentication,] def list(self, request, *args, **kwargs): """ 查看购物车信息 :param request: :param args: :param kwargs: :return: """ ret = {'code':10000,'data':None,'error':None} try: # request.user和request.auth是源码返回的 # 如果自定义认证类返回了一个元组,元组里面有2个值。 # 它会覆盖上面2个值,request.user和request.auth print(request.user) # 认证类返回的第一个值 print(request.auth) # 认证类返回的第二个值 # 获取token print('shopping',request.query_params.get('token')) shopping_car_course_list = [] # pattern = "shopping_car_%s_*" % (request.user.id,) pattern = "shopping_car_%s_%s" % (request.user.id,'*',) user_key_list = CONN.keys(pattern) for key in user_key_list: temp = { 'id': CONN.hget(key, 'id').decode('utf-8'), 'name': CONN.hget(key, 'name').decode('utf-8'), 'img':CONN.hget(key, 'img').decode('utf-8'), 'default_price_id':CONN.hget(key, 'default_price_id').decode('utf-8'), 'price_policy_dict': json.loads(CONN.hget(key, 'price_policy_dict').decode('utf-8')) } shopping_car_course_list.append(temp) ret['data'] = shopping_car_course_list except Exception as e: # print(e) ret['code'] = 10005 ret['error'] = '获取购物车数据失败' # print(ret) # print(json.dumps(ret)) return Response(ret) def create(self, request, *args, **kwargs): """ 加入购物车 :param request: :param args: :param kwargs: :return: """ """ 1. 接受用户选中的课程ID和价格策略ID 2. 判断合法性 - 课程是否存在? - 价格策略是否合法? 3. 把商品和价格策略信息放入购物车 SHOPPING_CAR 注意:用户ID=1 """ # 1.接受用户选中的课程ID和价格策略ID """ 相关问题: a. 如果让你编写一个API程序,你需要先做什么? - 业务需求 - 统一数据传输格式 - 表结构设计 - 程序开发 b. django restful framework的解析器的parser_classes的作用? 根据请求中Content-Type请求头的值,选择指定解析对请求体中的数据进行解析。 如: 请求头中含有Content-type: application/json 则内部使用的是JSONParser,JSONParser可以自动去请求体request.body中 获取请求数据,然后进行 字节转字符串、json.loads反序列化; c. 支持多个解析器(一般只是使用JSONParser即可) """ course_id = request.data.get('courseid') policy_id = request.data.get('policyid') # 2. 判断合法性 # - 课程是否存在? # - 价格策略是否合法? # 2.1 课程是否存在? course = models.Course.objects.filter(id=course_id).first() if not course: return Response({'code': 10001, 'error': '课程不存在'}) # 2.2 价格策略是否合法? price_policy_queryset = course.price_policy.all() price_policy_dict = {} for item in price_policy_queryset: temp = { 'id': item.id, 'price': item.price, 'valid_period': item.valid_period, 'valid_period_display': item.get_valid_period_display() } price_policy_dict[item.id] = temp print(price_policy_dict,type(price_policy_dict)) print(policy_id,type(policy_id)) if policy_id not in price_policy_dict: return Response({'code': 10002, 'error': '傻×,价格策略别瞎改'}) # 3. 把商品和价格策略信息放入购物车 SHOPPING_CAR """ 购物车中要放: 课程ID 课程名称 课程图片 默认选中的价格策略 所有价格策略 { shopping_car_1_1:{ id:课程ID name:课程名称 img:课程图片 defaut:默认选中的价格策略 price_list:所有价格策略 }, } """ pattern = "shopping_car_%s_%s" % (request.user.id, '*',) keys = CONN.keys(pattern) if keys and len(keys) >= 1000: return Response({'code': 10009, 'error': '购物车东西太多,先去结算再进行购买..'}) # key = "shopping_car_%s_%s" %(request.user.id,course_id,) key = "shopping_car_%s_%s" % (request.user.id, course_id,) print(key,'1111111111') CONN.hset(key, 'id', course_id) CONN.hset(key, 'name', course.name) CONN.hset(key, 'img', course.course_img) CONN.hset(key, 'default_price_id', policy_id) CONN.hset(key, 'price_policy_dict', json.dumps(price_policy_dict)) CONN.expire(key, 60 * 12 * 24) # 有效期 return Response({'code': 10000, 'data': '购买成功'}) def destroy(self,request,*args,**kwargs): """ 删除购物车中的某个课程 :param request: :param args: :param kwargs: :return: """ response = BaseResponse() try: # courseid = request.GET.get('courseid') courseid = request.data.get('courseid') print(courseid) # key = "shopping_car_%s_%s" % (request.user.id,courseid) key = "shopping_car_%s_%s" % (request.user.id, courseid,) CONN.delete(key) response.data = '删除成功' except Exception as e: response.code = 10006 response.error = '删除失败' return Response(response.dict) def update(self,request,*args,**kwargs): """ 修改用户选中的价格策略 :param request: :param args: :param kwargs: :return: """ """ 1. 获取课程ID、要修改的价格策略ID 2. 校验合法性(去redis中) """ response = BaseResponse() try: course_id = request.data.get('courseid') policy_id = str(request.data.get('policyid')) if request.data.get('policyid') else None # key = 'shopping_car_%s_%s' %(request.user.id,course_id,) key = "shopping_car_%s_%s" % (request.user.id, course_id,) if not CONN.exists(key): response.code = 10007 response.error = '课程不存在' return Response(response.dict) price_policy_dict = json.loads(CONN.hget(key, 'price_policy_dict').decode('utf-8')) if policy_id not in price_policy_dict: response.code = 10008 response.error = '价格策略不存在' return Response(response.dict) CONN.hset(key,'default_price_id',policy_id) CONN.expire(key, 20 * 60) response.data = '修改成功' except Exception as e: response.code = 10009 response.error = '修改失败' return Response(response.dict)
测试get请求
全局配置
假设有100个类,有98个视图要认证。可以加到全局里面,修改settings.py
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
REST_FRAMEWORK = { 'DEFAULT_VERSIONING_CLASS':'rest_framework.versioning.URLPathVersioning', 'VERSION_PARAM':'version', 'DEFAULT_VERSION':'v1', 'ALLOWED_VERSIONS':['v1','v2'], 'PAGE_SIZE':20, 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', 'DEFAULT_AUTHENTICATION_CLASSES':['api.utils.auth.LuffyAuthentication',] }
那么登录和查看课程,是不需要认证的。怎么忽略呢?
修改views目录下的auth.py,定义认证类为空列表,表示不认证!
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from rest_framework.response import Response from rest_framework.views import APIView from rest_framework.viewsets import ViewSetMixin from api import models from api.utils.response import BaseResponse import uuid class AuthView(ViewSetMixin,APIView): authentication_classes = [] # 空列表表示不认证 def login(self,request,*args,**kwargs): """ 用户登陆认证 :param request: :param args: :param kwargs: :return: """ response = BaseResponse() # 默认状态 try: user = request.data.get('username') pwd = request.data.get('password') # 验证用户和密码 obj = models.Account.objects.filter(username=user,password=pwd).first() if not obj: response.code = 10002 response.error = '用户名或密码错误' else: uid = str(uuid.uuid4()) # 生成唯一id # 保存到数据库中,update_or_create表示更新或者创建 # user=obj,这个是判断条件。当条件成立,更新token字段,值为uid # 当条件不成立时,增加一条记录。注意:增加时,有2个字段,分别是user和token models.UserToken.objects.update_or_create(user=obj, defaults={'token': uid}) response.code = 99999 response.data = uid except Exception as e: response.code = 10005 response.error = '操作异常' return Response(response.dict)
使用postman测试登录
查看返回结果
修改settings.py,注释掉全局认证。因为这里用到的登录认证的视图不多
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
REST_FRAMEWORK = { 'DEFAULT_VERSIONING_CLASS':'rest_framework.versioning.URLPathVersioning', 'VERSION_PARAM':'version', 'DEFAULT_VERSION':'v1', 'ALLOWED_VERSIONS':['v1','v2'], 'PAGE_SIZE':20, 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', # 'DEFAULT_AUTHENTICATION_CLASSES':['api.utils.auth.LuffyAuthentication',] }
问题:认证类为什么要继承BaseAuthentication?
查看源码BaseAuthentication
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class BaseAuthentication(object): """ All authentication classes should extend BaseAuthentication. """ def authenticate(self, request): """ Authenticate the request and return a two-tuple of (user, token). """ raise NotImplementedError(".authenticate() must be overridden.") def authenticate_header(self, request): """ Return a string to be used as the value of the `WWW-Authenticate` header in a `401 Unauthenticated` response, or `None` if the authentication scheme should return `403 Permission Denied` responses. """ pass
发现,只要执行了authenticate方法,它会执行raise。它会主动报错
为了不让它报错,子类继承BaseAuthentication后,必须重写authenticate方法,才不会报错。
这样做的目的,是为了约束子类,哪些方法,必须要定义!
二、结算中心
点击去结算,会发送一次post请求。那么它该发送什么数据呢?
只需要发送课程id就可以了?为什么呢?
因为redis中有购物车相关数据!后台根据课程id去购物车中获取,要结算的课程就可以了!
结算中心和购物车一样,也是一个临时数据。它也需要放到redis中!
先来看购物车的数据结构
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
购物车 = { 'shopping_car_1_3':{ name:'', src:'xx' price_id:1, price_dict = { 1:.... } }, 'shopping_car_1_1':{ ... }, 'shopping_car_1_5':{ ... }, }
再来看结算中新的数据结构
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
结算中心 = { 'payment_1_3':{ id:3, mame:Django框架学习, price_id:1, price_priod:30, price:199, defaul_coupon_id:0, coupon_dict: { ----> 绑定了课程3的优惠券 0: '请选择课程优惠券', 1:'xxx', 2:'xxx', 3:'xxx', 4:'xxx', } }, 'payment_1_1':{ id:1, mame:Django框架学习, price_id:1, price_priod:30, price:199, defaul_coupon_id:0, coupon_dict: { ----> 绑定了课程1的优惠券 0: '请选择课程优惠券', 1:'xxx', 2:'xxx', 3:'xxx', 4:'xxx', } }, }
优惠券
优惠券分为2大类:绑定课程和非绑定课程
点击去结算
在左下角,展示的是非绑定课程的优惠券。
在右边的下拉菜单中,展示的是绑定课程的优惠券
在views目录下,创建文件payment.py
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import json import redis from django.conf import settings from rest_framework.views import APIView from rest_framework.viewsets import ViewSetMixin from rest_framework.response import Response from api.utils.auth import LuffyAuthentication from api import models from api.utils.response import BaseResponse # redis连接 CONN = redis.Redis(host=settings.REDIS_SERVER.get('host'),port=settings.REDIS_SERVER.get('port')) class PaymentView(ViewSetMixin, APIView): authentication_classes = [LuffyAuthentication, ] def create(self, request, *args, **kwargs): """ 在结算中添加课程 :param request: :param args: :param kwargs: :return: """ # 1.接受用户选择的要结算的课程ID列表 # 2.清空当前用户request.user.id结算中心的数据 # key = payment_1* # 3.循环要加入结算中的所有课程ID列表 """ for course_id in 用户提交课程ID列表: 3.1 根据course_id,request.user.id去购物车中获取商品信息:商品名称、图片、价格(id,周期,显示周期,价格) 3.2 根据course_id,request.user.id获取 - 当前用户 - 当前课程 - 可用的优惠券 加入结算中心 提示:可以使用contenttypes """ # 4.获取当前用户所有未绑定课程优惠券 # - 未使用 # - 有效期内 # - 加入结算中心:glocal_coupon_用户ID def list(self, request, *args, **kwargs): """ 查看结算中心 :param request: :param args: :param kwargs: :return: """ # 1. 根据用户ID去结算中心获取该用户所有要结算课程 # 2. 根据用户ID去结算中心获取该用户所有可用未绑定课程的优惠券 # 3. 用户表中获取贝里余额 # 4. 以上数据构造成一个字典 return Response('...') def update(self, request, *args, **kwargs): """ 更新优惠券 :param request: :param args: :param kwargs: :return: """ # 1. 获取用户提交: # course_id=1,coupon_id=3 # course_id=0,coupon_id=6 # 2. course_id=1 --> 去结算中心获取当前用户所拥有的绑定当前课程优惠,并进行校验 # - 成功:defaul_coupon_id=3 # - 否则:非法请求 # 2. course_id=0 --> 去结算中心获取当前用户所拥有的未绑定课程优惠,并进行校验 # - 成功:defaul_coupon_id=3 # - 否则:非法请求
course_id为空,表示 未绑定课程,否则为绑定课程
这里面展示的是一些业务逻辑,需要自己用代码来填充
提示你的代码编写能力!
三、django-redis
介绍
django-redis 基于 BSD 许可, 是一个使 Django 支持 Redis cache/session 后端的全功能组件
django-redis 中文文档,请参考
http://django-redis-chs.readthedocs.io/zh_CN/latest/
为何要用 django-redis ?
因为:
- 持续更新
- 本地化的 redis-py URL 符号连接字符串
- 可扩展客户端
- 可扩展解析器
- 可扩展序列器
- 默认客户端主/从支持
- 完善的测试
- 已在一些项目的生产环境中作为 cache 和 session 使用
- 支持永不超时设置
- 原生进入 redis 客户端/连接池支持
- 高可配置 ( 例如仿真缓存的异常行为 )
- 默认支持 unix 套接字
- 支持 Python 2.7, 3.4, 3.5 以及 3.6
安装
安装 django-redis 最简单的方法就是用 pip :
pip install django-redis
作为 cache backend 使用配置
为了使用 django-redis , 你应该将你的 django cache setting 改成这样:
CACHES = { "default": { "BACKEND": "django_redis.cache.RedisCache", "LOCATION": "redis://127.0.0.1:6379/1", "OPTIONS": { "CLIENT_CLASS": "django_redis.client.DefaultClient", } } }
举例:
在上面购物车中,使用了缓存。结算中心也需要使用缓存,那么就可以定义一个全局配置。当需要使用时,导入一下配置即可!
修改settings.py,最后一行添加
# ######django-redis的配置 ################# CACHES = { "default": { "BACKEND": "django_redis.cache.RedisCache", "LOCATION": "redis://192.168.218.140:6379", "OPTIONS": { "CLIENT_CLASS": "django_redis.client.DefaultClient", "CONNECTION_POOL_KWARGS": {"max_connections": 100}, # "PASSWORD": "密码", } } }
参数解释:
BACKEND 表示后台连接
OPTIONS 表示参数
CONNECTION_POOL_KWARGS 表示连接池。max_connections表示最大连接数
连接池,请参考链接:
https://baike.baidu.com/item/%E8%BF%9E%E6%8E%A5%E6%B1%A0%E6%8A%80%E6%9C%AF/523659?fr=aladdin
上面定义了100个连接池,假设100进程,都在使用连接池。当地101个访问时,会等待。直到有空闲的进程时,才处理!
不过redis的处理是很快的,很少会出现等待的情况!
使用连接池,有很多优点:
1.减少连接创建时间
2.简化的编程模式
3.受控的资源使用
使用连接池,性能会更高好!
视图中使用
加上2行代码,就可以了
from django_redis import get_redis_connection CONN = get_redis_connection("default")
这里的default指的是settings.py中CACHES配置项的default
修改views目录下的shoppingcar.py
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from rest_framework.views import APIView from rest_framework.viewsets import ViewSetMixin from rest_framework.response import Response from api import models from api.utils.response import BaseResponse import json from api.utils.auth import LuffyAuthentication from django_redis import get_redis_connection CONN = get_redis_connection("default") # 使用redis连接池 class ShoppingCartView(ViewSetMixin,APIView): # 开启认证,指定认证类 authentication_classes = [LuffyAuthentication,] def list(self, request, *args, **kwargs): """ 查看购物车信息 :param request: :param args: :param kwargs: :return: """ ret = {'code':10000,'data':None,'error':None} try: # request.user和request.auth是源码返回的 # 如果自定义认证类返回了一个元组,元组里面有2个值。 # 它会覆盖上面2个值,request.user和request.auth print(request.user) # 认证类返回的第一个值 print(request.auth) # 认证类返回的第二个值 # 获取token print('shopping',request.query_params.get('token')) shopping_car_course_list = [] # pattern = "shopping_car_%s_*" % (request.user.id,) pattern = "shopping_car_%s_%s" % (request.user.id,'*',) user_key_list = CONN.keys(pattern) for key in user_key_list: temp = { 'id': CONN.hget(key, 'id').decode('utf-8'), 'name': CONN.hget(key, 'name').decode('utf-8'), 'img':CONN.hget(key, 'img').decode('utf-8'), 'default_price_id':CONN.hget(key, 'default_price_id').decode('utf-8'), 'price_policy_dict': json.loads(CONN.hget(key, 'price_policy_dict').decode('utf-8')) } shopping_car_course_list.append(temp) ret['data'] = shopping_car_course_list except Exception as e: # print(e) ret['code'] = 10005 ret['error'] = '获取购物车数据失败' # print(ret) # print(json.dumps(ret)) return Response(ret) def create(self, request, *args, **kwargs): """ 加入购物车 :param request: :param args: :param kwargs: :return: """ """ 1. 接受用户选中的课程ID和价格策略ID 2. 判断合法性 - 课程是否存在? - 价格策略是否合法? 3. 把商品和价格策略信息放入购物车 SHOPPING_CAR 注意:用户ID=1 """ # 1.接受用户选中的课程ID和价格策略ID """ 相关问题: a. 如果让你编写一个API程序,你需要先做什么? - 业务需求 - 统一数据传输格式 - 表结构设计 - 程序开发 b. django restful framework的解析器的parser_classes的作用? 根据请求中Content-Type请求头的值,选择指定解析对请求体中的数据进行解析。 如: 请求头中含有Content-type: application/json 则内部使用的是JSONParser,JSONParser可以自动去请求体request.body中 获取请求数据,然后进行 字节转字符串、json.loads反序列化; c. 支持多个解析器(一般只是使用JSONParser即可) """ course_id = request.data.get('courseid') policy_id = request.data.get('policyid') # 2. 判断合法性 # - 课程是否存在? # - 价格策略是否合法? # 2.1 课程是否存在? course = models.Course.objects.filter(id=course_id).first() if not course: return Response({'code': 10001, 'error': '课程不存在'}) # 2.2 价格策略是否合法? price_policy_queryset = course.price_policy.all() price_policy_dict = {} for item in price_policy_queryset: temp = { 'id': item.id, 'price': item.price, 'valid_period': item.valid_period, 'valid_period_display': item.get_valid_period_display() } price_policy_dict[item.id] = temp print(price_policy_dict,type(price_policy_dict)) print(policy_id,type(policy_id)) if policy_id not in price_policy_dict: return Response({'code': 10002, 'error': '傻×,价格策略别瞎改'}) # 3. 把商品和价格策略信息放入购物车 SHOPPING_CAR """ 购物车中要放: 课程ID 课程名称 课程图片 默认选中的价格策略 所有价格策略 { shopping_car_1_1:{ id:课程ID name:课程名称 img:课程图片 defaut:默认选中的价格策略 price_list:所有价格策略 }, } """ pattern = "shopping_car_%s_%s" % (request.user.id, '*',) keys = CONN.keys(pattern) if keys and len(keys) >= 1000: return Response({'code': 10009, 'error': '购物车东西太多,先去结算再进行购买..'}) # key = "shopping_car_%s_%s" %(request.user.id,course_id,) key = "shopping_car_%s_%s" % (request.user.id, course_id,) print(key,'1111111111') CONN.hset(key, 'id', course_id) CONN.hset(key, 'name', course.name) CONN.hset(key, 'img', course.course_img) CONN.hset(key, 'default_price_id', policy_id) CONN.hset(key, 'price_policy_dict', json.dumps(price_policy_dict)) CONN.expire(key, 60 * 12 * 24) # 有效期 return Response({'code': 10000, 'data': '购买成功'}) def destroy(self,request,*args,**kwargs): """ 删除购物车中的某个课程 :param request: :param args: :param kwargs: :return: """ response = BaseResponse() try: # courseid = request.GET.get('courseid') courseid = request.data.get('courseid') print(courseid) # key = "shopping_car_%s_%s" % (request.user.id,courseid) key = "shopping_car_%s_%s" % (request.user.id, courseid,) CONN.delete(key) response.data = '删除成功' except Exception as e: response.code = 10006 response.error = '删除失败' return Response(response.dict) def update(self,request,*args,**kwargs): """ 修改用户选中的价格策略 :param request: :param args: :param kwargs: :return: """ """ 1. 获取课程ID、要修改的价格策略ID 2. 校验合法性(去redis中) """ response = BaseResponse() try: course_id = request.data.get('courseid') policy_id = str(request.data.get('policyid')) if request.data.get('policyid') else None # key = 'shopping_car_%s_%s' %(request.user.id,course_id,) key = "shopping_car_%s_%s" % (request.user.id, course_id,) if not CONN.exists(key): response.code = 10007 response.error = '课程不存在' return Response(response.dict) price_policy_dict = json.loads(CONN.hget(key, 'price_policy_dict').decode('utf-8')) if policy_id not in price_policy_dict: response.code = 10008 response.error = '价格策略不存在' return Response(response.dict) CONN.hset(key,'default_price_id',policy_id) CONN.expire(key, 20 * 60) response.data = '修改成功' except Exception as e: response.code = 10009 response.error = '修改失败' return Response(response.dict)
使用postman测试访问,要带上正确的token
访问正常
作为 session backend 使用配置
Django 默认可以使用任何 cache backend 作为 session backend, 将 django-redis 作为 session 储存后端不用安装任何额外的 backend
SESSION_ENGINE = "django.contrib.sessions.backends.cache" SESSION_CACHE_ALIAS = "default"
举例:
修改settings.py
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# ######django-redis的配置 ################# CACHES = { "default": { "BACKEND": "django_redis.cache.RedisCache", "LOCATION": "redis://192.168.218.140:6379", "OPTIONS": { "CLIENT_CLASS": "django_redis.client.DefaultClient", "CONNECTION_POOL_KWARGS": {"max_connections": 100}, # "PASSWORD": "密码", } } } ###使用redis缓存session SESSION_ENGINE = 'django.contrib.sessions.backends.cache' # 引擎 SESSION_CACHE_ALIAS = 'default' # 使用的缓存别名(默认内存缓存,也可以是memcache),此处别名依赖缓存的设置 SESSION_COOKIE_NAME = "sessionid" # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串 SESSION_COOKIE_PATH = "/" # Session的cookie保存的路径 SESSION_COOKIE_DOMAIN = None # Session的cookie保存的域名 SESSION_COOKIE_SECURE = False # 是否Https传输cookie SESSION_COOKIE_HTTPONLY = True # 是否Session的cookie只支持http传输 SESSION_COOKIE_AGE = 1209600 # Session的cookie失效日期(2周) SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 是否关闭浏览器使得Session过期 SESSION_SAVE_EVERY_REQUEST = False # 是否每次请求都保存Session,默认修改之后才保存
简单来讲,加上2行就可以了。下面的那些配置,是参考源码设置的。
比如session失效时间是2周
如果需要修改,在这里指定一下,就可以了!
注意:里面的defalut就是redis配置的defalut,名字是一一对应的!
总结:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1. django-redis的作用 - 连接redis并在redis中进行操作(含redis连接池)。 2. 帮助用户将session放到redis - django-redis的配置 - session的配置
作业:
完整结算中心的代码,实现以下功能:
1. 添加
2. 查看
3. 修改
注意:使用认证+django-redis
修改utils目录下的auth.py
当为GET请求时,从url中取token,否则从请求体中获取token
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from rest_framework.authentication import BaseAuthentication from rest_framework.exceptions import AuthenticationFailed from api import models class LuffyAuthentication(BaseAuthentication): def authenticate(self, request): """ 用户认证 :param request: :return: """ # print(request.method) # 判断请求方式 if request.method == "GET": token = request.query_params.get('token') else: token = request.data.get('token') # print('auth',token) token_obj = models.UserToken.objects.filter(token=token).first() if not token_obj: # 认证失败 raise AuthenticationFailed({'code':1008,'error':'认证失败'}) # 认证成功 # return (token_obj.user,token_obj) return (token_obj.user,token_obj)
修改models.py,在用户表增加字段
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation from django.contrib.contenttypes.models import ContentType from django.db.models import Q from django.utils.safestring import mark_safe from django.db import models import hashlib # ######################## 课程相关 ######################## class CourseCategory(models.Model): """课程大类, e.g 前端 后端...""" name = models.CharField(max_length=64, unique=True) def __str__(self): return "%s" % self.name class Meta: verbose_name_plural = "01.课程大类" class CourseSubCategory(models.Model): """课程子类, e.g python linux """ category = models.ForeignKey("CourseCategory") name = models.CharField(max_length=64, unique=True) def __str__(self): return "%s" % self.name class Meta: verbose_name_plural = "02.课程子类" class DegreeCourse(models.Model): """学位课程""" name = models.CharField(max_length=128, unique=True) course_img = models.CharField(max_length=255, verbose_name="缩略图") brief = models.TextField(verbose_name="学位课程简介", ) total_scholarship = models.PositiveIntegerField(verbose_name="总奖学金(贝里)", default=40000) # 2000 2000 mentor_compensation_bonus = models.PositiveIntegerField(verbose_name="本课程的导师辅导费用(贝里)", default=15000) period = models.PositiveIntegerField(verbose_name="建议学习周期(days)", default=150) # 为了计算学位奖学金 prerequisite = models.TextField(verbose_name="课程先修要求", max_length=1024) teachers = models.ManyToManyField("Teacher", verbose_name="课程讲师") # 用于GenericForeignKey反向查询, 不会生成表字段,切勿删除 # coupon = GenericRelation("Coupon") # 用于GenericForeignKey反向查询,不会生成表字段,切勿删除 degreecourse_price_policy = GenericRelation("PricePolicy") def __str__(self): return self.name class Meta: verbose_name_plural = "03.学位课" class Teacher(models.Model): """讲师、导师表""" name = models.CharField(max_length=32) role_choices = ((0, '讲师'), (1, '导师')) role = models.SmallIntegerField(choices=role_choices, default=0) title = models.CharField(max_length=64, verbose_name="职位、职称") signature = models.CharField(max_length=255, help_text="导师签名", blank=True, null=True) image = models.CharField(max_length=128) brief = models.TextField(max_length=1024) def __str__(self): return self.name class Meta: verbose_name_plural = "04.导师或讲师" class Scholarship(models.Model): """学位课程奖学金""" degree_course = models.ForeignKey("DegreeCourse") time_percent = models.PositiveSmallIntegerField(verbose_name="奖励档位(时间百分比)", help_text="只填百分值,如80,代表80%") value = models.PositiveIntegerField(verbose_name="奖学金数额") def __str__(self): return "%s:%s" % (self.degree_course, self.value) class Meta: verbose_name_plural = "05.学位课奖学金" class Course(models.Model): """专题课/学位课模块表""" name = models.CharField(max_length=128, unique=True) course_img = models.CharField(max_length=255) sub_category = models.ForeignKey("CourseSubCategory") course_type_choices = ((0, '付费'), (1, 'VIP专享'), (2, '学位课程')) course_type = models.SmallIntegerField(choices=course_type_choices) # 不为空;学位课的某个模块 # 为空;专题课 degree_course = models.ForeignKey("DegreeCourse", blank=True, null=True, help_text="若是学位课程,此处关联学位表") brief = models.TextField(verbose_name="课程概述", max_length=2048) level_choices = ((0, '初级'), (1, '中级'), (2, '高级')) 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) # order = models.IntegerField("课程顺序", help_text="从上一个课程数字往后排") attachment_path = models.CharField(max_length=128, verbose_name="课件路径", blank=True, null=True) status_choices = ((0, '上线'), (1, '下线'), (2, '预上线')) status = models.SmallIntegerField(choices=status_choices, default=0) template_id = models.SmallIntegerField("前端模板id", default=1) coupon = GenericRelation("Coupon") # 用于GenericForeignKey反向查询,不会生成表字段,切勿删除 price_policy = GenericRelation("PricePolicy") asked_question = GenericRelation("OftenAskedQuestion") def __str__(self): return "%s(%s)" % (self.name, self.get_course_type_display()) def save(self, *args, **kwargs): if self.course_type == 2: if not self.degree_course: raise ValueError("学位课程必须关联对应的学位表") super(Course, self).save(*args, **kwargs) class Meta: verbose_name_plural = "06.专题课或学位课模块" class CourseDetail(models.Model): """课程详情页内容""" course = models.OneToOneField("Course") 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) 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 = "07.课程或学位模块详细" class OftenAskedQuestion(models.Model): """常见问题""" content_type = models.ForeignKey(ContentType) # 关联course or degree_course object_id = models.PositiveIntegerField() content_object = GenericForeignKey('content_type', 'object_id') question = models.CharField(max_length=255) answer = models.TextField(max_length=1024) def __str__(self): return "%s-%s" % (self.content_object, self.question) class Meta: unique_together = ('content_type', 'object_id', 'question') verbose_name_plural = "08. 常见问题" class CourseOutline(models.Model): """课程大纲""" course_detail = models.ForeignKey("CourseDetail") title = models.CharField(max_length=128) # 前端显示顺序 order = models.PositiveSmallIntegerField(default=1) content = models.TextField("内容", max_length=2048) def __str__(self): return "%s" % self.title class Meta: unique_together = ('course_detail', 'title') verbose_name_plural = "09. 课程大纲" class CourseChapter(models.Model): """课程章节""" course = models.ForeignKey("Course") chapter = models.SmallIntegerField(verbose_name="第几章", default=1) name = models.CharField(max_length=128) summary = models.TextField(verbose_name="章节介绍", blank=True, null=True) pub_date = models.DateField(verbose_name="发布日期", auto_now_add=True) class Meta: unique_together = ("course", 'chapter') verbose_name_plural = "10. 课程章节" def __str__(self): return "%s:(第%s章)%s" % (self.course, self.chapter, self.name) class CourseSection(models.Model): """课时目录""" chapter = models.ForeignKey("CourseChapter") name = models.CharField(max_length=128) order = models.PositiveSmallIntegerField(verbose_name="课时排序", help_text="建议每个课时之间空1至2个值,以备后续插入课时") section_type_choices = ((0, '文档'), (1, '练习'), (2, '视频')) section_type = models.SmallIntegerField(default=2, choices=section_type_choices) section_link = models.CharField(max_length=255, blank=True, null=True, help_text="若是video,填vid,若是文档,填link") video_time = models.CharField(verbose_name="视频时长", blank=True, null=True, max_length=32) # 仅在前端展示使用 pub_date = models.DateTimeField(verbose_name="发布时间", auto_now_add=True) free_trail = models.BooleanField("是否可试看", default=False) class Meta: unique_together = ('chapter', 'section_link') verbose_name_plural = "11. 课时" def __str__(self): return "%s-%s" % (self.chapter, self.name) class Homework(models.Model): chapter = models.ForeignKey("CourseChapter") title = models.CharField(max_length=128, verbose_name="作业题目") order = models.PositiveSmallIntegerField("作业顺序", help_text="同一课程的每个作业之前的order值间隔1-2个数") homework_type_choices = ((0, '作业'), (1, '模块通关考核')) homework_type = models.SmallIntegerField(choices=homework_type_choices, default=0) requirement = models.TextField(max_length=1024, verbose_name="作业需求") threshold = models.TextField(max_length=1024, verbose_name="踩分点") recommend_period = models.PositiveSmallIntegerField("推荐完成周期(天)", default=7) scholarship_value = models.PositiveSmallIntegerField("为该作业分配的奖学金(贝里)") note = models.TextField(blank=True, null=True) enabled = models.BooleanField(default=True, help_text="本作业如果后期不需要了,不想让学员看到,可以设置为False") class Meta: unique_together = ("chapter", "title") verbose_name_plural = "12. 章节作业" def __str__(self): return "%s - %s" % (self.chapter, self.title) # class CourseReview(models.Model): # """课程评价""" # enrolled_course = models.OneToOneField("EnrolledCourse") # about_teacher = models.FloatField(default=0, verbose_name="讲师讲解是否清晰") # about_video = models.FloatField(default=0, verbose_name="内容实用") # about_course = models.FloatField(default=0, verbose_name="课程内容通俗易懂") # review = models.TextField(max_length=1024, verbose_name="评价") # disagree_number = models.IntegerField(default=0, verbose_name="踩") # agree_number = models.IntegerField(default=0, verbose_name="赞同数") # tags = models.ManyToManyField("Tags", blank=True, verbose_name="标签") # date = models.DateTimeField(auto_now_add=True, verbose_name="评价日期") # is_recommend = models.BooleanField("热评推荐", default=False) # hide = models.BooleanField("不在前端页面显示此条评价", default=False) # # def __str__(self): # return "%s-%s" % (self.enrolled_course.course, self.review) # # class Meta: # verbose_name_plural = "13. 课程评价(购买课程后才能评价)" # # # class DegreeCourseReview(models.Model): # """学位课程评价 # 为了以后可以定制单独的评价内容,所以不与普通课程的评价混在一起,单独建表 # """ # enrolled_course = models.ForeignKey("EnrolledDegreeCourse") # course = models.ForeignKey("Course", verbose_name="评价学位模块", blank=True, null=True, # help_text="不填写即代表评价整个学位课程", limit_choices_to={'course_type': 2}) # about_teacher = models.FloatField(default=0, verbose_name="讲师讲解是否清晰") # about_video = models.FloatField(default=0, verbose_name="视频质量") # about_course = models.FloatField(default=0, verbose_name="课程") # review = models.TextField(max_length=1024, verbose_name="评价") # disagree_number = models.IntegerField(default=0, verbose_name="踩") # agree_number = models.IntegerField(default=0, verbose_name="赞同数") # tags = models.ManyToManyField("Tags", blank=True, verbose_name="标签") # date = models.DateTimeField(auto_now_add=True, verbose_name="评价日期") # is_recommend = models.BooleanField("热评推荐", default=False) # hide = models.BooleanField("不在前端页面显示此条评价", default=False) # # def __str__(self): # return "%s-%s" % (self.enrolled_course, self.review) # # class Meta: # verbose_name_plural = "14. 学位课评价(购买课程后才能评价)" class PricePolicy(models.Model): """价格与有课程效期表""" content_type = models.ForeignKey(ContentType) # 关联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 = "15. 价格策略" def __str__(self): return "%s(%s)%s" % (self.content_object, self.get_valid_period_display(), self.price) # ################################### 优惠券相关 ################################# class Coupon(models.Model): """优惠券生成规则""" name = models.CharField(max_length=64, verbose_name="活动名称") brief = models.TextField(blank=True, null=True, verbose_name="优惠券介绍") coupon_type_choices = ((0, '立减'), (1, '满减券'), (2, '折扣券')) coupon_type = models.SmallIntegerField(choices=coupon_type_choices, default=0, verbose_name="券类型") money_equivalent_value = models.IntegerField(verbose_name="等值货币") off_percent = models.PositiveSmallIntegerField("折扣百分比", help_text="只针对折扣券,例7.9折,写79", blank=True, null=True) minimum_consume = models.PositiveIntegerField("最低消费", default=0, help_text="仅在满减券时填写此字段") content_type = models.ForeignKey(ContentType, blank=True, null=True) object_id = models.PositiveIntegerField("绑定课程", blank=True, null=True, help_text="可以把优惠券跟课程绑定") content_object = GenericForeignKey('content_type', 'object_id') quantity = models.PositiveIntegerField("数量(张)", default=1) open_date = models.DateField("优惠券领取开始时间") close_date = models.DateField("优惠券领取结束时间") valid_begin_date = models.DateField(verbose_name="有效期开始时间", blank=True, null=True) valid_end_date = models.DateField(verbose_name="有效结束时间", blank=True, null=True) # coupon_valid_days = models.PositiveIntegerField(verbose_name="优惠券有效期(天)", blank=True, null=True, # help_text="自券被领时开始算起") date = models.DateTimeField(auto_now_add=True) class Meta: verbose_name_plural = "31. 优惠券生成记录" def __str__(self): return "%s(%s)" % (self.get_coupon_type_display(), self.name) class CouponRecord(models.Model): """优惠券发放、消费纪录""" coupon = models.ForeignKey("Coupon") account = models.ForeignKey("Account", verbose_name="拥有者") number = models.CharField(max_length=64, unique=True) status_choices = ((0, '未使用'), (1, '已使用'), (2, '已过期')) status = models.SmallIntegerField(choices=status_choices, default=0) get_time = models.DateTimeField(verbose_name="领取时间", help_text="用户领取时间") used_time = models.DateTimeField(blank=True, null=True, verbose_name="使用时间") # order = models.ForeignKey("Order", blank=True, null=True, verbose_name="关联订单") # 一个订单可以有多个优惠券 order_id = models.IntegerField(verbose_name='关联订单ID') class Meta: verbose_name_plural = "32. 用户优惠券" def __str__(self): return '%s-%s-%s' % (self.account, self.number, self.status) class Account(models.Model): username = models.CharField("用户名", max_length=64, unique=True) email = models.EmailField( verbose_name='邮箱', max_length=255, unique=True, blank=True, null=True ) password = models.CharField('密码', max_length=128) balance = models.FloatField('贝里',default=0) class Meta: verbose_name_plural = "33. 用户表" class UserToken(models.Model): user = models.OneToOneField(to='Account') token = models.CharField(max_length=36) class Meta: verbose_name_plural = "34. token表"
执行2个命令,生成字段
python manage.py makemigrations
python manage.py migrate
为用户加点钱
修改admin.py,注册所有表
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from django.contrib import admin # Register your models here. from api import models admin.site.register(models.CourseCategory) admin.site.register(models.CourseSubCategory) admin.site.register(models.DegreeCourse) admin.site.register(models.Teacher) admin.site.register(models.Scholarship) admin.site.register(models.Course) admin.site.register(models.CourseDetail) admin.site.register(models.OftenAskedQuestion) admin.site.register(models.CourseOutline) admin.site.register(models.CourseChapter) admin.site.register(models.CourseSection) admin.site.register(models.Homework) admin.site.register(models.PricePolicy) admin.site.register(models.Coupon) admin.site.register(models.CouponRecord) admin.site.register(models.Account)
进入admin后台,添加几条优惠券,并绑定用户
list
修改payment.py,先做get请求的
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import json import redis from django.conf import settings from rest_framework.views import APIView from rest_framework.viewsets import ViewSetMixin from rest_framework.response import Response from api.utils.auth import LuffyAuthentication from api import models from api.utils.response import BaseResponse from django_redis import get_redis_connection CONN = get_redis_connection("default") # 使用redis连接池 class PaymentView(ViewSetMixin, APIView): authentication_classes = [LuffyAuthentication, ] def create(self, request, *args, **kwargs): """ 在结算中添加课程 :param request: :param args: :param kwargs: :return: """ # 1.接收用户选择的要结算的课程ID列表 # 2.清空当前用户request.user.id结算中心的数据 # key = payment_1* # 3.循环要加入结算中的所有课程ID列表 """ for course_id in 用户提交课程ID列表: 3.1 根据course_id,request.user.id去购物车中获取商品信息:商品名称、图片、价格(id,周期,显示周期,价格) 3.2 根据course_id,request.user.id获取 - 当前用户 - 当前课程 - 可用的优惠券 加入结算中心 提示:可以使用contenttypes """ # 4.获取当前用户所有未绑定课程优惠券 # - 未使用 # - 有效期内 # - 加入结算中心:glocal_coupon_用户ID def list(self, request, *args, **kwargs): """ 查看结算中心 :param request: :param args: :param kwargs: :return: """ # 1. 根据用户ID去结算中心获取该用户所有要结算课程 course_id = request.query_params.get('course_id') print('课程id',course_id) obj = models.Course.objects.filter(id=course_id).first() print('结算课程',obj.name) # 2. 根据用户ID去结算中心获取该用户所有可用未绑定课程的优惠券 user_id =request.user.id print('用户id', user_id) obj2 = models.CouponRecord.objects.filter(account=user_id, coupon__object_id__isnull=True).first() # print(obj2.coupon.get_coupon_type_display()) if obj2.coupon.coupon_type == 0: print('{}{}'.format(obj2.coupon.get_coupon_type_display(),obj2.coupon.money_equivalent_value)) elif obj2.coupon.coupon_type == 1: print('满{}减{}'.format(obj2.coupon.minimum_consume,obj2.coupon.money_equivalent_value)) else: print(obj2.coupon.id) print('{}折'.format(obj2.coupon.off_percent)) # 3. 用户表中获取贝里余额 beili = models.Account.objects.filter(id=user_id).first() print('用户贝里',beili.balance) # 4. 以上数据构造成一个字典 return Response('...') def update(self, request, *args, **kwargs): """ 更新优惠券 :param request: :param args: :param kwargs: :return: """ # 1. 获取用户提交: # course_id=1,coupon_id=3 # course_id=0,coupon_id=6 # 2. course_id=1 --> 去结算中心获取当前用户所拥有的绑定当前课程优惠,并进行校验 # - 成功:defaul_coupon_id=3 # - 否则:非法请求 # 3. course_id=0 --> 去结算中心获取当前用户所拥有的未绑定课程优惠,并进行校验 # - 成功:defaul_coupon_id=3 # - 否则:非法请求
使用postman发送GET请求
查看Pycharm控制台输出
课程id 1 结算课程 Python开发入门7天特训营 用户id 1 立减10 用户贝里 100.0