簡體   English   中英

Django-如何在另一個模型的創建中更新模型的字段?

[英]Django - How to update a field for a model on another model's creation?

我有兩個模型,一個產品模型和一個評級模型。 我想要完成的是,每次創建“評級”時,通過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 (創建后發布?)信號?

做這個的最好方式是什么? 我是否使用post_save(創建后發布?)信號?

我想這里的問題不在於如何從技術上做到這一點,而更多的是如何使它健壯 畢竟,創建重要的評分不僅是重要的:如果人們更改了評分或刪除了評分,那么平均評分也需要更新。 甚至有可能使用級聯定義ForeignKey ,然后刪除與Rating相關的內容可能會導致刪除多個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

或者,如果要在QuerySet加載多個Product ,則可以執行.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以強制重新計算,例如使用post_save等。但是請注意,信號可以post_save (例如與.update(..)一個查詢集的,或者通過bulk_create(..)因此,它仍然是一個好主意,定期同步的平均評分。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM