如何在Django中基于Rating模型更新Product模型的评分统计字段
Django 实现提交评分自动更新产品统计字段方案
方案一:使用Django信号(更推荐,解耦性更好)
这种方式可以统一处理评分新增、修改、删除三个场景的统计更新,不需要耦合到模型业务逻辑里。
- 首先在Rating模型所在的app下新建
signals.py,或者直接在models.py中添加如下代码:
from django.db.models.signals import post_save, post_delete from django.dispatch import receiver from django.db.models import Avg, Count from .models import Rating @receiver(post_save, sender=Rating) @receiver(post_delete, sender=Rating) def update_product_rating_stats(sender, instance, **kwargs): # 获取关联的产品对象 product = instance.product # 聚合计算当前产品的总评分数量和平均评分 rating_stats = product.product_ratings.aggregate( total_count=Count('id'), avg_rating=Avg('product_rating') ) # 更新产品字段,平均评分按IntegerField要求做四舍五入处理 product.total_rating_count = rating_stats['total_count'] product.avg_rating = round(rating_stats['avg_rating']) if rating_stats['avg_rating'] else None # 仅更新指定字段,提高效率避免触发其他无关逻辑 product.save(update_fields=['total_rating_count', 'avg_rating'])
- 如果你将信号写在单独的
signals.py中,需要到对应app的apps.py中注册信号才会生效:
from django.apps import AppConfig class YourAppNameConfig(AppConfig): default_auto_field = 'django.db.models.BigAutoField' name = '你的app名称' def ready(self): import 你的app名称.signals
方案二:重写Rating模型的save/delete方法
适合小型项目,不需要额外注册信号,直接在Rating模型中添加两个方法即可:
class Rating(models.Model): product = models.ForeignKey(Product, on_delete=models.CASCADE, related_name='product_ratings') user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='user_rating') product_rating = models.IntegerField(verbose_name='Rating', blank=False) product_comment = models.TextField(verbose_name='Comment', blank=False) def save(self, *args, **kwargs): super().save(*args, **kwargs) # 保存评分后更新产品统计 rating_stats = self.product.product_ratings.aggregate( total_count=Count('id'), avg_rating=Avg('product_rating') ) self.product.total_rating_count = rating_stats['total_count'] self.product.avg_rating = round(rating_stats['avg_rating']) if rating_stats['avg_rating'] else None self.product.save(update_fields=['total_rating_count', 'avg_rating']) def delete(self, *args, **kwargs): product = self.product super().delete(*args, **kwargs) # 删除评分后更新产品统计 rating_stats = product.product_ratings.aggregate( total_count=Count('id'), avg_rating=Avg('product_rating') ) product.total_rating_count = rating_stats['total_count'] product.avg_rating = round(rating_stats['avg_rating']) if rating_stats['avg_rating'] else None product.save(update_fields=['total_rating_count', 'avg_rating'])
注意事项
- 当前Product模型中
avg_rating为IntegerField,所以做了四舍五入取整处理,如果你需要保留小数可以将字段改为FloatField或者DecimalField,调整计算逻辑即可。 - 以上两种方案仅在单条评分增删改时生效,如果有批量操作评分的需求,需要手动调用统计更新逻辑。
内容的提问来源于stack exchange,提问作者Nahidujjaman Hridoy
相关产品推荐
相关产品推荐

