youssefrag
youssefrag

Reputation: 11

How to count reviews for a product in django?

I am building an ecommerce website with django. In my models I have a Product and review model. How should i connect the two for the number of reviews and average rating attribute?

This is my current models file

class Product(models.Model):
    name = models.CharField(max_length=200, null=True, blank=True)
    brand = models.CharField(max_length=200, null=True, blank=True)
    image = models.ImageField(null=True, blank=True, default='placeholder.png')
    description = models.TextField(null=True, blank=True)
    rating = models.DecimalField(max_digits=7, decimal_places=2, null=True, blank=True)
    price = models.DecimalField(max_digits=7, decimal_places=2, null=True, blank=True)
    countInStock = models.IntegerField(null=True, blank=True, default=0)
    id = models.UUIDField(default=uuid.uuid4, max_length=36, unique=True, primary_key=True, editable=False)

    numReviews = [Count the number of reviews where product.id matches self.id]

    averageRating = [Sum up the ratings in reviews for this product and divide them by their count]

    def __str__(self):
        return str(self.name)


class Review(models.Model):
    product = models.ForeignKey(Product, on_delete=models.SET_NULL, null=True)
    user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
    rating = models.IntegerField(null=True, blank=True, default=0)
    comment = models.TextField(null=True, blank=True)
    createdAt = models.DateTimeField(auto_now_add=True)
    id = models.UUIDField(default=uuid.uuid4, max_length=36, unique=True, primary_key=True, editable=False)

    def __str__(self):
        return f'{self.user} review for {self.product}'

As you can see the numReviews and average rating columns are meant to connect both tables. I have been trying to figure out how to do it correctly with no success.

Any help would be greatly appreciated

Upvotes: 1

Views: 588

Answers (1)

Nealium
Nealium

Reputation: 2268

I would make them into model methods.. I don't think there will be any issues that the Review object is defined below the method

and for the Avg I used a Django command aggregate which forces the DB to do the work.

models.py

class Product(models.Model):
    name = models.CharField(max_length=200, null=True, blank=True)
    brand = models.CharField(max_length=200, null=True, blank=True)
    image = models.ImageField(null=True, blank=True, default='placeholder.png')
    description = models.TextField(null=True, blank=True)
    rating = models.DecimalField(max_digits=7, decimal_places=2, null=True, blank=True)
    price = models.DecimalField(max_digits=7, decimal_places=2, null=True, blank=True)
    countInStock = models.IntegerField(null=True, blank=True, default=0)
    id = models.UUIDField(default=uuid.uuid4, max_length=36, unique=True, primary_key=True, editable=False)

    def __str__(self):
        return str(self.name)

    def num_of_reviews(self):
        return Review.objects.filter(product=self).count()

    def average_rating(self):
        from django.db.models import Avg
        return Review.objects.filter(product=self).aggregate(Avg('rating'))['rating__avg']

Use

obj = Product.objects.all().first()
obj.num_of_reviews()
obj.average_rating()

Edit

Reverse relationship per @NixonSparrow
def num_of_reviews(self):
    return self.review_set.count()

def average_rating(self):
    from django.db.models import Avg
    return self.review_set.aggregate(Avg('rating'))['rating__avg']

Upvotes: 1

Related Questions