Benjamin Smith Max
Benjamin Smith Max

Reputation: 2748

Get the average from list of values of foreign key object fields

I am making a movie rating app. Each movie can be rated by users.

class MovieRating(models.Model):
    movie = models.OneToOneField(Movie)
    users = models.ManyToManyField(User, through='UserRating')
    average_rating = ???

    or whichever is better

    @property
    def average_rating(self):
        ???

class UserRating(models.Model):
    movie_rating = models.ForeignKey(MovieRating)
    user = models.ForeignKey(User)
    rating = models.PositiveIntegerField(
        default=1,
        validators=[
            MinValueValidator(1),
            MaxValueValidator(10)
        ]
    )
    review = models.TextField()
    pub_date = models.DateTimeField(default=timezone.now)

    class Meta:
        unique_together = ('movie', 'user')

I want to get the average rating of for that movie. Meaning

average_rating = (Total rating by user for a MovieRating) / (Total users of MovieRating)

I can get the total users of a MovieRating by

movie_rating.users.count()

But how do I get the rating of every users in Total? Or else what can I do to get the average rating for a movie?

update:

Now I am doing this way, but I don't know whether this is the right way to go:

def average_rating(self):
    if self.users.count() == 0:
        return 0.0
    rates = 0
    rating_list = [user_rating.rating for user_rating in self.userrating_set.all()]
    for r in rating_list:
        rates = r + rates
    return rates / self.users.count()

Upvotes: 0

Views: 1033

Answers (2)

yummies
yummies

Reputation: 696

Django supports aggregation queries, including averaging. In your case you can aggregate on the related field through the object's reverse relation to its intermediary model. Since aggregations are performed natively by the database, they are generally recommended over manually calculating their values.

So in your average rating property method

from django.db.models import Avg

...

@property
def average_rating(self):
    return self.userrating_set.aggregate(Avg('rating'))['rating__avg']

Upvotes: 4

chaos
chaos

Reputation: 490

Try something like:

sum([rate.rating for rate in movie_rating.userrating_set.all()])

Upvotes: 0

Related Questions