I have an application built in Django. The application allows businesses to manage their day-to-day operations and has features like; HR Management, Sales, Point of Sale, Accounting, etc.
For businesses to be able to attach discounts on their products, I have created a Discount
model:
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()
Discounts have a start date and an expiry date which have been included in the model, they also have a status field that will determine if the status is expired, active, or inactive.
One of the challenges I was facing had to do with at what point I should update the status of discounts. To overcome this challenge, I created a Model Manager to have a central place where the logic of updating the status is placed;
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")
I have four views at the moment that are dealing with the discounts:
Dicount List View
: Where all the discounts are listed belonging to that particular business (with pagination).Detail Discount View
: Where a detail view of the discount is shown.Edit Discount View
: Where a user can edit the discount after viewing it in the detail viewPoint of Sale View
: Where a sale is taken place and a discount may be used during checkout.
The code is working perfectly well except that I have one more issue…
If we have a look at the Discount List 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)
You can see that we have a queryset
where we are fetching all the discounts belonging to the current business
discounts = Discount.objects.filter(business__business_id=business_id)
Before this queryset is completed, DiscountModelManager
is called and the statuses are updated. Right now, it’s not a big problem as we are just starting out, but once we have millions of discounts in the table, then this approach is not optimal at all as all the discounts belonging to a particular business are updated.
Is there a better and more efficient way to approach this?