如何在Django中设计满足业务规则的促销模型
Django 套餐促销模型实现方案
核心设计思路
完全匹配现有业务规则,不侵入已有的Plan模型原有结构,将促销逻辑独立封装在单独模型中,通过外键和套餐关联,避免修改原套餐数据导致的一致性问题。
促销模型代码实现
from django.db import models from django.db.models import F from django.utils import timezone # 复用你现有代码中的Plan模型即可,导入路径替换为你的实际应用路径 # from your_app.models import Plan class PlanPromotion(models.Model): # 基础信息字段 promotionID = models.BigAutoField(primary_key=True, verbose_name="促销活动ID") promotionName = models.CharField(max_length=255, verbose_name="促销活动名称") # 关联对应套餐,一个套餐可以配置多个待生效、历史促销活动 plan = models.ForeignKey( Plan, on_delete=models.CASCADE, related_name="promotions", verbose_name="关联套餐" ) # 促销专属权益值:按照业务规则,促销仅调整权益比例 promoBenefitPercentage = models.FloatField(default=0, verbose_name="促销权益比例") # 限制规则1:参与用户数限制 userLimit = models.PositiveIntegerField( null=True, blank=True, verbose_name="可参与用户上限,留空表示不限制" ) joinedUserCount = models.PositiveIntegerField(default=0, verbose_name="已参与用户数") # 限制规则2:活动时间范围限制 startTime = models.DateTimeField(verbose_name="活动生效开始时间") endTime = models.DateTimeField(verbose_name="活动生效结束时间") # 状态字段 isActive = models.BooleanField(default=True, verbose_name="是否启用活动") createTime = models.DateTimeField(auto_now_add=True, verbose_name="创建时间") updateTime = models.DateTimeField(auto_now=True, verbose_name="更新时间") def __str__(self): return f"{self.promotionName} | 关联套餐:{self.plan.planName}" def is_available(self): """判断当前促销是否处于可参与状态""" now = timezone.now() # 活动未启用直接返回不可用 if not self.isActive: return False # 不在活动时间窗口内返回不可用 if not (self.startTime <= now <= self.endTime): return False # 配置了用户上限且已达上限返回不可用 if self.userLimit is not None and self.joinedUserCount >= self.userLimit: return False return True class Meta: verbose_name = "套餐促销活动" verbose_name_plural = verbose_name
字段与逻辑说明
- 关联设计:通过外键绑定
Plan模型,配置related_name="promotions"后,可以直接通过plan_instance.promotions.all()查询某个套餐下的所有促销配置。 - 权益存储:独立设置
promoBenefitPercentage字段存储促销期的权益比例,不直接修改原Plan模型的benefitPercentage字段。如果直接修改原套餐字段,促销结束后需要手动回滚数据,还可能出现时间差导致非活动时段的用户误享促销的问题,独立存储可以保证原套餐基础数据不受活动影响。 - 人数限制:
userLimit字段留空时表示不限制参与人数,搭配joinedUserCount统计实际参与量,判断是否达到参与上限。 - 时间限制:通过
startTime、endTime配置活动生效的时间窗口,精确到时分秒,支持任意时段的活动配置。 - 状态判断:封装
is_available()方法统一处理促销生效判断逻辑,业务层不需要重复编写判断条件,直接调用方法即可确认促销是否可参与。
业务使用参考
- 配置促销:品牌合作方选择已有套餐后,传入促销名称、促销权益比例、用户上限、生效时间段即可完成促销创建。
- 权益计算:用户访问套餐时,先查询对应套餐下所有启用的促销,调用
is_available()筛选出当前生效的促销,如果存在有效促销则使用promoBenefitPercentage作为实际权益比例,没有有效促销时使用原Plan的benefitPercentage即可。 - 参与计数:用户成功参与促销活动后,使用F表达式原子更新参与人数,避免并发场景下计数不准、超出人数限制的问题,示例代码:
PlanPromotion.objects.filter(promotionID=target_promo_id).update( joinedUserCount=F('joinedUserCount') + 1 )
如果业务要求同一套餐同一时间段只能存在一个生效促销,可以在模型保存时增加时间范围重叠校验,或者在Meta中添加对应的数据库约束即可。
内容的提问来源于stack exchange,提问作者bugFixer
相关产品推荐
相关产品推荐

