Nahidujjaman Hridoy
Nahidujjaman Hridoy

Reputation: 2217

How to update a modelfield based on another model in Django?

I have two models in Django.

One for the products and another is for ratings.

Product Model:

class Product(models.Model):
    ...
    product_name = models.CharField(max_length=264, blank=False, verbose_name='Product Name')
    product_slug = models.SlugField(blank=True)
   
    avg_rating = models.IntegerField(blank=True, null=True, verbose_name='Average Rating', help_text='Do not Edit!')
    total_rating_count = models.IntegerField(blank=True, null=True, verbose_name='Total Rating', help_text='Do not Edit!')

    def save(self, *args, **kwargs):
        ...
        super(Product, self).save(*args, **kwargs)

    def __str__(self):
        return self.product_name

Rating Model:

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)

What I want to do is that when a user rates a product, I want to update the avg_rating and total_rating_count in the Product model.

How can I do that? Thank you.

Upvotes: 0

Views: 48

Answers (1)

iri
iri

Reputation: 744

To echo what Willem has said, I would remove those fields and calculate them like this:

from django.db.models import Avg

class Product(models.Model):
    ...
    product_name = models.CharField(max_length=264, blank=False, verbose_name='Product Name')
    product_slug = models.SlugField(blank=True)

    @property
    def avg_rating(self):
        return self.product_ratings.all().aggregate(Avg('product_rating'))['product_rating__avg']

    @property
    def total_rating_count(self):
        return self.product_ratings.all().count()

You can then access this information like any other attribute:

product = Product.objects.first()
product.avg_rating

I would consider using a cached_property for performance considerations.

Upvotes: 2

Related Questions