我有两个模型,一个产品模型和一个评级模型。我想要完成的是,每次创建“评级”时,通过API POST到使用DRF创建的端点,我都想计算和更新关联产品中的average_rating
字段。
class Product(models.Model):
name = models.CharField(max_length=100)
...
average_rating = models.DecimalField(max_digits=3, decimal_places=2)
def __str__(self):
return self.name
class Rating(models.Model):
rating = models.IntegerField()
product = models.ForeignKey('Product', related_name='ratings')
def __str__(self):
return "{}".format(self.rating)
做到这一点的最佳方法是什么?我是否使用post_save
(创建后?)信号?
答案 0 :(得分:1)
做到这一点的最佳方法是什么?我是否使用post_save(创建后发布?)信号?
问题不在于我想如何从技术上做到这一点,而更多的是如何使这种健壮。毕竟,创建重要的评分不仅是重要的:如果人们更改了评分或删除了评分,那么平均评分也需要更新。甚至有可能使用级联定义ForeignKey
,然后删除与Rating
相关的内容可能会导致删除多个等级,从而更新多个Product
。因此,使平均同步变得相当困难。尤其是如果您允许其他程序来操纵数据库。
因此,计算平均评分可能更好。例如,使用聚合:
from django.db.models import Avg
class Product(models.Model):
name = models.CharField(max_length=100)
@property
def average_rating(self):
return self.ratings.aggregate(average_rating=Avg('rating'))['average_rating']
def __str__(self):
return self.name
或者,如果您想在Product
中加载多个QuerySet
,则可以执行.annotate(..)
来批量计算平均评分:
Product.objects.annotate(
average_rating=Avg('rating__rating')
)
这里Product
的属性为average_rating
,它是与相关的评分的平均评分。
如果收视率的数量可能很大,则可能需要花费大量时间来计算平均值。在这种情况下,我建议添加一个字段,并使用定期任务来更新。例如:
from django.db.models import Avg, OuterRef, Subquery
class Product(models.Model):
name = models.CharField(max_length=100)
avg_rating=models.DecimalField(
max_digits=3,
decimal_places=2,
null=True,
default=None
)
@property
def average_rating(self):
return self.avg_rating or self.ratings.aggregate(average_rating=Avg('rating'))['average_rating']
@classmethod
def update_averages(cls):
subq = cls.objects.filter(
id=OuterRef('id')
).annotate(
avg=Avg('rating__rating')
).values('avg')[:1]
cls.objects.update(
avg_rating=Subquery(subq)
)
def __str__(self):
return self.name
然后,您可以定期致电Product.update_averages()
来更新所有产品的平均评分。如果您创建,更新或删除评级,则可以将相关产品的avg_rating
字段设置为None
以强制进行重新计算,例如使用{{1} }等。但是请注意,可以绕过信号(例如,使用查询集的post_save
或使用.update(..)
),因此定期同步平均评分仍然是一个好主意。