python 全栈开发,Day104(DRF用户认证,结算中心,django-redis)
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)
解釋:
索引有2個功能:加快查詢和約束。
這里的約束指的是唯一索引,聯合唯一索引。
索引遵循的原則: 最左前綴原則
你可以認為聯合索引是闖關游戲的設計例如你這個聯合索引是state/city/zipCode那么state就是第一關 city是第二關, zipCode就是第三關你必須匹配了第一關,才能匹配第二關,匹配了第一關和第二關,才能匹配第三關你不能直接到第二關的索引的格式就是第一層是state,第二層才是city索引是因為B+樹結構 所以查找快 如果單看第三列 是非排序的。 多列索引是先按照第一列進行排序,然后在第一列排好序的基礎上再對第二列排序,如果沒有第一列的話,直接訪問第二列,那第二列肯定是無序的,直接訪問后面的列就用不到索引了。 所以如果不是在前面列的基礎上而是但看后面某一列,索引是失效的。 View Code簡而言之,只要where條件包含最左邊的字段,那么它就會用到組合索引,反之亦然!
如果創建了組合索引,但是卻沒有命中,這是浪費磁盤空間。因為索引也占用磁盤!
?
10. 假設學?生Student和教師Teacher關系模型如下:(4分) Student(學號、姓名、性別、類型、身份證號) Teacher(教師號、姓名、性別、類型、身份證號、工資)
其中,學?生表中類別為“本科生”和“研究生”兩類;性別為“男”和“女”兩類。
a. 性別為女的所有學生。
b. 學生表中類別分別對應的個數。
select 類型,count(1) from Student group by 類型 View Codec.工資少于10000的女教師的身份證和姓名。
select 身份證,姓名 from Teacher where 性別= '女' and 類型='研究生' and 工資 < 10000 View Coded.? 研究生教師平均工資、最?高和最低工資。
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. 各個商品的售賣情況,需要字段:商品名、購買總數、商品收?入(單價*數量量)
SELECTproducts. NAME,sum(orders.quantity),products.price * sum(orders.quantity) FROMorders LEFT JOIN products ON products.id = orders.product_id GROUP BYorders.product_id View Code?
b. 所有用戶在2018-01-01至2018-02-01下單次數、下單金額、商城下單次數、商城下單金額
# 注意:最后的期限要加1天。因為23:59:59也是屬于當天的 SELECTcount(1),sum(reservations.price),sum(orders.quantity),products.price * sum(orders.quantity) FROMreservations LEFT JOIN orders ON orders.reservations_id = reservations.id LEFT JOIN products ON products.id = orders.product_id WHEREreservations.created_at BETWEEN 2018-01-01 AND 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 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 uuidclass 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 = 10002response.error = '用戶名或密碼錯誤'else:uid = str(uuid.uuid4()) # 生成唯一idresponse.code = 99999response.data = uidexcept Exception 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 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.nameclass 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.nameclass 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 2000mentor_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.nameclass 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.nameclass 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.courseclass Meta:verbose_name_plural = "07.課程或學位模塊詳細"class OftenAskedQuestion(models.Model):"""常見問題"""content_type = models.ForeignKey(ContentType) # 關聯course or degree_courseobject_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.titleclass 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_courseobject_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表" View Code使用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 uuidclass 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 = 10002response.error = '用戶名或密碼錯誤'else:uid = str(uuid.uuid4()) # 生成唯一id# 保存到數據庫中,update_or_create表示更新或者創建# user=obj,這個是判斷條件。當條件成立,更新token字段,值為uid# 當條件不成立時,增加一條記錄。注意:增加時,有2個字段,分別是user和tokenmodels.UserToken.objects.update_or_create(user=obj, defaults={'token': uid})response.code = 99999response.data = uidexcept Exception as e:response.code = 10005response.error = '操作異常'return Response(response.dict) View Code再次發送POST請求,輸入正確的用戶名和密碼
查看表api_usertoken,發現和返回結果是一樣的!
再發送一次
表的數據隨之更新
?
增加認證
不光購物車會用到用戶認證,結算中心也需要用到認證,還有其他的視圖,也同樣需要登錄才能使用。
所以,這個認證類需要放到utils里面
在utils目錄中新建文件auth.py
from rest_framework.authentication import BaseAuthentication from rest_framework.exceptions import AuthenticationFailedfrom api import modelsclass LuffyAuthentication(BaseAuthentication):def authenticate(self, request):"""用戶認證:param request::return:"""# 獲取get參數中的tokentoken = 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表的一條記錄,也就是一個objectreturn (token_obj.user,token_obj) View Code修改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.authprint(request.user) # 認證類返回的第一個值print(request.auth) # 認證類返回的第二個值# 獲取tokenprint('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_listexcept Exception as e:# print(e)ret['code'] = 10005ret['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和價格策略ID2. 判斷合法性- 課程是否存在?- 價格策略是否合法?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] = tempprint(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:課程IDname:課程名稱img:課程圖片defaut:默認選中的價格策略price_list:所有價格策略},}"""pattern = "shopping_car_%s_%s" % (USER_ID, '*',)keys = CONN.keys(pattern)if keys and len(keys) >= 1000:return Response({'code': 10009, 'error': '購物車東西太多,先去結算再進行購買..'})# key = "shopping_car_%s_%s" %(USER_ID,course_id,)key = "shopping_car_%s_%s" % (USER_ID, course_id,)print(key,'1111111111')CONN.hset(key, 'id', course_id)CONN.hset(key, 'name', course.name)CONN.hset(key, 'img', course.course_img)CONN.hset(key, 'default_price_id', policy_id)CONN.hset(key, 'price_policy_dict', json.dumps(price_policy_dict))CONN.expire(key, 60 * 60 * 24) # 有效期,單位秒。表示一天return Response({'code': 10000, 'data': '購買成功'})def destroy(self,request,*args,**kwargs):"""刪除購物車中的某個課程:param request::param args::param kwargs::return:"""response = BaseResponse()try:# courseid = request.GET.get('courseid')courseid = request.data.get('courseid')print(courseid)# key = "shopping_car_%s_%s" % (USER_ID,courseid)key = "shopping_car_%s_%s" % (USER_ID, courseid,)CONN.delete(key)response.data = '刪除成功'except Exception as e:response.code = 10006response.error = '刪除失敗'return Response(response.dict)def update(self,request,*args,**kwargs):"""修改用戶選中的價格策略:param request::param args::param kwargs::return:""""""1. 獲取課程ID、要修改的價格策略ID2. 校驗合法性(去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 = 10007response.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 = 10008response.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 = 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 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.authprint(request.user) # 認證類返回的第一個值print(request.auth) # 認證類返回的第二個值# 獲取tokenprint('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_listexcept Exception as e:# print(e)ret['code'] = 10005ret['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和價格策略ID2. 判斷合法性- 課程是否存在?- 價格策略是否合法?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] = tempprint(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:課程IDname:課程名稱img:課程圖片defaut:默認選中的價格策略price_list:所有價格策略},}"""pattern = "shopping_car_%s_%s" % (request.user.id, '*',)keys = CONN.keys(pattern)if keys and len(keys) >= 1000:return Response({'code': 10009, 'error': '購物車東西太多,先去結算再進行購買..'})# key = "shopping_car_%s_%s" %(request.user.id,course_id,)key = "shopping_car_%s_%s" % (request.user.id, course_id,)print(key,'1111111111')CONN.hset(key, 'id', course_id)CONN.hset(key, 'name', course.name)CONN.hset(key, 'img', course.course_img)CONN.hset(key, 'default_price_id', policy_id)CONN.hset(key, 'price_policy_dict', json.dumps(price_policy_dict))CONN.expire(key, 60 * 12 * 24) # 有效期return Response({'code': 10000, 'data': '購買成功'})def destroy(self,request,*args,**kwargs):"""刪除購物車中的某個課程:param request::param args::param kwargs::return:"""response = BaseResponse()try:# courseid = request.GET.get('courseid')courseid = request.data.get('courseid')print(courseid)# key = "shopping_car_%s_%s" % (request.user.id,courseid)key = "shopping_car_%s_%s" % (request.user.id, courseid,)CONN.delete(key)response.data = '刪除成功'except Exception as e:response.code = 10006response.error = '刪除失敗'return Response(response.dict)def update(self,request,*args,**kwargs):"""修改用戶選中的價格策略:param request::param args::param kwargs::return:""""""1. 獲取課程ID、要修改的價格策略ID2. 校驗合法性(去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 = 10007response.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 = 10008response.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 = 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 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 uuidclass 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 = 10002response.error = '用戶名或密碼錯誤'else:uid = str(uuid.uuid4()) # 生成唯一id# 保存到數據庫中,update_or_create表示更新或者創建# user=obj,這個是判斷條件。當條件成立,更新token字段,值為uid# 當條件不成立時,增加一條記錄。注意:增加時,有2個字段,分別是user和tokenmodels.UserToken.objects.update_or_create(user=obj, defaults={'token': uid})response.code = 99999response.data = uidexcept Exception 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
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 theauthentication 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
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_用戶IDdef 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 Codecourse_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_connectionCONN = 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.authprint(request.user) # 認證類返回的第一個值print(request.auth) # 認證類返回的第二個值# 獲取tokenprint('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_listexcept Exception as e:# print(e)ret['code'] = 10005ret['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和價格策略ID2. 判斷合法性- 課程是否存在?- 價格策略是否合法?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] = tempprint(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:課程IDname:課程名稱img:課程圖片defaut:默認選中的價格策略price_list:所有價格策略},}"""pattern = "shopping_car_%s_%s" % (request.user.id, '*',)keys = CONN.keys(pattern)if keys and len(keys) >= 1000:return Response({'code': 10009, 'error': '購物車東西太多,先去結算再進行購買..'})# key = "shopping_car_%s_%s" %(request.user.id,course_id,)key = "shopping_car_%s_%s" % (request.user.id, course_id,)print(key,'1111111111')CONN.hset(key, 'id', course_id)CONN.hset(key, 'name', course.name)CONN.hset(key, 'img', course.course_img)CONN.hset(key, 'default_price_id', policy_id)CONN.hset(key, 'price_policy_dict', json.dumps(price_policy_dict))CONN.expire(key, 60 * 12 * 24) # 有效期return Response({'code': 10000, 'data': '購買成功'})def destroy(self,request,*args,**kwargs):"""刪除購物車中的某個課程:param request::param args::param kwargs::return:"""response = BaseResponse()try:# courseid = request.GET.get('courseid')courseid = request.data.get('courseid')print(courseid)# key = "shopping_car_%s_%s" % (request.user.id,courseid)key = "shopping_car_%s_%s" % (request.user.id, courseid,)CONN.delete(key)response.data = '刪除成功'except Exception as e:response.code = 10006response.error = '刪除失敗'return Response(response.dict)def update(self,request,*args,**kwargs):"""修改用戶選中的價格策略:param request::param args::param kwargs::return:""""""1. 獲取課程ID、要修改的價格策略ID2. 校驗合法性(去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 = 10007response.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 = 10008response.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 = 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 import BaseAuthentication from rest_framework.exceptions import AuthenticationFailedfrom api import modelsclass 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) View Code修改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.nameclass 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.nameclass 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 2000mentor_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.nameclass 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.nameclass 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.courseclass Meta:verbose_name_plural = "07.課程或學位模塊詳細"class OftenAskedQuestion(models.Model):"""常見問題"""content_type = models.ForeignKey(ContentType) # 關聯course or degree_courseobject_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.titleclass 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_courseobject_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表" View Code執行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) View Code進入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 BaseResponsefrom django_redis import get_redis_connectionCONN = 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_用戶IDdef 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天特訓營 用戶id 1 立減10 用戶貝里 100.0?
posted @ 2018-08-14 15:13 肖祥 閱讀( ...) 評論( ...) 編輯 收藏總結
以上是生活随笔為你收集整理的python 全栈开发,Day104(DRF用户认证,结算中心,django-redis)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: luogu1091合唱队形
- 下一篇: 我的世界minecraft最新版1.16