python费用结算系统_python 全栈开发,Day104(DRF用户认证,结算中心,django-redis)
考試第二部分: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)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 非标自动化企业前十名_非标设备的现状
- 下一篇: python好多模块和c相识_Pytho