考试第二部分:MySQL数据库

6.  MySQL中char和varchar的区别(1分)

char是定长,varchar是变长。

char的查询速度比varchar要快。

View Code

7.   MySQL中varchar(50)的50表示什什么意思?(1分)

是字符长度。一个中文,也是一个字符。

View Code

8. left join、right join以及inner join的区别?(2分)

left join,表示左连接,以左表为基准,如果左表有不匹配的,显示为空

right join,表示右连接,以右表为基准,如果右表有不匹配的,显示为空

inner join,表示内连接,只显示2个表条件符合的记录,不匹配的不显示

View Code

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)

a,d,f,g 会命中索引

View Code

解释:

索引有2个功能:加快查询和约束。

这里的约束指的是唯一索引,联合唯一索引。

索引遵循的原则: 最左前缀原则

你可以认为联合索引是闯关游戏的设计

例如你这个联合索引是state/city/zipCode

那么state就是第一关 city是第二关, zipCode就是第三关

你必须匹配了第一关,才能匹配第二关,匹配了第一关和第二关,才能匹配第三关

你不能直接到第二关的

索引的格式就是第一层是state,第二层才是city

索引是因为B+树结构 所以查找快 如果单看第三列 是非排序的。

多列索引是先按照第一列进行排序,然后在第一列排好序的基础上再对第二列排序,如果没有第一列的话,直接访问第二列,那第二列肯定是无序的,直接访问后面的列就用不到索引了。

所以如果不是在前面列的基础上而是但看后面某一列,索引是失效的。

View Code

简而言之,只要where条件包含最左边的字段,那么它就会用到组合索引,反之亦然!

如果创建了组合索引,但是却没有命中,这是浪费磁盘空间。因为索引也占用磁盘!

10. 假设学⽣生Student和教师Teacher关系模型如下:(4分) Student(学号、姓名、性别、类型、身份证号) Teacher(教师号、姓名、性别、类型、身份证号、工资)

其中,学⽣生表中类别为“本科生”和“研究生”两类;性别为“男”和“女”两类。

a. 性别为女的所有学生。

select * from Student where 性别='女'

View Code

b. 学生表中类别分别对应的个数。

select 类型,count(1) from Student group by 类型

View Code

c.工资少于10000的女教师的身份证和姓名。

select 身份证,姓名 from Teacher where 性别= '女' and 类型='研究生' and 工资 < 10000

View Code

d.  研究生教师平均工资、最⾼高和最低工资。

select AVG(工资),MAX(工资),MIN(工资) from Teacher wherer 类型='研究生'

View Code

11. 根据如下表结构建表:(2分)

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;

View Code

12.    根据如下表查询每个⽤用户第⼀一次下订单的时间。(2分)

#第一次下单时间,就是时间最早的

select name,MIN(order_time) from table group by name

View Code

13. 有⼀一个订单系统包含订单信息、商品信息、价格信息且还要⼀一些状态,如何设计表结构(2分)

#最简单的设计

商品表-id-名称-价格-描述信息

订单表-id-订单号(唯一)-商品id-用户id

用户表-id-username- password

View Code

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. 各个商品的售卖情况,需要字段:商品名、购买总数、商品收⼊入(单价*数量量)

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

View Code

b. 所有用户在2018-01-01至2018-02-01下单次数、下单金额、商城下单次数、商城下单金额

#注意:最后的期限要加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 BETWEEN2018-01-01AND reservations.created_at'2018-02-02'

View Code

c. 历月下单用户数:下单1次的用户数、下单2次的用户数、下单3次及以上的用户数

#下单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;

View Code

15.  根据表写SQL语句句:(5分)

• 查询所有同学的学号、姓名、班级名称。(1分)

select student.sid,student.sname,class.caption from student left jon class on class.cid = student.class_id

View Code

• 查询没有学⽣生的所有班级。(2分)

select class.caption from student left jon class on class.cid = student.class_id where class.cid is null

View Code

• 查询有学⽣生的所有班级的名称和学数量量。(2分)

select class.caption,count(1) from student left jon class on class.cid = student.class_id

View Code

一、DRF用户认证

