Tilen Kričaj
Tilen Kričaj

Reputation: 43

Django: Calculate average recipe rating

I am creating a website that lets users post recipes. When the user clicks on a recipe they can see more details about it and they can also comment and give a rating. My question is how can I calculate the average recipe rating here

Models.py

class Recept(models.Model):

    class NewManager(models.Manager):
        def get_queryset(self):
            return super().get_queryset()

    naslov = models.CharField(max_length=100)
    sestavine = models.CharField(max_length=100)
    priprava = models.TextField()
    rec_img = models.ImageField(upload_to='rec_pics', default='default2.jpg')
    datum = models.DateTimeField(default=timezone.now)
    avtor = models.ForeignKey(User, on_delete=models.CASCADE)
    category = models.ForeignKey(Category, on_delete=models.PROTECT, default=1)
    likes = models.ManyToManyField(
        User, related_name="blog_recept", blank=True)
    favorites = models.ManyToManyField(
        User, related_name='favorite', default=None, blank=True)
    newmanager = NewManager()


CHOICES = (
    (1, '1 stars'),
    (2, '2 stars'),
    (3, '3 stars'),
    (4, '4 stars'),
    (5, '5 stars'),

)


class Reviews(models.Model):
    recept = models.ForeignKey(
        Recept, related_name="reviews", on_delete=models.CASCADE)
    user = models.ForeignKey(
        User, related_name="reviews", on_delete=models.CASCADE)
    content = models.TextField(blank=True, null=True)
    stars = models.IntegerField(choices=CHOICES)
    datum = models.DateTimeField(default=timezone.now)

Views.py

class PostDetailView(FormMixin, DetailView):
    model = Recept
    form_class = CommentForm

    def get_success_url(self):
        return reverse('recept-detail', kwargs={'pk': self.object.id})

    def get_context_data(self, **kwargs):

        context = super(PostDetailView, self).get_context_data(**kwargs)

        get_recept = get_object_or_404(Recept, id=self.kwargs['pk'])

        fav = bool
        if get_recept.favorites.filter(id=self.request.user.id).exists():
            fav = True
        total_sestavine = get_recept.vejice()
        total_likes = get_recept.total_likes()
        total_likes2 = get_recept.total_likes2()  # pokliče functions

        liked = False
        if get_recept.likes.filter(id=self.request.user.id).exists():
            liked = True
        if get_recept.likes.exists():
            last_like = get_recept.last_like()
            context['last_like'] = last_like
        else:
            context['last_like'] = 0

        context['total_likes'] = total_likes
        context['total_likes2'] = total_likes2
        context['liked'] = liked
        context['fav'] = fav
        context['total_sestavine'] = total_sestavine
        context['form'] = CommentForm(
            initial={'recept': self.object, 'user': self.request.user})
        return context

    def post(self, request, *args, **kwargs):
        self.object = self.get_object()
        form = self.get_form()
        if form.is_valid():
            return self.form_valid(form)
        else:
            return self.form_invalid(form)

    def form_valid(self, form):
        form.save()
        return super(PostDetailView, self).form_valid(form)

So for example, if a recipe has 2 reviews and one review has 3 stars other has 5 stars the average would be 4.

Upvotes: 0

Views: 308

Answers (1)

Harris Irfan
Harris Irfan

Reputation: 218

To calculate the average recipe rating for a given recipe, you can create a helper method like shown below:

from django.db.models import Avg

def calculate_average_rating(recipe):
    average_rating = Reviews.objects.all().filter(recipe=recipe).aggregate(Avg('stars'))['stars__avg']
    return average_rating

Explanation:

Django provides aggregate functions that we can use to extract average, maximum, minimum, etc. from a queryset. We have used the Avg function. The aggregate function Avg returns the average as a form of dictionary: {'stars__avg': 4} for example. To extract the actual value from the returned dictionary, we have appended ['stars__avg'] at the end.

Upvotes: 1

Related Questions