python 全栈开发,Day104(DRF用户认证,结算中心,django-redis)
考试第二部分:MySQL数据库
6. MySQL中char和varchar的区别(1分)
char是定长,varchar是变长。
char的查询速度比varchar要快。
7. MySQL中varchar(50)的50表示什什么意思?(1分)
是字符长度。一个中文,也是一个字符。
8. left join、right join以及inner join的区别?(2分)
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)
a,d,f,g 会命中索引
解释:
索引有2个功能:加快查询和约束。
这里的约束指的是唯一索引,联合唯一索引。
索引遵循的原则: 最左前缀原则
你可以认为联合索引是闯关游戏的设计 例如你这个联合索引是state/city/zipCode 那么state就是第一关 city是第二关, zipCode就是第三关 你必须匹配了第一关,才能匹配第二关,匹配了第一关和第二关,才能匹配第三关 你不能直接到第二关的 索引的格式就是第一层是state,第二层才是city 索引是因为B+树结构 所以查找快 如果单看第三列 是非排序的。
多列索引是先按照第一列进行排序,然后在第一列排好序的基础上再对第二列排序,如果没有第一列的话,直接访问第二列,那第二列肯定是无序的,直接访问后面的列就用不到索引了。
所以如果不是在前面列的基础上而是但看后面某一列,索引是失效的。
简而言之,只要where条件包含最左边的字段,那么它就会用到组合索引,反之亦然!
如果创建了组合索引,但是却没有命中,这是浪费磁盘空间。因为索引也占用磁盘!
10. 假设学⽣生Student和教师Teacher关系模型如下:(4分) Student(学号、姓名、性别、类型、身份证号) Teacher(教师号、姓名、性别、类型、身份证号、工资)
其中,学⽣生表中类别为“本科生”和“研究生”两类;性别为“男”和“女”两类。
a. 性别为女的所有学生。
select * from Student where 性别='女'
b. 学生表中类别分别对应的个数。
select 类型,count(1) from Student group by 类型
c.工资少于10000的女教师的身份证和姓名。
select 身份证,姓名 from Teacher where 性别= '女' and 类型='研究生' and 工资 < 10000
d. 研究生教师平均工资、最⾼高和最低工资。
select AVG(工资),MAX(工资),MIN(工资) from Teacher wherer 类型='研究生'
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;
12. 根据如下表查询每个⽤用户第⼀一次下订单的时间。(2分)
# 第一次下单时间,就是时间最早的
select name,MIN(order_time) from table group by name
13. 有⼀一个订单系统包含订单信息、商品信息、价格信息且还要⼀一些状态,如何设计表结构(2分)
#最简单的设计 商品表
- 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. 各个商品的售卖情况,需要字段:商品名、购买总数、商品收⼊入(单价*数量量)
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下单次数、下单金额、商城下单次数、商城下单金额
# 注意:最后的期限要加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次及以上的用户数
# 下单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分)
select student.sid,student.sname,class.caption from student left jon class on class.cid = student.class_id
• 查询没有学⽣生的所有班级。(2分)
select class.caption from student left jon class on class.cid = student.class_id where class.cid is null
• 查询有学⽣生的所有班级的名称和学数量量。(2分)
select class.caption,count(1) from student left jon class on class.cid = student.class_id
一、DRF用户认证
流程图
请求到达视图的时候,需要进行认证。
认证是在中间件之后的。如果一旦认证失败,则返回信息给用户
启动项目luffcity,访问购物车
注意:要启动redis,否则提示获取购物车数据失败
添加认证
购物车需要登录才能查看,登录成功后,返回一个token
修改views目录下的auth.py
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表
它和用户表是一对一的关系!
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到数据库中
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
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模块
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,'')
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
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,'')
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
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,定义认证类为空列表,表示不认证!
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,注释掉全局认证。因为这里用到的登录认证的视图不多
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
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中!
先来看购物车的数据结构
购物车 = {
'shopping_car_1_3':{
name:'',
src:'xx'
price_id:1,
price_dict = {
1:....
}
},
'shopping_car_1_1':{
...
},
'shopping_car_1_5':{
...
}, }
再来看结算中新的数据结构
结算中心 = {
'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
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
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,'')
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
# ######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,名字是一一对应的!
总结:
1. django-redis的作用
- 连接redis并在redis中进行操作(含redis连接池)。 2. 帮助用户将session放到redis
- django-redis的配置
- session的配置
作业:
完整结算中心的代码,实现以下功能:
1. 添加
2. 查看
3. 修改
注意:使用认证+django-redis
修改utils目录下的auth.py
当为GET请求时,从url中取token,否则从请求体中获取token
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,在用户表增加字段
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,注册所有表
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请求的
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
python 全栈开发,Day104(DRF用户认证,结算中心,django-redis)的更多相关文章
- python 全栈开发,Day98(路飞学城背景,django ContentType组件,表结构讲解)
昨日内容回顾 1. 为什么要做前后端分离? - 前后端交给不同的人来编写,职责划分明确. - API (IOS,安卓,PC,微信小程序...) - vue.js等框架编写前端时,会比之前写jQuery ...
- python 全栈开发,Day97(Token 认证的来龙去脉,DRF认证,DRF权限,DRF节流)
昨日内容回顾 1. 五个葫芦娃和三行代码 APIView(views.View) 1. 封装了Django的request - request.query_params --> 取URL中的参数 ...
- python 全栈开发,Day95(RESTful API介绍,基于Django实现RESTful API,DRF 序列化)
昨日内容回顾 1. rest framework serializer(序列化)的简单使用 QuerySet([ obj, obj, obj]) --> JSON格式数据 0. 安装和导入: p ...
- 巨蟒python全栈开发django10:ajax&&登录认证
通过题目进行知识点回顾: 聚合查询 From django.db.models import Avg,Min,Max,F,Q,Count,Sum #查询书籍的平均值 Ret= Models.Book. ...
- Python 全栈开发【第0篇】:目录
Python 全栈开发[第0篇]:目录 第一阶段:Python 开发入门 Python 全栈开发[第一篇]:计算机原理&Linux系统入门 Python 全栈开发[第二篇]:Python基 ...
- python 全栈开发,Day99(作业讲解,DRF版本,DRF分页,DRF序列化进阶)
昨日内容回顾 1. 为什么要做前后端分离? - 前后端交给不同的人来编写,职责划分明确. - API (IOS,安卓,PC,微信小程序...) - vue.js等框架编写前端时,会比之前写jQuery ...
- python全栈开发-Day2 布尔、流程控制、循环
python全栈开发-Day2 布尔 流程控制 循环 一.布尔 1.概述 #布尔值,一个True一个False #计算机俗称电脑,即我们编写程序让计算机运行时,应该是让计算机无限接近人脑,或者说人 ...
- python全栈开发中级班全程笔记(第二模块、第四章)(常用模块导入)
python全栈开发笔记第二模块 第四章 :常用模块(第二部分) 一.os 模块的 详解 1.os.getcwd() :得到当前工作目录,即当前python解释器所在目录路径 impor ...
- Python全栈开发【面向对象进阶】
Python全栈开发[面向对象进阶] 本节内容: isinstance(obj,cls)和issubclass(sub,super) 反射 __setattr__,__delattr__,__geta ...
随机推荐
- 搭建ftp
相信很多人都知道ftp吧.打个比方.在你们公司或者学校里面有一个ftp地址,里面存放了你们所需要的一些常用的资源.这样的话你们就可以随时登录这个ftp来拉取你所需要的资源(在范围内),简单的来说,ft ...
- idea常用快捷键及自定义快捷键汇总
以下都是挨个进行验证过的 生成get和set方法为:alt+insert 自动补全返回值,自动补全变量名称和属性名称:ctrl+alt+v 输入System.out.println()的快捷方法是:输 ...
- Python异常处理和进程线程-day09
写在前面 上课第九天,打卡: 最坏的结果,不过是大器晚成: 一.异常处理 - 1.语法错误导致的异常 - 这种错误,根本过不了python解释器的语法检测,必须在程序运行前就修正: - 2.逻辑上的异 ...
- js 报delete object in strict mode
JAVA->Compiler->Building->No strictly compatible JRE for execution environment available Ig ...
- Javaweb中提到的反射浅析(附源码)
反射:一个jdk5.0的新特性,高级运用.在后期的框架中,这个是一大重点,现在估计我们都不会太多的接触他的.但是为了后面的铺垫,我想还是先了解一下: 先构造一个类,然后我们用反射来获取,调用里面的方法 ...
- xss漏洞利用
简述 跨站脚本攻击(也称为XSS)指利用网站漏洞从用户那里恶意盗取信息.攻击者通过在链接中插入恶意代码,就能够盗取用户信息.攻击者通常会在有漏洞的程序中插入 JavaScript.VBScript. ...
- luogu P2502 [HAOI2006]旅行
传送门 边数只有5000,可以考虑\(O(m^2)\)算法,即把所有边按边权升序排序,然后依次枚举每条边\(i\),从这条边开始依次加边,加到起点和终点在一个连通块为止.这个过程可以用并查集维护.那么 ...
- mysql gtid 第一篇
GTID1 简介 就是全局事务ID(global transaction identifier )2 构成 uuid+transaction_id 3 格式 7a07cd08-ac1b-11 ...
- 【BARTS计划】【Share_Week1】社交产品思考
Share:每周分享篇有观点和思考的技术文章 社交梦是每个互联网大厂都在做的,好像大家都默认了一种说法:没有社交功能的产品是不完整的,不做社交产品的公司是缺少战略眼光的.但就目前来看,微信的社交霸 ...
- JAVA中的引用
关于值类型和引用类型的话题,C++.JAVA.python.go.C#等等高级语言都有相关的概念,只要理解了其底层工作原理,可以说即使是不同的语言,在面试学习工作实践中都可以信手拈来(不要太纠集语言) ...