Django REST电商:分类与商品优惠功能实现技术求助
实现分类优惠与商品单独优惠的落地方案
1. 先确认模型的核心字段(补全必要逻辑)
假设你的基础模型结构如下,先确保优惠模型包含生效状态判断:
from django.db import models from django.utils import timezone class Category(models.Model): name = models.CharField(max_length=100) # 其他字段... class Product(models.Model): category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name='products') name = models.CharField(max_length=200) original_price = models.DecimalField(max_digits=10, decimal_places=2) # 其他字段... class CategoryOffer(models.Model): category = models.OneToOneField(Category, on_delete=models.CASCADE) discount_percent = models.IntegerField(default=0) # 折扣百分比,比如10代表10% off start_date = models.DateTimeField() end_date = models.DateTimeField() @property def is_active(self): return self.start_date <= timezone.now() <= self.end_date class ProductOffer(models.Model): product = models.OneToOneField(Product, on_delete=models.CASCADE) discount_percent = models.IntegerField(default=0) start_date = models.DateTimeField() end_date = models.DateTimeField() @property def is_active(self): return self.start_date <= timezone.now() <= self.end_date
2. 在Product模型中添加实时计算最终价格的方法
核心逻辑:商品优惠优先于分类优惠,都没有则用原价。直接在模型里写方法,确保任何地方调用都能拿到正确价格:
class Product(models.Model): # 原有字段... @property def final_price(self): # 先检查商品自身是否有生效的优惠 try: product_offer = self.productoffer if product_offer.is_active and product_offer.discount_percent > 0: discount = self.original_price * (product_offer.discount_percent / 100) return self.original_price - discount except ProductOffer.DoesNotExist: pass # 再检查所属分类是否有生效的优惠 try: category_offer = self.category.categoryoffer if category_offer.is_active and category_offer.discount_percent > 0: discount = self.original_price * (category_offer.discount_percent / 100) return self.original_price - discount except CategoryOffer.DoesNotExist: pass # 无优惠返回原价 return self.original_price
3. 序列化器中暴露最终价格字段
在Django REST的序列化器里,直接把final_price作为只读字段返回:
from rest_framework import serializers class ProductSerializer(serializers.ModelSerializer): final_price = serializers.DecimalField(max_digits=10, decimal_places=2, read_only=True) class Meta: model = Product fields = ['id', 'name', 'original_price', 'final_price', 'category']
4. 查询优化(避免N+1数据库问题)
如果批量查询商品,直接用prefetch_related关联优惠数据,减少数据库查询次数:
# 在视图中查询商品时 products = Product.objects.all().prefetch_related('productoffer', 'category__categoryoffer')
常见报错排查
- 如果出现
RelatedObjectDoesNotExist:是因为没有给商品/分类创建优惠时直接调用self.productoffer,所以必须用try-except捕获,或者用self.productoffer_set.first()替代(OneToOne关联用try-except更严谨)。 - 如果优惠不生效:检查
is_active方法的时间判断,确保timezone.now()和你的时区配置一致(在settings.py里设置USE_TZ = True并配置TIME_ZONE)。 - 如果序列化器返回字段错误:确保
final_price在序列化器的fields列表中,并且已声明为只读字段。
内容的提问来源于stack exchange,提问作者Sins97
相关产品推荐
相关产品推荐