流程图

请求到达视图的时候,需要进行认证。

认证是在中间件之后的。如果一旦认证失败,则返回信息给用户

启动项目luffcity,访问购物车

注意:要启动redis,否则提示获取购物车数据失败

添加认证

购物车需要登录才能查看,登录成功后,返回一个token

修改views目录下的auth.py

from rest_framework.response importResponsefrom rest_framework.views importAPIViewfrom rest_framework.viewsets importViewSetMixinfrom api importmodelsfrom api.utils.response importBaseResponseimportuuidclassAuthView(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 notobj:

response.code= 10002response.error= '用户名或密码错误'

else:

uid= str(uuid.uuid4()) #生成唯一id

response.code = 99999response.data=uidexceptException as e:

response.code= 10005response.error= '操作异常'

return Response(response.dict)

View Code

请确保已经生成了表api_account,并添加了一条记录

如果没有,请参考昨天的文档!

测试用户和密码

查看返回结果

输入正确的用户名和密码

查看返回结果,返回一个随机码。这个就是token

9999表示登录成功

既然token已经生成了,并返回给了客户端。那么服务器如何验证客户端的token是否合法呢?

答案是,服务器需要保存token。推荐加一个有效期,比如微信公众号的token有效期为8个小时!

增加token表

修改models.py,增加token表

它和用户表是一对一的关系!

from django.contrib.contenttypes.fields importGenericForeignKey, GenericRelationfrom django.contrib.contenttypes.models importContentTypefrom django.db.models importQfrom django.utils.safestring importmark_safefrom django.db importmodelsimporthashlib######################### 课程相关 ########################

classCourseCategory(models.Model):"""课程大类, e.g 前端 后端..."""name= models.CharField(max_length=64, unique=True)def __str__(self):return "%s" %self.nameclassMeta:

verbose_name_plural= "01.课程大类"

classCourseSubCategory(models.Model):"""课程子类, e.g python linux"""category= models.ForeignKey("CourseCategory")

name= models.CharField(max_length=64, unique=True)def __str__(self):return "%s" %self.nameclassMeta:

verbose_name_plural= "02.课程子类"

classDegreeCourse(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):returnself.nameclassMeta:

verbose_name_plural= "03.学位课"

classTeacher(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):returnself.nameclassMeta:

verbose_name_plural= "04.导师或讲师"

classScholarship(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)classMeta:

verbose_name_plural= "05.学位课奖学金"

classCourse(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 notself.degree_course:raise ValueError("学位课程必须关联对应的学位表")

super(Course, self).save(*args, **kwargs)classMeta:

verbose_name_plural= "06.专题课或学位课模块"

classCourseDetail(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.courseclassMeta:

verbose_name_plural= "07.课程或学位模块详细"

classOftenAskedQuestion(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)classMeta:

unique_together= ('content_type', 'object_id', 'question')

verbose_name_plural= "08. 常见问题"

classCourseOutline(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.titleclassMeta:

unique_together= ('course_detail', 'title')

verbose_name_plural= "09. 课程大纲"

classCourseChapter(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)classMeta:

unique_together= ("course", 'chapter')

verbose_name_plural= "10. 课程章节"

def __str__(self):return "%s:(第%s章)%s" %(self.course, self.chapter, self.name)classCourseSection(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)classMeta:

unique_together= ('chapter', 'section_link')

verbose_name_plural= "11. 课时"

def __str__(self):return "%s-%s" %(self.chapter, self.name)classHomework(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")classMeta:

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. 学位课评价(购买课程后才能评价)"

classPricePolicy(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()classMeta:

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)#################################### 优惠券相关 #################################

classCoupon(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)classMeta:

verbose_name_plural= "31. 优惠券生成记录"

def __str__(self):return "%s(%s)" %(self.get_coupon_type_display(), self.name)classCouponRecord(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')classMeta:

verbose_name_plural= "32. 用户优惠券"

def __str__(self):return '%s-%s-%s' %(self.account, self.number, self.status)classAccount(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)classMeta:

verbose_name_plural= "33. 用户表"

classUserToken(models.Model):

user= models.OneToOneField(to='Account')

token= models.CharField(max_length=36)classMeta:

verbose_name_plural= "34. token表"

View Code

使用2个命令,生成表

python manage.py makemigrations

python manage.py migrate

修改views目录下的auth.py,保存token到数据库中

from rest_framework.response importResponsefrom rest_framework.views importAPIViewfrom rest_framework.viewsets importViewSetMixinfrom api importmodelsfrom api.utils.response importBaseResponseimportuuidclassAuthView(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 notobj:

response.code= 10002response.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= 99999response.data=uidexceptException as e:

response.code= 10005response.error= '操作异常'

return Response(response.dict)

View Code

再次发送POST请求,输入正确的用户名和密码

查看表api_usertoken,发现和返回结果是一样的!

再发送一次

表的数据随之更新

增加认证

不光购物车会用到用户认证,结算中心也需要用到认证,还有其他的视图,也同样需要登录才能使用。

所以,这个认证类需要放到utils里面

在utils目录中新建文件auth.py

from rest_framework.authentication importBaseAuthenticationfrom rest_framework.exceptions importAuthenticationFailedfrom api importmodelsclassLuffyAuthentication(BaseAuthentication):defauthenticate(self, request):"""用户认证

:param request:

:return:"""

#获取get参数中的token

token = request.query_params.get('token')#判断token是否在数据库中

token_obj = models.UserToken.objects.filter(token=token).first()if nottoken_obj:#认证失败

raise AuthenticationFailed({'code':1008,'error':'认证失败'})#认证成功

#return 必须返回2个参数,请参考源码解析

#这里的token_obj.user,表示UserToken表中的user字段

#token_obj就是UserToken表的一条记录,也就是一个object

return (token_obj.user,token_obj)

View Code

修改views目录下的shoppingcart.py,导入utils下的auth模块

from rest_framework.views importAPIViewfrom rest_framework.viewsets importViewSetMixinfrom rest_framework.response importResponsefrom api importmodelsfrom api.utils.response importBaseResponseimportjsonimportredisfrom django.conf importsettingsfrom api.utils.auth importLuffyAuthentication#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:{},#}

classShoppingCartView(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 inuser_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_listexceptException as e:#print(e)

ret['code'] = 10005ret['error'] = '获取购物车数据失败'

#print(ret)

#print(json.dumps(ret))

returnResponse(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 notcourse:return Response({'code': 10001, 'error': '课程不存在'})#2.2 价格策略是否合法?

price_policy_queryset =course.price_policy.all()

price_policy_dict={}for item inprice_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]=tempprint(price_policy_dict,type(price_policy_dict))print(policy_id,type(policy_id))if policy_id not inprice_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= '删除成功'

exceptException as e:

response.code= 10006response.error= '删除失败'

returnResponse(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') elseNone#key = 'shopping_car_%s_%s' %(USER_ID,course_id,)

key = "shopping_car_%s_%s" %(USER_ID, course_id,)if notCONN.exists(key):

response.code= 10007response.error= '课程不存在'

returnResponse(response.dict)

price_policy_dict= json.loads(CONN.hget(key, 'price_policy_dict').decode('utf-8'))if policy_id not inprice_policy_dict:

response.code= 10008response.error= '价格策略不存在'

returnResponse(response.dict)

CONN.hset(key,'default_price_id',policy_id)

CONN.expire(key,20 * 60) #有效期20分钟

response.data = '修改成功'

exceptException as e:

response.code= 10009response.error= '修改失败'

return Response(response.dict)

View Code

参数说明:

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

from rest_framework.views importAPIViewfrom rest_framework.viewsets importViewSetMixinfrom rest_framework.response importResponsefrom api importmodelsfrom api.utils.response importBaseResponseimportjsonimportredisfrom django.conf importsettingsfrom api.utils.auth importLuffyAuthentication#redis连接

CONN = redis.Redis(host=settings.REDIS_SERVER.get('host'),port=settings.REDIS_SERVER.get('port'))classShoppingCartView(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 inuser_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_listexceptException as e:#print(e)

ret['code'] = 10005ret['error'] = '获取购物车数据失败'

#print(ret)

#print(json.dumps(ret))

returnResponse(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 notcourse:return Response({'code': 10001, 'error': '课程不存在'})#2.2 价格策略是否合法?

price_policy_queryset =course.price_policy.all()

price_policy_dict={}for item inprice_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]=tempprint(price_policy_dict,type(price_policy_dict))print(policy_id,type(policy_id))if policy_id not inprice_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= '删除成功'

exceptException as e:

response.code= 10006response.error= '删除失败'

returnResponse(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') elseNone#key = 'shopping_car_%s_%s' %(request.user.id,course_id,)

key = "shopping_car_%s_%s" %(request.user.id, course_id,)if notCONN.exists(key):

response.code= 10007response.error= '课程不存在'

returnResponse(response.dict)

price_policy_dict= json.loads(CONN.hget(key, 'price_policy_dict').decode('utf-8'))if policy_id not inprice_policy_dict:

response.code= 10008response.error= '价格策略不存在'

returnResponse(response.dict)

CONN.hset(key,'default_price_id',policy_id)

CONN.expire(key,20 * 60)

response.data= '修改成功'

exceptException as e:

response.code= 10009response.error= '修改失败'

return Response(response.dict)

View Code

测试get请求

全局配置

假设有100个类,有98个视图要认证。可以加到全局里面,修改settings.py

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',]

}

View Code

那么登录和查看课程,是不需要认证的。怎么忽略呢?

修改views目录下的auth.py,定义认证类为空列表,表示不认证!

from rest_framework.response importResponsefrom rest_framework.views importAPIViewfrom rest_framework.viewsets importViewSetMixinfrom api importmodelsfrom api.utils.response importBaseResponseimportuuidclassAuthView(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 notobj:

response.code= 10002response.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= 99999response.data=uidexceptException as e:

response.code= 10005response.error= '操作异常'

return Response(response.dict)

View Code

使用postman测试登录

查看返回结果

修改settings.py,注释掉全局认证。因为这里用到的登录认证的视图不多

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',]

}

View Code

问题:认证类为什么要继承BaseAuthentication?

查看源码BaseAuthentication

classBaseAuthentication(object):"""All authentication classes should extend BaseAuthentication."""

defauthenticate(self, request):"""Authenticate the request and return a two-tuple of (user, token)."""

raise NotImplementedError(".authenticate() must be overridden.")defauthenticate_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

View Code

发现,只要执行了authenticate方法,它会执行raise。它会主动报错

为了不让它报错,子类继承BaseAuthentication后,必须重写authenticate方法,才不会报错。

这样做的目的,是为了约束子类,哪些方法,必须要定义!

二、结算中心

点击去结算,会发送一次post请求。那么它该发送什么数据呢?

只需要发送课程id就可以了?为什么呢?

因为redis中有购物车相关数据!后台根据课程id去购物车中获取,要结算的课程就可以了!

结算中心和购物车一样,也是一个临时数据。它也需要放到redis中!

先来看购物车的数据结构

购物车 ={'shopping_car_1_3':{

name:'',

src:'xx'price_id:1,

price_dict={1:....

}

},'shopping_car_1_1':{

...

},'shopping_car_1_5':{

...

},

}

View Code

再来看结算中新的数据结构

结算中心 ={'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',

}

},

}

View Code

优惠券

优惠券分为2大类:绑定课程和非绑定课程

点击去结算

在左下角,展示的是非绑定课程的优惠券。

在右边的下拉菜单中,展示的是绑定课程的优惠券

在views目录下,创建文件payment.py

importjsonimportredisfrom django.conf importsettingsfrom rest_framework.views importAPIViewfrom rest_framework.viewsets importViewSetMixinfrom rest_framework.response importResponsefrom api.utils.auth importLuffyAuthenticationfrom api importmodelsfrom api.utils.response importBaseResponse#redis连接

CONN = redis.Redis(host=settings.REDIS_SERVER.get('host'),port=settings.REDIS_SERVER.get('port'))classPaymentView(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

#- 否则:非法请求

View Code

course_id为空,表示 未绑定课程,否则为绑定课程

这里面展示的是一些业务逻辑,需要自己用代码来填充

提示你的代码编写能力!

三、django-redis

介绍

django-redis 基于 BSD 许可, 是一个使 Django 支持 Redis cache/session 后端的全功能组件

django-redis 中文文档,请参考

为何要用 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表示最大连接数

连接池,请参考链接:

上面定义了100个连接池,假设100进程,都在使用连接池。当地101个访问时,会等待。直到有空闲的进程时,才处理!

不过redis的处理是很快的,很少会出现等待的情况!

使用连接池,有很多优点:

1.减少连接创建时间

2.简化的编程模式

3.受控的资源使用

使用连接池,性能会更高好!

视图中使用

加上2行代码,就可以了

from django_redis importget_redis_connection

CONN= get_redis_connection("default")

这里的default指的是settings.py中CACHES配置项的default

修改views目录下的shoppingcar.py

from rest_framework.views importAPIViewfrom rest_framework.viewsets importViewSetMixinfrom rest_framework.response importResponsefrom api importmodelsfrom api.utils.response importBaseResponseimportjsonfrom api.utils.auth importLuffyAuthenticationfrom django_redis importget_redis_connection

CONN= get_redis_connection("default") #使用redis连接池

classShoppingCartView(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 inuser_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_listexceptException as e:#print(e)

ret['code'] = 10005ret['error'] = '获取购物车数据失败'

#print(ret)

#print(json.dumps(ret))

returnResponse(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 notcourse:return Response({'code': 10001, 'error': '课程不存在'})#2.2 价格策略是否合法?

price_policy_queryset =course.price_policy.all()

price_policy_dict={}for item inprice_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]=tempprint(price_policy_dict,type(price_policy_dict))print(policy_id,type(policy_id))if policy_id not inprice_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= '删除成功'

exceptException as e:

response.code= 10006response.error= '删除失败'

returnResponse(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') elseNone#key = 'shopping_car_%s_%s' %(request.user.id,course_id,)

key = "shopping_car_%s_%s" %(request.user.id, course_id,)if notCONN.exists(key):

response.code= 10007response.error= '课程不存在'

returnResponse(response.dict)

price_policy_dict= json.loads(CONN.hget(key, 'price_policy_dict').decode('utf-8'))if policy_id not inprice_policy_dict:

response.code= 10008response.error= '价格策略不存在'

returnResponse(response.dict)

CONN.hset(key,'default_price_id',policy_id)

CONN.expire(key,20 * 60)

response.data= '修改成功'

exceptException as e:

response.code= 10009response.error= '修改失败'

return Response(response.dict)

View Code

使用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

#######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,默认修改之后才保存

View Code

简单来讲,加上2行就可以了。下面的那些配置,是参考源码设置的。

比如session失效时间是2周

如果需要修改,在这里指定一下,就可以了!

注意:里面的defalut就是redis配置的defalut,名字是一一对应的!

总结:

1. django-redis的作用-连接redis并在redis中进行操作(含redis连接池)。2. 帮助用户将session放到redis- django-redis的配置- session的配置

View Code

作业:

完整结算中心的代码,实现以下功能:

1. 添加

2. 查看

3. 修改

注意:使用认证+django-redis

修改utils目录下的auth.py

当为GET请求时,从url中取token,否则从请求体中获取token

from rest_framework.authentication importBaseAuthenticationfrom rest_framework.exceptions importAuthenticationFailedfrom api importmodelsclassLuffyAuthentication(BaseAuthentication):defauthenticate(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 nottoken_obj:#认证失败

raise AuthenticationFailed({'code':1008,'error':'认证失败'})#认证成功

#return (token_obj.user,token_obj)

return (token_obj.user,token_obj)

View Code

修改models.py,在用户表增加字段

from django.contrib.contenttypes.fields importGenericForeignKey, GenericRelationfrom django.contrib.contenttypes.models importContentTypefrom django.db.models importQfrom django.utils.safestring importmark_safefrom django.db importmodelsimporthashlib######################### 课程相关 ########################

classCourseCategory(models.Model):"""课程大类, e.g 前端 后端..."""name= models.CharField(max_length=64, unique=True)def __str__(self):return "%s" %self.nameclassMeta:

verbose_name_plural= "01.课程大类"

classCourseSubCategory(models.Model):"""课程子类, e.g python linux"""category= models.ForeignKey("CourseCategory")

name= models.CharField(max_length=64, unique=True)def __str__(self):return "%s" %self.nameclassMeta:

verbose_name_plural= "02.课程子类"

classDegreeCourse(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):returnself.nameclassMeta:

verbose_name_plural= "03.学位课"

classTeacher(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):returnself.nameclassMeta:

verbose_name_plural= "04.导师或讲师"

classScholarship(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)classMeta:

verbose_name_plural= "05.学位课奖学金"

classCourse(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 notself.degree_course:raise ValueError("学位课程必须关联对应的学位表")

super(Course, self).save(*args, **kwargs)classMeta:

verbose_name_plural= "06.专题课或学位课模块"

classCourseDetail(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.courseclassMeta:

verbose_name_plural= "07.课程或学位模块详细"

classOftenAskedQuestion(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)classMeta:

unique_together= ('content_type', 'object_id', 'question')

verbose_name_plural= "08. 常见问题"

classCourseOutline(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.titleclassMeta:

unique_together= ('course_detail', 'title')

verbose_name_plural= "09. 课程大纲"

classCourseChapter(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)classMeta:

unique_together= ("course", 'chapter')

verbose_name_plural= "10. 课程章节"

def __str__(self):return "%s:(第%s章)%s" %(self.course, self.chapter, self.name)classCourseSection(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)classMeta:

unique_together= ('chapter', 'section_link')

verbose_name_plural= "11. 课时"

def __str__(self):return "%s-%s" %(self.chapter, self.name)classHomework(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")classMeta:

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. 学位课评价(购买课程后才能评价)"

classPricePolicy(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()classMeta:

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)#################################### 优惠券相关 #################################

classCoupon(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)classMeta:

verbose_name_plural= "31. 优惠券生成记录"

def __str__(self):return "%s(%s)" %(self.get_coupon_type_display(), self.name)classCouponRecord(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')classMeta:

verbose_name_plural= "32. 用户优惠券"

def __str__(self):return '%s-%s-%s' %(self.account, self.number, self.status)classAccount(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)classMeta:

verbose_name_plural= "33. 用户表"

classUserToken(models.Model):

user= models.OneToOneField(to='Account')

token= models.CharField(max_length=36)classMeta:

verbose_name_plural= "34. token表"

View Code

执行2个命令,生成字段

python manage.py makemigrations

python manage.py migrate

为用户加点钱

修改admin.py,注册所有表

from django.contrib importadmin#Register your models here.

from api importmodels

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)

View Code

进入admin后台,添加几条优惠券,并绑定用户

list

修改payment.py,先做get请求的

importjsonimportredisfrom django.conf importsettingsfrom rest_framework.views importAPIViewfrom rest_framework.viewsets importViewSetMixinfrom rest_framework.response importResponsefrom api.utils.auth importLuffyAuthenticationfrom api importmodelsfrom api.utils.response importBaseResponsefrom django_redis importget_redis_connection

CONN= get_redis_connection("default") #使用redis连接池

classPaymentView(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.idprint('用户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

#- 否则:非法请求

View Code

使用postman发送GET请求

查看Pycharm控制台输出

课程id 1结算课程 Python开发入门7天特训营

用户id1立减10

用户贝里100.0

python费用结算系统_python 全栈开发,Day104(DRF用户认证,结算中心,django-redis)相关推荐

  1. python 全栈开发,Day104(DRF用户认证,结算中心,django-redis)

    python 全栈开发,Day104(DRF用户认证,结算中心,django-redis) 考试第二部分:MySQL数据库 6.  MySQL中char和varchar的区别(1分) char是定长, ...

  2. 路飞学城python全栈开发_python 全栈开发,Day98(路飞学城背景,django ContentType组件,表结构讲解)...

    昨日内容回顾 1. 为什么要做前后端分离?-前后端交给不同的人来编写,职责划分明确.-API (IOS,安卓,PC,微信小程序...)-vue.js等框架编写前端时,会比之前写jQuery更简单快捷. ...

  3. python 仪表盘监控_Python 全栈开发 -- 监控篇

    如果你已经玩转了 Python 编程语言语法,肯定想用这些知识,开发一款应用程序,它可以是在网上,可以炫耀或出售,那就需要全栈式开发 Python.具体如何创建,部署和运行生产 Python Web ...

  4. python第一次考试_python 全栈开发,Day16(函数第一次考试)

    考试题 Python11 期第二次考试(基础数据类型与函数部分) 考试时长:3个小时 满分:105分 一,选择题(每题2分,共24分) 1.python不支持的数据类型有 A.char B.int C ...

  5. format函数python生成列表_python 全栈开发,Day14(列表推导式,生成器表达式,内置函数)...

    一.列表生成式 生成1-100的列表 li = [] for i in range(1,101): li.append(i) print(li) 执行输出: [1,2,3...] 生成python1期 ...

  6. python简介动画_python 全栈开发,Day53(jQuery的介绍,jQuery的选择器,jQuery动画效果)...

    01-jQuery的介绍 1.为什么要使用jQuery 在用js写代码时,会遇到一些问题: window.onload 事件有事件覆盖的问题,因此只能写一个事件. 代码容错性差. 浏览器兼容性问题. ...

  7. python 动画代码_python 全栈开发,Day53(jQuery的介绍,jQuery的选择器,jQuery动画效果)...

    js总结 js: 1.ECMAScript5 ES5语法 2.DOM CRUD 获取 3种方式 id tag className //面向对象 对象 : 属性和方法 某个对象中 function $( ...

  8. python 创业项目_python 全栈开发,Day126(创业故事,软件部需求,内容采集,显示内容图文列表,MongoDB数据导入导出JSON)...

    用户注册 用户名 密码 确认密码 昵称 男 女 年龄 电话 注册 返回

  9. python全栈开发优势_Python全栈开发多少钱?学Python价格贵吗?

    Python全栈开发培训多少钱?学习Python是大家进入编程世界的理想之选,而且Python也是一门非常受欢迎的编程,可以从事的领域有很多. 从目前市场上的行情来说,一般情况下Python培训的费用 ...

最新文章

  1. 两张图看懂GC 日志
  2. FusionCharts下载地址汇总及更新介绍
  3. git常见的回退操作
  4. ios 字符串转数组_ES6中常用的数组操作-必须收藏
  5. windows 编程 —— 消息与参数(定时器、初始化消息、改变大小)
  6. 期待已久的Java 9 今日发布
  7. [CSA49G][XSY3315] Bunny on Number Line (DP)
  8. Hadoop无法上传文件查找原因
  9. Mac下使用gitHub
  10. 计算机基础1.4习题答案,中职计算机应用基础习题集及答案~.doc
  11. JDK与JRE各种版本下载地址
  12. java交通灯英文文献,194关于单片机智能交通灯控制系统设计有关的外文文献翻译成品:基于单片机的智能交通控制系统(中英文双语对照)7...
  13. 在CMD里进行复制粘贴的方法
  14. CF949 简要题解
  15. 计算机网络 『内部网关协议IGP中的路由信息协议RIP』
  16. Marvell宣战博通网络交换芯片,一大波饿狼扑食而来
  17. 走进AI时代的文档识别技术 之文档重建
  18. 详解比较好的音视频通话SDK开发包的技术要点
  19. 西门子300系列:复杂数据类型UDT
  20. STC8A单片机应用开发

热门文章

  1. (chap7 确保WEB安全的HTTPS) HTTPS加密
  2. vnctf——cm1
  3. nginx.conf配置文件
  4. 【django】创建django项目工程
  5. css select-style属性,beautifulsoupcss Select查找不存在特定属性(style for ex)的标记
  6. python将索引升序_程序在Python中按升序删除元素后获取列表的索引
  7. VS2019编译32位程序运行于XP系统
  8. 【技术学习】一次Node.js站点渗透
  9. 殊途同归的CVE-2012-0774 TrueType字体整数溢出漏洞分析
  10. Python中MD5加密字符串