模型设计
之前一直以为物品的discount策略会放到前端做来做判断,直到视频到这个阶段,才发现是在后端做判断,而且各种策略是存放在数据表格中的,很妙!直接看一下表格设计,如下图:
要实现discount策略我们需要新建四个表:
- discount类型表:存放discount的具体类型
- price策略表:存放discount类型对应的计算方法,discount类型作为外键
- discount活动:存放一个活动的名称,开始、结束时间
- 关系表:课程与discount以及活动之间的关系
这样设计,我们在查询一个课程的时候,我们能够获得discount的策略、discount的类型、discount活动的名称信息,方便对课程的价格进行相应策略计算。
模型代码
"""价格相关的模型"""
class CourseDiscountType(BaseModel):
"""课程优惠类型"""
name = models.CharField(max_length=32, verbose_name="优惠类型名称")
remark = models.CharField(max_length=250, blank=True, null=True, verbose_name="备注信息")
class Meta:
db_table = "ly_course_discount_type"
verbose_name = "课程优惠类型"
verbose_name_plural = "课程优惠类型"
def __str__(self):
return "%s" % (self.name)
class CourseDiscount(BaseModel):
"""课程优惠模型"""
discount_type = models.ForeignKey("CourseDiscountType", on_delete=models.CASCADE, related_name='coursediscounts',
verbose_name="优惠类型")
condition = models.IntegerField(blank=True, default=0, verbose_name="满足优惠的价格条件",help_text="设置参与优惠的价格门槛,表示商品必须在xx价格以上的时候才参与优惠活动,<br>如果不填,则不设置门槛")
sale = models.TextField(verbose_name="优惠公式",blank=True,null=True, help_text="""
不填表示免费;<br>
*号开头表示折扣价,例如*0.82表示八二折;<br>
-号开头则表示减免,例如-20表示原价-20;<br>
如果需要表示满减,则需要使用 原价-优惠价格,例如表示课程价格大于100,优惠10;大于200,优惠20,格式如下:<br>
满100-10<br>
满200-25<br>
""")
class Meta:
db_table = "ly_course_discount"
verbose_name = "价格优惠策略"
verbose_name_plural = "价格优惠策略"
def __str__(self):
return "价格优惠:%s,优惠条件:%s,优惠值:%s" % (self.discount_type.name, self.condition, self.sale)
class Activity(BaseModel):
"""优惠活动"""
name = models.CharField(max_length=150, verbose_name="活动名称")
start_time = models.DateTimeField(verbose_name="优惠策略的开始时间")
end_time = models.DateTimeField(verbose_name="优惠策略的结束时间")
remark = models.CharField(max_length=250, blank=True, null=True, verbose_name="备注信息")
class Meta:
db_table = "ly_activity"
verbose_name="商品活动"
verbose_name_plural="商品活动"
def __str__(self):
return self.name
class CoursePriceDiscount(BaseModel):
"""课程与优惠策略的关系表"""
course = models.ForeignKey("Course",on_delete=models.CASCADE, related_name="activeprices",verbose_name="课程")
active = models.ForeignKey("Activity",on_delete=models.DO_NOTHING, related_name="activecourses",verbose_name="活动")
discount = models.ForeignKey("CourseDiscount",on_delete=models.CASCADE,related_name="discountcourse",verbose_name="优惠折扣")
class Meta:
db_table = "ly_course_price_dicount"
verbose_name="课程与优惠策略的关系表"
verbose_name_plural="课程与优惠策略的关系表"
def __str__(self):
return "课程:%s,优惠活动: %s,开始时间:%s,结束时间:%s" % (self.course.name, self.active.name, self.active.start_time,self.active.end_time)
写好模型代码之后需要进行数据迁移和xadmin注册,这里就略过。
具体实现
有了上述的模型,我们就可以在想要对价格进行处理的模型中添加下面的一些方法:
@property
def discount_name(self):
"""如果商品有参与了活动,则返回折扣类型"""
name = ""
# 获取当前课程参与的所有活动
active_list = self.active_list()
if len(active_list) > 0:
"""当前课程如果有参与到1个以上活动时才有优惠类型"""
active =active_list[0]
name = active.discount.discount_type.name
return name
def active_list(self):
"""获取当前课程参与的活动"""
return self.activeprices.filter(is_show=True, is_delete=False, active__start_time__lte=datetime.now(),
active__end_time__gte=datetime.now()).order_by("-orders", "-id")
@property
def real_price(self):
"""课程的真实价格"""
# 默认真实为原价
price = self.price
active_list = self.active_list()
if len(active_list) > 0:
"""如果当前课程有参与了活动"""
active = active_list[0]
# 参与活动的价格门槛
condition = active.discount.condition
sale = active.discount.sale
self.price = float(self.price)
if self.price >= condition:
"""只有原价满足价格门槛才进行优惠计算"""
if sale == "":
"""限时免费"""
price = 0
elif sale[0] == "*":
"""限时折扣"""
price = self.price * float(sale[1:])
elif sale[0] == "-":
"""限时减免"""
if self.price - float(sale[1:]) >= 0:
price = self.price - float(sale[1:])
elif sale[0] == "满":
"""满减"""
sale_list = sale.split("\r\n")
price_list = [] # 设置一个列表,把当前课程原价满足的满减条件全部保存进去
# 把满减的每一个选项在循环中,提取条件价格和课程原价进行判断
for sale_item in sale_list:
item = sale_item[1:]
condition_price,condition_sale = item.split("-")
if self.price >= float(condition_price):
price_list.append(float(condition_sale) )
if self.price - max(price_list) >= 0:
price = self.price - max(price_list) # 课程原价 - 最大优惠
return "%.2f" % price
有一些值得关注的点,这一段代码:return self.activeprices.filter(is_show=True, is_delete=False, active__start_time__lte=datetime.now(), active__end_time__gte=datetime.now()).order_by("-orders", "-id")
- 为什么在course表中没有activeprices字段,但是却能够使用self.activeprices?
因为我们在定义discount与课程关系模型的时候,声明了以course表为外键,并且设置了related_name="activeprices"
。这么设置,能够让在主表中直接使用设置的值来查询子表中的信息。这篇博客讲的比较详细https://blog.csdn.net/hpu_yly_bj/article/details/78939748 - active__end_time__gte是什么意思?
discount与课程关系模型中有activate字段,而且也是作为这个表的外键,可以通过"_X_"的方法来查询activate中的X字段,而gte又表示大于等于的意思。所以,整句话就是查询活动时间在活动开始时间到活动结束时间之间的课程。
__gt 大于
__gte 大于等于
__lt 小于
__lte 小于等于