[Django]-Realizing rating in django

2πŸ‘

βœ…

You should either go with a through field like this:

class Story(models.Model):
    rates = models.ManyToManyField(User, through=Rating)

class Rating(models.Model):
    rate = models.FloatField(validators=[MinValueValidator(0.0), MaxValueValidator(10.0)])
    story = models.ForeignKey(Story, on_delete=models.CASCADE)
    user = models.ForeignKey(User, on_delete=models.CASCADE)

or you can do it your way with a separate model which in this case your either should remove the rate field from Story model or remove the story field from Rating model:

class Story(models.Model):
    ...
    # rate = models.(help here) No need anymore

class Rating(models.Model):
    rate = models.FloatField(validators=[MinValueValidator(0.0), MaxValueValidator(10.0)])
    story = models.ForeignKey(Story, on_delete=models.CASCADE)
    user = models.ForeignKey(User, on_delete=models.CASCADE)

and your queryset will be something like this:

story.rating_set.all()

Which will include all the ratings for the selected story instance.

πŸ‘€Navid Zarepak

4πŸ‘

As @Liudvikas Bajarunas said, it’s enough to define story as a foreign key on the Rating model. You can access the story ratings using rating_set:

story_ratings = story.rating_set.all()

See the documentation on following relationships backwards for more info.

You can combine that approach with aggregation to get the average rating of a story:

class Story(models.Model):
    ...
    @property
    def average_rating(self):
        return self.rating_set.all().aggregate(Avg('rate'))['rate__avg']
πŸ‘€Pierre V.

4πŸ‘

There are some improvements that you can make:

  1. It is better to refer to the user model with the AUTH_USER_MODEL setting [Django-doc] to refer to the user model, since you can later change your mind about it;
  2. You probably want to make user and story unique together, such that a user can not make two ratings for the same story;
  3. some databases, like PostgreSQL allow us to enforce range constraints at the database level, and thus make it more safe.

we thus can rewrite this to:

from django.conf import settings
from django.db import models
from django.db.models import CheckConstraint, Q, UniqueConstraint

class Rating(models.Model):
    rate = models.FloatField(validators=[MinValueValidator(0.0), MaxValueValidator(10.0)])
    story = models.ForeignKey(Story, on_delete=models.CASCADE)
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)

    class Meta:
        constraints = [
            CheckConstraint(check=Q(rate__range=(0, 10)), name='valid_rate'),
            UniqueConstraint(fields=['user', 'story'], name='rating_once')
        ]

Leave a comment