我有一个用 Django 构建的应用程序。该应用程序允许企业管理其日常运营,并具有以下功能:人力资源管理、销售、销售点、会计等
为了让企业能够在其产品上附加折扣,我创建了一个
Discount
模型:
class Discount(CommonField):
name = models.CharField(max_length=255, blank=True, null=True)
discount = models.DecimalField(max_digits=15, decimal_places=2)
discount_type = models.CharField(max_length=255, choices=DISCOUNT_TYPE_CHOICES, blank=True, null=True)
discounted_products_count = models.PositiveSmallIntegerField(default=0)
start_date = models.DateTimeField(blank=True, null=True)
expiry_date = models.DateTimeField(blank=True, null=True)
status = models.CharField(max_length=255, default="inactive", choices=DISCOUNT_STATUS)
objects = DiscountModelManager()
折扣具有已包含在模型中的开始日期和到期日期,它们还有一个状态字段,用于确定状态是过期、活动还是非活动。
我面临的挑战之一是我应该在什么时候更新折扣状态。为了克服这一挑战,我创建了一个 Model Manager 来放置更新状态逻辑的中心位置;
class DiscountModelManager(TenantAwareManager):
def get_queryset(self):
queryset = super().get_queryset()
self.change_promo_code_if_end_date_extended(queryset)
self.activate_discount_if_start_date_reached(queryset)
self.expire_discount_if_expiry_date_reached(queryset)
return super().get_queryset()
def change_promo_code_if_end_date_extended(self, queryset):
"""
Activates promo codes if expiry_date has been extended and the status is expired.
"""
queryset.filter(expiry_date__gte=timezone.now(), status="expired").update(status="active")
def activate_discount_if_start_date_reached(self, queryset):
"""
Activates promo codes if start_date has been reached and the status is inactive.
"""
queryset.filter(start_date__lte=timezone.now(), status="inactive").update(status="active")
def expire_discount_if_expiry_date_reached(self, queryset):
queryset.filter(expiry_date__lte=timezone.now()).update(status="expired")
目前我对折扣有四种看法:
Dicount List View
:列出属于该特定业务的所有折扣(带分页)。Detail Discount View
:显示折扣的详细视图。Edit Discount View
:用户可以在详细视图中查看折扣后编辑折扣Point of Sale View
:进行促销并且结帐时可以使用折扣的地方。代码运行得非常好,除了我还有一个问题......
如果我们看一下折扣列表视图:
class DiscountListView(LoginRequiredMixin, View):
def get(self, request):
business_id = current_business_id()
discounts = Discount.objects.filter(business__business_id=business_id)
paginator = Paginator(discounts, 25)
page_number = request.GET.get("page")
page = paginator.get_page(page_number)
context = {
"table_headers": HTMLTemplateTags().table_headers["discounts"],
"page": page,
"search_query": search_query,
"paginator": paginator,
}
return render(request, "pages/sales/discounts/discount_list.html", context)
您可以看到我们有一个
queryset
,我们正在其中获取属于当前业务的所有折扣
discounts = Discount.objects.filter(business__business_id=business_id)
在此查询集完成之前,会调用
DiscountModelManager
并更新状态。现在,这不是一个大问题,因为我们才刚刚开始,但是一旦我们的表中有数百万个折扣,那么这种方法根本就不是最佳的,因为属于特定业务的所有折扣都会更新。
有没有更好、更有效的方法来解决这个问题?
我认为让该字段具有时间敏感性更好不。只需在需要时确定状态即可,例如:
from django.db.models.functions import Now
class ActiveDiscountManager(models.Manager):
def get_queryset(self, *args, **kwargs):
return (
super()
.get_queryset(*args, **kwargs)
.filter(start_date__gte=Now(), expiry_date__lte=Now())
)
class Discount(CommonField):
start_date = models.DateTimeField(blank=True, null=True, db_index=True)
expiry_date = models.DateTimeField(blank=True, null=True, db_index=True)
# no status field
objects = models.Manager()
active = ActiveDiscountManager()
@property
def status(self):
if self.start_date is None or self.start_date < datetime.now():
return 'inactive'
elif self.end_date and self.end_date < datetime.now():
return 'expired'
else:
return active
因此,我们不会更改状态或存储它,我们只是在需要时确定它。这也意味着,如果
end_date
延长或缩短,过滤将自动包含/排除 Discount
以及 Discount.active.all()
项目。